All of lore.kernel.org
 help / color / mirror / Atom feed
From: Miquel Raynal <miquel.raynal@bootlin.com>
To: Richard Weinberger <richard@nod.at>,
	Vignesh Raghavendra <vigneshr@ti.com>,
	Tudor Ambarus <Tudor.Ambarus@microchip.com>,
	Pratyush Yadav <p.yadav@ti.com>, Michael Walle <michael@walle.cc>,
	<linux-mtd@lists.infradead.org>
Cc: Julien Su <juliensu@mxic.com.tw>,
	Jaime Liao <jaimeliao@mxic.com.tw>,
	Thomas Petazzoni <thomas.petazzoni@bootlin.com>
Subject: Re: [PATCH v2 8/9] mtd: spi-nor: Enhance locking to support reads while writes
Date: Thu, 1 Dec 2022 11:54:40 +0100	[thread overview]
Message-ID: <20221201115440.496c8544@xps-13> (raw)
In-Reply-To: <20221110155513.819798-9-miquel.raynal@bootlin.com>

Hello,

miquel.raynal@bootlin.com wrote on Thu, 10 Nov 2022 16:55:12 +0100:

> On devices featuring several banks, the Read While Write (RWW) feature
> is here to improve the overall performance when performing parallel
> reads and writes at different locations (different banks). The following
> constraints have to be taken into account:
> 1#: A single operation can be performed in a given bank.
> 2#: Only a single program or erase operation can happen on the entire chip.
> 3#: The I/O bus is unique and thus is the most constrained resource, all
>     spi-nor operations requiring access to the spi bus (through the spi
>     controller) must be serialized until the bus exchanges are over. So
>     we must ensure a single operation can be "sent" at a time.
> 4#: Any other operation that would not be either a read or a write or an
>     erase is considered requiring access to the full chip and cannot be
>     parallelized, we then need to ensure the full chip is in the idle
>     state when this occurs.
> 
> All these constraints can easily be managed with a proper locking model:
> 1#: Is protected by a per-bank mutex. Only a single operation can happen
>     in a specific bank at any times. If the bank mutex is not available,
>     the operation cannot start.

I just discovered and fixed a bug in this version related to the erase
path, the indexes (at/len) may in some cases be updated by the
function, leading to the locks not being released as expected. I just
discovered that while playing with UBI. I will soon provide a v3 with
this fixed (I am testing now with io_paral which seems happy).

Thanks,
Miquèl

> 2#: Is handled by the pe_mode mutex which is acquired before any write
>     or erase, and is released only at the very end of the
>     operation. This way, no other destructive operation on the chip can
>     start during this time frame.
> 3#: A device-wide mutex is introduced in order to capture and serialize
>     bus accessed. This is the one being released "sooner" than before,
>     because we only need to protect the chip against other SPI accesses
>     during the I/O phase, which for the destructive operations is the
>     beginning of the operation (when we send the command cycles and
>     eventually the data), while the second part of the operation (the
>     erase delay or the programmation delay) is when we can do something
>     else with another bank.
> 4#: Is handled by the "generic" helpers which existed before, where
>     basically all the locks are taken before the operation can start,
>     and all locks are released once done.
> 
> As many devices still do not support this feature, the original lock is
> also kept in a union: either the feature is available and we initialize
> and use the new locks, or it is not and we keep using the previous
> logic.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> ---
>  drivers/mtd/spi-nor/core.c  | 145 ++++++++++++++++++++++++++++++++----
>  include/linux/mtd/spi-nor.h |  12 ++-
>  2 files changed, 143 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
> index dfda350d5056..b467d5bf0f2a 100644
> --- a/drivers/mtd/spi-nor/core.c
> +++ b/drivers/mtd/spi-nor/core.c
> @@ -1086,6 +1086,61 @@ static void spi_nor_unprep(struct spi_nor *nor)
>  		nor->controller_ops->unprepare(nor);
>  }
>  
> +static bool spi_nor_parallel_locking(struct spi_nor *nor)
> +{
> +	return nor->info->n_banks > 1 && nor->info->no_sfdp_flags & SPI_NOR_RWW;
> +}
> +
> +static void spi_nor_lock_all_banks(struct spi_nor *nor)
> +{
> +	int i;
> +
> +	for (i = 0; i < nor->info->n_banks; i++)
> +		mutex_lock(&nor->locks.bank[i]);
> +}
> +
> +static void spi_nor_unlock_all_banks(struct spi_nor *nor)
> +{
> +	int i;
> +
> +	for (i = nor->info->n_banks; i >= 0; i--)
> +		mutex_unlock(&nor->locks.bank[i]);
> +}
> +
> +static void spi_nor_lock_banks(struct spi_nor *nor, loff_t start, size_t len)
> +{
> +	int first, last, i;
> +
> +	first = DIV_ROUND_DOWN_ULL(start, nor->params->bank_size);
> +	last = DIV_ROUND_DOWN_ULL(start + len - 1, nor->params->bank_size);
> +
> +	for (i = first; i <= last; i++)
> +		mutex_lock(&nor->locks.bank[i]);
> +}
> +
> +static void spi_nor_unlock_banks(struct spi_nor *nor, loff_t start, size_t len)
> +{
> +	int first, last, i;
> +
> +	first = DIV_ROUND_DOWN_ULL(start, nor->params->bank_size);
> +	last = DIV_ROUND_DOWN_ULL(start + len - 1, nor->params->bank_size);
> +
> +	for (i = last; i >= first; i--)
> +		mutex_unlock(&nor->locks.bank[i]);
> +}
> +
> +static void spi_nor_lock_device(struct spi_nor *nor)
> +{
> +	if (spi_nor_parallel_locking(nor))
> +		mutex_lock(&nor->locks.device);
> +}
> +
> +static void spi_nor_unlock_device(struct spi_nor *nor)
> +{
> +	if (spi_nor_parallel_locking(nor))
> +		mutex_unlock(&nor->locks.device);
> +}
> +
>  /* Generic helpers for internal locking and serialization */
>  int spi_nor_lock_and_prep(struct spi_nor *nor)
>  {
> @@ -1095,14 +1150,26 @@ int spi_nor_lock_and_prep(struct spi_nor *nor)
>  	if (ret)
>  		return ret;
>  
> -	mutex_lock(&nor->lock);
> +	if (!spi_nor_parallel_locking(nor)) {
> +		mutex_lock(&nor->lock);
> +	} else {
> +		spi_nor_lock_all_banks(nor);
> +		mutex_lock(&nor->locks.pe_mode);
> +		mutex_lock(&nor->locks.device);
> +	}
>  
>  	return 0;
>  }
>  
>  void spi_nor_unlock_and_unprep(struct spi_nor *nor)
>  {
> -	mutex_unlock(&nor->lock);
> +	if (!spi_nor_parallel_locking(nor)) {
> +		mutex_unlock(&nor->lock);
> +	} else {
> +		mutex_unlock(&nor->locks.device);
> +		mutex_unlock(&nor->locks.pe_mode);
> +		spi_nor_unlock_all_banks(nor);
> +	}
>  
>  	spi_nor_unprep(nor);
>  }
> @@ -1116,14 +1183,24 @@ static int spi_nor_lock_and_prep_pe(struct spi_nor *nor, loff_t start, size_t le
>  	if (ret)
>  		return ret;
>  
> -	mutex_lock(&nor->lock);
> +	if (!spi_nor_parallel_locking(nor)) {
> +		mutex_lock(&nor->lock);
> +	} else {
> +		spi_nor_lock_banks(nor, start, len);
> +		mutex_lock(&nor->locks.pe_mode);
> +	}
>  
>  	return 0;
>  }
>  
>  static void spi_nor_unlock_and_unprep_pe(struct spi_nor *nor, loff_t start, size_t len)
>  {
> -	mutex_unlock(&nor->lock);
> +	if (!spi_nor_parallel_locking(nor)) {
> +		mutex_unlock(&nor->lock);
> +	} else {
> +		mutex_unlock(&nor->locks.pe_mode);
> +		spi_nor_unlock_banks(nor, start, len);
> +	}
>  
>  	spi_nor_unprep(nor);
>  }
> @@ -1137,14 +1214,20 @@ static int spi_nor_lock_and_prep_rd(struct spi_nor *nor, loff_t start, size_t le
>  	if (ret)
>  		return ret;
>  
> -	mutex_lock(&nor->lock);
> +	if (!spi_nor_parallel_locking(nor))
> +		mutex_lock(&nor->lock);
> +	else
> +		spi_nor_lock_banks(nor, start, len);
>  
>  	return 0;
>  }
>  
>  static void spi_nor_unlock_and_unprep_rd(struct spi_nor *nor, loff_t start, size_t len)
>  {
> -	mutex_unlock(&nor->lock);
> +	if (!spi_nor_parallel_locking(nor))
> +		mutex_unlock(&nor->lock);
> +	else
> +		spi_nor_unlock_banks(nor, start, len);
>  
>  	spi_nor_unprep(nor);
>  }
> @@ -1451,11 +1534,16 @@ static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64 addr, u32 len)
>  			dev_vdbg(nor->dev, "erase_cmd->size = 0x%08x, erase_cmd->opcode = 0x%02x, erase_cmd->count = %u\n",
>  				 cmd->size, cmd->opcode, cmd->count);
>  
> +			spi_nor_lock_device(nor);
> +
>  			ret = spi_nor_write_enable(nor);
> -			if (ret)
> +			if (ret) {
> +				spi_nor_unlock_device(nor);
>  				goto destroy_erase_cmd_list;
> +			}
>  
>  			ret = spi_nor_erase_sector(nor, addr);
> +			spi_nor_unlock_device(nor);
>  			if (ret)
>  				goto destroy_erase_cmd_list;
>  
> @@ -1508,11 +1596,16 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
>  	if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
>  		unsigned long timeout;
>  
> +		spi_nor_lock_device(nor);
> +
>  		ret = spi_nor_write_enable(nor);
> -		if (ret)
> +		if (ret) {
> +			spi_nor_unlock_device(nor);
>  			goto erase_err;
> +		}
>  
>  		ret = spi_nor_erase_chip(nor);
> +		spi_nor_unlock_device(nor);
>  		if (ret)
>  			goto erase_err;
>  
> @@ -1537,11 +1630,16 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
>  	/* "sector"-at-a-time erase */
>  	} else if (spi_nor_has_uniform_erase(nor)) {
>  		while (len) {
> +			spi_nor_lock_device(nor);
> +
>  			ret = spi_nor_write_enable(nor);
> -			if (ret)
> +			if (ret) {
> +				spi_nor_unlock_device(nor);
>  				goto erase_err;
> +			}
>  
>  			ret = spi_nor_erase_sector(nor, addr);
> +			spi_nor_unlock_device(nor);
>  			if (ret)
>  				goto erase_err;
>  
> @@ -1733,11 +1831,13 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
>  			size_t *retlen, u_char *buf)
>  {
>  	struct spi_nor *nor = mtd_to_spi_nor(mtd);
> +	loff_t from_lock = from;
> +	size_t len_lock = len;
>  	ssize_t ret;
>  
>  	dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
>  
> -	ret = spi_nor_lock_and_prep_rd(nor, from, len);
> +	ret = spi_nor_lock_and_prep_rd(nor, from_lock, len_lock);
>  	if (ret)
>  		return ret;
>  
> @@ -1746,7 +1846,9 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
>  
>  		addr = spi_nor_convert_addr(nor, addr);
>  
> +		spi_nor_lock_device(nor);
>  		ret = spi_nor_read_data(nor, addr, len, buf);
> +		spi_nor_unlock_device(nor);
>  		if (ret == 0) {
>  			/* We shouldn't see 0-length reads */
>  			ret = -EIO;
> @@ -1764,7 +1866,7 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
>  	ret = 0;
>  
>  read_err:
> -	spi_nor_unlock_and_unprep_rd(nor, from, len);
> +	spi_nor_unlock_and_unprep_rd(nor, from_lock, len_lock);
>  
>  	return ret;
>  }
> @@ -1809,11 +1911,16 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
>  
>  		addr = spi_nor_convert_addr(nor, addr);
>  
> +		spi_nor_lock_device(nor);
> +
>  		ret = spi_nor_write_enable(nor);
> -		if (ret)
> +		if (ret) {
> +			spi_nor_unlock_device(nor);
>  			goto write_err;
> +		}
>  
>  		ret = spi_nor_write_data(nor, addr, page_remain, buf + i);
> +		spi_nor_unlock_device(nor);
>  		if (ret < 0)
>  			goto write_err;
>  		written = ret;
> @@ -3030,7 +3137,19 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
>  
>  	nor->info = info;
>  
> -	mutex_init(&nor->lock);
> +	if (!spi_nor_parallel_locking(nor)) {
> +		mutex_init(&nor->lock);
> +	} else {
> +		mutex_init(&nor->locks.device);
> +		mutex_init(&nor->locks.pe_mode);
> +		nor->locks.bank = kmalloc(sizeof(struct mutex) * nor->info->n_banks,
> +					  GFP_KERNEL);
> +		if (!nor->locks.bank)
> +			return -ENOMEM;
> +
> +		for (i = 0; i < nor->info->n_banks; i++)
> +			mutex_init(&nor->locks.bank[i]);
> +	}
>  
>  	/* Init flash parameters based on flash_info struct and SFDP */
>  	ret = spi_nor_init_params(nor);
> diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
> index 42218a1164f6..02c9a3cf924e 100644
> --- a/include/linux/mtd/spi-nor.h
> +++ b/include/linux/mtd/spi-nor.h
> @@ -344,6 +344,9 @@ struct spi_nor_flash_parameter;
>   * struct spi_nor - Structure for defining the SPI NOR layer
>   * @mtd:		an mtd_info structure
>   * @lock:		the lock for the read/write/erase/lock/unlock operations
> + * @locks.device:	the lock for the I/O bus
> + * @locks.pe_mode:	the lock for the program/erase mode for RWW operations
> + * @locks.bank:		the lock for every available bank
>   * @dev:		pointer to an SPI device or an SPI NOR controller device
>   * @spimem:		pointer to the SPI memory device
>   * @bouncebuf:		bounce buffer used when the buffer passed by the MTD
> @@ -374,7 +377,14 @@ struct spi_nor_flash_parameter;
>   */
>  struct spi_nor {
>  	struct mtd_info		mtd;
> -	struct mutex		lock;
> +	union {
> +		struct mutex	lock;
> +		struct {
> +			struct mutex device;
> +			struct mutex pe_mode;
> +			struct mutex *bank;
> +		} locks;
> +	};
>  	struct device		*dev;
>  	struct spi_mem		*spimem;
>  	u8			*bouncebuf;


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

  reply	other threads:[~2022-12-01 10:55 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-11-10 15:55 [PATCH v2 0/9] mtd: spi-nor: read while write support Miquel Raynal
2022-11-10 15:55 ` [PATCH v2 1/9] mtd: spi-nor: Create macros to define chip IDs and geometries Miquel Raynal
2022-11-20 15:57   ` Pratyush Yadav
2022-11-10 15:55 ` [PATCH v2 2/9] mtd: spi-nor: Introduce the concept of bank Miquel Raynal
2022-11-20 16:11   ` Pratyush Yadav
2022-11-21  8:26     ` Miquel Raynal
2022-11-10 15:55 ` [PATCH v2 3/9] mtd: spi-nor: Add a macro to define more banks Miquel Raynal
2022-11-20 16:13   ` Pratyush Yadav
2022-11-10 15:55 ` [PATCH v2 4/9] mtd: spi-nor: Reorder the preparation vs locking steps Miquel Raynal
2022-11-10 15:55 ` [PATCH v2 5/9] mtd: spi-nor: Separate preparation and locking Miquel Raynal
2022-11-10 15:55 ` [PATCH v2 6/9] mtd: spi-nor: Prepare the introduction of a new locking mechanism Miquel Raynal
2022-11-10 15:55 ` [PATCH v2 7/9] mtd: spi-nor: Add a RWW flag Miquel Raynal
2022-11-10 15:55 ` [PATCH v2 8/9] mtd: spi-nor: Enhance locking to support reads while writes Miquel Raynal
2022-12-01 10:54   ` Miquel Raynal [this message]
2022-11-10 15:55 ` [PATCH v2 9/9] mtd: spi-nor: macronix: Add support for mx25uw51245g with RWW Miquel Raynal
2022-11-20 15:41 ` [PATCH v2 0/9] mtd: spi-nor: read while write support Pratyush Yadav
2022-11-23 17:19   ` Miquel Raynal

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20221201115440.496c8544@xps-13 \
    --to=miquel.raynal@bootlin.com \
    --cc=Tudor.Ambarus@microchip.com \
    --cc=jaimeliao@mxic.com.tw \
    --cc=juliensu@mxic.com.tw \
    --cc=linux-mtd@lists.infradead.org \
    --cc=michael@walle.cc \
    --cc=p.yadav@ti.com \
    --cc=richard@nod.at \
    --cc=thomas.petazzoni@bootlin.com \
    --cc=vigneshr@ti.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.