All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/8] mtd: spi-nor: read while write support
@ 2023-02-01 11:35 ` Miquel Raynal
  0 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-02-01 11:35 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel, Miquel Raynal

Hello folks,

Here is the follow-up of the RFC trying to bring a little bit of
parallelism to support SPI-NOR Read While Write feature on parts
supporting it and featuring several banks.

I have received some hardware to make it work, so since the RFC, the
series has been updated to fix my mistakes, but the overall idea is the
same.

There is nothing Macronix specific in the implementation, the operations
and opcodes are exactly the same as before. The only difference being:
we may consider the chip usable when it is in the busy state during a
write or an erase. Any chip with an internal split allowing to perform
parallel operations might possibly leverage the benefits of this
implementation.

The first patches are just refactoring and preparation work, there is
almost no functional change, it's just a way to prepare the introduction
of the new locking mechanism and hopefully provide the cleanest and
simplest diff possible for this new feature. The actual change is all
contained in "mtd: spi-nor: Enhance locking to support reads while
writes". The logic is described in the commit log and copy/pasted here
for clarity:

"
    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 (common hardware limitation to limit costs)
    3#: Reads must remain serialized even though reads on different banks
        might occur at the same time.
    4#: 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.
    5#: 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 enforced by a bitfield of the in-use banks, so that only a single
        operation can happen in a specific bank at any time.
    2#: Is handled by the ongoing_pe boolean which is set 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#: An ongoing_rd boolean allows to track the ongoing reads, so that
        only one can be performed at a time.
    4#: An ongoing_io boolean 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 possibly the data), while the
        second part of the operation (the erase delay or the
        programmation delay) is when we can do something else in another
        bank.
    5#: Is handled by the three booleans presented above, if any of them is
        set, the chip is not yet ready for the operation and must wait.
    
    All these internal variables are protected by the existing lock, so that
    changes in this structure are atomic. The serialization is handled with
    a wait queue."

Here is now a benchmark with a Macronix MX25UW51245G with 4 banks and RWW
support:

     // Testing the two accesses in the same bank
     $ flash_speed -b0 -k0 -c10 -d /dev/mtd0
     [...]
     testing read while write latency
     read while write took 51ms, read ended after 51ms

     // Testing the two accesses within different banks
     $ flash_speed -b0 -k4096 -c10 -d /dev/mtd0
     [...]
     testing read while write latency
     read while write took 51ms, read ended after 20ms

Parallel accesses have been validated with io_paral. A slight increase
of the time spent on this test has however been noticed. With my
configuration, over a limited number of blocks, the overall operation
took 22 min without any RWW changes up to 27 min with these changes,
maybe due to the number of additional scheduling situations involved).

Here is a branch with the mtd-utils patch bringing support for this
additional "-k" parameter in flash_speed (for the second block to use
during RWW testing), used to get the above results:
https://github.com/miquelraynal/mtd-utils/compare/master...rww

Cheers,
Miquèl

Changes in v4:
* Dropped patch 1/9 which got applied.
* s/SPI-NOR/SPI NOR/
* Turned n_banks into an u8 and moved it below in the struct to avoid
  padding.
* Updated the S3AN_INFO macro to set n_banks to 1 by default.
* Renamed the lock and prep helper to follow the order of each
  operation.
* Reworded a commit log to fit the recent changes upstream.

Changes in v3:
* Fix the bank offsets calculations by providing the same values when
  locking and when unlocking (might be changed by the functions themselves
  without use noticing).
* I completely changed the way the locking works because there was a new
  constraint: reads cannot be interrupted and status reads cannot happen
  during a read. Hence, as the multi-locks design was starting to be too
  messy, I changed the implementation to use a bunch of variables to
  track the read while write state, protected by the main spi-nor
  lock. If the internal state does not allow the operation, a sleep
  starts in a queue, until the threads are woken up after a state
  update. I know it is very verbose, I am open to suggestions.


Miquel Raynal (8):
  mtd: spi-nor: Introduce the concept of bank
  mtd: spi-nor: Add a macro to define more banks
  mtd: spi-nor: Reorder the preparation vs locking steps
  mtd: spi-nor: Separate preparation and locking
  mtd: spi-nor: Prepare the introduction of a new locking mechanism
  mtd: spi-nor: Add a RWW flag
  mtd: spi-nor: Enhance locking to support reads while writes
  mtd: spi-nor: macronix: Add support for mx25uw51245g with RWW

 drivers/mtd/spi-nor/core.c     | 396 +++++++++++++++++++++++++++++++--
 drivers/mtd/spi-nor/core.h     |  26 ++-
 drivers/mtd/spi-nor/macronix.c |   3 +
 drivers/mtd/spi-nor/xilinx.c   |   1 +
 include/linux/mtd/spi-nor.h    |  13 ++
 5 files changed, 409 insertions(+), 30 deletions(-)

-- 
2.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [PATCH v4 0/8] mtd: spi-nor: read while write support
@ 2023-02-01 11:35 ` Miquel Raynal
  0 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-02-01 11:35 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel, Miquel Raynal

Hello folks,

Here is the follow-up of the RFC trying to bring a little bit of
parallelism to support SPI-NOR Read While Write feature on parts
supporting it and featuring several banks.

I have received some hardware to make it work, so since the RFC, the
series has been updated to fix my mistakes, but the overall idea is the
same.

There is nothing Macronix specific in the implementation, the operations
and opcodes are exactly the same as before. The only difference being:
we may consider the chip usable when it is in the busy state during a
write or an erase. Any chip with an internal split allowing to perform
parallel operations might possibly leverage the benefits of this
implementation.

The first patches are just refactoring and preparation work, there is
almost no functional change, it's just a way to prepare the introduction
of the new locking mechanism and hopefully provide the cleanest and
simplest diff possible for this new feature. The actual change is all
contained in "mtd: spi-nor: Enhance locking to support reads while
writes". The logic is described in the commit log and copy/pasted here
for clarity:

"
    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 (common hardware limitation to limit costs)
    3#: Reads must remain serialized even though reads on different banks
        might occur at the same time.
    4#: 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.
    5#: 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 enforced by a bitfield of the in-use banks, so that only a single
        operation can happen in a specific bank at any time.
    2#: Is handled by the ongoing_pe boolean which is set 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#: An ongoing_rd boolean allows to track the ongoing reads, so that
        only one can be performed at a time.
    4#: An ongoing_io boolean 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 possibly the data), while the
        second part of the operation (the erase delay or the
        programmation delay) is when we can do something else in another
        bank.
    5#: Is handled by the three booleans presented above, if any of them is
        set, the chip is not yet ready for the operation and must wait.
    
    All these internal variables are protected by the existing lock, so that
    changes in this structure are atomic. The serialization is handled with
    a wait queue."

Here is now a benchmark with a Macronix MX25UW51245G with 4 banks and RWW
support:

     // Testing the two accesses in the same bank
     $ flash_speed -b0 -k0 -c10 -d /dev/mtd0
     [...]
     testing read while write latency
     read while write took 51ms, read ended after 51ms

     // Testing the two accesses within different banks
     $ flash_speed -b0 -k4096 -c10 -d /dev/mtd0
     [...]
     testing read while write latency
     read while write took 51ms, read ended after 20ms

Parallel accesses have been validated with io_paral. A slight increase
of the time spent on this test has however been noticed. With my
configuration, over a limited number of blocks, the overall operation
took 22 min without any RWW changes up to 27 min with these changes,
maybe due to the number of additional scheduling situations involved).

Here is a branch with the mtd-utils patch bringing support for this
additional "-k" parameter in flash_speed (for the second block to use
during RWW testing), used to get the above results:
https://github.com/miquelraynal/mtd-utils/compare/master...rww

Cheers,
Miquèl

Changes in v4:
* Dropped patch 1/9 which got applied.
* s/SPI-NOR/SPI NOR/
* Turned n_banks into an u8 and moved it below in the struct to avoid
  padding.
* Updated the S3AN_INFO macro to set n_banks to 1 by default.
* Renamed the lock and prep helper to follow the order of each
  operation.
* Reworded a commit log to fit the recent changes upstream.

Changes in v3:
* Fix the bank offsets calculations by providing the same values when
  locking and when unlocking (might be changed by the functions themselves
  without use noticing).
* I completely changed the way the locking works because there was a new
  constraint: reads cannot be interrupted and status reads cannot happen
  during a read. Hence, as the multi-locks design was starting to be too
  messy, I changed the implementation to use a bunch of variables to
  track the read while write state, protected by the main spi-nor
  lock. If the internal state does not allow the operation, a sleep
  starts in a queue, until the threads are woken up after a state
  update. I know it is very verbose, I am open to suggestions.


Miquel Raynal (8):
  mtd: spi-nor: Introduce the concept of bank
  mtd: spi-nor: Add a macro to define more banks
  mtd: spi-nor: Reorder the preparation vs locking steps
  mtd: spi-nor: Separate preparation and locking
  mtd: spi-nor: Prepare the introduction of a new locking mechanism
  mtd: spi-nor: Add a RWW flag
  mtd: spi-nor: Enhance locking to support reads while writes
  mtd: spi-nor: macronix: Add support for mx25uw51245g with RWW

 drivers/mtd/spi-nor/core.c     | 396 +++++++++++++++++++++++++++++++--
 drivers/mtd/spi-nor/core.h     |  26 ++-
 drivers/mtd/spi-nor/macronix.c |   3 +
 drivers/mtd/spi-nor/xilinx.c   |   1 +
 include/linux/mtd/spi-nor.h    |  13 ++
 5 files changed, 409 insertions(+), 30 deletions(-)

-- 
2.34.1


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

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [PATCH v4 1/8] mtd: spi-nor: Introduce the concept of bank
  2023-02-01 11:35 ` Miquel Raynal
@ 2023-02-01 11:35   ` Miquel Raynal
  -1 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-02-01 11:35 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel, Miquel Raynal

SPI NOR chips are made of pages, which gathered in small groups make
(erase) sectors. Sectors, gathered together, make banks inside the
chip. So far there was only one bank per device supported, but we are
about to introduce support for new chips featuring several banks (up to
4 so far) where different operations may happen in parallel.

Let's allow describing these additional bank parameters, and let's do
this independently of any other value (like the number of sectors) with
an absolute value.

By default we consider that all chips have a single bank.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Reviewed-by: Pratyush Yadav <pratyush@kernel.org>
---
 drivers/mtd/spi-nor/core.c   |  3 ++-
 drivers/mtd/spi-nor/core.h   | 18 ++++++++++++------
 drivers/mtd/spi-nor/xilinx.c |  1 +
 3 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index b500655f7937..3845de9c874c 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -2574,7 +2574,8 @@ static void spi_nor_init_default_params(struct spi_nor *nor)
 
 	/* Set SPI NOR sizes. */
 	params->writesize = 1;
-	params->size = (u64)info->sector_size * info->n_sectors;
+	params->bank_size = (u64)info->sector_size * info->n_sectors;
+	params->size = params->bank_size * info->n_banks;
 	params->page_size = info->page_size;
 
 	if (!(info->flags & SPI_NOR_NO_FR)) {
diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index f6d012e1f681..768ccd1a5a8a 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -336,7 +336,8 @@ struct spi_nor_otp {
  * by the spi_nor_fixups hooks, or dynamically when parsing the JESD216
  * Serial Flash Discoverable Parameters (SFDP) tables.
  *
- * @size:		the flash memory density in bytes.
+ * @bank_size:		the flash memory bank density in bytes.
+ * @size:		the total flash memory density in bytes.
  * @writesize		Minimal writable flash unit size. Defaults to 1. Set to
  *			ECC unit size for ECC-ed flashes.
  * @page_size:		the page size of the SPI NOR flash memory.
@@ -374,6 +375,7 @@ struct spi_nor_otp {
  * @locking_ops:	SPI NOR locking methods.
  */
 struct spi_nor_flash_parameter {
+	u64				bank_size;
 	u64				size;
 	u32				writesize;
 	u32				page_size;
@@ -434,7 +436,8 @@ struct spi_nor_fixups {
  * @id_len:         the number of bytes of ID.
  * @sector_size:    the size listed here is what works with SPINOR_OP_SE, which
  *                  isn't necessarily called a "sector" by the vendor.
- * @n_sectors:      the number of sectors.
+ * @n_sectors:      the number of sectors per bank.
+ * @n_banks:        the number of banks.
  * @page_size:      the flash's page size.
  * @addr_nbytes:    number of address bytes to send.
  *
@@ -495,6 +498,7 @@ struct flash_info {
 	unsigned sector_size;
 	u16 n_sectors;
 	u16 page_size;
+	u8 n_banks;
 	u8 addr_nbytes;
 
 	bool parse_sfdp;
@@ -540,24 +544,26 @@ struct flash_info {
 	.id = { SPI_NOR_ID_3ITEMS(_jedec_id), SPI_NOR_ID_3ITEMS(_ext_id) }, \
 	.id_len = 6
 
-#define SPI_NOR_GEOMETRY(_sector_size, _n_sectors)			\
+#define SPI_NOR_GEOMETRY(_sector_size, _n_sectors, _n_banks)		\
 	.sector_size = (_sector_size),					\
 	.n_sectors = (_n_sectors),					\
-	.page_size = 256
+	.page_size = 256,						\
+	.n_banks = (_n_banks)
 
 /* Used when the "_ext_id" is two bytes at most */
 #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors)		\
 	SPI_NOR_ID((_jedec_id), (_ext_id)),				\
-	SPI_NOR_GEOMETRY((_sector_size), (_n_sectors)),
+	SPI_NOR_GEOMETRY((_sector_size), (_n_sectors), 1),
 
 #define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors)		\
 	SPI_NOR_ID6((_jedec_id), (_ext_id)),				\
-	SPI_NOR_GEOMETRY((_sector_size), (_n_sectors)),
+	SPI_NOR_GEOMETRY((_sector_size), (_n_sectors), 1),
 
 #define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_nbytes)	\
 		.sector_size = (_sector_size),				\
 		.n_sectors = (_n_sectors),				\
 		.page_size = (_page_size),				\
+		.n_banks = 1,						\
 		.addr_nbytes = (_addr_nbytes),				\
 		.flags = SPI_NOR_NO_ERASE | SPI_NOR_NO_FR,		\
 
diff --git a/drivers/mtd/spi-nor/xilinx.c b/drivers/mtd/spi-nor/xilinx.c
index 5723157739fc..7175de8aa336 100644
--- a/drivers/mtd/spi-nor/xilinx.c
+++ b/drivers/mtd/spi-nor/xilinx.c
@@ -31,6 +31,7 @@
 		.sector_size = (8 * (_page_size)),			\
 		.n_sectors = (_n_sectors),				\
 		.page_size = (_page_size),				\
+		.n_banks = 1,						\
 		.addr_nbytes = 3,					\
 		.flags = SPI_NOR_NO_FR
 
-- 
2.34.1


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

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v4 1/8] mtd: spi-nor: Introduce the concept of bank
@ 2023-02-01 11:35   ` Miquel Raynal
  0 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-02-01 11:35 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel, Miquel Raynal

SPI NOR chips are made of pages, which gathered in small groups make
(erase) sectors. Sectors, gathered together, make banks inside the
chip. So far there was only one bank per device supported, but we are
about to introduce support for new chips featuring several banks (up to
4 so far) where different operations may happen in parallel.

Let's allow describing these additional bank parameters, and let's do
this independently of any other value (like the number of sectors) with
an absolute value.

By default we consider that all chips have a single bank.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Reviewed-by: Pratyush Yadav <pratyush@kernel.org>
---
 drivers/mtd/spi-nor/core.c   |  3 ++-
 drivers/mtd/spi-nor/core.h   | 18 ++++++++++++------
 drivers/mtd/spi-nor/xilinx.c |  1 +
 3 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index b500655f7937..3845de9c874c 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -2574,7 +2574,8 @@ static void spi_nor_init_default_params(struct spi_nor *nor)
 
 	/* Set SPI NOR sizes. */
 	params->writesize = 1;
-	params->size = (u64)info->sector_size * info->n_sectors;
+	params->bank_size = (u64)info->sector_size * info->n_sectors;
+	params->size = params->bank_size * info->n_banks;
 	params->page_size = info->page_size;
 
 	if (!(info->flags & SPI_NOR_NO_FR)) {
diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index f6d012e1f681..768ccd1a5a8a 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -336,7 +336,8 @@ struct spi_nor_otp {
  * by the spi_nor_fixups hooks, or dynamically when parsing the JESD216
  * Serial Flash Discoverable Parameters (SFDP) tables.
  *
- * @size:		the flash memory density in bytes.
+ * @bank_size:		the flash memory bank density in bytes.
+ * @size:		the total flash memory density in bytes.
  * @writesize		Minimal writable flash unit size. Defaults to 1. Set to
  *			ECC unit size for ECC-ed flashes.
  * @page_size:		the page size of the SPI NOR flash memory.
@@ -374,6 +375,7 @@ struct spi_nor_otp {
  * @locking_ops:	SPI NOR locking methods.
  */
 struct spi_nor_flash_parameter {
+	u64				bank_size;
 	u64				size;
 	u32				writesize;
 	u32				page_size;
@@ -434,7 +436,8 @@ struct spi_nor_fixups {
  * @id_len:         the number of bytes of ID.
  * @sector_size:    the size listed here is what works with SPINOR_OP_SE, which
  *                  isn't necessarily called a "sector" by the vendor.
- * @n_sectors:      the number of sectors.
+ * @n_sectors:      the number of sectors per bank.
+ * @n_banks:        the number of banks.
  * @page_size:      the flash's page size.
  * @addr_nbytes:    number of address bytes to send.
  *
@@ -495,6 +498,7 @@ struct flash_info {
 	unsigned sector_size;
 	u16 n_sectors;
 	u16 page_size;
+	u8 n_banks;
 	u8 addr_nbytes;
 
 	bool parse_sfdp;
@@ -540,24 +544,26 @@ struct flash_info {
 	.id = { SPI_NOR_ID_3ITEMS(_jedec_id), SPI_NOR_ID_3ITEMS(_ext_id) }, \
 	.id_len = 6
 
-#define SPI_NOR_GEOMETRY(_sector_size, _n_sectors)			\
+#define SPI_NOR_GEOMETRY(_sector_size, _n_sectors, _n_banks)		\
 	.sector_size = (_sector_size),					\
 	.n_sectors = (_n_sectors),					\
-	.page_size = 256
+	.page_size = 256,						\
+	.n_banks = (_n_banks)
 
 /* Used when the "_ext_id" is two bytes at most */
 #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors)		\
 	SPI_NOR_ID((_jedec_id), (_ext_id)),				\
-	SPI_NOR_GEOMETRY((_sector_size), (_n_sectors)),
+	SPI_NOR_GEOMETRY((_sector_size), (_n_sectors), 1),
 
 #define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors)		\
 	SPI_NOR_ID6((_jedec_id), (_ext_id)),				\
-	SPI_NOR_GEOMETRY((_sector_size), (_n_sectors)),
+	SPI_NOR_GEOMETRY((_sector_size), (_n_sectors), 1),
 
 #define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_nbytes)	\
 		.sector_size = (_sector_size),				\
 		.n_sectors = (_n_sectors),				\
 		.page_size = (_page_size),				\
+		.n_banks = 1,						\
 		.addr_nbytes = (_addr_nbytes),				\
 		.flags = SPI_NOR_NO_ERASE | SPI_NOR_NO_FR,		\
 
diff --git a/drivers/mtd/spi-nor/xilinx.c b/drivers/mtd/spi-nor/xilinx.c
index 5723157739fc..7175de8aa336 100644
--- a/drivers/mtd/spi-nor/xilinx.c
+++ b/drivers/mtd/spi-nor/xilinx.c
@@ -31,6 +31,7 @@
 		.sector_size = (8 * (_page_size)),			\
 		.n_sectors = (_n_sectors),				\
 		.page_size = (_page_size),				\
+		.n_banks = 1,						\
 		.addr_nbytes = 3,					\
 		.flags = SPI_NOR_NO_FR
 
-- 
2.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v4 2/8] mtd: spi-nor: Add a macro to define more banks
  2023-02-01 11:35 ` Miquel Raynal
@ 2023-02-01 11:35   ` Miquel Raynal
  -1 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-02-01 11:35 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel, Miquel Raynal

Most of the chips on the market only feature a single bank. However, new
chips may support more than a single bank, with the possibility to
parallelize some operations. Let's introduce an INFOB() macro which also
takes a n_bank parameter.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Reviewed-by: Pratyush Yadav <pratyush@kernel.org>
---
 drivers/mtd/spi-nor/core.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index 768ccd1a5a8a..888a08c37d8c 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -555,6 +555,10 @@ struct flash_info {
 	SPI_NOR_ID((_jedec_id), (_ext_id)),				\
 	SPI_NOR_GEOMETRY((_sector_size), (_n_sectors), 1),
 
+#define INFOB(_jedec_id, _ext_id, _sector_size, _n_sectors, _n_banks)	\
+	SPI_NOR_ID((_jedec_id), (_ext_id)),				\
+	SPI_NOR_GEOMETRY((_sector_size), (_n_sectors), (_n_banks)),
+
 #define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors)		\
 	SPI_NOR_ID6((_jedec_id), (_ext_id)),				\
 	SPI_NOR_GEOMETRY((_sector_size), (_n_sectors), 1),
-- 
2.34.1


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

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v4 2/8] mtd: spi-nor: Add a macro to define more banks
@ 2023-02-01 11:35   ` Miquel Raynal
  0 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-02-01 11:35 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel, Miquel Raynal

Most of the chips on the market only feature a single bank. However, new
chips may support more than a single bank, with the possibility to
parallelize some operations. Let's introduce an INFOB() macro which also
takes a n_bank parameter.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Reviewed-by: Pratyush Yadav <pratyush@kernel.org>
---
 drivers/mtd/spi-nor/core.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index 768ccd1a5a8a..888a08c37d8c 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -555,6 +555,10 @@ struct flash_info {
 	SPI_NOR_ID((_jedec_id), (_ext_id)),				\
 	SPI_NOR_GEOMETRY((_sector_size), (_n_sectors), 1),
 
+#define INFOB(_jedec_id, _ext_id, _sector_size, _n_sectors, _n_banks)	\
+	SPI_NOR_ID((_jedec_id), (_ext_id)),				\
+	SPI_NOR_GEOMETRY((_sector_size), (_n_sectors), (_n_banks)),
+
 #define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors)		\
 	SPI_NOR_ID6((_jedec_id), (_ext_id)),				\
 	SPI_NOR_GEOMETRY((_sector_size), (_n_sectors), 1),
-- 
2.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v4 3/8] mtd: spi-nor: Reorder the preparation vs locking steps
  2023-02-01 11:35 ` Miquel Raynal
@ 2023-02-01 11:35   ` Miquel Raynal
  -1 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-02-01 11:35 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel, Miquel Raynal

The ->prepare()/->unprepare() hooks are now legacy, we no longer accept
new drivers supporting them. The only remaining controllers using them
acquires a per-chip mutex, which should not interfere with the rest of
the operation done in the core. As a result, we should be safe to
reorganize these helpers to first perform the preparation, before
acquiring the core locks. This is necessary in order to be able to
improve the locking mechanism in the core (coming next). No side effects
are expected.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 drivers/mtd/spi-nor/core.c | 23 ++++++++++-------------
 1 file changed, 10 insertions(+), 13 deletions(-)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 3845de9c874c..01932dbaa5b9 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -1071,27 +1071,24 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor)
 	}
 }
 
-int spi_nor_lock_and_prep(struct spi_nor *nor)
+int spi_nor_prep_and_lock(struct spi_nor *nor)
 {
 	int ret = 0;
 
-	mutex_lock(&nor->lock);
-
-	if (nor->controller_ops &&  nor->controller_ops->prepare) {
+	if (nor->controller_ops && nor->controller_ops->prepare)
 		ret = nor->controller_ops->prepare(nor);
-		if (ret) {
-			mutex_unlock(&nor->lock);
-			return ret;
-		}
-	}
+
+	mutex_lock(&nor->lock);
+
 	return ret;
 }
 
 void spi_nor_unlock_and_unprep(struct spi_nor *nor)
 {
+	mutex_unlock(&nor->lock);
+
 	if (nor->controller_ops && nor->controller_ops->unprepare)
 		nor->controller_ops->unprepare(nor);
-	mutex_unlock(&nor->lock);
 }
 
 static u32 spi_nor_convert_addr(struct spi_nor *nor, loff_t addr)
@@ -1447,7 +1444,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
 	addr = instr->addr;
 	len = instr->len;
 
-	ret = spi_nor_lock_and_prep(nor);
+	ret = spi_nor_prep_and_lock(nor);
 	if (ret)
 		return ret;
 
@@ -1707,7 +1704,7 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
 
 	dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
 
-	ret = spi_nor_lock_and_prep(nor);
+	ret = spi_nor_prep_and_lock(nor);
 	if (ret)
 		return ret;
 
@@ -1753,7 +1750,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
 
 	dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
 
-	ret = spi_nor_lock_and_prep(nor);
+	ret = spi_nor_prep_and_lock(nor);
 	if (ret)
 		return ret;
 
-- 
2.34.1


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

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v4 3/8] mtd: spi-nor: Reorder the preparation vs locking steps
@ 2023-02-01 11:35   ` Miquel Raynal
  0 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-02-01 11:35 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel, Miquel Raynal

The ->prepare()/->unprepare() hooks are now legacy, we no longer accept
new drivers supporting them. The only remaining controllers using them
acquires a per-chip mutex, which should not interfere with the rest of
the operation done in the core. As a result, we should be safe to
reorganize these helpers to first perform the preparation, before
acquiring the core locks. This is necessary in order to be able to
improve the locking mechanism in the core (coming next). No side effects
are expected.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 drivers/mtd/spi-nor/core.c | 23 ++++++++++-------------
 1 file changed, 10 insertions(+), 13 deletions(-)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 3845de9c874c..01932dbaa5b9 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -1071,27 +1071,24 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor)
 	}
 }
 
-int spi_nor_lock_and_prep(struct spi_nor *nor)
+int spi_nor_prep_and_lock(struct spi_nor *nor)
 {
 	int ret = 0;
 
-	mutex_lock(&nor->lock);
-
-	if (nor->controller_ops &&  nor->controller_ops->prepare) {
+	if (nor->controller_ops && nor->controller_ops->prepare)
 		ret = nor->controller_ops->prepare(nor);
-		if (ret) {
-			mutex_unlock(&nor->lock);
-			return ret;
-		}
-	}
+
+	mutex_lock(&nor->lock);
+
 	return ret;
 }
 
 void spi_nor_unlock_and_unprep(struct spi_nor *nor)
 {
+	mutex_unlock(&nor->lock);
+
 	if (nor->controller_ops && nor->controller_ops->unprepare)
 		nor->controller_ops->unprepare(nor);
-	mutex_unlock(&nor->lock);
 }
 
 static u32 spi_nor_convert_addr(struct spi_nor *nor, loff_t addr)
@@ -1447,7 +1444,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
 	addr = instr->addr;
 	len = instr->len;
 
-	ret = spi_nor_lock_and_prep(nor);
+	ret = spi_nor_prep_and_lock(nor);
 	if (ret)
 		return ret;
 
@@ -1707,7 +1704,7 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
 
 	dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
 
-	ret = spi_nor_lock_and_prep(nor);
+	ret = spi_nor_prep_and_lock(nor);
 	if (ret)
 		return ret;
 
@@ -1753,7 +1750,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
 
 	dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
 
-	ret = spi_nor_lock_and_prep(nor);
+	ret = spi_nor_prep_and_lock(nor);
 	if (ret)
 		return ret;
 
-- 
2.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v4 4/8] mtd: spi-nor: Separate preparation and locking
  2023-02-01 11:35 ` Miquel Raynal
@ 2023-02-01 11:35   ` Miquel Raynal
  -1 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-02-01 11:35 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel, Miquel Raynal

While this operation will remain a single function call in the end,
let's extract the logic of the [un]prepare calls within their own static
helper. We will soon add new flavors of the *_[un]prepare_and_[un]lock()
helpers, having the preparation logic outside will save us from duplicating
code over and over again.

There is no functional change.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 drivers/mtd/spi-nor/core.c | 24 ++++++++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 01932dbaa5b9..0ec85f5e7043 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -1071,24 +1071,40 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor)
 	}
 }
 
-int spi_nor_prep_and_lock(struct spi_nor *nor)
+static int spi_nor_prep(struct spi_nor *nor)
 {
 	int ret = 0;
 
 	if (nor->controller_ops && nor->controller_ops->prepare)
 		ret = nor->controller_ops->prepare(nor);
 
+	return ret;
+}
+
+static void spi_nor_unprep(struct spi_nor *nor)
+{
+	if (nor->controller_ops && nor->controller_ops->unprepare)
+		nor->controller_ops->unprepare(nor);
+}
+
+int spi_nor_prep_and_lock(struct spi_nor *nor)
+{
+	int ret;
+
+	ret = spi_nor_prep(nor);
+	if (ret)
+		return ret;
+
 	mutex_lock(&nor->lock);
 
-	return ret;
+	return 0;
 }
 
 void spi_nor_unlock_and_unprep(struct spi_nor *nor)
 {
 	mutex_unlock(&nor->lock);
 
-	if (nor->controller_ops && nor->controller_ops->unprepare)
-		nor->controller_ops->unprepare(nor);
+	spi_nor_unprep(nor);
 }
 
 static u32 spi_nor_convert_addr(struct spi_nor *nor, loff_t addr)
-- 
2.34.1


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

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v4 4/8] mtd: spi-nor: Separate preparation and locking
@ 2023-02-01 11:35   ` Miquel Raynal
  0 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-02-01 11:35 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel, Miquel Raynal

While this operation will remain a single function call in the end,
let's extract the logic of the [un]prepare calls within their own static
helper. We will soon add new flavors of the *_[un]prepare_and_[un]lock()
helpers, having the preparation logic outside will save us from duplicating
code over and over again.

There is no functional change.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 drivers/mtd/spi-nor/core.c | 24 ++++++++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 01932dbaa5b9..0ec85f5e7043 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -1071,24 +1071,40 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor)
 	}
 }
 
-int spi_nor_prep_and_lock(struct spi_nor *nor)
+static int spi_nor_prep(struct spi_nor *nor)
 {
 	int ret = 0;
 
 	if (nor->controller_ops && nor->controller_ops->prepare)
 		ret = nor->controller_ops->prepare(nor);
 
+	return ret;
+}
+
+static void spi_nor_unprep(struct spi_nor *nor)
+{
+	if (nor->controller_ops && nor->controller_ops->unprepare)
+		nor->controller_ops->unprepare(nor);
+}
+
+int spi_nor_prep_and_lock(struct spi_nor *nor)
+{
+	int ret;
+
+	ret = spi_nor_prep(nor);
+	if (ret)
+		return ret;
+
 	mutex_lock(&nor->lock);
 
-	return ret;
+	return 0;
 }
 
 void spi_nor_unlock_and_unprep(struct spi_nor *nor)
 {
 	mutex_unlock(&nor->lock);
 
-	if (nor->controller_ops && nor->controller_ops->unprepare)
-		nor->controller_ops->unprepare(nor);
+	spi_nor_unprep(nor);
 }
 
 static u32 spi_nor_convert_addr(struct spi_nor *nor, loff_t addr)
-- 
2.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v4 5/8] mtd: spi-nor: Prepare the introduction of a new locking mechanism
  2023-02-01 11:35 ` Miquel Raynal
@ 2023-02-01 11:36   ` Miquel Raynal
  -1 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-02-01 11:36 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel, Miquel Raynal

This commit alone just introduces two new "prepare and lock" pairs of
helpers which do the exact same thing as before. They will soon be
improved in a followup commit which actually brings the logic, but I
figured out it was more readable to do it this way.

One new pair is suffixed _pe which stands for "program and erase" and
hence is being called by spi_nor_write() and spi_nor_erase().

The other pair is suffixed _rd which stands for "read" and hence is
being called by spi_nor_read().

One note however, these extra helpers will need to know the operation
range, so they come with two new parameters to define it. Otherwise
there is no functional change.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 drivers/mtd/spi-nor/core.c | 59 ++++++++++++++++++++++++++++++++++----
 1 file changed, 53 insertions(+), 6 deletions(-)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 0ec85f5e7043..ac4627e0d6c2 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -1087,6 +1087,7 @@ static void spi_nor_unprep(struct spi_nor *nor)
 		nor->controller_ops->unprepare(nor);
 }
 
+/* Generic helpers for internal locking and serialization */
 int spi_nor_prep_and_lock(struct spi_nor *nor)
 {
 	int ret;
@@ -1107,6 +1108,48 @@ void spi_nor_unlock_and_unprep(struct spi_nor *nor)
 	spi_nor_unprep(nor);
 }
 
+/* Internal locking helpers for program and erase operations */
+static int spi_nor_prep_and_lock_pe(struct spi_nor *nor, loff_t start, size_t len)
+{
+	int ret;
+
+	ret = spi_nor_prep(nor);
+	if (ret)
+		return ret;
+
+	mutex_lock(&nor->lock);
+
+	return 0;
+}
+
+static void spi_nor_unlock_and_unprep_pe(struct spi_nor *nor, loff_t start, size_t len)
+{
+	mutex_unlock(&nor->lock);
+
+	spi_nor_unprep(nor);
+}
+
+/* Internal locking helpers for read operations */
+static int spi_nor_prep_and_lock_rd(struct spi_nor *nor, loff_t start, size_t len)
+{
+	int ret;
+
+	ret = spi_nor_prep(nor);
+	if (ret)
+		return ret;
+
+	mutex_lock(&nor->lock);
+
+	return 0;
+}
+
+static void spi_nor_unlock_and_unprep_rd(struct spi_nor *nor, loff_t start, size_t len)
+{
+	mutex_unlock(&nor->lock);
+
+	spi_nor_unprep(nor);
+}
+
 static u32 spi_nor_convert_addr(struct spi_nor *nor, loff_t addr)
 {
 	if (!nor->params->convert_addr)
@@ -1460,7 +1503,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
 	addr = instr->addr;
 	len = instr->len;
 
-	ret = spi_nor_prep_and_lock(nor);
+	ret = spi_nor_prep_and_lock_pe(nor, instr->addr, instr->len);
 	if (ret)
 		return ret;
 
@@ -1523,7 +1566,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
 	ret = spi_nor_write_disable(nor);
 
 erase_err:
-	spi_nor_unlock_and_unprep(nor);
+	spi_nor_unlock_and_unprep_pe(nor, instr->addr, instr->len);
 
 	return ret;
 }
@@ -1716,11 +1759,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_prep_and_lock(nor);
+	ret = spi_nor_prep_and_lock_rd(nor, from_lock, len_lock);
 	if (ret)
 		return ret;
 
@@ -1747,7 +1792,8 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
 	ret = 0;
 
 read_err:
-	spi_nor_unlock_and_unprep(nor);
+	spi_nor_unlock_and_unprep_rd(nor, from_lock, len_lock);
+
 	return ret;
 }
 
@@ -1766,7 +1812,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
 
 	dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
 
-	ret = spi_nor_prep_and_lock(nor);
+	ret = spi_nor_prep_and_lock_pe(nor, to, len);
 	if (ret)
 		return ret;
 
@@ -1808,7 +1854,8 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
 	}
 
 write_err:
-	spi_nor_unlock_and_unprep(nor);
+	spi_nor_unlock_and_unprep_pe(nor, to, len);
+
 	return ret;
 }
 
-- 
2.34.1


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

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v4 5/8] mtd: spi-nor: Prepare the introduction of a new locking mechanism
@ 2023-02-01 11:36   ` Miquel Raynal
  0 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-02-01 11:36 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel, Miquel Raynal

This commit alone just introduces two new "prepare and lock" pairs of
helpers which do the exact same thing as before. They will soon be
improved in a followup commit which actually brings the logic, but I
figured out it was more readable to do it this way.

One new pair is suffixed _pe which stands for "program and erase" and
hence is being called by spi_nor_write() and spi_nor_erase().

The other pair is suffixed _rd which stands for "read" and hence is
being called by spi_nor_read().

One note however, these extra helpers will need to know the operation
range, so they come with two new parameters to define it. Otherwise
there is no functional change.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 drivers/mtd/spi-nor/core.c | 59 ++++++++++++++++++++++++++++++++++----
 1 file changed, 53 insertions(+), 6 deletions(-)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 0ec85f5e7043..ac4627e0d6c2 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -1087,6 +1087,7 @@ static void spi_nor_unprep(struct spi_nor *nor)
 		nor->controller_ops->unprepare(nor);
 }
 
+/* Generic helpers for internal locking and serialization */
 int spi_nor_prep_and_lock(struct spi_nor *nor)
 {
 	int ret;
@@ -1107,6 +1108,48 @@ void spi_nor_unlock_and_unprep(struct spi_nor *nor)
 	spi_nor_unprep(nor);
 }
 
+/* Internal locking helpers for program and erase operations */
+static int spi_nor_prep_and_lock_pe(struct spi_nor *nor, loff_t start, size_t len)
+{
+	int ret;
+
+	ret = spi_nor_prep(nor);
+	if (ret)
+		return ret;
+
+	mutex_lock(&nor->lock);
+
+	return 0;
+}
+
+static void spi_nor_unlock_and_unprep_pe(struct spi_nor *nor, loff_t start, size_t len)
+{
+	mutex_unlock(&nor->lock);
+
+	spi_nor_unprep(nor);
+}
+
+/* Internal locking helpers for read operations */
+static int spi_nor_prep_and_lock_rd(struct spi_nor *nor, loff_t start, size_t len)
+{
+	int ret;
+
+	ret = spi_nor_prep(nor);
+	if (ret)
+		return ret;
+
+	mutex_lock(&nor->lock);
+
+	return 0;
+}
+
+static void spi_nor_unlock_and_unprep_rd(struct spi_nor *nor, loff_t start, size_t len)
+{
+	mutex_unlock(&nor->lock);
+
+	spi_nor_unprep(nor);
+}
+
 static u32 spi_nor_convert_addr(struct spi_nor *nor, loff_t addr)
 {
 	if (!nor->params->convert_addr)
@@ -1460,7 +1503,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
 	addr = instr->addr;
 	len = instr->len;
 
-	ret = spi_nor_prep_and_lock(nor);
+	ret = spi_nor_prep_and_lock_pe(nor, instr->addr, instr->len);
 	if (ret)
 		return ret;
 
@@ -1523,7 +1566,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
 	ret = spi_nor_write_disable(nor);
 
 erase_err:
-	spi_nor_unlock_and_unprep(nor);
+	spi_nor_unlock_and_unprep_pe(nor, instr->addr, instr->len);
 
 	return ret;
 }
@@ -1716,11 +1759,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_prep_and_lock(nor);
+	ret = spi_nor_prep_and_lock_rd(nor, from_lock, len_lock);
 	if (ret)
 		return ret;
 
@@ -1747,7 +1792,8 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
 	ret = 0;
 
 read_err:
-	spi_nor_unlock_and_unprep(nor);
+	spi_nor_unlock_and_unprep_rd(nor, from_lock, len_lock);
+
 	return ret;
 }
 
@@ -1766,7 +1812,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
 
 	dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
 
-	ret = spi_nor_prep_and_lock(nor);
+	ret = spi_nor_prep_and_lock_pe(nor, to, len);
 	if (ret)
 		return ret;
 
@@ -1808,7 +1854,8 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
 	}
 
 write_err:
-	spi_nor_unlock_and_unprep(nor);
+	spi_nor_unlock_and_unprep_pe(nor, to, len);
+
 	return ret;
 }
 
-- 
2.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v4 6/8] mtd: spi-nor: Add a RWW flag
  2023-02-01 11:35 ` Miquel Raynal
@ 2023-02-01 11:36   ` Miquel Raynal
  -1 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-02-01 11:36 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel, Miquel Raynal

Introduce a new (no SFDP) flag for the feature that we are about to
support: Read While Write. This means, if the chip has several banks and
supports RWW, once a page of data to write has been transferred into the
chip's internal SRAM, another read operation happening on a different
bank can be performed during the tPROG delay.

Adding this new flag involves enlarging the no_sfdp_flags variable to 16
bits.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 drivers/mtd/spi-nor/core.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index 888a08c37d8c..aebd92f4884f 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -473,6 +473,7 @@ struct spi_nor_fixups {
  *   SPI_NOR_OCTAL_READ:      flash supports Octal Read.
  *   SPI_NOR_OCTAL_DTR_READ:  flash supports octal DTR Read.
  *   SPI_NOR_OCTAL_DTR_PP:    flash supports Octal DTR Page Program.
+ *   SPI_NOR_RWW:             flash supports reads while write.
  *
  * @fixup_flags:    flags that indicate support that can be discovered via SFDP
  *                  ideally, but can not be discovered for this particular flash
@@ -514,7 +515,7 @@ struct flash_info {
 #define SPI_NOR_NO_FR			BIT(8)
 #define SPI_NOR_QUAD_PP			BIT(9)
 
-	u8 no_sfdp_flags;
+	u16 no_sfdp_flags;
 #define SPI_NOR_SKIP_SFDP		BIT(0)
 #define SECT_4K				BIT(1)
 #define SPI_NOR_DUAL_READ		BIT(3)
@@ -522,6 +523,7 @@ struct flash_info {
 #define SPI_NOR_OCTAL_READ		BIT(5)
 #define SPI_NOR_OCTAL_DTR_READ		BIT(6)
 #define SPI_NOR_OCTAL_DTR_PP		BIT(7)
+#define SPI_NOR_RWW			BIT(8)
 
 	u8 fixup_flags;
 #define SPI_NOR_4B_OPCODES		BIT(0)
-- 
2.34.1


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

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v4 6/8] mtd: spi-nor: Add a RWW flag
@ 2023-02-01 11:36   ` Miquel Raynal
  0 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-02-01 11:36 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel, Miquel Raynal

Introduce a new (no SFDP) flag for the feature that we are about to
support: Read While Write. This means, if the chip has several banks and
supports RWW, once a page of data to write has been transferred into the
chip's internal SRAM, another read operation happening on a different
bank can be performed during the tPROG delay.

Adding this new flag involves enlarging the no_sfdp_flags variable to 16
bits.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 drivers/mtd/spi-nor/core.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index 888a08c37d8c..aebd92f4884f 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -473,6 +473,7 @@ struct spi_nor_fixups {
  *   SPI_NOR_OCTAL_READ:      flash supports Octal Read.
  *   SPI_NOR_OCTAL_DTR_READ:  flash supports octal DTR Read.
  *   SPI_NOR_OCTAL_DTR_PP:    flash supports Octal DTR Page Program.
+ *   SPI_NOR_RWW:             flash supports reads while write.
  *
  * @fixup_flags:    flags that indicate support that can be discovered via SFDP
  *                  ideally, but can not be discovered for this particular flash
@@ -514,7 +515,7 @@ struct flash_info {
 #define SPI_NOR_NO_FR			BIT(8)
 #define SPI_NOR_QUAD_PP			BIT(9)
 
-	u8 no_sfdp_flags;
+	u16 no_sfdp_flags;
 #define SPI_NOR_SKIP_SFDP		BIT(0)
 #define SECT_4K				BIT(1)
 #define SPI_NOR_DUAL_READ		BIT(3)
@@ -522,6 +523,7 @@ struct flash_info {
 #define SPI_NOR_OCTAL_READ		BIT(5)
 #define SPI_NOR_OCTAL_DTR_READ		BIT(6)
 #define SPI_NOR_OCTAL_DTR_PP		BIT(7)
+#define SPI_NOR_RWW			BIT(8)
 
 	u8 fixup_flags;
 #define SPI_NOR_4B_OPCODES		BIT(0)
-- 
2.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v4 7/8] mtd: spi-nor: Enhance locking to support reads while writes
  2023-02-01 11:35 ` Miquel Raynal
@ 2023-02-01 11:36   ` Miquel Raynal
  -1 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-02-01 11:36 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel, Miquel Raynal

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 (common hardware limitation to limit costs)
3#: Reads must remain serialized even though reads on different banks
    might occur at the same time.
4#: 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.
5#: 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 enforced by a bitfield of the in-use banks, so that only a single
    operation can happen in a specific bank at any time.
2#: Is handled by the ongoing_pe boolean which is set 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#: An ongoing_rd boolean allows to track the ongoing reads, so that
    only one can be performed at a time.
4#: An ongoing_io boolean 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
    possibly the data), while the second part of the operation (the
    erase delay or the programmation delay) is when we can do something
    else in another bank.
5#: Is handled by the three booleans presented above, if any of them is
    set, the chip is not yet ready for the operation and must wait.

All these internal variables are protected by the existing lock, so that
changes in this structure are atomic. The serialization is handled with
a wait queue.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 drivers/mtd/spi-nor/core.c  | 319 ++++++++++++++++++++++++++++++++++--
 include/linux/mtd/spi-nor.h |  13 ++
 2 files changed, 317 insertions(+), 15 deletions(-)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index ac4627e0d6c2..ad2436e3688f 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -589,6 +589,66 @@ int spi_nor_sr_ready(struct spi_nor *nor)
 	return !(nor->bouncebuf[0] & SR_WIP);
 }
 
+/**
+ * spi_nor_parallel_locking() - Checks if the RWW locking scheme shall be used
+ * @nor:	pointer to 'struct spi_nor'.
+ *
+ * Return: true if parallel locking is enabled, false otherwise.
+ */
+static bool spi_nor_parallel_locking(struct spi_nor *nor)
+{
+	if (nor->controller_ops &&
+	    (nor->controller_ops->prepare || nor->controller_ops->unprepare))
+		return false;
+
+	return nor->info->n_banks > 1 && nor->info->no_sfdp_flags & SPI_NOR_RWW;
+}
+
+/* Locking helpers for status read operations */
+static int spi_nor_rww_start_rdst(struct spi_nor *nor)
+{
+	int ret = -EAGAIN;
+
+	mutex_lock(&nor->lock);
+
+	if (nor->rww.ongoing_io || nor->rww.ongoing_rd)
+		goto busy;
+
+	nor->rww.ongoing_io = true;
+	nor->rww.ongoing_rd = true;
+	ret = 0;
+
+busy:
+	mutex_unlock(&nor->lock);
+	return ret;
+}
+
+static void spi_nor_rww_end_rdst(struct spi_nor *nor)
+{
+	mutex_lock(&nor->lock);
+
+	nor->rww.ongoing_io = false;
+	nor->rww.ongoing_rd = false;
+
+	mutex_unlock(&nor->lock);
+}
+
+static int spi_nor_lock_rdst(struct spi_nor *nor)
+{
+	if (spi_nor_parallel_locking(nor))
+		return spi_nor_rww_start_rdst(nor);
+
+	return 0;
+}
+
+static void spi_nor_unlock_rdst(struct spi_nor *nor)
+{
+	if (spi_nor_parallel_locking(nor)) {
+		spi_nor_rww_end_rdst(nor);
+		wake_up(&nor->rww.wait);
+	}
+}
+
 /**
  * spi_nor_ready() - Query the flash to see if it is ready for new commands.
  * @nor:	pointer to 'struct spi_nor'.
@@ -597,11 +657,21 @@ int spi_nor_sr_ready(struct spi_nor *nor)
  */
 static int spi_nor_ready(struct spi_nor *nor)
 {
+	int ret;
+
+	ret = spi_nor_lock_rdst(nor);
+	if (ret)
+		return 0;
+
 	/* Flashes might override the standard routine. */
 	if (nor->params->ready)
-		return nor->params->ready(nor);
+		ret = nor->params->ready(nor);
+	else
+		ret = spi_nor_sr_ready(nor);
 
-	return spi_nor_sr_ready(nor);
+	spi_nor_unlock_rdst(nor);
+
+	return ret;
 }
 
 /**
@@ -1087,7 +1157,81 @@ static void spi_nor_unprep(struct spi_nor *nor)
 		nor->controller_ops->unprepare(nor);
 }
 
+static void spi_nor_offset_to_banks(struct spi_nor *nor, loff_t start, size_t len,
+				    unsigned int *first, unsigned int *last)
+{
+	*first = DIV_ROUND_DOWN_ULL(start, nor->params->bank_size);
+	*last = DIV_ROUND_DOWN_ULL(start + len - 1, nor->params->bank_size);
+}
+
 /* Generic helpers for internal locking and serialization */
+static bool spi_nor_rww_start_io(struct spi_nor *nor)
+{
+	bool start = false;
+
+	mutex_lock(&nor->lock);
+
+	if (nor->rww.ongoing_io)
+		goto busy;
+
+	nor->rww.ongoing_io = true;
+	start = true;
+
+busy:
+	mutex_unlock(&nor->lock);
+	return start;
+}
+
+static void spi_nor_rww_end_io(struct spi_nor *nor)
+{
+	mutex_lock(&nor->lock);
+	nor->rww.ongoing_io = false;
+	mutex_unlock(&nor->lock);
+}
+
+static int spi_nor_lock_device(struct spi_nor *nor)
+{
+	if (!spi_nor_parallel_locking(nor))
+		return 0;
+
+	return wait_event_killable(nor->rww.wait, spi_nor_rww_start_io(nor));
+}
+
+static void spi_nor_unlock_device(struct spi_nor *nor)
+{
+	if (spi_nor_parallel_locking(nor))
+		spi_nor_rww_end_io(nor);
+}
+
+/* Generic helpers for internal locking and serialization */
+static bool spi_nor_rww_start_exclusive(struct spi_nor *nor)
+{
+	bool start = false;
+
+	mutex_lock(&nor->lock);
+
+	if (nor->rww.ongoing_io || nor->rww.ongoing_rd || nor->rww.ongoing_pe)
+		goto busy;
+
+	nor->rww.ongoing_io = true;
+	nor->rww.ongoing_rd = true;
+	nor->rww.ongoing_pe = true;
+	start = true;
+
+busy:
+	mutex_unlock(&nor->lock);
+	return start;
+}
+
+static void spi_nor_rww_end_exclusive(struct spi_nor *nor)
+{
+	mutex_lock(&nor->lock);
+	nor->rww.ongoing_io = false;
+	nor->rww.ongoing_rd = false;
+	nor->rww.ongoing_pe = false;
+	mutex_unlock(&nor->lock);
+}
+
 int spi_nor_prep_and_lock(struct spi_nor *nor)
 {
 	int ret;
@@ -1096,19 +1240,71 @@ int spi_nor_prep_and_lock(struct spi_nor *nor)
 	if (ret)
 		return ret;
 
-	mutex_lock(&nor->lock);
+	if (!spi_nor_parallel_locking(nor))
+		mutex_lock(&nor->lock);
+	else
+		ret = wait_event_killable(nor->rww.wait,
+					  spi_nor_rww_start_exclusive(nor));
 
-	return 0;
+	return ret;
 }
 
 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 {
+		spi_nor_rww_end_exclusive(nor);
+		wake_up(&nor->rww.wait);
+	}
 
 	spi_nor_unprep(nor);
 }
 
 /* Internal locking helpers for program and erase operations */
+static bool spi_nor_rww_start_pe(struct spi_nor *nor, loff_t start, size_t len)
+{
+	unsigned int first, last;
+	bool started = false;
+	int bank;
+
+	mutex_lock(&nor->lock);
+
+	if (nor->rww.ongoing_io || nor->rww.ongoing_rd || nor->rww.ongoing_pe)
+		goto busy;
+
+	spi_nor_offset_to_banks(nor, start, len, &first, &last);
+	for (bank = first; bank <= last; bank++)
+		if (nor->rww.used_banks & BIT(bank))
+			goto busy;
+
+	for (bank = first; bank <= last; bank++)
+		nor->rww.used_banks |= BIT(bank);
+
+	nor->rww.ongoing_pe = true;
+	started = true;
+
+busy:
+	mutex_unlock(&nor->lock);
+	return started;
+}
+
+static void spi_nor_rww_end_pe(struct spi_nor *nor, loff_t start, size_t len)
+{
+	unsigned int first, last;
+	int bank;
+
+	mutex_lock(&nor->lock);
+
+	spi_nor_offset_to_banks(nor, start, len, &first, &last);
+	for (bank = first; bank <= last; bank++)
+		nor->rww.used_banks &= ~BIT(bank);
+
+	nor->rww.ongoing_pe = false;
+
+	mutex_unlock(&nor->lock);
+}
+
 static int spi_nor_prep_and_lock_pe(struct spi_nor *nor, loff_t start, size_t len)
 {
 	int ret;
@@ -1117,19 +1313,73 @@ static int spi_nor_prep_and_lock_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
+		ret = wait_event_killable(nor->rww.wait,
+					  spi_nor_rww_start_pe(nor, start, len));
 
-	return 0;
+	return ret;
 }
 
 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 {
+		spi_nor_rww_end_pe(nor, start, len);
+		wake_up(&nor->rww.wait);
+	}
 
 	spi_nor_unprep(nor);
 }
 
 /* Internal locking helpers for read operations */
+static bool spi_nor_rww_start_rd(struct spi_nor *nor, loff_t start, size_t len)
+{
+	unsigned int first, last;
+	bool started = false;
+	int bank;
+
+	mutex_lock(&nor->lock);
+
+	if (nor->rww.ongoing_io || nor->rww.ongoing_rd)
+		goto busy;
+
+	spi_nor_offset_to_banks(nor, start, len, &first, &last);
+	for (bank = first; bank <= last; bank++)
+		if (nor->rww.used_banks & BIT(bank))
+			goto busy;
+
+	for (bank = first; bank <= last; bank++)
+		nor->rww.used_banks |= BIT(bank);
+
+	nor->rww.ongoing_io = true;
+	nor->rww.ongoing_rd = true;
+	started = true;
+
+busy:
+	mutex_unlock(&nor->lock);
+	return started;
+}
+
+static void spi_nor_rww_end_rd(struct spi_nor *nor, loff_t start, size_t len)
+{
+	unsigned int first, last;
+	int bank;
+
+	mutex_lock(&nor->lock);
+
+	spi_nor_offset_to_banks(nor, start, len, &first, &last);
+	for (bank = first; bank <= last; bank++)
+		nor->rww.used_banks &= ~BIT(bank);
+
+	nor->rww.ongoing_io = false;
+	nor->rww.ongoing_rd = false;
+
+	mutex_unlock(&nor->lock);
+}
+
 static int spi_nor_prep_and_lock_rd(struct spi_nor *nor, loff_t start, size_t len)
 {
 	int ret;
@@ -1138,14 +1388,23 @@ static int spi_nor_prep_and_lock_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
+		ret = wait_event_killable(nor->rww.wait,
+					  spi_nor_rww_start_rd(nor, start, len));
 
-	return 0;
+	return ret;
 }
 
 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_rww_end_rd(nor, start, len);
+		wake_up(&nor->rww.wait);
+	}
 
 	spi_nor_unprep(nor);
 }
@@ -1454,11 +1713,18 @@ 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);
 
-			ret = spi_nor_write_enable(nor);
+			ret = spi_nor_lock_device(nor);
 			if (ret)
 				goto destroy_erase_cmd_list;
 
+			ret = spi_nor_write_enable(nor);
+			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;
 
@@ -1511,11 +1777,18 @@ 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;
 
-		ret = spi_nor_write_enable(nor);
+		ret = spi_nor_lock_device(nor);
 		if (ret)
 			goto erase_err;
 
+		ret = spi_nor_write_enable(nor);
+		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;
 
@@ -1540,11 +1813,18 @@ 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) {
-			ret = spi_nor_write_enable(nor);
+			ret = spi_nor_lock_device(nor);
 			if (ret)
 				goto erase_err;
 
+			ret = spi_nor_write_enable(nor);
+			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;
 
@@ -1837,11 +2117,18 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
 
 		addr = spi_nor_convert_addr(nor, addr);
 
-		ret = spi_nor_write_enable(nor);
+		ret = spi_nor_lock_device(nor);
 		if (ret)
 			goto write_err;
 
+		ret = spi_nor_write_enable(nor);
+		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;
@@ -3111,6 +3398,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 	nor->info = info;
 
 	mutex_init(&nor->lock);
+	if (spi_nor_parallel_locking(nor))
+		init_waitqueue_head(&nor->rww.wait);
 
 	/* 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 25765556223a..ec338463d563 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -344,6 +344,12 @@ 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
+ * @rww:		Read-While-Write (RWW) sync lock
+ * @rww.wait:		wait queue for the RWW sync
+ * @rww.ongoing_io:	the bus is busy
+ * @rww.ongoing_rd:	a read is ongoing on the chip
+ * @rww.ongoing_pe:	a program/erase is ongoing on the chip
+ * @rww.used_banks:	bitmap of the banks in use
  * @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
@@ -377,6 +383,13 @@ struct spi_nor_flash_parameter;
 struct spi_nor {
 	struct mtd_info		mtd;
 	struct mutex		lock;
+	struct {
+		wait_queue_head_t wait;
+		bool		ongoing_io;
+		bool		ongoing_rd;
+		bool		ongoing_pe;
+		unsigned int	used_banks;
+	} rww;
 	struct device		*dev;
 	struct spi_mem		*spimem;
 	u8			*bouncebuf;
-- 
2.34.1


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

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v4 7/8] mtd: spi-nor: Enhance locking to support reads while writes
@ 2023-02-01 11:36   ` Miquel Raynal
  0 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-02-01 11:36 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel, Miquel Raynal

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 (common hardware limitation to limit costs)
3#: Reads must remain serialized even though reads on different banks
    might occur at the same time.
4#: 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.
5#: 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 enforced by a bitfield of the in-use banks, so that only a single
    operation can happen in a specific bank at any time.
2#: Is handled by the ongoing_pe boolean which is set 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#: An ongoing_rd boolean allows to track the ongoing reads, so that
    only one can be performed at a time.
4#: An ongoing_io boolean 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
    possibly the data), while the second part of the operation (the
    erase delay or the programmation delay) is when we can do something
    else in another bank.
5#: Is handled by the three booleans presented above, if any of them is
    set, the chip is not yet ready for the operation and must wait.

All these internal variables are protected by the existing lock, so that
changes in this structure are atomic. The serialization is handled with
a wait queue.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 drivers/mtd/spi-nor/core.c  | 319 ++++++++++++++++++++++++++++++++++--
 include/linux/mtd/spi-nor.h |  13 ++
 2 files changed, 317 insertions(+), 15 deletions(-)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index ac4627e0d6c2..ad2436e3688f 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -589,6 +589,66 @@ int spi_nor_sr_ready(struct spi_nor *nor)
 	return !(nor->bouncebuf[0] & SR_WIP);
 }
 
+/**
+ * spi_nor_parallel_locking() - Checks if the RWW locking scheme shall be used
+ * @nor:	pointer to 'struct spi_nor'.
+ *
+ * Return: true if parallel locking is enabled, false otherwise.
+ */
+static bool spi_nor_parallel_locking(struct spi_nor *nor)
+{
+	if (nor->controller_ops &&
+	    (nor->controller_ops->prepare || nor->controller_ops->unprepare))
+		return false;
+
+	return nor->info->n_banks > 1 && nor->info->no_sfdp_flags & SPI_NOR_RWW;
+}
+
+/* Locking helpers for status read operations */
+static int spi_nor_rww_start_rdst(struct spi_nor *nor)
+{
+	int ret = -EAGAIN;
+
+	mutex_lock(&nor->lock);
+
+	if (nor->rww.ongoing_io || nor->rww.ongoing_rd)
+		goto busy;
+
+	nor->rww.ongoing_io = true;
+	nor->rww.ongoing_rd = true;
+	ret = 0;
+
+busy:
+	mutex_unlock(&nor->lock);
+	return ret;
+}
+
+static void spi_nor_rww_end_rdst(struct spi_nor *nor)
+{
+	mutex_lock(&nor->lock);
+
+	nor->rww.ongoing_io = false;
+	nor->rww.ongoing_rd = false;
+
+	mutex_unlock(&nor->lock);
+}
+
+static int spi_nor_lock_rdst(struct spi_nor *nor)
+{
+	if (spi_nor_parallel_locking(nor))
+		return spi_nor_rww_start_rdst(nor);
+
+	return 0;
+}
+
+static void spi_nor_unlock_rdst(struct spi_nor *nor)
+{
+	if (spi_nor_parallel_locking(nor)) {
+		spi_nor_rww_end_rdst(nor);
+		wake_up(&nor->rww.wait);
+	}
+}
+
 /**
  * spi_nor_ready() - Query the flash to see if it is ready for new commands.
  * @nor:	pointer to 'struct spi_nor'.
@@ -597,11 +657,21 @@ int spi_nor_sr_ready(struct spi_nor *nor)
  */
 static int spi_nor_ready(struct spi_nor *nor)
 {
+	int ret;
+
+	ret = spi_nor_lock_rdst(nor);
+	if (ret)
+		return 0;
+
 	/* Flashes might override the standard routine. */
 	if (nor->params->ready)
-		return nor->params->ready(nor);
+		ret = nor->params->ready(nor);
+	else
+		ret = spi_nor_sr_ready(nor);
 
-	return spi_nor_sr_ready(nor);
+	spi_nor_unlock_rdst(nor);
+
+	return ret;
 }
 
 /**
@@ -1087,7 +1157,81 @@ static void spi_nor_unprep(struct spi_nor *nor)
 		nor->controller_ops->unprepare(nor);
 }
 
+static void spi_nor_offset_to_banks(struct spi_nor *nor, loff_t start, size_t len,
+				    unsigned int *first, unsigned int *last)
+{
+	*first = DIV_ROUND_DOWN_ULL(start, nor->params->bank_size);
+	*last = DIV_ROUND_DOWN_ULL(start + len - 1, nor->params->bank_size);
+}
+
 /* Generic helpers for internal locking and serialization */
+static bool spi_nor_rww_start_io(struct spi_nor *nor)
+{
+	bool start = false;
+
+	mutex_lock(&nor->lock);
+
+	if (nor->rww.ongoing_io)
+		goto busy;
+
+	nor->rww.ongoing_io = true;
+	start = true;
+
+busy:
+	mutex_unlock(&nor->lock);
+	return start;
+}
+
+static void spi_nor_rww_end_io(struct spi_nor *nor)
+{
+	mutex_lock(&nor->lock);
+	nor->rww.ongoing_io = false;
+	mutex_unlock(&nor->lock);
+}
+
+static int spi_nor_lock_device(struct spi_nor *nor)
+{
+	if (!spi_nor_parallel_locking(nor))
+		return 0;
+
+	return wait_event_killable(nor->rww.wait, spi_nor_rww_start_io(nor));
+}
+
+static void spi_nor_unlock_device(struct spi_nor *nor)
+{
+	if (spi_nor_parallel_locking(nor))
+		spi_nor_rww_end_io(nor);
+}
+
+/* Generic helpers for internal locking and serialization */
+static bool spi_nor_rww_start_exclusive(struct spi_nor *nor)
+{
+	bool start = false;
+
+	mutex_lock(&nor->lock);
+
+	if (nor->rww.ongoing_io || nor->rww.ongoing_rd || nor->rww.ongoing_pe)
+		goto busy;
+
+	nor->rww.ongoing_io = true;
+	nor->rww.ongoing_rd = true;
+	nor->rww.ongoing_pe = true;
+	start = true;
+
+busy:
+	mutex_unlock(&nor->lock);
+	return start;
+}
+
+static void spi_nor_rww_end_exclusive(struct spi_nor *nor)
+{
+	mutex_lock(&nor->lock);
+	nor->rww.ongoing_io = false;
+	nor->rww.ongoing_rd = false;
+	nor->rww.ongoing_pe = false;
+	mutex_unlock(&nor->lock);
+}
+
 int spi_nor_prep_and_lock(struct spi_nor *nor)
 {
 	int ret;
@@ -1096,19 +1240,71 @@ int spi_nor_prep_and_lock(struct spi_nor *nor)
 	if (ret)
 		return ret;
 
-	mutex_lock(&nor->lock);
+	if (!spi_nor_parallel_locking(nor))
+		mutex_lock(&nor->lock);
+	else
+		ret = wait_event_killable(nor->rww.wait,
+					  spi_nor_rww_start_exclusive(nor));
 
-	return 0;
+	return ret;
 }
 
 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 {
+		spi_nor_rww_end_exclusive(nor);
+		wake_up(&nor->rww.wait);
+	}
 
 	spi_nor_unprep(nor);
 }
 
 /* Internal locking helpers for program and erase operations */
+static bool spi_nor_rww_start_pe(struct spi_nor *nor, loff_t start, size_t len)
+{
+	unsigned int first, last;
+	bool started = false;
+	int bank;
+
+	mutex_lock(&nor->lock);
+
+	if (nor->rww.ongoing_io || nor->rww.ongoing_rd || nor->rww.ongoing_pe)
+		goto busy;
+
+	spi_nor_offset_to_banks(nor, start, len, &first, &last);
+	for (bank = first; bank <= last; bank++)
+		if (nor->rww.used_banks & BIT(bank))
+			goto busy;
+
+	for (bank = first; bank <= last; bank++)
+		nor->rww.used_banks |= BIT(bank);
+
+	nor->rww.ongoing_pe = true;
+	started = true;
+
+busy:
+	mutex_unlock(&nor->lock);
+	return started;
+}
+
+static void spi_nor_rww_end_pe(struct spi_nor *nor, loff_t start, size_t len)
+{
+	unsigned int first, last;
+	int bank;
+
+	mutex_lock(&nor->lock);
+
+	spi_nor_offset_to_banks(nor, start, len, &first, &last);
+	for (bank = first; bank <= last; bank++)
+		nor->rww.used_banks &= ~BIT(bank);
+
+	nor->rww.ongoing_pe = false;
+
+	mutex_unlock(&nor->lock);
+}
+
 static int spi_nor_prep_and_lock_pe(struct spi_nor *nor, loff_t start, size_t len)
 {
 	int ret;
@@ -1117,19 +1313,73 @@ static int spi_nor_prep_and_lock_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
+		ret = wait_event_killable(nor->rww.wait,
+					  spi_nor_rww_start_pe(nor, start, len));
 
-	return 0;
+	return ret;
 }
 
 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 {
+		spi_nor_rww_end_pe(nor, start, len);
+		wake_up(&nor->rww.wait);
+	}
 
 	spi_nor_unprep(nor);
 }
 
 /* Internal locking helpers for read operations */
+static bool spi_nor_rww_start_rd(struct spi_nor *nor, loff_t start, size_t len)
+{
+	unsigned int first, last;
+	bool started = false;
+	int bank;
+
+	mutex_lock(&nor->lock);
+
+	if (nor->rww.ongoing_io || nor->rww.ongoing_rd)
+		goto busy;
+
+	spi_nor_offset_to_banks(nor, start, len, &first, &last);
+	for (bank = first; bank <= last; bank++)
+		if (nor->rww.used_banks & BIT(bank))
+			goto busy;
+
+	for (bank = first; bank <= last; bank++)
+		nor->rww.used_banks |= BIT(bank);
+
+	nor->rww.ongoing_io = true;
+	nor->rww.ongoing_rd = true;
+	started = true;
+
+busy:
+	mutex_unlock(&nor->lock);
+	return started;
+}
+
+static void spi_nor_rww_end_rd(struct spi_nor *nor, loff_t start, size_t len)
+{
+	unsigned int first, last;
+	int bank;
+
+	mutex_lock(&nor->lock);
+
+	spi_nor_offset_to_banks(nor, start, len, &first, &last);
+	for (bank = first; bank <= last; bank++)
+		nor->rww.used_banks &= ~BIT(bank);
+
+	nor->rww.ongoing_io = false;
+	nor->rww.ongoing_rd = false;
+
+	mutex_unlock(&nor->lock);
+}
+
 static int spi_nor_prep_and_lock_rd(struct spi_nor *nor, loff_t start, size_t len)
 {
 	int ret;
@@ -1138,14 +1388,23 @@ static int spi_nor_prep_and_lock_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
+		ret = wait_event_killable(nor->rww.wait,
+					  spi_nor_rww_start_rd(nor, start, len));
 
-	return 0;
+	return ret;
 }
 
 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_rww_end_rd(nor, start, len);
+		wake_up(&nor->rww.wait);
+	}
 
 	spi_nor_unprep(nor);
 }
@@ -1454,11 +1713,18 @@ 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);
 
-			ret = spi_nor_write_enable(nor);
+			ret = spi_nor_lock_device(nor);
 			if (ret)
 				goto destroy_erase_cmd_list;
 
+			ret = spi_nor_write_enable(nor);
+			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;
 
@@ -1511,11 +1777,18 @@ 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;
 
-		ret = spi_nor_write_enable(nor);
+		ret = spi_nor_lock_device(nor);
 		if (ret)
 			goto erase_err;
 
+		ret = spi_nor_write_enable(nor);
+		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;
 
@@ -1540,11 +1813,18 @@ 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) {
-			ret = spi_nor_write_enable(nor);
+			ret = spi_nor_lock_device(nor);
 			if (ret)
 				goto erase_err;
 
+			ret = spi_nor_write_enable(nor);
+			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;
 
@@ -1837,11 +2117,18 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
 
 		addr = spi_nor_convert_addr(nor, addr);
 
-		ret = spi_nor_write_enable(nor);
+		ret = spi_nor_lock_device(nor);
 		if (ret)
 			goto write_err;
 
+		ret = spi_nor_write_enable(nor);
+		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;
@@ -3111,6 +3398,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 	nor->info = info;
 
 	mutex_init(&nor->lock);
+	if (spi_nor_parallel_locking(nor))
+		init_waitqueue_head(&nor->rww.wait);
 
 	/* 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 25765556223a..ec338463d563 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -344,6 +344,12 @@ 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
+ * @rww:		Read-While-Write (RWW) sync lock
+ * @rww.wait:		wait queue for the RWW sync
+ * @rww.ongoing_io:	the bus is busy
+ * @rww.ongoing_rd:	a read is ongoing on the chip
+ * @rww.ongoing_pe:	a program/erase is ongoing on the chip
+ * @rww.used_banks:	bitmap of the banks in use
  * @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
@@ -377,6 +383,13 @@ struct spi_nor_flash_parameter;
 struct spi_nor {
 	struct mtd_info		mtd;
 	struct mutex		lock;
+	struct {
+		wait_queue_head_t wait;
+		bool		ongoing_io;
+		bool		ongoing_rd;
+		bool		ongoing_pe;
+		unsigned int	used_banks;
+	} rww;
 	struct device		*dev;
 	struct spi_mem		*spimem;
 	u8			*bouncebuf;
-- 
2.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v4 8/8] mtd: spi-nor: macronix: Add support for mx25uw51245g with RWW
  2023-02-01 11:35 ` Miquel Raynal
@ 2023-02-01 11:36   ` Miquel Raynal
  -1 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-02-01 11:36 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel, Miquel Raynal

Describe this new part and provide the RWW flag for it.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 drivers/mtd/spi-nor/macronix.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c
index 6853ec9ae65d..655e7eec865c 100644
--- a/drivers/mtd/spi-nor/macronix.c
+++ b/drivers/mtd/spi-nor/macronix.c
@@ -82,6 +82,9 @@ static const struct flash_info macronix_nor_parts[] = {
 	{ "mx25u51245g", INFO(0xc2253a, 0, 64 * 1024, 1024)
 		NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
 		FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
+	{ "mx25uw51245g", INFOB(0xc2813a, 0, 16 * 1024, 1024, 4)
+		NO_SFDP_FLAGS(SECT_4K | SPI_NOR_RWW)
+		FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
 	{ "mx25v8035f",  INFO(0xc22314, 0, 64 * 1024,  16)
 		NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
 			      SPI_NOR_QUAD_READ) },
-- 
2.34.1


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

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v4 8/8] mtd: spi-nor: macronix: Add support for mx25uw51245g with RWW
@ 2023-02-01 11:36   ` Miquel Raynal
  0 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-02-01 11:36 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel, Miquel Raynal

Describe this new part and provide the RWW flag for it.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 drivers/mtd/spi-nor/macronix.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c
index 6853ec9ae65d..655e7eec865c 100644
--- a/drivers/mtd/spi-nor/macronix.c
+++ b/drivers/mtd/spi-nor/macronix.c
@@ -82,6 +82,9 @@ static const struct flash_info macronix_nor_parts[] = {
 	{ "mx25u51245g", INFO(0xc2253a, 0, 64 * 1024, 1024)
 		NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
 		FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
+	{ "mx25uw51245g", INFOB(0xc2813a, 0, 16 * 1024, 1024, 4)
+		NO_SFDP_FLAGS(SECT_4K | SPI_NOR_RWW)
+		FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
 	{ "mx25v8035f",  INFO(0xc22314, 0, 64 * 1024,  16)
 		NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
 			      SPI_NOR_QUAD_READ) },
-- 
2.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 6/8] mtd: spi-nor: Add a RWW flag
  2023-02-01 11:36   ` Miquel Raynal
@ 2023-03-17  3:20     ` Tudor Ambarus
  -1 siblings, 0 replies; 48+ messages in thread
From: Tudor Ambarus @ 2023-03-17  3:20 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel

Hi, Miquel!

On 2/1/23 11:36, Miquel Raynal wrote:
> Introduce a new (no SFDP) flag for the feature that we are about to
> support: Read While Write. This means, if the chip has several banks and
> supports RWW, once a page of data to write has been transferred into the
> chip's internal SRAM, another read operation happening on a different
> bank can be performed during the tPROG delay.
> 
> Adding this new flag involves enlarging the no_sfdp_flags variable to 16
> bits.
> 

I've just skimmed over jesd216 - f version, and indeed it seems that
reads while write support is not covered by the standard. no_sfdp_flags
is not a good place to add this flag, because there can be flashes that
both support SFDP and RWW support. Please move this flag in info->flags.

BTW, does you flash define SFDP table(s)? You can dump the tables via
sysfs, here's an example:
https://lore.kernel.org/linux-mtd/20230203044853.23513-1-Takahiro.Kuwano@infineon.com/

Cheers,
ta

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

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 6/8] mtd: spi-nor: Add a RWW flag
@ 2023-03-17  3:20     ` Tudor Ambarus
  0 siblings, 0 replies; 48+ messages in thread
From: Tudor Ambarus @ 2023-03-17  3:20 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel

Hi, Miquel!

On 2/1/23 11:36, Miquel Raynal wrote:
> Introduce a new (no SFDP) flag for the feature that we are about to
> support: Read While Write. This means, if the chip has several banks and
> supports RWW, once a page of data to write has been transferred into the
> chip's internal SRAM, another read operation happening on a different
> bank can be performed during the tPROG delay.
> 
> Adding this new flag involves enlarging the no_sfdp_flags variable to 16
> bits.
> 

I've just skimmed over jesd216 - f version, and indeed it seems that
reads while write support is not covered by the standard. no_sfdp_flags
is not a good place to add this flag, because there can be flashes that
both support SFDP and RWW support. Please move this flag in info->flags.

BTW, does you flash define SFDP table(s)? You can dump the tables via
sysfs, here's an example:
https://lore.kernel.org/linux-mtd/20230203044853.23513-1-Takahiro.Kuwano@infineon.com/

Cheers,
ta

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 1/8] mtd: spi-nor: Introduce the concept of bank
  2023-02-01 11:35   ` Miquel Raynal
@ 2023-03-17  3:36     ` Tudor Ambarus
  -1 siblings, 0 replies; 48+ messages in thread
From: Tudor Ambarus @ 2023-03-17  3:36 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel



On 2/1/23 11:35, Miquel Raynal wrote:
> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
> index b500655f7937..3845de9c874c 100644
> --- a/drivers/mtd/spi-nor/core.c
> +++ b/drivers/mtd/spi-nor/core.c
> @@ -2574,7 +2574,8 @@ static void spi_nor_init_default_params(struct spi_nor *nor)
>  
>  	/* Set SPI NOR sizes. */
>  	params->writesize = 1;
> -	params->size = (u64)info->sector_size * info->n_sectors;
> +	params->bank_size = (u64)info->sector_size * info->n_sectors;
> +	params->size = params->bank_size * info->n_banks;

Please keep the n_sectors per flash, not per bank, you'll break the
block protection support for these flashes. See the email thread on your
v3 2/9.

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

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 1/8] mtd: spi-nor: Introduce the concept of bank
@ 2023-03-17  3:36     ` Tudor Ambarus
  0 siblings, 0 replies; 48+ messages in thread
From: Tudor Ambarus @ 2023-03-17  3:36 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel



On 2/1/23 11:35, Miquel Raynal wrote:
> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
> index b500655f7937..3845de9c874c 100644
> --- a/drivers/mtd/spi-nor/core.c
> +++ b/drivers/mtd/spi-nor/core.c
> @@ -2574,7 +2574,8 @@ static void spi_nor_init_default_params(struct spi_nor *nor)
>  
>  	/* Set SPI NOR sizes. */
>  	params->writesize = 1;
> -	params->size = (u64)info->sector_size * info->n_sectors;
> +	params->bank_size = (u64)info->sector_size * info->n_sectors;
> +	params->size = params->bank_size * info->n_banks;

Please keep the n_sectors per flash, not per bank, you'll break the
block protection support for these flashes. See the email thread on your
v3 2/9.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 3/8] mtd: spi-nor: Reorder the preparation vs locking steps
  2023-02-01 11:35   ` Miquel Raynal
@ 2023-03-17  3:39     ` Tudor Ambarus
  -1 siblings, 0 replies; 48+ messages in thread
From: Tudor Ambarus @ 2023-03-17  3:39 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel



On 2/1/23 11:35, Miquel Raynal wrote:
> The ->prepare()/->unprepare() hooks are now legacy, we no longer accept
> new drivers supporting them. The only remaining controllers using them
> acquires a per-chip mutex, which should not interfere with the rest of
> the operation done in the core. As a result, we should be safe to
> reorganize these helpers to first perform the preparation, before
> acquiring the core locks. This is necessary in order to be able to
> improve the locking mechanism in the core (coming next). No side effects
> are expected.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>

Reviewed-by: Tudor Ambarus <tudor.ambarus@linaro.org>

> ---
>  drivers/mtd/spi-nor/core.c | 23 ++++++++++-------------
>  1 file changed, 10 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
> index 3845de9c874c..01932dbaa5b9 100644
> --- a/drivers/mtd/spi-nor/core.c
> +++ b/drivers/mtd/spi-nor/core.c
> @@ -1071,27 +1071,24 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor)
>  	}
>  }
>  
> -int spi_nor_lock_and_prep(struct spi_nor *nor)
> +int spi_nor_prep_and_lock(struct spi_nor *nor)
>  {
>  	int ret = 0;
>  
> -	mutex_lock(&nor->lock);
> -
> -	if (nor->controller_ops &&  nor->controller_ops->prepare) {
> +	if (nor->controller_ops && nor->controller_ops->prepare)
>  		ret = nor->controller_ops->prepare(nor);
> -		if (ret) {
> -			mutex_unlock(&nor->lock);
> -			return ret;
> -		}
> -	}
> +
> +	mutex_lock(&nor->lock);
> +
>  	return ret;
>  }
>  
>  void spi_nor_unlock_and_unprep(struct spi_nor *nor)
>  {
> +	mutex_unlock(&nor->lock);
> +
>  	if (nor->controller_ops && nor->controller_ops->unprepare)
>  		nor->controller_ops->unprepare(nor);
> -	mutex_unlock(&nor->lock);
>  }
>  
>  static u32 spi_nor_convert_addr(struct spi_nor *nor, loff_t addr)
> @@ -1447,7 +1444,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
>  	addr = instr->addr;
>  	len = instr->len;
>  
> -	ret = spi_nor_lock_and_prep(nor);
> +	ret = spi_nor_prep_and_lock(nor);
>  	if (ret)
>  		return ret;
>  
> @@ -1707,7 +1704,7 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
>  
>  	dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
>  
> -	ret = spi_nor_lock_and_prep(nor);
> +	ret = spi_nor_prep_and_lock(nor);
>  	if (ret)
>  		return ret;
>  
> @@ -1753,7 +1750,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
>  
>  	dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
>  
> -	ret = spi_nor_lock_and_prep(nor);
> +	ret = spi_nor_prep_and_lock(nor);
>  	if (ret)
>  		return ret;
>  

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

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 3/8] mtd: spi-nor: Reorder the preparation vs locking steps
@ 2023-03-17  3:39     ` Tudor Ambarus
  0 siblings, 0 replies; 48+ messages in thread
From: Tudor Ambarus @ 2023-03-17  3:39 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel



On 2/1/23 11:35, Miquel Raynal wrote:
> The ->prepare()/->unprepare() hooks are now legacy, we no longer accept
> new drivers supporting them. The only remaining controllers using them
> acquires a per-chip mutex, which should not interfere with the rest of
> the operation done in the core. As a result, we should be safe to
> reorganize these helpers to first perform the preparation, before
> acquiring the core locks. This is necessary in order to be able to
> improve the locking mechanism in the core (coming next). No side effects
> are expected.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>

Reviewed-by: Tudor Ambarus <tudor.ambarus@linaro.org>

> ---
>  drivers/mtd/spi-nor/core.c | 23 ++++++++++-------------
>  1 file changed, 10 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
> index 3845de9c874c..01932dbaa5b9 100644
> --- a/drivers/mtd/spi-nor/core.c
> +++ b/drivers/mtd/spi-nor/core.c
> @@ -1071,27 +1071,24 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor)
>  	}
>  }
>  
> -int spi_nor_lock_and_prep(struct spi_nor *nor)
> +int spi_nor_prep_and_lock(struct spi_nor *nor)
>  {
>  	int ret = 0;
>  
> -	mutex_lock(&nor->lock);
> -
> -	if (nor->controller_ops &&  nor->controller_ops->prepare) {
> +	if (nor->controller_ops && nor->controller_ops->prepare)
>  		ret = nor->controller_ops->prepare(nor);
> -		if (ret) {
> -			mutex_unlock(&nor->lock);
> -			return ret;
> -		}
> -	}
> +
> +	mutex_lock(&nor->lock);
> +
>  	return ret;
>  }
>  
>  void spi_nor_unlock_and_unprep(struct spi_nor *nor)
>  {
> +	mutex_unlock(&nor->lock);
> +
>  	if (nor->controller_ops && nor->controller_ops->unprepare)
>  		nor->controller_ops->unprepare(nor);
> -	mutex_unlock(&nor->lock);
>  }
>  
>  static u32 spi_nor_convert_addr(struct spi_nor *nor, loff_t addr)
> @@ -1447,7 +1444,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
>  	addr = instr->addr;
>  	len = instr->len;
>  
> -	ret = spi_nor_lock_and_prep(nor);
> +	ret = spi_nor_prep_and_lock(nor);
>  	if (ret)
>  		return ret;
>  
> @@ -1707,7 +1704,7 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
>  
>  	dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
>  
> -	ret = spi_nor_lock_and_prep(nor);
> +	ret = spi_nor_prep_and_lock(nor);
>  	if (ret)
>  		return ret;
>  
> @@ -1753,7 +1750,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
>  
>  	dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
>  
> -	ret = spi_nor_lock_and_prep(nor);
> +	ret = spi_nor_prep_and_lock(nor);
>  	if (ret)
>  		return ret;
>  

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 3/8] mtd: spi-nor: Reorder the preparation vs locking steps
  2023-03-17  3:39     ` Tudor Ambarus
@ 2023-03-17  3:51       ` Tudor Ambarus
  -1 siblings, 0 replies; 48+ messages in thread
From: Tudor Ambarus @ 2023-03-17  3:51 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel



On 3/17/23 03:39, Tudor Ambarus wrote:
> 
> 
> On 2/1/23 11:35, Miquel Raynal wrote:
>> The ->prepare()/->unprepare() hooks are now legacy, we no longer accept
>> new drivers supporting them. The only remaining controllers using them
>> acquires a per-chip mutex, which should not interfere with the rest of
>> the operation done in the core. As a result, we should be safe to
>> reorganize these helpers to first perform the preparation, before
>> acquiring the core locks. This is necessary in order to be able to
>> improve the locking mechanism in the core (coming next). No side effects
>> are expected.
>>
>> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> 
> Reviewed-by: Tudor Ambarus <tudor.ambarus@linaro.org>

No, I take back my R-b. The reorder is fine, but you missed to update
otp.c and swp.c, see below.
> 
>> ---
>>  drivers/mtd/spi-nor/core.c | 23 ++++++++++-------------
>>  1 file changed, 10 insertions(+), 13 deletions(-)
>>
>> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
>> index 3845de9c874c..01932dbaa5b9 100644
>> --- a/drivers/mtd/spi-nor/core.c
>> +++ b/drivers/mtd/spi-nor/core.c
>> @@ -1071,27 +1071,24 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor)
>>  	}
>>  }
>>  
>> -int spi_nor_lock_and_prep(struct spi_nor *nor)
>> +int spi_nor_prep_and_lock(struct spi_nor *nor)

$ git grep spi_nor_lock_and_prep
drivers/mtd/spi-nor/core.h:int spi_nor_lock_and_prep(struct spi_nor *nor);
drivers/mtd/spi-nor/otp.c:      ret = spi_nor_lock_and_prep(nor);
drivers/mtd/spi-nor/otp.c:      ret = spi_nor_lock_and_prep(nor);
drivers/mtd/spi-nor/otp.c:      ret = spi_nor_lock_and_prep(nor);
drivers/mtd/spi-nor/otp.c:      ret = spi_nor_lock_and_prep(nor);
drivers/mtd/spi-nor/sst.c:      ret = spi_nor_lock_and_prep(nor);
drivers/mtd/spi-nor/swp.c:      ret = spi_nor_lock_and_prep(nor);
drivers/mtd/spi-nor/swp.c:      ret = spi_nor_lock_and_prep(nor);
drivers/mtd/spi-nor/swp.c:      ret = spi_nor_lock_and_prep(nor);

Please make sure you try a build after each patch, you'll affect bisects:

arm-linux-gnueabi-ld: drivers/mtd/spi-nor/swp.o: in function
`spi_nor_unlock':
swp.c:(.text+0x6d0): undefined reference to `spi_nor_lock_and_prep'
arm-linux-gnueabi-ld: drivers/mtd/spi-nor/swp.o: in function
`spi_nor_is_locked':
swp.c:(.text+0x72c): undefined reference to `spi_nor_lock_and_prep'
arm-linux-gnueabi-ld: drivers/mtd/spi-nor/swp.o: in function `spi_nor_lock':
swp.c:(.text+0x788): undefined reference to `spi_nor_lock_and_prep'
arm-linux-gnueabi-ld: drivers/mtd/spi-nor/swp.o: in function
`spi_nor_try_unlock_all':
swp.c:(.text+0x804): undefined reference to `spi_nor_lock_and_prep'
arm-linux-gnueabi-ld: drivers/mtd/spi-nor/otp.o: in function
`spi_nor_mtd_otp_info':
otp.c:(.text+0x30): undefined reference to `spi_nor_lock_and_prep'
arm-linux-gnueabi-ld: drivers/mtd/spi-nor/otp.o:otp.c:(.text+0x138):
more undefined references to `spi_nor_lock_and_prep' follow
make[1]: *** [scripts/Makefile.vmlinux:34: vmlinux] Error 1
make: *** [Makefile:1252: vmlinux] Error 2

>>  {
>>  	int ret = 0;
>>  
>> -	mutex_lock(&nor->lock);
>> -
>> -	if (nor->controller_ops &&  nor->controller_ops->prepare) {
>> +	if (nor->controller_ops && nor->controller_ops->prepare)
>>  		ret = nor->controller_ops->prepare(nor);
>> -		if (ret) {
>> -			mutex_unlock(&nor->lock);
>> -			return ret;
>> -		}
>> -	}
>> +
>> +	mutex_lock(&nor->lock);
>> +
>>  	return ret;
>>  }
>>  
>>  void spi_nor_unlock_and_unprep(struct spi_nor *nor)
>>  {
>> +	mutex_unlock(&nor->lock);
>> +
>>  	if (nor->controller_ops && nor->controller_ops->unprepare)
>>  		nor->controller_ops->unprepare(nor);
>> -	mutex_unlock(&nor->lock);
>>  }
>>  
>>  static u32 spi_nor_convert_addr(struct spi_nor *nor, loff_t addr)
>> @@ -1447,7 +1444,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
>>  	addr = instr->addr;
>>  	len = instr->len;
>>  
>> -	ret = spi_nor_lock_and_prep(nor);
>> +	ret = spi_nor_prep_and_lock(nor);
>>  	if (ret)
>>  		return ret;
>>  
>> @@ -1707,7 +1704,7 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
>>  
>>  	dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
>>  
>> -	ret = spi_nor_lock_and_prep(nor);
>> +	ret = spi_nor_prep_and_lock(nor);
>>  	if (ret)
>>  		return ret;
>>  
>> @@ -1753,7 +1750,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
>>  
>>  	dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
>>  
>> -	ret = spi_nor_lock_and_prep(nor);
>> +	ret = spi_nor_prep_and_lock(nor);
>>  	if (ret)
>>  		return ret;
>>  

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

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 3/8] mtd: spi-nor: Reorder the preparation vs locking steps
@ 2023-03-17  3:51       ` Tudor Ambarus
  0 siblings, 0 replies; 48+ messages in thread
From: Tudor Ambarus @ 2023-03-17  3:51 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel



On 3/17/23 03:39, Tudor Ambarus wrote:
> 
> 
> On 2/1/23 11:35, Miquel Raynal wrote:
>> The ->prepare()/->unprepare() hooks are now legacy, we no longer accept
>> new drivers supporting them. The only remaining controllers using them
>> acquires a per-chip mutex, which should not interfere with the rest of
>> the operation done in the core. As a result, we should be safe to
>> reorganize these helpers to first perform the preparation, before
>> acquiring the core locks. This is necessary in order to be able to
>> improve the locking mechanism in the core (coming next). No side effects
>> are expected.
>>
>> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> 
> Reviewed-by: Tudor Ambarus <tudor.ambarus@linaro.org>

No, I take back my R-b. The reorder is fine, but you missed to update
otp.c and swp.c, see below.
> 
>> ---
>>  drivers/mtd/spi-nor/core.c | 23 ++++++++++-------------
>>  1 file changed, 10 insertions(+), 13 deletions(-)
>>
>> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
>> index 3845de9c874c..01932dbaa5b9 100644
>> --- a/drivers/mtd/spi-nor/core.c
>> +++ b/drivers/mtd/spi-nor/core.c
>> @@ -1071,27 +1071,24 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor)
>>  	}
>>  }
>>  
>> -int spi_nor_lock_and_prep(struct spi_nor *nor)
>> +int spi_nor_prep_and_lock(struct spi_nor *nor)

$ git grep spi_nor_lock_and_prep
drivers/mtd/spi-nor/core.h:int spi_nor_lock_and_prep(struct spi_nor *nor);
drivers/mtd/spi-nor/otp.c:      ret = spi_nor_lock_and_prep(nor);
drivers/mtd/spi-nor/otp.c:      ret = spi_nor_lock_and_prep(nor);
drivers/mtd/spi-nor/otp.c:      ret = spi_nor_lock_and_prep(nor);
drivers/mtd/spi-nor/otp.c:      ret = spi_nor_lock_and_prep(nor);
drivers/mtd/spi-nor/sst.c:      ret = spi_nor_lock_and_prep(nor);
drivers/mtd/spi-nor/swp.c:      ret = spi_nor_lock_and_prep(nor);
drivers/mtd/spi-nor/swp.c:      ret = spi_nor_lock_and_prep(nor);
drivers/mtd/spi-nor/swp.c:      ret = spi_nor_lock_and_prep(nor);

Please make sure you try a build after each patch, you'll affect bisects:

arm-linux-gnueabi-ld: drivers/mtd/spi-nor/swp.o: in function
`spi_nor_unlock':
swp.c:(.text+0x6d0): undefined reference to `spi_nor_lock_and_prep'
arm-linux-gnueabi-ld: drivers/mtd/spi-nor/swp.o: in function
`spi_nor_is_locked':
swp.c:(.text+0x72c): undefined reference to `spi_nor_lock_and_prep'
arm-linux-gnueabi-ld: drivers/mtd/spi-nor/swp.o: in function `spi_nor_lock':
swp.c:(.text+0x788): undefined reference to `spi_nor_lock_and_prep'
arm-linux-gnueabi-ld: drivers/mtd/spi-nor/swp.o: in function
`spi_nor_try_unlock_all':
swp.c:(.text+0x804): undefined reference to `spi_nor_lock_and_prep'
arm-linux-gnueabi-ld: drivers/mtd/spi-nor/otp.o: in function
`spi_nor_mtd_otp_info':
otp.c:(.text+0x30): undefined reference to `spi_nor_lock_and_prep'
arm-linux-gnueabi-ld: drivers/mtd/spi-nor/otp.o:otp.c:(.text+0x138):
more undefined references to `spi_nor_lock_and_prep' follow
make[1]: *** [scripts/Makefile.vmlinux:34: vmlinux] Error 1
make: *** [Makefile:1252: vmlinux] Error 2

>>  {
>>  	int ret = 0;
>>  
>> -	mutex_lock(&nor->lock);
>> -
>> -	if (nor->controller_ops &&  nor->controller_ops->prepare) {
>> +	if (nor->controller_ops && nor->controller_ops->prepare)
>>  		ret = nor->controller_ops->prepare(nor);
>> -		if (ret) {
>> -			mutex_unlock(&nor->lock);
>> -			return ret;
>> -		}
>> -	}
>> +
>> +	mutex_lock(&nor->lock);
>> +
>>  	return ret;
>>  }
>>  
>>  void spi_nor_unlock_and_unprep(struct spi_nor *nor)
>>  {
>> +	mutex_unlock(&nor->lock);
>> +
>>  	if (nor->controller_ops && nor->controller_ops->unprepare)
>>  		nor->controller_ops->unprepare(nor);
>> -	mutex_unlock(&nor->lock);
>>  }
>>  
>>  static u32 spi_nor_convert_addr(struct spi_nor *nor, loff_t addr)
>> @@ -1447,7 +1444,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
>>  	addr = instr->addr;
>>  	len = instr->len;
>>  
>> -	ret = spi_nor_lock_and_prep(nor);
>> +	ret = spi_nor_prep_and_lock(nor);
>>  	if (ret)
>>  		return ret;
>>  
>> @@ -1707,7 +1704,7 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
>>  
>>  	dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
>>  
>> -	ret = spi_nor_lock_and_prep(nor);
>> +	ret = spi_nor_prep_and_lock(nor);
>>  	if (ret)
>>  		return ret;
>>  
>> @@ -1753,7 +1750,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
>>  
>>  	dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
>>  
>> -	ret = spi_nor_lock_and_prep(nor);
>> +	ret = spi_nor_prep_and_lock(nor);
>>  	if (ret)
>>  		return ret;
>>  

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 0/8] mtd: spi-nor: read while write support
  2023-02-01 11:35 ` Miquel Raynal
@ 2023-03-17  4:13   ` Tudor Ambarus
  -1 siblings, 0 replies; 48+ messages in thread
From: Tudor Ambarus @ 2023-03-17  4:13 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel



On 2/1/23 11:35, Miquel Raynal wrote:
> Hello folks,
> 
> Here is the follow-up of the RFC trying to bring a little bit of
> parallelism to support SPI-NOR Read While Write feature on parts
> supporting it and featuring several banks.
> 
> I have received some hardware to make it work, so since the RFC, the
> series has been updated to fix my mistakes, but the overall idea is the
> same.
> 
> There is nothing Macronix specific in the implementation, the operations
> and opcodes are exactly the same as before. The only difference being:
> we may consider the chip usable when it is in the busy state during a
> write or an erase. Any chip with an internal split allowing to perform
> parallel operations might possibly leverage the benefits of this
> implementation.
> 
> The first patches are just refactoring and preparation work, there is
> almost no functional change, it's just a way to prepare the introduction
> of the new locking mechanism and hopefully provide the cleanest and
> simplest diff possible for this new feature. The actual change is all
> contained in "mtd: spi-nor: Enhance locking to support reads while
> writes". The logic is described in the commit log and copy/pasted here
> for clarity:
> 
> "
>     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 (common hardware limitation to limit costs)
>     3#: Reads must remain serialized even though reads on different banks
>         might occur at the same time.
>     4#: 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.
>     5#: 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 enforced by a bitfield of the in-use banks, so that only a single
>         operation can happen in a specific bank at any time.
>     2#: Is handled by the ongoing_pe boolean which is set 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#: An ongoing_rd boolean allows to track the ongoing reads, so that
>         only one can be performed at a time.
>     4#: An ongoing_io boolean 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 possibly the data), while the
>         second part of the operation (the erase delay or the
>         programmation delay) is when we can do something else in another
>         bank.
>     5#: Is handled by the three booleans presented above, if any of them is
>         set, the chip is not yet ready for the operation and must wait.
>     
>     All these internal variables are protected by the existing lock, so that
>     changes in this structure are atomic. The serialization is handled with
>     a wait queue."
> 
> Here is now a benchmark with a Macronix MX25UW51245G with 4 banks and RWW
> support:
> 
>      // Testing the two accesses in the same bank
>      $ flash_speed -b0 -k0 -c10 -d /dev/mtd0
>      [...]
>      testing read while write latency
>      read while write took 51ms, read ended after 51ms
> 
>      // Testing the two accesses within different banks
>      $ flash_speed -b0 -k4096 -c10 -d /dev/mtd0
>      [...]
>      testing read while write latency
>      read while write took 51ms, read ended after 20ms
> 
> Parallel accesses have been validated with io_paral. A slight increase
> of the time spent on this test has however been noticed. With my

how do the other tests look? Is there any change in performance for
flashes that do not support RWW?

> configuration, over a limited number of blocks, the overall operation
> took 22 min without any RWW changes up to 27 min with these changes,
> maybe due to the number of additional scheduling situations involved).
> 
> Here is a branch with the mtd-utils patch bringing support for this
> additional "-k" parameter in flash_speed (for the second block to use
> during RWW testing), used to get the above results:
> https://github.com/miquelraynal/mtd-utils/compare/master...rww
> 
> Cheers,
> Miquèl
> 
> Changes in v4:
> * Dropped patch 1/9 which got applied.
> * s/SPI-NOR/SPI NOR/
> * Turned n_banks into an u8 and moved it below in the struct to avoid
>   padding.
> * Updated the S3AN_INFO macro to set n_banks to 1 by default.
> * Renamed the lock and prep helper to follow the order of each
>   operation.
> * Reworded a commit log to fit the recent changes upstream.
> 
> Changes in v3:
> * Fix the bank offsets calculations by providing the same values when
>   locking and when unlocking (might be changed by the functions themselves
>   without use noticing).
> * I completely changed the way the locking works because there was a new
>   constraint: reads cannot be interrupted and status reads cannot happen
>   during a read. Hence, as the multi-locks design was starting to be too
>   messy, I changed the implementation to use a bunch of variables to
>   track the read while write state, protected by the main spi-nor
>   lock. If the internal state does not allow the operation, a sleep
>   starts in a queue, until the threads are woken up after a state
>   update. I know it is very verbose, I am open to suggestions.
> 
> 
> Miquel Raynal (8):
>   mtd: spi-nor: Introduce the concept of bank
>   mtd: spi-nor: Add a macro to define more banks
>   mtd: spi-nor: Reorder the preparation vs locking steps
>   mtd: spi-nor: Separate preparation and locking
>   mtd: spi-nor: Prepare the introduction of a new locking mechanism
>   mtd: spi-nor: Add a RWW flag
>   mtd: spi-nor: Enhance locking to support reads while writes
>   mtd: spi-nor: macronix: Add support for mx25uw51245g with RWW
> 
>  drivers/mtd/spi-nor/core.c     | 396 +++++++++++++++++++++++++++++++--
>  drivers/mtd/spi-nor/core.h     |  26 ++-
>  drivers/mtd/spi-nor/macronix.c |   3 +
>  drivers/mtd/spi-nor/xilinx.c   |   1 +
>  include/linux/mtd/spi-nor.h    |  13 ++
>  5 files changed, 409 insertions(+), 30 deletions(-)
> 

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

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 0/8] mtd: spi-nor: read while write support
@ 2023-03-17  4:13   ` Tudor Ambarus
  0 siblings, 0 replies; 48+ messages in thread
From: Tudor Ambarus @ 2023-03-17  4:13 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel



On 2/1/23 11:35, Miquel Raynal wrote:
> Hello folks,
> 
> Here is the follow-up of the RFC trying to bring a little bit of
> parallelism to support SPI-NOR Read While Write feature on parts
> supporting it and featuring several banks.
> 
> I have received some hardware to make it work, so since the RFC, the
> series has been updated to fix my mistakes, but the overall idea is the
> same.
> 
> There is nothing Macronix specific in the implementation, the operations
> and opcodes are exactly the same as before. The only difference being:
> we may consider the chip usable when it is in the busy state during a
> write or an erase. Any chip with an internal split allowing to perform
> parallel operations might possibly leverage the benefits of this
> implementation.
> 
> The first patches are just refactoring and preparation work, there is
> almost no functional change, it's just a way to prepare the introduction
> of the new locking mechanism and hopefully provide the cleanest and
> simplest diff possible for this new feature. The actual change is all
> contained in "mtd: spi-nor: Enhance locking to support reads while
> writes". The logic is described in the commit log and copy/pasted here
> for clarity:
> 
> "
>     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 (common hardware limitation to limit costs)
>     3#: Reads must remain serialized even though reads on different banks
>         might occur at the same time.
>     4#: 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.
>     5#: 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 enforced by a bitfield of the in-use banks, so that only a single
>         operation can happen in a specific bank at any time.
>     2#: Is handled by the ongoing_pe boolean which is set 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#: An ongoing_rd boolean allows to track the ongoing reads, so that
>         only one can be performed at a time.
>     4#: An ongoing_io boolean 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 possibly the data), while the
>         second part of the operation (the erase delay or the
>         programmation delay) is when we can do something else in another
>         bank.
>     5#: Is handled by the three booleans presented above, if any of them is
>         set, the chip is not yet ready for the operation and must wait.
>     
>     All these internal variables are protected by the existing lock, so that
>     changes in this structure are atomic. The serialization is handled with
>     a wait queue."
> 
> Here is now a benchmark with a Macronix MX25UW51245G with 4 banks and RWW
> support:
> 
>      // Testing the two accesses in the same bank
>      $ flash_speed -b0 -k0 -c10 -d /dev/mtd0
>      [...]
>      testing read while write latency
>      read while write took 51ms, read ended after 51ms
> 
>      // Testing the two accesses within different banks
>      $ flash_speed -b0 -k4096 -c10 -d /dev/mtd0
>      [...]
>      testing read while write latency
>      read while write took 51ms, read ended after 20ms
> 
> Parallel accesses have been validated with io_paral. A slight increase
> of the time spent on this test has however been noticed. With my

how do the other tests look? Is there any change in performance for
flashes that do not support RWW?

> configuration, over a limited number of blocks, the overall operation
> took 22 min without any RWW changes up to 27 min with these changes,
> maybe due to the number of additional scheduling situations involved).
> 
> Here is a branch with the mtd-utils patch bringing support for this
> additional "-k" parameter in flash_speed (for the second block to use
> during RWW testing), used to get the above results:
> https://github.com/miquelraynal/mtd-utils/compare/master...rww
> 
> Cheers,
> Miquèl
> 
> Changes in v4:
> * Dropped patch 1/9 which got applied.
> * s/SPI-NOR/SPI NOR/
> * Turned n_banks into an u8 and moved it below in the struct to avoid
>   padding.
> * Updated the S3AN_INFO macro to set n_banks to 1 by default.
> * Renamed the lock and prep helper to follow the order of each
>   operation.
> * Reworded a commit log to fit the recent changes upstream.
> 
> Changes in v3:
> * Fix the bank offsets calculations by providing the same values when
>   locking and when unlocking (might be changed by the functions themselves
>   without use noticing).
> * I completely changed the way the locking works because there was a new
>   constraint: reads cannot be interrupted and status reads cannot happen
>   during a read. Hence, as the multi-locks design was starting to be too
>   messy, I changed the implementation to use a bunch of variables to
>   track the read while write state, protected by the main spi-nor
>   lock. If the internal state does not allow the operation, a sleep
>   starts in a queue, until the threads are woken up after a state
>   update. I know it is very verbose, I am open to suggestions.
> 
> 
> Miquel Raynal (8):
>   mtd: spi-nor: Introduce the concept of bank
>   mtd: spi-nor: Add a macro to define more banks
>   mtd: spi-nor: Reorder the preparation vs locking steps
>   mtd: spi-nor: Separate preparation and locking
>   mtd: spi-nor: Prepare the introduction of a new locking mechanism
>   mtd: spi-nor: Add a RWW flag
>   mtd: spi-nor: Enhance locking to support reads while writes
>   mtd: spi-nor: macronix: Add support for mx25uw51245g with RWW
> 
>  drivers/mtd/spi-nor/core.c     | 396 +++++++++++++++++++++++++++++++--
>  drivers/mtd/spi-nor/core.h     |  26 ++-
>  drivers/mtd/spi-nor/macronix.c |   3 +
>  drivers/mtd/spi-nor/xilinx.c   |   1 +
>  include/linux/mtd/spi-nor.h    |  13 ++
>  5 files changed, 409 insertions(+), 30 deletions(-)
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 7/8] mtd: spi-nor: Enhance locking to support reads while writes
  2023-02-01 11:36   ` Miquel Raynal
@ 2023-03-17  5:59     ` Tudor Ambarus
  -1 siblings, 0 replies; 48+ messages in thread
From: Tudor Ambarus @ 2023-03-17  5:59 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel

Hi, Miquel,

I find the overall idea good.

On 2/1/23 11:36, Miquel Raynal wrote:
> 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 (common hardware limitation to limit costs)
> 3#: Reads must remain serialized even though reads on different banks
>     might occur at the same time.

3# is unclear if one limits just at reading the commit message. Are the
reads serialized per bank or per flash?

After reading the code, it looks like all the reads are serialized per
flash regardless if it reads registers or memory. I assume you meant
that crossing a bank boundary with a single read is fine. But can you
really read from bank 1 and bank 3 at the same time? The code doesn't
take this into consideration.

> 4#: 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.
> 5#: 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 enforced by a bitfield of the in-use banks, so that only a single
>     operation can happen in a specific bank at any time.
> 2#: Is handled by the ongoing_pe boolean which is set 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#: An ongoing_rd boolean allows to track the ongoing reads, so that
>     only one can be performed at a time.
> 4#: An ongoing_io boolean 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
>     possibly the data), while the second part of the operation (the
>     erase delay or the programmation delay) is when we can do something
>     else in another bank.
> 5#: Is handled by the three booleans presented above, if any of them is
>     set, the chip is not yet ready for the operation and must wait.
> 
> All these internal variables are protected by the existing lock, so that
> changes in this structure are atomic. The serialization is handled with
> a wait queue.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> ---
>  drivers/mtd/spi-nor/core.c  | 319 ++++++++++++++++++++++++++++++++++--
>  include/linux/mtd/spi-nor.h |  13 ++
>  2 files changed, 317 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
> index ac4627e0d6c2..ad2436e3688f 100644
> --- a/drivers/mtd/spi-nor/core.c
> +++ b/drivers/mtd/spi-nor/core.c
> @@ -589,6 +589,66 @@ int spi_nor_sr_ready(struct spi_nor *nor)
>  	return !(nor->bouncebuf[0] & SR_WIP);
>  }
>  
> +/**
> + * spi_nor_parallel_locking() - Checks if the RWW locking scheme shall be used
> + * @nor:	pointer to 'struct spi_nor'.
> + *
> + * Return: true if parallel locking is enabled, false otherwise.
> + */
> +static bool spi_nor_parallel_locking(struct spi_nor *nor)
> +{
> +	if (nor->controller_ops &&
> +	    (nor->controller_ops->prepare || nor->controller_ops->unprepare))
> +		return false;

We won't allow controller drivers in spi-nor/controllers to benefit of
this feature, just do:
	if (nor->controller_ops)
		return false;
> +
> +	return nor->info->n_banks > 1 && nor->info->no_sfdp_flags & SPI_NOR_RWW;

we don't play with flash info flags throughout the core. Introduce a
SNOR_F equivalent flag, see how they're used. You'll be able to get rid
of the n_banks check as well.

> +}
> +
> +/* Locking helpers for status read operations */
> +static int spi_nor_rww_start_rdst(struct spi_nor *nor)
> +{
> +	int ret = -EAGAIN;

you can have a pointer to rww here, you'll avoid all those dereferences
from nor. I would add such a pointer wherever there is more than one
dereference, so the comment is for the entire patch.
> +
> +	mutex_lock(&nor->lock);
> +
> +	if (nor->rww.ongoing_io || nor->rww.ongoing_rd)
> +		goto busy;
> +
> +	nor->rww.ongoing_io = true;
> +	nor->rww.ongoing_rd = true;
> +	ret = 0;
> +
> +busy:
> +	mutex_unlock(&nor->lock);
> +	return ret;
> +}
> +
> +static void spi_nor_rww_end_rdst(struct spi_nor *nor)
> +{
> +	mutex_lock(&nor->lock);
> +
> +	nor->rww.ongoing_io = false;
> +	nor->rww.ongoing_rd = false;
> +
> +	mutex_unlock(&nor->lock);
> +}
> +
> +static int spi_nor_lock_rdst(struct spi_nor *nor)
> +{
> +	if (spi_nor_parallel_locking(nor))
> +		return spi_nor_rww_start_rdst(nor);
> +
> +	return 0;
> +}
> +
> +static void spi_nor_unlock_rdst(struct spi_nor *nor)
> +{
> +	if (spi_nor_parallel_locking(nor)) {
> +		spi_nor_rww_end_rdst(nor);
> +		wake_up(&nor->rww.wait);
> +	}
> +}
> +
>  /**
>   * spi_nor_ready() - Query the flash to see if it is ready for new commands.
>   * @nor:	pointer to 'struct spi_nor'.
> @@ -597,11 +657,21 @@ int spi_nor_sr_ready(struct spi_nor *nor)
>   */
>  static int spi_nor_ready(struct spi_nor *nor)
>  {
> +	int ret;
> +
> +	ret = spi_nor_lock_rdst(nor);
> +	if (ret)
> +		return 0;
> +
>  	/* Flashes might override the standard routine. */
>  	if (nor->params->ready)
> -		return nor->params->ready(nor);
> +		ret = nor->params->ready(nor);
> +	else
> +		ret = spi_nor_sr_ready(nor);
>  
> -	return spi_nor_sr_ready(nor);
> +	spi_nor_unlock_rdst(nor);
> +
> +	return ret;
>  }
>  
>  /**
> @@ -1087,7 +1157,81 @@ static void spi_nor_unprep(struct spi_nor *nor)
>  		nor->controller_ops->unprepare(nor);
>  }
>  
> +static void spi_nor_offset_to_banks(struct spi_nor *nor, loff_t start, size_t len,

pass directly the bank_size instead of the pointer to nor, you'll avoid
the double dereference.

> +				    unsigned int *first, unsigned int *last)

unsigned long long *first, *last ?

> +{
> +	*first = DIV_ROUND_DOWN_ULL(start, nor->params->bank_size);
> +	*last = DIV_ROUND_DOWN_ULL(start + len - 1, nor->params->bank_size);
> +}
> +
>  /* Generic helpers for internal locking and serialization */
> +static bool spi_nor_rww_start_io(struct spi_nor *nor)
> +{
> +	bool start = false;
> +
> +	mutex_lock(&nor->lock);
> +
> +	if (nor->rww.ongoing_io)
> +		goto busy;
> +
> +	nor->rww.ongoing_io = true;
> +	start = true;
> +
> +busy:
> +	mutex_unlock(&nor->lock);
> +	return start;
> +}
> +
> +static void spi_nor_rww_end_io(struct spi_nor *nor)
> +{
> +	mutex_lock(&nor->lock);
> +	nor->rww.ongoing_io = false;
> +	mutex_unlock(&nor->lock);
> +}
> +
> +static int spi_nor_lock_device(struct spi_nor *nor)
> +{
> +	if (!spi_nor_parallel_locking(nor))
> +		return 0;
> +
> +	return wait_event_killable(nor->rww.wait, spi_nor_rww_start_io(nor));
> +}
> +
> +static void spi_nor_unlock_device(struct spi_nor *nor)
> +{
> +	if (spi_nor_parallel_locking(nor))
> +		spi_nor_rww_end_io(nor);

shall we wake_up here too?

> +}
> +
> +/* Generic helpers for internal locking and serialization */
> +static bool spi_nor_rww_start_exclusive(struct spi_nor *nor)
> +{
> +	bool start = false;
> +
> +	mutex_lock(&nor->lock);
> +
> +	if (nor->rww.ongoing_io || nor->rww.ongoing_rd || nor->rww.ongoing_pe)
> +		goto busy;
> +
> +	nor->rww.ongoing_io = true;
> +	nor->rww.ongoing_rd = true;
> +	nor->rww.ongoing_pe = true;
> +	start = true;
> +
> +busy:
> +	mutex_unlock(&nor->lock);
> +	return start;
> +}
> +
> +static void spi_nor_rww_end_exclusive(struct spi_nor *nor)
> +{
> +	mutex_lock(&nor->lock);
> +	nor->rww.ongoing_io = false;
> +	nor->rww.ongoing_rd = false;
> +	nor->rww.ongoing_pe = false;
> +	mutex_unlock(&nor->lock);
> +}
> +
>  int spi_nor_prep_and_lock(struct spi_nor *nor)
>  {
>  	int ret;
> @@ -1096,19 +1240,71 @@ int spi_nor_prep_and_lock(struct spi_nor *nor)
>  	if (ret)
>  		return ret;
>  
> -	mutex_lock(&nor->lock);
> +	if (!spi_nor_parallel_locking(nor))
> +		mutex_lock(&nor->lock);
> +	else
> +		ret = wait_event_killable(nor->rww.wait,
> +					  spi_nor_rww_start_exclusive(nor));
>

No, don't touch spi_nor_prep_and_lock() and spi_nor_unlock_and_unprep(),
you're giving the impresion that users of it (OTP, SWP) are safe to use
them while reads or PE are in progress, which is not the case, because
you don't guard the ops that they're using. You'll also have to document
the flash info RWW flag ands say it is mutual exclusive with SWP and OTP
features.

> -	return 0;
> +	return ret;
>  }
>  
>  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 {
> +		spi_nor_rww_end_exclusive(nor);
> +		wake_up(&nor->rww.wait);
> +	}
>  
>  	spi_nor_unprep(nor);
>  }
>  
>  /* Internal locking helpers for program and erase operations */
> +static bool spi_nor_rww_start_pe(struct spi_nor *nor, loff_t start, size_t len)
> +{
> +	unsigned int first, last;
> +	bool started = false;
> +	int bank;
> +
> +	mutex_lock(&nor->lock);
> +
> +	if (nor->rww.ongoing_io || nor->rww.ongoing_rd || nor->rww.ongoing_pe)
> +		goto busy;
> +
> +	spi_nor_offset_to_banks(nor, start, len, &first, &last);
> +	for (bank = first; bank <= last; bank++)
> +		if (nor->rww.used_banks & BIT(bank))
> +			goto busy;
> +
> +	for (bank = first; bank <= last; bank++)
you can avoid this second look by introducing a local used_banks
variable and have the mask set in the previous loop. Then you'll just do
an init at this point.

> +		nor->rww.used_banks |= BIT(bank);
> +
> +	nor->rww.ongoing_pe = true;
> +	started = true;
> +
> +busy:
> +	mutex_unlock(&nor->lock);
> +	return started;
> +}
> +
> +static void spi_nor_rww_end_pe(struct spi_nor *nor, loff_t start, size_t len)
> +{
> +	unsigned int first, last;
> +	int bank;
> +
> +	mutex_lock(&nor->lock);
> +
> +	spi_nor_offset_to_banks(nor, start, len, &first, &last);
> +	for (bank = first; bank <= last; bank++)
> +		nor->rww.used_banks &= ~BIT(bank);
> +
> +	nor->rww.ongoing_pe = false;
> +
> +	mutex_unlock(&nor->lock);
> +}
> +
>  static int spi_nor_prep_and_lock_pe(struct spi_nor *nor, loff_t start, size_t len)
>  {
>  	int ret;
> @@ -1117,19 +1313,73 @@ static int spi_nor_prep_and_lock_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
> +		ret = wait_event_killable(nor->rww.wait,
> +					  spi_nor_rww_start_pe(nor, start, len));
>  
> -	return 0;
> +	return ret;
>  }
>  
>  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 {
> +		spi_nor_rww_end_pe(nor, start, len);
> +		wake_up(&nor->rww.wait);
> +	}
>  
>  	spi_nor_unprep(nor);
>  }
>  
>  /* Internal locking helpers for read operations */
> +static bool spi_nor_rww_start_rd(struct spi_nor *nor, loff_t start, size_t len)
> +{
> +	unsigned int first, last;
> +	bool started = false;
> +	int bank;
> +
> +	mutex_lock(&nor->lock);
> +
> +	if (nor->rww.ongoing_io || nor->rww.ongoing_rd)
> +		goto busy;
> +
> +	spi_nor_offset_to_banks(nor, start, len, &first, &last);
> +	for (bank = first; bank <= last; bank++)
> +		if (nor->rww.used_banks & BIT(bank))
> +			goto busy;
> +
> +	for (bank = first; bank <= last; bank++)

mask to avoid 2nd loop

Cheers,
ta

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

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 7/8] mtd: spi-nor: Enhance locking to support reads while writes
@ 2023-03-17  5:59     ` Tudor Ambarus
  0 siblings, 0 replies; 48+ messages in thread
From: Tudor Ambarus @ 2023-03-17  5:59 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel

Hi, Miquel,

I find the overall idea good.

On 2/1/23 11:36, Miquel Raynal wrote:
> 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 (common hardware limitation to limit costs)
> 3#: Reads must remain serialized even though reads on different banks
>     might occur at the same time.

3# is unclear if one limits just at reading the commit message. Are the
reads serialized per bank or per flash?

After reading the code, it looks like all the reads are serialized per
flash regardless if it reads registers or memory. I assume you meant
that crossing a bank boundary with a single read is fine. But can you
really read from bank 1 and bank 3 at the same time? The code doesn't
take this into consideration.

> 4#: 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.
> 5#: 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 enforced by a bitfield of the in-use banks, so that only a single
>     operation can happen in a specific bank at any time.
> 2#: Is handled by the ongoing_pe boolean which is set 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#: An ongoing_rd boolean allows to track the ongoing reads, so that
>     only one can be performed at a time.
> 4#: An ongoing_io boolean 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
>     possibly the data), while the second part of the operation (the
>     erase delay or the programmation delay) is when we can do something
>     else in another bank.
> 5#: Is handled by the three booleans presented above, if any of them is
>     set, the chip is not yet ready for the operation and must wait.
> 
> All these internal variables are protected by the existing lock, so that
> changes in this structure are atomic. The serialization is handled with
> a wait queue.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> ---
>  drivers/mtd/spi-nor/core.c  | 319 ++++++++++++++++++++++++++++++++++--
>  include/linux/mtd/spi-nor.h |  13 ++
>  2 files changed, 317 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
> index ac4627e0d6c2..ad2436e3688f 100644
> --- a/drivers/mtd/spi-nor/core.c
> +++ b/drivers/mtd/spi-nor/core.c
> @@ -589,6 +589,66 @@ int spi_nor_sr_ready(struct spi_nor *nor)
>  	return !(nor->bouncebuf[0] & SR_WIP);
>  }
>  
> +/**
> + * spi_nor_parallel_locking() - Checks if the RWW locking scheme shall be used
> + * @nor:	pointer to 'struct spi_nor'.
> + *
> + * Return: true if parallel locking is enabled, false otherwise.
> + */
> +static bool spi_nor_parallel_locking(struct spi_nor *nor)
> +{
> +	if (nor->controller_ops &&
> +	    (nor->controller_ops->prepare || nor->controller_ops->unprepare))
> +		return false;

We won't allow controller drivers in spi-nor/controllers to benefit of
this feature, just do:
	if (nor->controller_ops)
		return false;
> +
> +	return nor->info->n_banks > 1 && nor->info->no_sfdp_flags & SPI_NOR_RWW;

we don't play with flash info flags throughout the core. Introduce a
SNOR_F equivalent flag, see how they're used. You'll be able to get rid
of the n_banks check as well.

> +}
> +
> +/* Locking helpers for status read operations */
> +static int spi_nor_rww_start_rdst(struct spi_nor *nor)
> +{
> +	int ret = -EAGAIN;

you can have a pointer to rww here, you'll avoid all those dereferences
from nor. I would add such a pointer wherever there is more than one
dereference, so the comment is for the entire patch.
> +
> +	mutex_lock(&nor->lock);
> +
> +	if (nor->rww.ongoing_io || nor->rww.ongoing_rd)
> +		goto busy;
> +
> +	nor->rww.ongoing_io = true;
> +	nor->rww.ongoing_rd = true;
> +	ret = 0;
> +
> +busy:
> +	mutex_unlock(&nor->lock);
> +	return ret;
> +}
> +
> +static void spi_nor_rww_end_rdst(struct spi_nor *nor)
> +{
> +	mutex_lock(&nor->lock);
> +
> +	nor->rww.ongoing_io = false;
> +	nor->rww.ongoing_rd = false;
> +
> +	mutex_unlock(&nor->lock);
> +}
> +
> +static int spi_nor_lock_rdst(struct spi_nor *nor)
> +{
> +	if (spi_nor_parallel_locking(nor))
> +		return spi_nor_rww_start_rdst(nor);
> +
> +	return 0;
> +}
> +
> +static void spi_nor_unlock_rdst(struct spi_nor *nor)
> +{
> +	if (spi_nor_parallel_locking(nor)) {
> +		spi_nor_rww_end_rdst(nor);
> +		wake_up(&nor->rww.wait);
> +	}
> +}
> +
>  /**
>   * spi_nor_ready() - Query the flash to see if it is ready for new commands.
>   * @nor:	pointer to 'struct spi_nor'.
> @@ -597,11 +657,21 @@ int spi_nor_sr_ready(struct spi_nor *nor)
>   */
>  static int spi_nor_ready(struct spi_nor *nor)
>  {
> +	int ret;
> +
> +	ret = spi_nor_lock_rdst(nor);
> +	if (ret)
> +		return 0;
> +
>  	/* Flashes might override the standard routine. */
>  	if (nor->params->ready)
> -		return nor->params->ready(nor);
> +		ret = nor->params->ready(nor);
> +	else
> +		ret = spi_nor_sr_ready(nor);
>  
> -	return spi_nor_sr_ready(nor);
> +	spi_nor_unlock_rdst(nor);
> +
> +	return ret;
>  }
>  
>  /**
> @@ -1087,7 +1157,81 @@ static void spi_nor_unprep(struct spi_nor *nor)
>  		nor->controller_ops->unprepare(nor);
>  }
>  
> +static void spi_nor_offset_to_banks(struct spi_nor *nor, loff_t start, size_t len,

pass directly the bank_size instead of the pointer to nor, you'll avoid
the double dereference.

> +				    unsigned int *first, unsigned int *last)

unsigned long long *first, *last ?

> +{
> +	*first = DIV_ROUND_DOWN_ULL(start, nor->params->bank_size);
> +	*last = DIV_ROUND_DOWN_ULL(start + len - 1, nor->params->bank_size);
> +}
> +
>  /* Generic helpers for internal locking and serialization */
> +static bool spi_nor_rww_start_io(struct spi_nor *nor)
> +{
> +	bool start = false;
> +
> +	mutex_lock(&nor->lock);
> +
> +	if (nor->rww.ongoing_io)
> +		goto busy;
> +
> +	nor->rww.ongoing_io = true;
> +	start = true;
> +
> +busy:
> +	mutex_unlock(&nor->lock);
> +	return start;
> +}
> +
> +static void spi_nor_rww_end_io(struct spi_nor *nor)
> +{
> +	mutex_lock(&nor->lock);
> +	nor->rww.ongoing_io = false;
> +	mutex_unlock(&nor->lock);
> +}
> +
> +static int spi_nor_lock_device(struct spi_nor *nor)
> +{
> +	if (!spi_nor_parallel_locking(nor))
> +		return 0;
> +
> +	return wait_event_killable(nor->rww.wait, spi_nor_rww_start_io(nor));
> +}
> +
> +static void spi_nor_unlock_device(struct spi_nor *nor)
> +{
> +	if (spi_nor_parallel_locking(nor))
> +		spi_nor_rww_end_io(nor);

shall we wake_up here too?

> +}
> +
> +/* Generic helpers for internal locking and serialization */
> +static bool spi_nor_rww_start_exclusive(struct spi_nor *nor)
> +{
> +	bool start = false;
> +
> +	mutex_lock(&nor->lock);
> +
> +	if (nor->rww.ongoing_io || nor->rww.ongoing_rd || nor->rww.ongoing_pe)
> +		goto busy;
> +
> +	nor->rww.ongoing_io = true;
> +	nor->rww.ongoing_rd = true;
> +	nor->rww.ongoing_pe = true;
> +	start = true;
> +
> +busy:
> +	mutex_unlock(&nor->lock);
> +	return start;
> +}
> +
> +static void spi_nor_rww_end_exclusive(struct spi_nor *nor)
> +{
> +	mutex_lock(&nor->lock);
> +	nor->rww.ongoing_io = false;
> +	nor->rww.ongoing_rd = false;
> +	nor->rww.ongoing_pe = false;
> +	mutex_unlock(&nor->lock);
> +}
> +
>  int spi_nor_prep_and_lock(struct spi_nor *nor)
>  {
>  	int ret;
> @@ -1096,19 +1240,71 @@ int spi_nor_prep_and_lock(struct spi_nor *nor)
>  	if (ret)
>  		return ret;
>  
> -	mutex_lock(&nor->lock);
> +	if (!spi_nor_parallel_locking(nor))
> +		mutex_lock(&nor->lock);
> +	else
> +		ret = wait_event_killable(nor->rww.wait,
> +					  spi_nor_rww_start_exclusive(nor));
>

No, don't touch spi_nor_prep_and_lock() and spi_nor_unlock_and_unprep(),
you're giving the impresion that users of it (OTP, SWP) are safe to use
them while reads or PE are in progress, which is not the case, because
you don't guard the ops that they're using. You'll also have to document
the flash info RWW flag ands say it is mutual exclusive with SWP and OTP
features.

> -	return 0;
> +	return ret;
>  }
>  
>  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 {
> +		spi_nor_rww_end_exclusive(nor);
> +		wake_up(&nor->rww.wait);
> +	}
>  
>  	spi_nor_unprep(nor);
>  }
>  
>  /* Internal locking helpers for program and erase operations */
> +static bool spi_nor_rww_start_pe(struct spi_nor *nor, loff_t start, size_t len)
> +{
> +	unsigned int first, last;
> +	bool started = false;
> +	int bank;
> +
> +	mutex_lock(&nor->lock);
> +
> +	if (nor->rww.ongoing_io || nor->rww.ongoing_rd || nor->rww.ongoing_pe)
> +		goto busy;
> +
> +	spi_nor_offset_to_banks(nor, start, len, &first, &last);
> +	for (bank = first; bank <= last; bank++)
> +		if (nor->rww.used_banks & BIT(bank))
> +			goto busy;
> +
> +	for (bank = first; bank <= last; bank++)
you can avoid this second look by introducing a local used_banks
variable and have the mask set in the previous loop. Then you'll just do
an init at this point.

> +		nor->rww.used_banks |= BIT(bank);
> +
> +	nor->rww.ongoing_pe = true;
> +	started = true;
> +
> +busy:
> +	mutex_unlock(&nor->lock);
> +	return started;
> +}
> +
> +static void spi_nor_rww_end_pe(struct spi_nor *nor, loff_t start, size_t len)
> +{
> +	unsigned int first, last;
> +	int bank;
> +
> +	mutex_lock(&nor->lock);
> +
> +	spi_nor_offset_to_banks(nor, start, len, &first, &last);
> +	for (bank = first; bank <= last; bank++)
> +		nor->rww.used_banks &= ~BIT(bank);
> +
> +	nor->rww.ongoing_pe = false;
> +
> +	mutex_unlock(&nor->lock);
> +}
> +
>  static int spi_nor_prep_and_lock_pe(struct spi_nor *nor, loff_t start, size_t len)
>  {
>  	int ret;
> @@ -1117,19 +1313,73 @@ static int spi_nor_prep_and_lock_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
> +		ret = wait_event_killable(nor->rww.wait,
> +					  spi_nor_rww_start_pe(nor, start, len));
>  
> -	return 0;
> +	return ret;
>  }
>  
>  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 {
> +		spi_nor_rww_end_pe(nor, start, len);
> +		wake_up(&nor->rww.wait);
> +	}
>  
>  	spi_nor_unprep(nor);
>  }
>  
>  /* Internal locking helpers for read operations */
> +static bool spi_nor_rww_start_rd(struct spi_nor *nor, loff_t start, size_t len)
> +{
> +	unsigned int first, last;
> +	bool started = false;
> +	int bank;
> +
> +	mutex_lock(&nor->lock);
> +
> +	if (nor->rww.ongoing_io || nor->rww.ongoing_rd)
> +		goto busy;
> +
> +	spi_nor_offset_to_banks(nor, start, len, &first, &last);
> +	for (bank = first; bank <= last; bank++)
> +		if (nor->rww.used_banks & BIT(bank))
> +			goto busy;
> +
> +	for (bank = first; bank <= last; bank++)

mask to avoid 2nd loop

Cheers,
ta

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 8/8] mtd: spi-nor: macronix: Add support for mx25uw51245g with RWW
  2023-02-01 11:36   ` Miquel Raynal
@ 2023-03-17  6:09     ` Tudor Ambarus
  -1 siblings, 0 replies; 48+ messages in thread
From: Tudor Ambarus @ 2023-03-17  6:09 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Pratyush Yadav, Michael Walle, linux-mtd, Jaime Liao
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel

Hi, Jaime, Miquel,

Any news with the datasheet, can you send me a copy of it?

On 2/1/23 11:36, Miquel Raynal wrote:
> Describe this new part and provide the RWW flag for it.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> ---
>  drivers/mtd/spi-nor/macronix.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c
> index 6853ec9ae65d..655e7eec865c 100644
> --- a/drivers/mtd/spi-nor/macronix.c
> +++ b/drivers/mtd/spi-nor/macronix.c
> @@ -82,6 +82,9 @@ static const struct flash_info macronix_nor_parts[] = {
>  	{ "mx25u51245g", INFO(0xc2253a, 0, 64 * 1024, 1024)
>  		NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
>  		FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
> +	{ "mx25uw51245g", INFOB(0xc2813a, 0, 16 * 1024, 1024, 4)
> +		NO_SFDP_FLAGS(SECT_4K | SPI_NOR_RWW)
> +		FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },

Is this flash not really supporting SFDP?

We ask people that update or add new flash entries to do a erase, verify
erase, write, read back and compare test. Also to dump the sysfs
entries. Here's how to do that:

https://lore.kernel.org/lkml/97a3b023-b9bc-c34d-45a4-ddd56f47bd76@microchip.com/T/

Thanks,
ta

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

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 8/8] mtd: spi-nor: macronix: Add support for mx25uw51245g with RWW
@ 2023-03-17  6:09     ` Tudor Ambarus
  0 siblings, 0 replies; 48+ messages in thread
From: Tudor Ambarus @ 2023-03-17  6:09 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Pratyush Yadav, Michael Walle, linux-mtd, Jaime Liao
  Cc: Julien Su, Jaime Liao, Jaime Liao, Alvin Zhou, Thomas Petazzoni,
	Michal Simek, linux-arm-kernel

Hi, Jaime, Miquel,

Any news with the datasheet, can you send me a copy of it?

On 2/1/23 11:36, Miquel Raynal wrote:
> Describe this new part and provide the RWW flag for it.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> ---
>  drivers/mtd/spi-nor/macronix.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c
> index 6853ec9ae65d..655e7eec865c 100644
> --- a/drivers/mtd/spi-nor/macronix.c
> +++ b/drivers/mtd/spi-nor/macronix.c
> @@ -82,6 +82,9 @@ static const struct flash_info macronix_nor_parts[] = {
>  	{ "mx25u51245g", INFO(0xc2253a, 0, 64 * 1024, 1024)
>  		NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
>  		FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
> +	{ "mx25uw51245g", INFOB(0xc2813a, 0, 16 * 1024, 1024, 4)
> +		NO_SFDP_FLAGS(SECT_4K | SPI_NOR_RWW)
> +		FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },

Is this flash not really supporting SFDP?

We ask people that update or add new flash entries to do a erase, verify
erase, write, read back and compare test. Also to dump the sysfs
entries. Here's how to do that:

https://lore.kernel.org/lkml/97a3b023-b9bc-c34d-45a4-ddd56f47bd76@microchip.com/T/

Thanks,
ta

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 8/8] mtd: spi-nor: macronix: Add support for mx25uw51245g with RWW
  2023-03-17  6:09     ` Tudor Ambarus
@ 2023-03-17  7:43       ` liao jaime
  -1 siblings, 0 replies; 48+ messages in thread
From: liao jaime @ 2023-03-17  7:43 UTC (permalink / raw)
  To: Tudor Ambarus
  Cc: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Pratyush Yadav, Michael Walle, linux-mtd, Jaime Liao, Julien Su,
	Alvin Zhou, Thomas Petazzoni, Michal Simek, linux-arm-kernel

Hi Tudor

>
> Hi, Jaime, Miquel,
>
> Any news with the datasheet, can you send me a copy of it?
>
> On 2/1/23 11:36, Miquel Raynal wrote:
> > Describe this new part and provide the RWW flag for it.
> >
> > Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> > ---
> >  drivers/mtd/spi-nor/macronix.c | 3 +++
> >  1 file changed, 3 insertions(+)
> >
> > diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c
> > index 6853ec9ae65d..655e7eec865c 100644
> > --- a/drivers/mtd/spi-nor/macronix.c
> > +++ b/drivers/mtd/spi-nor/macronix.c
> > @@ -82,6 +82,9 @@ static const struct flash_info macronix_nor_parts[] = {
> >       { "mx25u51245g", INFO(0xc2253a, 0, 64 * 1024, 1024)
> >               NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
> >               FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
> > +     { "mx25uw51245g", INFOB(0xc2813a, 0, 16 * 1024, 1024, 4)
> > +             NO_SFDP_FLAGS(SECT_4K | SPI_NOR_RWW)
> > +             FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
>
> Is this flash not really supporting SFDP?
Yap MX25UW51245G have SFDP table support.
As below is the link of octal DTR feature patch which include
MX25UW51245G SFDP information.

https://patchwork.ozlabs.org/project/uboot/patch/20220718064922.20193-2-jaimeliao.tw@gmail.com/

Sorry for that I can't share the link of datasheet cause Macronix
didn't make it public.
>
> We ask people that update or add new flash entries to do a erase, verify
> erase, write, read back and compare test. Also to dump the sysfs
> entries. Here's how to do that:
>
> https://lore.kernel.org/lkml/97a3b023-b9bc-c34d-45a4-ddd56f47bd76@microchip.com/T/
>
> Thanks,
> ta
Thanks
Jaime

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

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 8/8] mtd: spi-nor: macronix: Add support for mx25uw51245g with RWW
@ 2023-03-17  7:43       ` liao jaime
  0 siblings, 0 replies; 48+ messages in thread
From: liao jaime @ 2023-03-17  7:43 UTC (permalink / raw)
  To: Tudor Ambarus
  Cc: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Pratyush Yadav, Michael Walle, linux-mtd, Jaime Liao, Julien Su,
	Alvin Zhou, Thomas Petazzoni, Michal Simek, linux-arm-kernel

Hi Tudor

>
> Hi, Jaime, Miquel,
>
> Any news with the datasheet, can you send me a copy of it?
>
> On 2/1/23 11:36, Miquel Raynal wrote:
> > Describe this new part and provide the RWW flag for it.
> >
> > Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> > ---
> >  drivers/mtd/spi-nor/macronix.c | 3 +++
> >  1 file changed, 3 insertions(+)
> >
> > diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c
> > index 6853ec9ae65d..655e7eec865c 100644
> > --- a/drivers/mtd/spi-nor/macronix.c
> > +++ b/drivers/mtd/spi-nor/macronix.c
> > @@ -82,6 +82,9 @@ static const struct flash_info macronix_nor_parts[] = {
> >       { "mx25u51245g", INFO(0xc2253a, 0, 64 * 1024, 1024)
> >               NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
> >               FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
> > +     { "mx25uw51245g", INFOB(0xc2813a, 0, 16 * 1024, 1024, 4)
> > +             NO_SFDP_FLAGS(SECT_4K | SPI_NOR_RWW)
> > +             FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
>
> Is this flash not really supporting SFDP?
Yap MX25UW51245G have SFDP table support.
As below is the link of octal DTR feature patch which include
MX25UW51245G SFDP information.

https://patchwork.ozlabs.org/project/uboot/patch/20220718064922.20193-2-jaimeliao.tw@gmail.com/

Sorry for that I can't share the link of datasheet cause Macronix
didn't make it public.
>
> We ask people that update or add new flash entries to do a erase, verify
> erase, write, read back and compare test. Also to dump the sysfs
> entries. Here's how to do that:
>
> https://lore.kernel.org/lkml/97a3b023-b9bc-c34d-45a4-ddd56f47bd76@microchip.com/T/
>
> Thanks,
> ta
Thanks
Jaime

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 8/8] mtd: spi-nor: macronix: Add support for mx25uw51245g with RWW
  2023-03-17  7:43       ` liao jaime
@ 2023-03-17  8:22         ` Tudor Ambarus
  -1 siblings, 0 replies; 48+ messages in thread
From: Tudor Ambarus @ 2023-03-17  8:22 UTC (permalink / raw)
  To: liao jaime
  Cc: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Pratyush Yadav, Michael Walle, linux-mtd, Jaime Liao, Julien Su,
	Alvin Zhou, Thomas Petazzoni, Michal Simek, linux-arm-kernel



On 3/17/23 07:43, liao jaime wrote:
> Hi Tudor
> 
>>
>> Hi, Jaime, Miquel,
>>
>> Any news with the datasheet, can you send me a copy of it?
>>
>> On 2/1/23 11:36, Miquel Raynal wrote:
>>> Describe this new part and provide the RWW flag for it.
>>>
>>> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
>>> ---
>>>  drivers/mtd/spi-nor/macronix.c | 3 +++
>>>  1 file changed, 3 insertions(+)
>>>
>>> diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c
>>> index 6853ec9ae65d..655e7eec865c 100644
>>> --- a/drivers/mtd/spi-nor/macronix.c
>>> +++ b/drivers/mtd/spi-nor/macronix.c
>>> @@ -82,6 +82,9 @@ static const struct flash_info macronix_nor_parts[] = {
>>>       { "mx25u51245g", INFO(0xc2253a, 0, 64 * 1024, 1024)
>>>               NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
>>>               FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
>>> +     { "mx25uw51245g", INFOB(0xc2813a, 0, 16 * 1024, 1024, 4)
>>> +             NO_SFDP_FLAGS(SECT_4K | SPI_NOR_RWW)
>>> +             FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
>>
>> Is this flash not really supporting SFDP?
> Yap MX25UW51245G have SFDP table support.

Okay, then for the next iteration of the series we should have
PARSE_SFDP set.

> As below is the link of octal DTR feature patch which include
> MX25UW51245G SFDP information.
> 
> https://patchwork.ozlabs.org/project/uboot/patch/20220718064922.20193-2-jaimeliao.tw@gmail.com/

cool, thanks.

I'd also like an erase, verify erase, write, read back and compare test
if possible, just to make sure that the flash works in it's first day.
Obviously in linux, not u-boot. The how is in the link below.

> 
> Sorry for that I can't share the link of datasheet cause Macronix
> didn't make it public.

Ok, no worries.

Cheers,
ta
>>
>> We ask people that update or add new flash entries to do a erase, verify
>> erase, write, read back and compare test. Also to dump the sysfs
>> entries. Here's how to do that:
>>
>> https://lore.kernel.org/lkml/97a3b023-b9bc-c34d-45a4-ddd56f47bd76@microchip.com/T/
>>
>> Thanks,
>> ta
> Thanks
> Jaime

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

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 8/8] mtd: spi-nor: macronix: Add support for mx25uw51245g with RWW
@ 2023-03-17  8:22         ` Tudor Ambarus
  0 siblings, 0 replies; 48+ messages in thread
From: Tudor Ambarus @ 2023-03-17  8:22 UTC (permalink / raw)
  To: liao jaime
  Cc: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Pratyush Yadav, Michael Walle, linux-mtd, Jaime Liao, Julien Su,
	Alvin Zhou, Thomas Petazzoni, Michal Simek, linux-arm-kernel



On 3/17/23 07:43, liao jaime wrote:
> Hi Tudor
> 
>>
>> Hi, Jaime, Miquel,
>>
>> Any news with the datasheet, can you send me a copy of it?
>>
>> On 2/1/23 11:36, Miquel Raynal wrote:
>>> Describe this new part and provide the RWW flag for it.
>>>
>>> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
>>> ---
>>>  drivers/mtd/spi-nor/macronix.c | 3 +++
>>>  1 file changed, 3 insertions(+)
>>>
>>> diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c
>>> index 6853ec9ae65d..655e7eec865c 100644
>>> --- a/drivers/mtd/spi-nor/macronix.c
>>> +++ b/drivers/mtd/spi-nor/macronix.c
>>> @@ -82,6 +82,9 @@ static const struct flash_info macronix_nor_parts[] = {
>>>       { "mx25u51245g", INFO(0xc2253a, 0, 64 * 1024, 1024)
>>>               NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
>>>               FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
>>> +     { "mx25uw51245g", INFOB(0xc2813a, 0, 16 * 1024, 1024, 4)
>>> +             NO_SFDP_FLAGS(SECT_4K | SPI_NOR_RWW)
>>> +             FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
>>
>> Is this flash not really supporting SFDP?
> Yap MX25UW51245G have SFDP table support.

Okay, then for the next iteration of the series we should have
PARSE_SFDP set.

> As below is the link of octal DTR feature patch which include
> MX25UW51245G SFDP information.
> 
> https://patchwork.ozlabs.org/project/uboot/patch/20220718064922.20193-2-jaimeliao.tw@gmail.com/

cool, thanks.

I'd also like an erase, verify erase, write, read back and compare test
if possible, just to make sure that the flash works in it's first day.
Obviously in linux, not u-boot. The how is in the link below.

> 
> Sorry for that I can't share the link of datasheet cause Macronix
> didn't make it public.

Ok, no worries.

Cheers,
ta
>>
>> We ask people that update or add new flash entries to do a erase, verify
>> erase, write, read back and compare test. Also to dump the sysfs
>> entries. Here's how to do that:
>>
>> https://lore.kernel.org/lkml/97a3b023-b9bc-c34d-45a4-ddd56f47bd76@microchip.com/T/
>>
>> Thanks,
>> ta
> Thanks
> Jaime

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 0/8] mtd: spi-nor: read while write support
  2023-03-17  4:13   ` Tudor Ambarus
@ 2023-03-24 13:51     ` Miquel Raynal
  -1 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-03-24 13:51 UTC (permalink / raw)
  To: Tudor Ambarus
  Cc: Richard Weinberger, Vignesh Raghavendra, Pratyush Yadav,
	Michael Walle, linux-mtd, Julien Su, Jaime Liao, Jaime Liao,
	Alvin Zhou, Thomas Petazzoni, Michal Simek, linux-arm-kernel

Hi Tudor,

tudor.ambarus@linaro.org wrote on Fri, 17 Mar 2023 04:13:27 +0000:

> On 2/1/23 11:35, Miquel Raynal wrote:
> > Hello folks,
> > 
> > Here is the follow-up of the RFC trying to bring a little bit of
> > parallelism to support SPI-NOR Read While Write feature on parts
> > supporting it and featuring several banks.
> > 
> > I have received some hardware to make it work, so since the RFC, the
> > series has been updated to fix my mistakes, but the overall idea is the
> > same.
> > 
> > There is nothing Macronix specific in the implementation, the operations
> > and opcodes are exactly the same as before. The only difference being:
> > we may consider the chip usable when it is in the busy state during a
> > write or an erase. Any chip with an internal split allowing to perform
> > parallel operations might possibly leverage the benefits of this
> > implementation.
> > 
> > The first patches are just refactoring and preparation work, there is
> > almost no functional change, it's just a way to prepare the introduction
> > of the new locking mechanism and hopefully provide the cleanest and
> > simplest diff possible for this new feature. The actual change is all
> > contained in "mtd: spi-nor: Enhance locking to support reads while
> > writes". The logic is described in the commit log and copy/pasted here
> > for clarity:
> > 
> > "
> >     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 (common hardware limitation to limit costs)
> >     3#: Reads must remain serialized even though reads on different banks
> >         might occur at the same time.
> >     4#: 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.
> >     5#: 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 enforced by a bitfield of the in-use banks, so that only a single
> >         operation can happen in a specific bank at any time.
> >     2#: Is handled by the ongoing_pe boolean which is set 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#: An ongoing_rd boolean allows to track the ongoing reads, so that
> >         only one can be performed at a time.
> >     4#: An ongoing_io boolean 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 possibly the data), while the
> >         second part of the operation (the erase delay or the
> >         programmation delay) is when we can do something else in another
> >         bank.
> >     5#: Is handled by the three booleans presented above, if any of them is
> >         set, the chip is not yet ready for the operation and must wait.
> >     
> >     All these internal variables are protected by the existing lock, so that
> >     changes in this structure are atomic. The serialization is handled with
> >     a wait queue."
> > 
> > Here is now a benchmark with a Macronix MX25UW51245G with 4 banks and RWW
> > support:
> > 
> >      // Testing the two accesses in the same bank
> >      $ flash_speed -b0 -k0 -c10 -d /dev/mtd0
> >      [...]
> >      testing read while write latency
> >      read while write took 51ms, read ended after 51ms
> > 
> >      // Testing the two accesses within different banks
> >      $ flash_speed -b0 -k4096 -c10 -d /dev/mtd0
> >      [...]
> >      testing read while write latency
> >      read while write took 51ms, read ended after 20ms
> > 
> > Parallel accesses have been validated with io_paral. A slight increase
> > of the time spent on this test has however been noticed. With my  
> 
> how do the other tests look? Is there any change in performance for
> flashes that do not support RWW?

The current implementation takes care of not changing anything with the
existing flashes, when I resend I'll provide all the logs you asked
for, plus another quick test without the RWW feature bit set.

> 
> > configuration, over a limited number of blocks, the overall operation
> > took 22 min without any RWW changes up to 27 min with these changes,
> > maybe due to the number of additional scheduling situations involved).
> > 
> > Here is a branch with the mtd-utils patch bringing support for this
> > additional "-k" parameter in flash_speed (for the second block to use
> > during RWW testing), used to get the above results:
> > https://github.com/miquelraynal/mtd-utils/compare/master...rww
> > 
> > Cheers,
> > Miquèl
> > 
> > Changes in v4:
> > * Dropped patch 1/9 which got applied.
> > * s/SPI-NOR/SPI NOR/
> > * Turned n_banks into an u8 and moved it below in the struct to avoid
> >   padding.
> > * Updated the S3AN_INFO macro to set n_banks to 1 by default.
> > * Renamed the lock and prep helper to follow the order of each
> >   operation.
> > * Reworded a commit log to fit the recent changes upstream.
> > 
> > Changes in v3:
> > * Fix the bank offsets calculations by providing the same values when
> >   locking and when unlocking (might be changed by the functions themselves
> >   without use noticing).
> > * I completely changed the way the locking works because there was a new
> >   constraint: reads cannot be interrupted and status reads cannot happen
> >   during a read. Hence, as the multi-locks design was starting to be too
> >   messy, I changed the implementation to use a bunch of variables to
> >   track the read while write state, protected by the main spi-nor
> >   lock. If the internal state does not allow the operation, a sleep
> >   starts in a queue, until the threads are woken up after a state
> >   update. I know it is very verbose, I am open to suggestions.
> > 
> > 
> > Miquel Raynal (8):
> >   mtd: spi-nor: Introduce the concept of bank
> >   mtd: spi-nor: Add a macro to define more banks
> >   mtd: spi-nor: Reorder the preparation vs locking steps
> >   mtd: spi-nor: Separate preparation and locking
> >   mtd: spi-nor: Prepare the introduction of a new locking mechanism
> >   mtd: spi-nor: Add a RWW flag
> >   mtd: spi-nor: Enhance locking to support reads while writes
> >   mtd: spi-nor: macronix: Add support for mx25uw51245g with RWW
> > 
> >  drivers/mtd/spi-nor/core.c     | 396 +++++++++++++++++++++++++++++++--
> >  drivers/mtd/spi-nor/core.h     |  26 ++-
> >  drivers/mtd/spi-nor/macronix.c |   3 +
> >  drivers/mtd/spi-nor/xilinx.c   |   1 +
> >  include/linux/mtd/spi-nor.h    |  13 ++
> >  5 files changed, 409 insertions(+), 30 deletions(-)
> >   


Thanks,
Miquèl

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

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 0/8] mtd: spi-nor: read while write support
@ 2023-03-24 13:51     ` Miquel Raynal
  0 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-03-24 13:51 UTC (permalink / raw)
  To: Tudor Ambarus
  Cc: Richard Weinberger, Vignesh Raghavendra, Pratyush Yadav,
	Michael Walle, linux-mtd, Julien Su, Jaime Liao, Jaime Liao,
	Alvin Zhou, Thomas Petazzoni, Michal Simek, linux-arm-kernel

Hi Tudor,

tudor.ambarus@linaro.org wrote on Fri, 17 Mar 2023 04:13:27 +0000:

> On 2/1/23 11:35, Miquel Raynal wrote:
> > Hello folks,
> > 
> > Here is the follow-up of the RFC trying to bring a little bit of
> > parallelism to support SPI-NOR Read While Write feature on parts
> > supporting it and featuring several banks.
> > 
> > I have received some hardware to make it work, so since the RFC, the
> > series has been updated to fix my mistakes, but the overall idea is the
> > same.
> > 
> > There is nothing Macronix specific in the implementation, the operations
> > and opcodes are exactly the same as before. The only difference being:
> > we may consider the chip usable when it is in the busy state during a
> > write or an erase. Any chip with an internal split allowing to perform
> > parallel operations might possibly leverage the benefits of this
> > implementation.
> > 
> > The first patches are just refactoring and preparation work, there is
> > almost no functional change, it's just a way to prepare the introduction
> > of the new locking mechanism and hopefully provide the cleanest and
> > simplest diff possible for this new feature. The actual change is all
> > contained in "mtd: spi-nor: Enhance locking to support reads while
> > writes". The logic is described in the commit log and copy/pasted here
> > for clarity:
> > 
> > "
> >     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 (common hardware limitation to limit costs)
> >     3#: Reads must remain serialized even though reads on different banks
> >         might occur at the same time.
> >     4#: 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.
> >     5#: 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 enforced by a bitfield of the in-use banks, so that only a single
> >         operation can happen in a specific bank at any time.
> >     2#: Is handled by the ongoing_pe boolean which is set 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#: An ongoing_rd boolean allows to track the ongoing reads, so that
> >         only one can be performed at a time.
> >     4#: An ongoing_io boolean 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 possibly the data), while the
> >         second part of the operation (the erase delay or the
> >         programmation delay) is when we can do something else in another
> >         bank.
> >     5#: Is handled by the three booleans presented above, if any of them is
> >         set, the chip is not yet ready for the operation and must wait.
> >     
> >     All these internal variables are protected by the existing lock, so that
> >     changes in this structure are atomic. The serialization is handled with
> >     a wait queue."
> > 
> > Here is now a benchmark with a Macronix MX25UW51245G with 4 banks and RWW
> > support:
> > 
> >      // Testing the two accesses in the same bank
> >      $ flash_speed -b0 -k0 -c10 -d /dev/mtd0
> >      [...]
> >      testing read while write latency
> >      read while write took 51ms, read ended after 51ms
> > 
> >      // Testing the two accesses within different banks
> >      $ flash_speed -b0 -k4096 -c10 -d /dev/mtd0
> >      [...]
> >      testing read while write latency
> >      read while write took 51ms, read ended after 20ms
> > 
> > Parallel accesses have been validated with io_paral. A slight increase
> > of the time spent on this test has however been noticed. With my  
> 
> how do the other tests look? Is there any change in performance for
> flashes that do not support RWW?

The current implementation takes care of not changing anything with the
existing flashes, when I resend I'll provide all the logs you asked
for, plus another quick test without the RWW feature bit set.

> 
> > configuration, over a limited number of blocks, the overall operation
> > took 22 min without any RWW changes up to 27 min with these changes,
> > maybe due to the number of additional scheduling situations involved).
> > 
> > Here is a branch with the mtd-utils patch bringing support for this
> > additional "-k" parameter in flash_speed (for the second block to use
> > during RWW testing), used to get the above results:
> > https://github.com/miquelraynal/mtd-utils/compare/master...rww
> > 
> > Cheers,
> > Miquèl
> > 
> > Changes in v4:
> > * Dropped patch 1/9 which got applied.
> > * s/SPI-NOR/SPI NOR/
> > * Turned n_banks into an u8 and moved it below in the struct to avoid
> >   padding.
> > * Updated the S3AN_INFO macro to set n_banks to 1 by default.
> > * Renamed the lock and prep helper to follow the order of each
> >   operation.
> > * Reworded a commit log to fit the recent changes upstream.
> > 
> > Changes in v3:
> > * Fix the bank offsets calculations by providing the same values when
> >   locking and when unlocking (might be changed by the functions themselves
> >   without use noticing).
> > * I completely changed the way the locking works because there was a new
> >   constraint: reads cannot be interrupted and status reads cannot happen
> >   during a read. Hence, as the multi-locks design was starting to be too
> >   messy, I changed the implementation to use a bunch of variables to
> >   track the read while write state, protected by the main spi-nor
> >   lock. If the internal state does not allow the operation, a sleep
> >   starts in a queue, until the threads are woken up after a state
> >   update. I know it is very verbose, I am open to suggestions.
> > 
> > 
> > Miquel Raynal (8):
> >   mtd: spi-nor: Introduce the concept of bank
> >   mtd: spi-nor: Add a macro to define more banks
> >   mtd: spi-nor: Reorder the preparation vs locking steps
> >   mtd: spi-nor: Separate preparation and locking
> >   mtd: spi-nor: Prepare the introduction of a new locking mechanism
> >   mtd: spi-nor: Add a RWW flag
> >   mtd: spi-nor: Enhance locking to support reads while writes
> >   mtd: spi-nor: macronix: Add support for mx25uw51245g with RWW
> > 
> >  drivers/mtd/spi-nor/core.c     | 396 +++++++++++++++++++++++++++++++--
> >  drivers/mtd/spi-nor/core.h     |  26 ++-
> >  drivers/mtd/spi-nor/macronix.c |   3 +
> >  drivers/mtd/spi-nor/xilinx.c   |   1 +
> >  include/linux/mtd/spi-nor.h    |  13 ++
> >  5 files changed, 409 insertions(+), 30 deletions(-)
> >   


Thanks,
Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 3/8] mtd: spi-nor: Reorder the preparation vs locking steps
  2023-03-17  3:51       ` Tudor Ambarus
@ 2023-03-24 15:28         ` Miquel Raynal
  -1 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-03-24 15:28 UTC (permalink / raw)
  To: Tudor Ambarus
  Cc: Richard Weinberger, Vignesh Raghavendra, Pratyush Yadav,
	Michael Walle, linux-mtd, Julien Su, Jaime Liao, Jaime Liao,
	Alvin Zhou, Thomas Petazzoni, Michal Simek, linux-arm-kernel

Hi Tudor,

tudor.ambarus@linaro.org wrote on Fri, 17 Mar 2023 03:51:37 +0000:

> On 3/17/23 03:39, Tudor Ambarus wrote:
> > 
> > 
> > On 2/1/23 11:35, Miquel Raynal wrote:  
> >> The ->prepare()/->unprepare() hooks are now legacy, we no longer accept
> >> new drivers supporting them. The only remaining controllers using them
> >> acquires a per-chip mutex, which should not interfere with the rest of
> >> the operation done in the core. As a result, we should be safe to
> >> reorganize these helpers to first perform the preparation, before
> >> acquiring the core locks. This is necessary in order to be able to
> >> improve the locking mechanism in the core (coming next). No side effects
> >> are expected.
> >>
> >> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>  
> > 
> > Reviewed-by: Tudor Ambarus <tudor.ambarus@linaro.org>  
> 
> No, I take back my R-b. The reorder is fine, but you missed to update
> otp.c and swp.c, see below.

/me guilty

Not sure how that slipped in. Anyhow, I'll go through the missing files
ofc.

Thanks,
Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 3/8] mtd: spi-nor: Reorder the preparation vs locking steps
@ 2023-03-24 15:28         ` Miquel Raynal
  0 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-03-24 15:28 UTC (permalink / raw)
  To: Tudor Ambarus
  Cc: Richard Weinberger, Vignesh Raghavendra, Pratyush Yadav,
	Michael Walle, linux-mtd, Julien Su, Jaime Liao, Jaime Liao,
	Alvin Zhou, Thomas Petazzoni, Michal Simek, linux-arm-kernel

Hi Tudor,

tudor.ambarus@linaro.org wrote on Fri, 17 Mar 2023 03:51:37 +0000:

> On 3/17/23 03:39, Tudor Ambarus wrote:
> > 
> > 
> > On 2/1/23 11:35, Miquel Raynal wrote:  
> >> The ->prepare()/->unprepare() hooks are now legacy, we no longer accept
> >> new drivers supporting them. The only remaining controllers using them
> >> acquires a per-chip mutex, which should not interfere with the rest of
> >> the operation done in the core. As a result, we should be safe to
> >> reorganize these helpers to first perform the preparation, before
> >> acquiring the core locks. This is necessary in order to be able to
> >> improve the locking mechanism in the core (coming next). No side effects
> >> are expected.
> >>
> >> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>  
> > 
> > Reviewed-by: Tudor Ambarus <tudor.ambarus@linaro.org>  
> 
> No, I take back my R-b. The reorder is fine, but you missed to update
> otp.c and swp.c, see below.

/me guilty

Not sure how that slipped in. Anyhow, I'll go through the missing files
ofc.

Thanks,
Miquèl

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

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 7/8] mtd: spi-nor: Enhance locking to support reads while writes
  2023-03-17  5:59     ` Tudor Ambarus
@ 2023-03-24 17:41       ` Miquel Raynal
  -1 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-03-24 17:41 UTC (permalink / raw)
  To: Tudor Ambarus
  Cc: Richard Weinberger, Vignesh Raghavendra, Pratyush Yadav,
	Michael Walle, linux-mtd, Julien Su, Jaime Liao, Jaime Liao,
	Alvin Zhou, Thomas Petazzoni, Michal Simek, linux-arm-kernel

Hi Tudor,

tudor.ambarus@linaro.org wrote on Fri, 17 Mar 2023 05:59:08 +0000:

> Hi, Miquel,
> 
> I find the overall idea good.

Thanks a lot for the detailed review!

> On 2/1/23 11:36, Miquel Raynal wrote:
> > 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 (common hardware limitation to limit costs)
> > 3#: Reads must remain serialized even though reads on different banks
> >     might occur at the same time.  
> 
> 3# is unclear if one limits just at reading the commit message. Are the
> reads serialized per bank or per flash?

Per flash.

> After reading the code, it looks like all the reads are serialized per
> flash regardless if it reads registers or memory. I assume you meant
> that crossing a bank boundary with a single read is fine.

Yes, I will update that item to clarify.

> But can you
> really read from bank 1 and bank 3 at the same time? The code doesn't
> take this into consideration.

Yes this is taken into account and supported, a read can cross a bank
boundary.

> 
> > 4#: 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.
> > 5#: 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 enforced by a bitfield of the in-use banks, so that only a single
> >     operation can happen in a specific bank at any time.
> > 2#: Is handled by the ongoing_pe boolean which is set 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#: An ongoing_rd boolean allows to track the ongoing reads, so that
> >     only one can be performed at a time.
> > 4#: An ongoing_io boolean 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
> >     possibly the data), while the second part of the operation (the
> >     erase delay or the programmation delay) is when we can do something
> >     else in another bank.
> > 5#: Is handled by the three booleans presented above, if any of them is
> >     set, the chip is not yet ready for the operation and must wait.
> > 
> > All these internal variables are protected by the existing lock, so that
> > changes in this structure are atomic. The serialization is handled with
> > a wait queue.
> > 
> > Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> > ---
> >  drivers/mtd/spi-nor/core.c  | 319 ++++++++++++++++++++++++++++++++++--
> >  include/linux/mtd/spi-nor.h |  13 ++
> >  2 files changed, 317 insertions(+), 15 deletions(-)
> > 
> > diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
> > index ac4627e0d6c2..ad2436e3688f 100644
> > --- a/drivers/mtd/spi-nor/core.c
> > +++ b/drivers/mtd/spi-nor/core.c
> > @@ -589,6 +589,66 @@ int spi_nor_sr_ready(struct spi_nor *nor)
> >  	return !(nor->bouncebuf[0] & SR_WIP);
> >  }
> >  
> > +/**
> > + * spi_nor_parallel_locking() - Checks if the RWW locking scheme shall be used
> > + * @nor:	pointer to 'struct spi_nor'.
> > + *
> > + * Return: true if parallel locking is enabled, false otherwise.
> > + */
> > +static bool spi_nor_parallel_locking(struct spi_nor *nor)
> > +{
> > +	if (nor->controller_ops &&
> > +	    (nor->controller_ops->prepare || nor->controller_ops->unprepare))
> > +		return false;  
> 
> We won't allow controller drivers in spi-nor/controllers to benefit of
> this feature, just do:
> 	if (nor->controller_ops)
> 		return false;

That is also checked in the spi-nor init helper, where SNOR_F_RWW is
now set, so no need to check it again and again.

> > +	return nor->info->n_banks > 1 && nor->info->no_sfdp_flags & SPI_NOR_RWW;  
> 
> we don't play with flash info flags throughout the core. Introduce a
> SNOR_F equivalent flag, see how they're used. You'll be able to get rid
> of the n_banks check as well.

Thanks for the clear pointers, looks much nicer now!

> > +}
> > +
> > +/* Locking helpers for status read operations */
> > +static int spi_nor_rww_start_rdst(struct spi_nor *nor)
> > +{
> > +	int ret = -EAGAIN;  
> 
> you can have a pointer to rww here, you'll avoid all those dereferences
> from nor. I would add such a pointer wherever there is more than one
> dereference, so the comment is for the entire patch.

haha, I guess this is a matter of taste, I'm not bothered by those, but
ok, I'll make the change here and after :-)

> > +
> > +	mutex_lock(&nor->lock);
> > +
> > +	if (nor->rww.ongoing_io || nor->rww.ongoing_rd)
> > +		goto busy;
> > +
> > +	nor->rww.ongoing_io = true;
> > +	nor->rww.ongoing_rd = true;
> > +	ret = 0;
> > +
> > +busy:
> > +	mutex_unlock(&nor->lock);
> > +	return ret;
> > +}
> > +
> > +static void spi_nor_rww_end_rdst(struct spi_nor *nor)
> > +{
> > +	mutex_lock(&nor->lock);
> > +
> > +	nor->rww.ongoing_io = false;
> > +	nor->rww.ongoing_rd = false;
> > +
> > +	mutex_unlock(&nor->lock);
> > +}
> > +
> > +static int spi_nor_lock_rdst(struct spi_nor *nor)
> > +{
> > +	if (spi_nor_parallel_locking(nor))
> > +		return spi_nor_rww_start_rdst(nor);
> > +
> > +	return 0;
> > +}
> > +
> > +static void spi_nor_unlock_rdst(struct spi_nor *nor)
> > +{
> > +	if (spi_nor_parallel_locking(nor)) {
> > +		spi_nor_rww_end_rdst(nor);
> > +		wake_up(&nor->rww.wait);
> > +	}
> > +}
> > +
> >  /**
> >   * spi_nor_ready() - Query the flash to see if it is ready for new commands.
> >   * @nor:	pointer to 'struct spi_nor'.
> > @@ -597,11 +657,21 @@ int spi_nor_sr_ready(struct spi_nor *nor)
> >   */
> >  static int spi_nor_ready(struct spi_nor *nor)
> >  {
> > +	int ret;
> > +
> > +	ret = spi_nor_lock_rdst(nor);
> > +	if (ret)
> > +		return 0;
> > +
> >  	/* Flashes might override the standard routine. */
> >  	if (nor->params->ready)
> > -		return nor->params->ready(nor);
> > +		ret = nor->params->ready(nor);
> > +	else
> > +		ret = spi_nor_sr_ready(nor);
> >  
> > -	return spi_nor_sr_ready(nor);
> > +	spi_nor_unlock_rdst(nor);
> > +
> > +	return ret;
> >  }
> >  
> >  /**
> > @@ -1087,7 +1157,81 @@ static void spi_nor_unprep(struct spi_nor *nor)
> >  		nor->controller_ops->unprepare(nor);
> >  }
> >  
> > +static void spi_nor_offset_to_banks(struct spi_nor *nor, loff_t start, size_t len,  
> 
> pass directly the bank_size instead of the pointer to nor, you'll avoid
> the double dereference.

Done

> 
> > +				    unsigned int *first, unsigned int *last)  
> 
> unsigned long long *first, *last ?

Actually I want these to remain unsigned int, the ULL suffix just mean
the input might be a 64-bit value, but it is quite common to treat the
output as 32-bit. Here we do not expect values greater than 4.

> > +{
> > +	*first = DIV_ROUND_DOWN_ULL(start, nor->params->bank_size);
> > +	*last = DIV_ROUND_DOWN_ULL(start + len - 1, nor->params->bank_size);
> > +}
> > +
> >  /* Generic helpers for internal locking and serialization */
> > +static bool spi_nor_rww_start_io(struct spi_nor *nor)
> > +{
> > +	bool start = false;
> > +
> > +	mutex_lock(&nor->lock);
> > +
> > +	if (nor->rww.ongoing_io)
> > +		goto busy;
> > +
> > +	nor->rww.ongoing_io = true;
> > +	start = true;
> > +
> > +busy:
> > +	mutex_unlock(&nor->lock);
> > +	return start;
> > +}
> > +
> > +static void spi_nor_rww_end_io(struct spi_nor *nor)
> > +{
> > +	mutex_lock(&nor->lock);
> > +	nor->rww.ongoing_io = false;
> > +	mutex_unlock(&nor->lock);
> > +}
> > +
> > +static int spi_nor_lock_device(struct spi_nor *nor)
> > +{
> > +	if (!spi_nor_parallel_locking(nor))
> > +		return 0;
> > +
> > +	return wait_event_killable(nor->rww.wait, spi_nor_rww_start_io(nor));
> > +}
> > +
> > +static void spi_nor_unlock_device(struct spi_nor *nor)
> > +{
> > +	if (spi_nor_parallel_locking(nor))
> > +		spi_nor_rww_end_io(nor);  
> 
> shall we wake_up here too?

True

> 
> > +}
> > +
> > +/* Generic helpers for internal locking and serialization */
> > +static bool spi_nor_rww_start_exclusive(struct spi_nor *nor)
> > +{
> > +	bool start = false;
> > +
> > +	mutex_lock(&nor->lock);
> > +
> > +	if (nor->rww.ongoing_io || nor->rww.ongoing_rd || nor->rww.ongoing_pe)
> > +		goto busy;
> > +
> > +	nor->rww.ongoing_io = true;
> > +	nor->rww.ongoing_rd = true;
> > +	nor->rww.ongoing_pe = true;
> > +	start = true;
> > +
> > +busy:
> > +	mutex_unlock(&nor->lock);
> > +	return start;
> > +}
> > +
> > +static void spi_nor_rww_end_exclusive(struct spi_nor *nor)
> > +{
> > +	mutex_lock(&nor->lock);
> > +	nor->rww.ongoing_io = false;
> > +	nor->rww.ongoing_rd = false;
> > +	nor->rww.ongoing_pe = false;
> > +	mutex_unlock(&nor->lock);
> > +}
> > +
> >  int spi_nor_prep_and_lock(struct spi_nor *nor)
> >  {
> >  	int ret;
> > @@ -1096,19 +1240,71 @@ int spi_nor_prep_and_lock(struct spi_nor *nor)
> >  	if (ret)
> >  		return ret;
> >  
> > -	mutex_lock(&nor->lock);
> > +	if (!spi_nor_parallel_locking(nor))
> > +		mutex_lock(&nor->lock);
> > +	else
> > +		ret = wait_event_killable(nor->rww.wait,
> > +					  spi_nor_rww_start_exclusive(nor));
> >  
> 
> No, don't touch spi_nor_prep_and_lock() and spi_nor_unlock_and_unprep(),
> you're giving the impresion that users of it (OTP, SWP) are safe to use
> them while reads or PE are in progress, which is not the case, because
> you don't guard the ops that they're using. You'll also have to document
> the flash info RWW flag ands say it is mutual exclusive with SWP and OTP
> features.

Actually I'm not getting what you mean here. Any access that is not
a pure read, write or erase (like register accesses, otp and swp
accesses) are automatically treated as needing full and exclusive
access to the chip. So everything works as before with these, even
if RWW is enabled, because that's not where we want performance
improvements. So I need to implement a dedicated "parallel_locking"
path inside these helpers to clearly enforcing an exclusive access to
the chip.

> > -	return 0;
> > +	return ret;
> >  }
> >  
> >  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 {
> > +		spi_nor_rww_end_exclusive(nor);
> > +		wake_up(&nor->rww.wait);
> > +	}
> >  
> >  	spi_nor_unprep(nor);
> >  }
> >  
> >  /* Internal locking helpers for program and erase operations */
> > +static bool spi_nor_rww_start_pe(struct spi_nor *nor, loff_t start, size_t len)
> > +{
> > +	unsigned int first, last;
> > +	bool started = false;
> > +	int bank;
> > +
> > +	mutex_lock(&nor->lock);
> > +
> > +	if (nor->rww.ongoing_io || nor->rww.ongoing_rd || nor->rww.ongoing_pe)
> > +		goto busy;
> > +
> > +	spi_nor_offset_to_banks(nor, start, len, &first, &last);
> > +	for (bank = first; bank <= last; bank++)
> > +		if (nor->rww.used_banks & BIT(bank))
> > +			goto busy;
> > +
> > +	for (bank = first; bank <= last; bank++)  
> you can avoid this second look by introducing a local used_banks
> variable and have the mask set in the previous loop. Then you'll just do
> an init at this point.

Done.

> 
> > +		nor->rww.used_banks |= BIT(bank);
> > +
> > +	nor->rww.ongoing_pe = true;
> > +	started = true;
> > +
> > +busy:
> > +	mutex_unlock(&nor->lock);
> > +	return started;
> > +}
> > +
> > +static void spi_nor_rww_end_pe(struct spi_nor *nor, loff_t start, size_t len)
> > +{
> > +	unsigned int first, last;
> > +	int bank;
> > +
> > +	mutex_lock(&nor->lock);
> > +
> > +	spi_nor_offset_to_banks(nor, start, len, &first, &last);
> > +	for (bank = first; bank <= last; bank++)
> > +		nor->rww.used_banks &= ~BIT(bank);
> > +
> > +	nor->rww.ongoing_pe = false;
> > +
> > +	mutex_unlock(&nor->lock);
> > +}
> > +
> >  static int spi_nor_prep_and_lock_pe(struct spi_nor *nor, loff_t start, size_t len)
> >  {
> >  	int ret;
> > @@ -1117,19 +1313,73 @@ static int spi_nor_prep_and_lock_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
> > +		ret = wait_event_killable(nor->rww.wait,
> > +					  spi_nor_rww_start_pe(nor, start, len));
> >  
> > -	return 0;
> > +	return ret;
> >  }
> >  
> >  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 {
> > +		spi_nor_rww_end_pe(nor, start, len);
> > +		wake_up(&nor->rww.wait);
> > +	}
> >  
> >  	spi_nor_unprep(nor);
> >  }
> >  
> >  /* Internal locking helpers for read operations */
> > +static bool spi_nor_rww_start_rd(struct spi_nor *nor, loff_t start, size_t len)
> > +{
> > +	unsigned int first, last;
> > +	bool started = false;
> > +	int bank;
> > +
> > +	mutex_lock(&nor->lock);
> > +
> > +	if (nor->rww.ongoing_io || nor->rww.ongoing_rd)
> > +		goto busy;
> > +
> > +	spi_nor_offset_to_banks(nor, start, len, &first, &last);
> > +	for (bank = first; bank <= last; bank++)
> > +		if (nor->rww.used_banks & BIT(bank))
> > +			goto busy;
> > +
> > +	for (bank = first; bank <= last; bank++)  
> 
> mask to avoid 2nd loop

Done as well.

Thanks a lot!
Miquèl

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

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 7/8] mtd: spi-nor: Enhance locking to support reads while writes
@ 2023-03-24 17:41       ` Miquel Raynal
  0 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-03-24 17:41 UTC (permalink / raw)
  To: Tudor Ambarus
  Cc: Richard Weinberger, Vignesh Raghavendra, Pratyush Yadav,
	Michael Walle, linux-mtd, Julien Su, Jaime Liao, Jaime Liao,
	Alvin Zhou, Thomas Petazzoni, Michal Simek, linux-arm-kernel

Hi Tudor,

tudor.ambarus@linaro.org wrote on Fri, 17 Mar 2023 05:59:08 +0000:

> Hi, Miquel,
> 
> I find the overall idea good.

Thanks a lot for the detailed review!

> On 2/1/23 11:36, Miquel Raynal wrote:
> > 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 (common hardware limitation to limit costs)
> > 3#: Reads must remain serialized even though reads on different banks
> >     might occur at the same time.  
> 
> 3# is unclear if one limits just at reading the commit message. Are the
> reads serialized per bank or per flash?

Per flash.

> After reading the code, it looks like all the reads are serialized per
> flash regardless if it reads registers or memory. I assume you meant
> that crossing a bank boundary with a single read is fine.

Yes, I will update that item to clarify.

> But can you
> really read from bank 1 and bank 3 at the same time? The code doesn't
> take this into consideration.

Yes this is taken into account and supported, a read can cross a bank
boundary.

> 
> > 4#: 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.
> > 5#: 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 enforced by a bitfield of the in-use banks, so that only a single
> >     operation can happen in a specific bank at any time.
> > 2#: Is handled by the ongoing_pe boolean which is set 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#: An ongoing_rd boolean allows to track the ongoing reads, so that
> >     only one can be performed at a time.
> > 4#: An ongoing_io boolean 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
> >     possibly the data), while the second part of the operation (the
> >     erase delay or the programmation delay) is when we can do something
> >     else in another bank.
> > 5#: Is handled by the three booleans presented above, if any of them is
> >     set, the chip is not yet ready for the operation and must wait.
> > 
> > All these internal variables are protected by the existing lock, so that
> > changes in this structure are atomic. The serialization is handled with
> > a wait queue.
> > 
> > Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> > ---
> >  drivers/mtd/spi-nor/core.c  | 319 ++++++++++++++++++++++++++++++++++--
> >  include/linux/mtd/spi-nor.h |  13 ++
> >  2 files changed, 317 insertions(+), 15 deletions(-)
> > 
> > diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
> > index ac4627e0d6c2..ad2436e3688f 100644
> > --- a/drivers/mtd/spi-nor/core.c
> > +++ b/drivers/mtd/spi-nor/core.c
> > @@ -589,6 +589,66 @@ int spi_nor_sr_ready(struct spi_nor *nor)
> >  	return !(nor->bouncebuf[0] & SR_WIP);
> >  }
> >  
> > +/**
> > + * spi_nor_parallel_locking() - Checks if the RWW locking scheme shall be used
> > + * @nor:	pointer to 'struct spi_nor'.
> > + *
> > + * Return: true if parallel locking is enabled, false otherwise.
> > + */
> > +static bool spi_nor_parallel_locking(struct spi_nor *nor)
> > +{
> > +	if (nor->controller_ops &&
> > +	    (nor->controller_ops->prepare || nor->controller_ops->unprepare))
> > +		return false;  
> 
> We won't allow controller drivers in spi-nor/controllers to benefit of
> this feature, just do:
> 	if (nor->controller_ops)
> 		return false;

That is also checked in the spi-nor init helper, where SNOR_F_RWW is
now set, so no need to check it again and again.

> > +	return nor->info->n_banks > 1 && nor->info->no_sfdp_flags & SPI_NOR_RWW;  
> 
> we don't play with flash info flags throughout the core. Introduce a
> SNOR_F equivalent flag, see how they're used. You'll be able to get rid
> of the n_banks check as well.

Thanks for the clear pointers, looks much nicer now!

> > +}
> > +
> > +/* Locking helpers for status read operations */
> > +static int spi_nor_rww_start_rdst(struct spi_nor *nor)
> > +{
> > +	int ret = -EAGAIN;  
> 
> you can have a pointer to rww here, you'll avoid all those dereferences
> from nor. I would add such a pointer wherever there is more than one
> dereference, so the comment is for the entire patch.

haha, I guess this is a matter of taste, I'm not bothered by those, but
ok, I'll make the change here and after :-)

> > +
> > +	mutex_lock(&nor->lock);
> > +
> > +	if (nor->rww.ongoing_io || nor->rww.ongoing_rd)
> > +		goto busy;
> > +
> > +	nor->rww.ongoing_io = true;
> > +	nor->rww.ongoing_rd = true;
> > +	ret = 0;
> > +
> > +busy:
> > +	mutex_unlock(&nor->lock);
> > +	return ret;
> > +}
> > +
> > +static void spi_nor_rww_end_rdst(struct spi_nor *nor)
> > +{
> > +	mutex_lock(&nor->lock);
> > +
> > +	nor->rww.ongoing_io = false;
> > +	nor->rww.ongoing_rd = false;
> > +
> > +	mutex_unlock(&nor->lock);
> > +}
> > +
> > +static int spi_nor_lock_rdst(struct spi_nor *nor)
> > +{
> > +	if (spi_nor_parallel_locking(nor))
> > +		return spi_nor_rww_start_rdst(nor);
> > +
> > +	return 0;
> > +}
> > +
> > +static void spi_nor_unlock_rdst(struct spi_nor *nor)
> > +{
> > +	if (spi_nor_parallel_locking(nor)) {
> > +		spi_nor_rww_end_rdst(nor);
> > +		wake_up(&nor->rww.wait);
> > +	}
> > +}
> > +
> >  /**
> >   * spi_nor_ready() - Query the flash to see if it is ready for new commands.
> >   * @nor:	pointer to 'struct spi_nor'.
> > @@ -597,11 +657,21 @@ int spi_nor_sr_ready(struct spi_nor *nor)
> >   */
> >  static int spi_nor_ready(struct spi_nor *nor)
> >  {
> > +	int ret;
> > +
> > +	ret = spi_nor_lock_rdst(nor);
> > +	if (ret)
> > +		return 0;
> > +
> >  	/* Flashes might override the standard routine. */
> >  	if (nor->params->ready)
> > -		return nor->params->ready(nor);
> > +		ret = nor->params->ready(nor);
> > +	else
> > +		ret = spi_nor_sr_ready(nor);
> >  
> > -	return spi_nor_sr_ready(nor);
> > +	spi_nor_unlock_rdst(nor);
> > +
> > +	return ret;
> >  }
> >  
> >  /**
> > @@ -1087,7 +1157,81 @@ static void spi_nor_unprep(struct spi_nor *nor)
> >  		nor->controller_ops->unprepare(nor);
> >  }
> >  
> > +static void spi_nor_offset_to_banks(struct spi_nor *nor, loff_t start, size_t len,  
> 
> pass directly the bank_size instead of the pointer to nor, you'll avoid
> the double dereference.

Done

> 
> > +				    unsigned int *first, unsigned int *last)  
> 
> unsigned long long *first, *last ?

Actually I want these to remain unsigned int, the ULL suffix just mean
the input might be a 64-bit value, but it is quite common to treat the
output as 32-bit. Here we do not expect values greater than 4.

> > +{
> > +	*first = DIV_ROUND_DOWN_ULL(start, nor->params->bank_size);
> > +	*last = DIV_ROUND_DOWN_ULL(start + len - 1, nor->params->bank_size);
> > +}
> > +
> >  /* Generic helpers for internal locking and serialization */
> > +static bool spi_nor_rww_start_io(struct spi_nor *nor)
> > +{
> > +	bool start = false;
> > +
> > +	mutex_lock(&nor->lock);
> > +
> > +	if (nor->rww.ongoing_io)
> > +		goto busy;
> > +
> > +	nor->rww.ongoing_io = true;
> > +	start = true;
> > +
> > +busy:
> > +	mutex_unlock(&nor->lock);
> > +	return start;
> > +}
> > +
> > +static void spi_nor_rww_end_io(struct spi_nor *nor)
> > +{
> > +	mutex_lock(&nor->lock);
> > +	nor->rww.ongoing_io = false;
> > +	mutex_unlock(&nor->lock);
> > +}
> > +
> > +static int spi_nor_lock_device(struct spi_nor *nor)
> > +{
> > +	if (!spi_nor_parallel_locking(nor))
> > +		return 0;
> > +
> > +	return wait_event_killable(nor->rww.wait, spi_nor_rww_start_io(nor));
> > +}
> > +
> > +static void spi_nor_unlock_device(struct spi_nor *nor)
> > +{
> > +	if (spi_nor_parallel_locking(nor))
> > +		spi_nor_rww_end_io(nor);  
> 
> shall we wake_up here too?

True

> 
> > +}
> > +
> > +/* Generic helpers for internal locking and serialization */
> > +static bool spi_nor_rww_start_exclusive(struct spi_nor *nor)
> > +{
> > +	bool start = false;
> > +
> > +	mutex_lock(&nor->lock);
> > +
> > +	if (nor->rww.ongoing_io || nor->rww.ongoing_rd || nor->rww.ongoing_pe)
> > +		goto busy;
> > +
> > +	nor->rww.ongoing_io = true;
> > +	nor->rww.ongoing_rd = true;
> > +	nor->rww.ongoing_pe = true;
> > +	start = true;
> > +
> > +busy:
> > +	mutex_unlock(&nor->lock);
> > +	return start;
> > +}
> > +
> > +static void spi_nor_rww_end_exclusive(struct spi_nor *nor)
> > +{
> > +	mutex_lock(&nor->lock);
> > +	nor->rww.ongoing_io = false;
> > +	nor->rww.ongoing_rd = false;
> > +	nor->rww.ongoing_pe = false;
> > +	mutex_unlock(&nor->lock);
> > +}
> > +
> >  int spi_nor_prep_and_lock(struct spi_nor *nor)
> >  {
> >  	int ret;
> > @@ -1096,19 +1240,71 @@ int spi_nor_prep_and_lock(struct spi_nor *nor)
> >  	if (ret)
> >  		return ret;
> >  
> > -	mutex_lock(&nor->lock);
> > +	if (!spi_nor_parallel_locking(nor))
> > +		mutex_lock(&nor->lock);
> > +	else
> > +		ret = wait_event_killable(nor->rww.wait,
> > +					  spi_nor_rww_start_exclusive(nor));
> >  
> 
> No, don't touch spi_nor_prep_and_lock() and spi_nor_unlock_and_unprep(),
> you're giving the impresion that users of it (OTP, SWP) are safe to use
> them while reads or PE are in progress, which is not the case, because
> you don't guard the ops that they're using. You'll also have to document
> the flash info RWW flag ands say it is mutual exclusive with SWP and OTP
> features.

Actually I'm not getting what you mean here. Any access that is not
a pure read, write or erase (like register accesses, otp and swp
accesses) are automatically treated as needing full and exclusive
access to the chip. So everything works as before with these, even
if RWW is enabled, because that's not where we want performance
improvements. So I need to implement a dedicated "parallel_locking"
path inside these helpers to clearly enforcing an exclusive access to
the chip.

> > -	return 0;
> > +	return ret;
> >  }
> >  
> >  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 {
> > +		spi_nor_rww_end_exclusive(nor);
> > +		wake_up(&nor->rww.wait);
> > +	}
> >  
> >  	spi_nor_unprep(nor);
> >  }
> >  
> >  /* Internal locking helpers for program and erase operations */
> > +static bool spi_nor_rww_start_pe(struct spi_nor *nor, loff_t start, size_t len)
> > +{
> > +	unsigned int first, last;
> > +	bool started = false;
> > +	int bank;
> > +
> > +	mutex_lock(&nor->lock);
> > +
> > +	if (nor->rww.ongoing_io || nor->rww.ongoing_rd || nor->rww.ongoing_pe)
> > +		goto busy;
> > +
> > +	spi_nor_offset_to_banks(nor, start, len, &first, &last);
> > +	for (bank = first; bank <= last; bank++)
> > +		if (nor->rww.used_banks & BIT(bank))
> > +			goto busy;
> > +
> > +	for (bank = first; bank <= last; bank++)  
> you can avoid this second look by introducing a local used_banks
> variable and have the mask set in the previous loop. Then you'll just do
> an init at this point.

Done.

> 
> > +		nor->rww.used_banks |= BIT(bank);
> > +
> > +	nor->rww.ongoing_pe = true;
> > +	started = true;
> > +
> > +busy:
> > +	mutex_unlock(&nor->lock);
> > +	return started;
> > +}
> > +
> > +static void spi_nor_rww_end_pe(struct spi_nor *nor, loff_t start, size_t len)
> > +{
> > +	unsigned int first, last;
> > +	int bank;
> > +
> > +	mutex_lock(&nor->lock);
> > +
> > +	spi_nor_offset_to_banks(nor, start, len, &first, &last);
> > +	for (bank = first; bank <= last; bank++)
> > +		nor->rww.used_banks &= ~BIT(bank);
> > +
> > +	nor->rww.ongoing_pe = false;
> > +
> > +	mutex_unlock(&nor->lock);
> > +}
> > +
> >  static int spi_nor_prep_and_lock_pe(struct spi_nor *nor, loff_t start, size_t len)
> >  {
> >  	int ret;
> > @@ -1117,19 +1313,73 @@ static int spi_nor_prep_and_lock_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
> > +		ret = wait_event_killable(nor->rww.wait,
> > +					  spi_nor_rww_start_pe(nor, start, len));
> >  
> > -	return 0;
> > +	return ret;
> >  }
> >  
> >  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 {
> > +		spi_nor_rww_end_pe(nor, start, len);
> > +		wake_up(&nor->rww.wait);
> > +	}
> >  
> >  	spi_nor_unprep(nor);
> >  }
> >  
> >  /* Internal locking helpers for read operations */
> > +static bool spi_nor_rww_start_rd(struct spi_nor *nor, loff_t start, size_t len)
> > +{
> > +	unsigned int first, last;
> > +	bool started = false;
> > +	int bank;
> > +
> > +	mutex_lock(&nor->lock);
> > +
> > +	if (nor->rww.ongoing_io || nor->rww.ongoing_rd)
> > +		goto busy;
> > +
> > +	spi_nor_offset_to_banks(nor, start, len, &first, &last);
> > +	for (bank = first; bank <= last; bank++)
> > +		if (nor->rww.used_banks & BIT(bank))
> > +			goto busy;
> > +
> > +	for (bank = first; bank <= last; bank++)  
> 
> mask to avoid 2nd loop

Done as well.

Thanks a lot!
Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 7/8] mtd: spi-nor: Enhance locking to support reads while writes
  2023-03-24 17:41       ` Miquel Raynal
@ 2023-03-27  9:29         ` Tudor Ambarus
  -1 siblings, 0 replies; 48+ messages in thread
From: Tudor Ambarus @ 2023-03-27  9:29 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Richard Weinberger, Vignesh Raghavendra, Pratyush Yadav,
	Michael Walle, linux-mtd, Julien Su, Jaime Liao, Jaime Liao,
	Alvin Zhou, Thomas Petazzoni, Michal Simek, linux-arm-kernel



On 3/24/23 17:41, Miquel Raynal wrote:
> Hi Tudor,
> 

Hi!

> tudor.ambarus@linaro.org wrote on Fri, 17 Mar 2023 05:59:08 +0000:
> 
>> Hi, Miquel,
>>
>> I find the overall idea good.
> 
> Thanks a lot for the detailed review!
> 
>> On 2/1/23 11:36, Miquel Raynal wrote:
>>> 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 (common hardware limitation to limit costs)
>>> 3#: Reads must remain serialized even though reads on different banks
>>>     might occur at the same time.  
>>
>> 3# is unclear if one limits just at reading the commit message. Are the
>> reads serialized per bank or per flash?
> 
> Per flash.
> 
>> After reading the code, it looks like all the reads are serialized per
>> flash regardless if it reads registers or memory. I assume you meant
>> that crossing a bank boundary with a single read is fine.
> 
> Yes, I will update that item to clarify.

thanks!

> 
>> But can you
>> really read from bank 1 and bank 3 at the same time? The code doesn't
>> take this into consideration.
> 
> Yes this is taken into account and supported, a read can cross a bank
> boundary.

No, I meant that you can't do a read from bank 1 and while the first
read is in progress, to start a second read from the 3rd bank and
process both reads in parallel, reading from both banks at the same
time. At least not with the current code, because you set
rww.{ongoing_io, ongoing_rd} to true and the second read will wait.
Cross boundary reads on successive banks should work with current code,
yes. So what does the hw support?

> 
>>
>>> 4#: 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.
>>> 5#: 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 enforced by a bitfield of the in-use banks, so that only a single
>>>     operation can happen in a specific bank at any time.
>>> 2#: Is handled by the ongoing_pe boolean which is set 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#: An ongoing_rd boolean allows to track the ongoing reads, so that
>>>     only one can be performed at a time.
>>> 4#: An ongoing_io boolean 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
>>>     possibly the data), while the second part of the operation (the
>>>     erase delay or the programmation delay) is when we can do something
>>>     else in another bank.
>>> 5#: Is handled by the three booleans presented above, if any of them is
>>>     set, the chip is not yet ready for the operation and must wait.
>>>
>>> All these internal variables are protected by the existing lock, so that
>>> changes in this structure are atomic. The serialization is handled with
>>> a wait queue.
>>>
>>> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
>>> ---
>>>  drivers/mtd/spi-nor/core.c  | 319 ++++++++++++++++++++++++++++++++++--
>>>  include/linux/mtd/spi-nor.h |  13 ++
>>>  2 files changed, 317 insertions(+), 15 deletions(-)
>>>
>>> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
>>> index ac4627e0d6c2..ad2436e3688f 100644
>>> --- a/drivers/mtd/spi-nor/core.c
>>> +++ b/drivers/mtd/spi-nor/core.c
>>> @@ -589,6 +589,66 @@ int spi_nor_sr_ready(struct spi_nor *nor)
>>>  	return !(nor->bouncebuf[0] & SR_WIP);
>>>  }
>>>  
>>> +/**
>>> + * spi_nor_parallel_locking() - Checks if the RWW locking scheme shall be used
>>> + * @nor:	pointer to 'struct spi_nor'.
>>> + *
>>> + * Return: true if parallel locking is enabled, false otherwise.
>>> + */
>>> +static bool spi_nor_parallel_locking(struct spi_nor *nor)
>>> +{
>>> +	if (nor->controller_ops &&
>>> +	    (nor->controller_ops->prepare || nor->controller_ops->unprepare))
>>> +		return false;  
>>
>> We won't allow controller drivers in spi-nor/controllers to benefit of
>> this feature, just do:
>> 	if (nor->controller_ops)
>> 		return false;
> 
> That is also checked in the spi-nor init helper, where SNOR_F_RWW is
> now set, so no need to check it again and again.
> 
>>> +	return nor->info->n_banks > 1 && nor->info->no_sfdp_flags & SPI_NOR_RWW;  
>>
>> we don't play with flash info flags throughout the core. Introduce a
>> SNOR_F equivalent flag, see how they're used. You'll be able to get rid
>> of the n_banks check as well.
> 
> Thanks for the clear pointers, looks much nicer now!
> 
>>> +}
>>> +
>>> +/* Locking helpers for status read operations */
>>> +static int spi_nor_rww_start_rdst(struct spi_nor *nor)
>>> +{
>>> +	int ret = -EAGAIN;  
>>
>> you can have a pointer to rww here, you'll avoid all those dereferences
>> from nor. I would add such a pointer wherever there is more than one
>> dereference, so the comment is for the entire patch.
> 
> haha, I guess this is a matter of taste, I'm not bothered by those, but
> ok, I'll make the change here and after :-)>
>>> +
>>> +	mutex_lock(&nor->lock);
>>> +
>>> +	if (nor->rww.ongoing_io || nor->rww.ongoing_rd)
>>> +		goto busy;
>>> +
>>> +	nor->rww.ongoing_io = true;
>>> +	nor->rww.ongoing_rd = true;
>>> +	ret = 0;
>>> +
>>> +busy:
>>> +	mutex_unlock(&nor->lock);
>>> +	return ret;
>>> +}
>>> +
>>> +static void spi_nor_rww_end_rdst(struct spi_nor *nor)
>>> +{
>>> +	mutex_lock(&nor->lock);
>>> +
>>> +	nor->rww.ongoing_io = false;
>>> +	nor->rww.ongoing_rd = false;
>>> +
>>> +	mutex_unlock(&nor->lock);
>>> +}
>>> +
>>> +static int spi_nor_lock_rdst(struct spi_nor *nor)
>>> +{
>>> +	if (spi_nor_parallel_locking(nor))
>>> +		return spi_nor_rww_start_rdst(nor);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static void spi_nor_unlock_rdst(struct spi_nor *nor)
>>> +{
>>> +	if (spi_nor_parallel_locking(nor)) {
>>> +		spi_nor_rww_end_rdst(nor);
>>> +		wake_up(&nor->rww.wait);
>>> +	}
>>> +}
>>> +
>>>  /**
>>>   * spi_nor_ready() - Query the flash to see if it is ready for new commands.
>>>   * @nor:	pointer to 'struct spi_nor'.
>>> @@ -597,11 +657,21 @@ int spi_nor_sr_ready(struct spi_nor *nor)
>>>   */
>>>  static int spi_nor_ready(struct spi_nor *nor)
>>>  {
>>> +	int ret;
>>> +
>>> +	ret = spi_nor_lock_rdst(nor);
>>> +	if (ret)
>>> +		return 0;
>>> +
>>>  	/* Flashes might override the standard routine. */
>>>  	if (nor->params->ready)
>>> -		return nor->params->ready(nor);
>>> +		ret = nor->params->ready(nor);
>>> +	else
>>> +		ret = spi_nor_sr_ready(nor);
>>>  
>>> -	return spi_nor_sr_ready(nor);
>>> +	spi_nor_unlock_rdst(nor);
>>> +
>>> +	return ret;
>>>  }
>>>  
>>>  /**
>>> @@ -1087,7 +1157,81 @@ static void spi_nor_unprep(struct spi_nor *nor)
>>>  		nor->controller_ops->unprepare(nor);
>>>  }
>>>  
>>> +static void spi_nor_offset_to_banks(struct spi_nor *nor, loff_t start, size_t len,  
>>
>> pass directly the bank_size instead of the pointer to nor, you'll avoid
>> the double dereference.
> 
> Done
> 
>>
>>> +				    unsigned int *first, unsigned int *last)  
>>
>> unsigned long long *first, *last ?
> 
> Actually I want these to remain unsigned int, the ULL suffix just mean
> the input might be a 64-bit value, but it is quite common to treat the
> output as 32-bit. Here we do not expect values greater than 4.

Ok. Then maybe we should match how we define nbanks in NOR. Was it a u8?

> 
>>> +{
>>> +	*first = DIV_ROUND_DOWN_ULL(start, nor->params->bank_size);
>>> +	*last = DIV_ROUND_DOWN_ULL(start + len - 1, nor->params->bank_size);
>>> +}
>>> +
>>>  /* Generic helpers for internal locking and serialization */
>>> +static bool spi_nor_rww_start_io(struct spi_nor *nor)
>>> +{
>>> +	bool start = false;
>>> +
>>> +	mutex_lock(&nor->lock);
>>> +
>>> +	if (nor->rww.ongoing_io)
>>> +		goto busy;
>>> +
>>> +	nor->rww.ongoing_io = true;
>>> +	start = true;
>>> +
>>> +busy:
>>> +	mutex_unlock(&nor->lock);
>>> +	return start;
>>> +}
>>> +
>>> +static void spi_nor_rww_end_io(struct spi_nor *nor)
>>> +{
>>> +	mutex_lock(&nor->lock);
>>> +	nor->rww.ongoing_io = false;
>>> +	mutex_unlock(&nor->lock);
>>> +}
>>> +
>>> +static int spi_nor_lock_device(struct spi_nor *nor)
>>> +{
>>> +	if (!spi_nor_parallel_locking(nor))
>>> +		return 0;
>>> +
>>> +	return wait_event_killable(nor->rww.wait, spi_nor_rww_start_io(nor));
>>> +}
>>> +
>>> +static void spi_nor_unlock_device(struct spi_nor *nor)
>>> +{
>>> +	if (spi_nor_parallel_locking(nor))
>>> +		spi_nor_rww_end_io(nor);  
>>
>> shall we wake_up here too?
> 
> True
> 
>>
>>> +}
>>> +
>>> +/* Generic helpers for internal locking and serialization */
>>> +static bool spi_nor_rww_start_exclusive(struct spi_nor *nor)
>>> +{
>>> +	bool start = false;
>>> +
>>> +	mutex_lock(&nor->lock);
>>> +
>>> +	if (nor->rww.ongoing_io || nor->rww.ongoing_rd || nor->rww.ongoing_pe)
>>> +		goto busy;
>>> +
>>> +	nor->rww.ongoing_io = true;
>>> +	nor->rww.ongoing_rd = true;
>>> +	nor->rww.ongoing_pe = true;
>>> +	start = true;
>>> +
>>> +busy:
>>> +	mutex_unlock(&nor->lock);
>>> +	return start;
>>> +}
>>> +
>>> +static void spi_nor_rww_end_exclusive(struct spi_nor *nor)
>>> +{
>>> +	mutex_lock(&nor->lock);
>>> +	nor->rww.ongoing_io = false;
>>> +	nor->rww.ongoing_rd = false;
>>> +	nor->rww.ongoing_pe = false;
>>> +	mutex_unlock(&nor->lock);
>>> +}
>>> +
>>>  int spi_nor_prep_and_lock(struct spi_nor *nor)
>>>  {
>>>  	int ret;
>>> @@ -1096,19 +1240,71 @@ int spi_nor_prep_and_lock(struct spi_nor *nor)
>>>  	if (ret)
>>>  		return ret;
>>>  
>>> -	mutex_lock(&nor->lock);
>>> +	if (!spi_nor_parallel_locking(nor))
>>> +		mutex_lock(&nor->lock);
>>> +	else
>>> +		ret = wait_event_killable(nor->rww.wait,
>>> +					  spi_nor_rww_start_exclusive(nor));
>>>  
>>
>> No, don't touch spi_nor_prep_and_lock() and spi_nor_unlock_and_unprep(),
>> you're giving the impresion that users of it (OTP, SWP) are safe to use
>> them while reads or PE are in progress, which is not the case, because
>> you don't guard the ops that they're using. You'll also have to document
>> the flash info RWW flag ands say it is mutual exclusive with SWP and OTP
>> features.
> 
> Actually I'm not getting what you mean here. Any access that is not
> a pure read, write or erase (like register accesses, otp and swp
> accesses) are automatically treated as needing full and exclusive
> access to the chip. So everything works as before with these, even
> if RWW is enabled, because that's not where we want performance

oh, yes, all SWP and OTP ops are guarded with spi_nor_prep_and_lock()
and spi_nor_unlock_and_unprep(), you should be good to go.

> improvements. So I need to implement a dedicated "parallel_locking"
> path inside these helpers to clearly enforcing an exclusive access to
> the chip.
> 

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

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 7/8] mtd: spi-nor: Enhance locking to support reads while writes
@ 2023-03-27  9:29         ` Tudor Ambarus
  0 siblings, 0 replies; 48+ messages in thread
From: Tudor Ambarus @ 2023-03-27  9:29 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Richard Weinberger, Vignesh Raghavendra, Pratyush Yadav,
	Michael Walle, linux-mtd, Julien Su, Jaime Liao, Jaime Liao,
	Alvin Zhou, Thomas Petazzoni, Michal Simek, linux-arm-kernel



On 3/24/23 17:41, Miquel Raynal wrote:
> Hi Tudor,
> 

Hi!

> tudor.ambarus@linaro.org wrote on Fri, 17 Mar 2023 05:59:08 +0000:
> 
>> Hi, Miquel,
>>
>> I find the overall idea good.
> 
> Thanks a lot for the detailed review!
> 
>> On 2/1/23 11:36, Miquel Raynal wrote:
>>> 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 (common hardware limitation to limit costs)
>>> 3#: Reads must remain serialized even though reads on different banks
>>>     might occur at the same time.  
>>
>> 3# is unclear if one limits just at reading the commit message. Are the
>> reads serialized per bank or per flash?
> 
> Per flash.
> 
>> After reading the code, it looks like all the reads are serialized per
>> flash regardless if it reads registers or memory. I assume you meant
>> that crossing a bank boundary with a single read is fine.
> 
> Yes, I will update that item to clarify.

thanks!

> 
>> But can you
>> really read from bank 1 and bank 3 at the same time? The code doesn't
>> take this into consideration.
> 
> Yes this is taken into account and supported, a read can cross a bank
> boundary.

No, I meant that you can't do a read from bank 1 and while the first
read is in progress, to start a second read from the 3rd bank and
process both reads in parallel, reading from both banks at the same
time. At least not with the current code, because you set
rww.{ongoing_io, ongoing_rd} to true and the second read will wait.
Cross boundary reads on successive banks should work with current code,
yes. So what does the hw support?

> 
>>
>>> 4#: 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.
>>> 5#: 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 enforced by a bitfield of the in-use banks, so that only a single
>>>     operation can happen in a specific bank at any time.
>>> 2#: Is handled by the ongoing_pe boolean which is set 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#: An ongoing_rd boolean allows to track the ongoing reads, so that
>>>     only one can be performed at a time.
>>> 4#: An ongoing_io boolean 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
>>>     possibly the data), while the second part of the operation (the
>>>     erase delay or the programmation delay) is when we can do something
>>>     else in another bank.
>>> 5#: Is handled by the three booleans presented above, if any of them is
>>>     set, the chip is not yet ready for the operation and must wait.
>>>
>>> All these internal variables are protected by the existing lock, so that
>>> changes in this structure are atomic. The serialization is handled with
>>> a wait queue.
>>>
>>> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
>>> ---
>>>  drivers/mtd/spi-nor/core.c  | 319 ++++++++++++++++++++++++++++++++++--
>>>  include/linux/mtd/spi-nor.h |  13 ++
>>>  2 files changed, 317 insertions(+), 15 deletions(-)
>>>
>>> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
>>> index ac4627e0d6c2..ad2436e3688f 100644
>>> --- a/drivers/mtd/spi-nor/core.c
>>> +++ b/drivers/mtd/spi-nor/core.c
>>> @@ -589,6 +589,66 @@ int spi_nor_sr_ready(struct spi_nor *nor)
>>>  	return !(nor->bouncebuf[0] & SR_WIP);
>>>  }
>>>  
>>> +/**
>>> + * spi_nor_parallel_locking() - Checks if the RWW locking scheme shall be used
>>> + * @nor:	pointer to 'struct spi_nor'.
>>> + *
>>> + * Return: true if parallel locking is enabled, false otherwise.
>>> + */
>>> +static bool spi_nor_parallel_locking(struct spi_nor *nor)
>>> +{
>>> +	if (nor->controller_ops &&
>>> +	    (nor->controller_ops->prepare || nor->controller_ops->unprepare))
>>> +		return false;  
>>
>> We won't allow controller drivers in spi-nor/controllers to benefit of
>> this feature, just do:
>> 	if (nor->controller_ops)
>> 		return false;
> 
> That is also checked in the spi-nor init helper, where SNOR_F_RWW is
> now set, so no need to check it again and again.
> 
>>> +	return nor->info->n_banks > 1 && nor->info->no_sfdp_flags & SPI_NOR_RWW;  
>>
>> we don't play with flash info flags throughout the core. Introduce a
>> SNOR_F equivalent flag, see how they're used. You'll be able to get rid
>> of the n_banks check as well.
> 
> Thanks for the clear pointers, looks much nicer now!
> 
>>> +}
>>> +
>>> +/* Locking helpers for status read operations */
>>> +static int spi_nor_rww_start_rdst(struct spi_nor *nor)
>>> +{
>>> +	int ret = -EAGAIN;  
>>
>> you can have a pointer to rww here, you'll avoid all those dereferences
>> from nor. I would add such a pointer wherever there is more than one
>> dereference, so the comment is for the entire patch.
> 
> haha, I guess this is a matter of taste, I'm not bothered by those, but
> ok, I'll make the change here and after :-)>
>>> +
>>> +	mutex_lock(&nor->lock);
>>> +
>>> +	if (nor->rww.ongoing_io || nor->rww.ongoing_rd)
>>> +		goto busy;
>>> +
>>> +	nor->rww.ongoing_io = true;
>>> +	nor->rww.ongoing_rd = true;
>>> +	ret = 0;
>>> +
>>> +busy:
>>> +	mutex_unlock(&nor->lock);
>>> +	return ret;
>>> +}
>>> +
>>> +static void spi_nor_rww_end_rdst(struct spi_nor *nor)
>>> +{
>>> +	mutex_lock(&nor->lock);
>>> +
>>> +	nor->rww.ongoing_io = false;
>>> +	nor->rww.ongoing_rd = false;
>>> +
>>> +	mutex_unlock(&nor->lock);
>>> +}
>>> +
>>> +static int spi_nor_lock_rdst(struct spi_nor *nor)
>>> +{
>>> +	if (spi_nor_parallel_locking(nor))
>>> +		return spi_nor_rww_start_rdst(nor);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static void spi_nor_unlock_rdst(struct spi_nor *nor)
>>> +{
>>> +	if (spi_nor_parallel_locking(nor)) {
>>> +		spi_nor_rww_end_rdst(nor);
>>> +		wake_up(&nor->rww.wait);
>>> +	}
>>> +}
>>> +
>>>  /**
>>>   * spi_nor_ready() - Query the flash to see if it is ready for new commands.
>>>   * @nor:	pointer to 'struct spi_nor'.
>>> @@ -597,11 +657,21 @@ int spi_nor_sr_ready(struct spi_nor *nor)
>>>   */
>>>  static int spi_nor_ready(struct spi_nor *nor)
>>>  {
>>> +	int ret;
>>> +
>>> +	ret = spi_nor_lock_rdst(nor);
>>> +	if (ret)
>>> +		return 0;
>>> +
>>>  	/* Flashes might override the standard routine. */
>>>  	if (nor->params->ready)
>>> -		return nor->params->ready(nor);
>>> +		ret = nor->params->ready(nor);
>>> +	else
>>> +		ret = spi_nor_sr_ready(nor);
>>>  
>>> -	return spi_nor_sr_ready(nor);
>>> +	spi_nor_unlock_rdst(nor);
>>> +
>>> +	return ret;
>>>  }
>>>  
>>>  /**
>>> @@ -1087,7 +1157,81 @@ static void spi_nor_unprep(struct spi_nor *nor)
>>>  		nor->controller_ops->unprepare(nor);
>>>  }
>>>  
>>> +static void spi_nor_offset_to_banks(struct spi_nor *nor, loff_t start, size_t len,  
>>
>> pass directly the bank_size instead of the pointer to nor, you'll avoid
>> the double dereference.
> 
> Done
> 
>>
>>> +				    unsigned int *first, unsigned int *last)  
>>
>> unsigned long long *first, *last ?
> 
> Actually I want these to remain unsigned int, the ULL suffix just mean
> the input might be a 64-bit value, but it is quite common to treat the
> output as 32-bit. Here we do not expect values greater than 4.

Ok. Then maybe we should match how we define nbanks in NOR. Was it a u8?

> 
>>> +{
>>> +	*first = DIV_ROUND_DOWN_ULL(start, nor->params->bank_size);
>>> +	*last = DIV_ROUND_DOWN_ULL(start + len - 1, nor->params->bank_size);
>>> +}
>>> +
>>>  /* Generic helpers for internal locking and serialization */
>>> +static bool spi_nor_rww_start_io(struct spi_nor *nor)
>>> +{
>>> +	bool start = false;
>>> +
>>> +	mutex_lock(&nor->lock);
>>> +
>>> +	if (nor->rww.ongoing_io)
>>> +		goto busy;
>>> +
>>> +	nor->rww.ongoing_io = true;
>>> +	start = true;
>>> +
>>> +busy:
>>> +	mutex_unlock(&nor->lock);
>>> +	return start;
>>> +}
>>> +
>>> +static void spi_nor_rww_end_io(struct spi_nor *nor)
>>> +{
>>> +	mutex_lock(&nor->lock);
>>> +	nor->rww.ongoing_io = false;
>>> +	mutex_unlock(&nor->lock);
>>> +}
>>> +
>>> +static int spi_nor_lock_device(struct spi_nor *nor)
>>> +{
>>> +	if (!spi_nor_parallel_locking(nor))
>>> +		return 0;
>>> +
>>> +	return wait_event_killable(nor->rww.wait, spi_nor_rww_start_io(nor));
>>> +}
>>> +
>>> +static void spi_nor_unlock_device(struct spi_nor *nor)
>>> +{
>>> +	if (spi_nor_parallel_locking(nor))
>>> +		spi_nor_rww_end_io(nor);  
>>
>> shall we wake_up here too?
> 
> True
> 
>>
>>> +}
>>> +
>>> +/* Generic helpers for internal locking and serialization */
>>> +static bool spi_nor_rww_start_exclusive(struct spi_nor *nor)
>>> +{
>>> +	bool start = false;
>>> +
>>> +	mutex_lock(&nor->lock);
>>> +
>>> +	if (nor->rww.ongoing_io || nor->rww.ongoing_rd || nor->rww.ongoing_pe)
>>> +		goto busy;
>>> +
>>> +	nor->rww.ongoing_io = true;
>>> +	nor->rww.ongoing_rd = true;
>>> +	nor->rww.ongoing_pe = true;
>>> +	start = true;
>>> +
>>> +busy:
>>> +	mutex_unlock(&nor->lock);
>>> +	return start;
>>> +}
>>> +
>>> +static void spi_nor_rww_end_exclusive(struct spi_nor *nor)
>>> +{
>>> +	mutex_lock(&nor->lock);
>>> +	nor->rww.ongoing_io = false;
>>> +	nor->rww.ongoing_rd = false;
>>> +	nor->rww.ongoing_pe = false;
>>> +	mutex_unlock(&nor->lock);
>>> +}
>>> +
>>>  int spi_nor_prep_and_lock(struct spi_nor *nor)
>>>  {
>>>  	int ret;
>>> @@ -1096,19 +1240,71 @@ int spi_nor_prep_and_lock(struct spi_nor *nor)
>>>  	if (ret)
>>>  		return ret;
>>>  
>>> -	mutex_lock(&nor->lock);
>>> +	if (!spi_nor_parallel_locking(nor))
>>> +		mutex_lock(&nor->lock);
>>> +	else
>>> +		ret = wait_event_killable(nor->rww.wait,
>>> +					  spi_nor_rww_start_exclusive(nor));
>>>  
>>
>> No, don't touch spi_nor_prep_and_lock() and spi_nor_unlock_and_unprep(),
>> you're giving the impresion that users of it (OTP, SWP) are safe to use
>> them while reads or PE are in progress, which is not the case, because
>> you don't guard the ops that they're using. You'll also have to document
>> the flash info RWW flag ands say it is mutual exclusive with SWP and OTP
>> features.
> 
> Actually I'm not getting what you mean here. Any access that is not
> a pure read, write or erase (like register accesses, otp and swp
> accesses) are automatically treated as needing full and exclusive
> access to the chip. So everything works as before with these, even
> if RWW is enabled, because that's not where we want performance

oh, yes, all SWP and OTP ops are guarded with spi_nor_prep_and_lock()
and spi_nor_unlock_and_unprep(), you should be good to go.

> improvements. So I need to implement a dedicated "parallel_locking"
> path inside these helpers to clearly enforcing an exclusive access to
> the chip.
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 0/8] mtd: spi-nor: read while write support
  2023-03-24 13:51     ` Miquel Raynal
@ 2023-03-27  9:34       ` Tudor Ambarus
  -1 siblings, 0 replies; 48+ messages in thread
From: Tudor Ambarus @ 2023-03-27  9:34 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Richard Weinberger, Vignesh Raghavendra, Pratyush Yadav,
	Michael Walle, linux-mtd, Julien Su, Jaime Liao, Jaime Liao,
	Alvin Zhou, Thomas Petazzoni, Michal Simek, linux-arm-kernel



On 3/24/23 13:51, Miquel Raynal wrote:
> Hi Tudor,

Hi!

> 
> tudor.ambarus@linaro.org wrote on Fri, 17 Mar 2023 04:13:27 +0000:
> 
>> On 2/1/23 11:35, Miquel Raynal wrote:
>>> Hello folks,
>>>
>>> Here is the follow-up of the RFC trying to bring a little bit of
>>> parallelism to support SPI-NOR Read While Write feature on parts
>>> supporting it and featuring several banks.
>>>
>>> I have received some hardware to make it work, so since the RFC, the
>>> series has been updated to fix my mistakes, but the overall idea is the
>>> same.
>>>
>>> There is nothing Macronix specific in the implementation, the operations
>>> and opcodes are exactly the same as before. The only difference being:
>>> we may consider the chip usable when it is in the busy state during a
>>> write or an erase. Any chip with an internal split allowing to perform
>>> parallel operations might possibly leverage the benefits of this
>>> implementation.
>>>
>>> The first patches are just refactoring and preparation work, there is
>>> almost no functional change, it's just a way to prepare the introduction
>>> of the new locking mechanism and hopefully provide the cleanest and
>>> simplest diff possible for this new feature. The actual change is all
>>> contained in "mtd: spi-nor: Enhance locking to support reads while
>>> writes". The logic is described in the commit log and copy/pasted here
>>> for clarity:
>>>
>>> "
>>>     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 (common hardware limitation to limit costs)
>>>     3#: Reads must remain serialized even though reads on different banks
>>>         might occur at the same time.
>>>     4#: 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.
>>>     5#: 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 enforced by a bitfield of the in-use banks, so that only a single
>>>         operation can happen in a specific bank at any time.
>>>     2#: Is handled by the ongoing_pe boolean which is set 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#: An ongoing_rd boolean allows to track the ongoing reads, so that
>>>         only one can be performed at a time.
>>>     4#: An ongoing_io boolean 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 possibly the data), while the
>>>         second part of the operation (the erase delay or the
>>>         programmation delay) is when we can do something else in another
>>>         bank.
>>>     5#: Is handled by the three booleans presented above, if any of them is
>>>         set, the chip is not yet ready for the operation and must wait.
>>>     
>>>     All these internal variables are protected by the existing lock, so that
>>>     changes in this structure are atomic. The serialization is handled with
>>>     a wait queue."
>>>
>>> Here is now a benchmark with a Macronix MX25UW51245G with 4 banks and RWW
>>> support:
>>>
>>>      // Testing the two accesses in the same bank
>>>      $ flash_speed -b0 -k0 -c10 -d /dev/mtd0
>>>      [...]
>>>      testing read while write latency
>>>      read while write took 51ms, read ended after 51ms
>>>
>>>      // Testing the two accesses within different banks
>>>      $ flash_speed -b0 -k4096 -c10 -d /dev/mtd0
>>>      [...]
>>>      testing read while write latency
>>>      read while write took 51ms, read ended after 20ms
>>>
>>> Parallel accesses have been validated with io_paral. A slight increase
>>> of the time spent on this test has however been noticed. With my  
>>
>> how do the other tests look? Is there any change in performance for
>> flashes that do not support RWW?
> 
> The current implementation takes care of not changing anything with the
> existing flashes, when I resend I'll provide all the logs you asked

yes, I saw. There are some ifs here and there, nothing scary, so I don't
expect any change in performance for the flashes without RWW support,
but it's always good to have a proof.
> for, plus another quick test without the RWW feature bit set.
> 

Cool, thanks! Cheers,
ta

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

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 0/8] mtd: spi-nor: read while write support
@ 2023-03-27  9:34       ` Tudor Ambarus
  0 siblings, 0 replies; 48+ messages in thread
From: Tudor Ambarus @ 2023-03-27  9:34 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Richard Weinberger, Vignesh Raghavendra, Pratyush Yadav,
	Michael Walle, linux-mtd, Julien Su, Jaime Liao, Jaime Liao,
	Alvin Zhou, Thomas Petazzoni, Michal Simek, linux-arm-kernel



On 3/24/23 13:51, Miquel Raynal wrote:
> Hi Tudor,

Hi!

> 
> tudor.ambarus@linaro.org wrote on Fri, 17 Mar 2023 04:13:27 +0000:
> 
>> On 2/1/23 11:35, Miquel Raynal wrote:
>>> Hello folks,
>>>
>>> Here is the follow-up of the RFC trying to bring a little bit of
>>> parallelism to support SPI-NOR Read While Write feature on parts
>>> supporting it and featuring several banks.
>>>
>>> I have received some hardware to make it work, so since the RFC, the
>>> series has been updated to fix my mistakes, but the overall idea is the
>>> same.
>>>
>>> There is nothing Macronix specific in the implementation, the operations
>>> and opcodes are exactly the same as before. The only difference being:
>>> we may consider the chip usable when it is in the busy state during a
>>> write or an erase. Any chip with an internal split allowing to perform
>>> parallel operations might possibly leverage the benefits of this
>>> implementation.
>>>
>>> The first patches are just refactoring and preparation work, there is
>>> almost no functional change, it's just a way to prepare the introduction
>>> of the new locking mechanism and hopefully provide the cleanest and
>>> simplest diff possible for this new feature. The actual change is all
>>> contained in "mtd: spi-nor: Enhance locking to support reads while
>>> writes". The logic is described in the commit log and copy/pasted here
>>> for clarity:
>>>
>>> "
>>>     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 (common hardware limitation to limit costs)
>>>     3#: Reads must remain serialized even though reads on different banks
>>>         might occur at the same time.
>>>     4#: 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.
>>>     5#: 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 enforced by a bitfield of the in-use banks, so that only a single
>>>         operation can happen in a specific bank at any time.
>>>     2#: Is handled by the ongoing_pe boolean which is set 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#: An ongoing_rd boolean allows to track the ongoing reads, so that
>>>         only one can be performed at a time.
>>>     4#: An ongoing_io boolean 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 possibly the data), while the
>>>         second part of the operation (the erase delay or the
>>>         programmation delay) is when we can do something else in another
>>>         bank.
>>>     5#: Is handled by the three booleans presented above, if any of them is
>>>         set, the chip is not yet ready for the operation and must wait.
>>>     
>>>     All these internal variables are protected by the existing lock, so that
>>>     changes in this structure are atomic. The serialization is handled with
>>>     a wait queue."
>>>
>>> Here is now a benchmark with a Macronix MX25UW51245G with 4 banks and RWW
>>> support:
>>>
>>>      // Testing the two accesses in the same bank
>>>      $ flash_speed -b0 -k0 -c10 -d /dev/mtd0
>>>      [...]
>>>      testing read while write latency
>>>      read while write took 51ms, read ended after 51ms
>>>
>>>      // Testing the two accesses within different banks
>>>      $ flash_speed -b0 -k4096 -c10 -d /dev/mtd0
>>>      [...]
>>>      testing read while write latency
>>>      read while write took 51ms, read ended after 20ms
>>>
>>> Parallel accesses have been validated with io_paral. A slight increase
>>> of the time spent on this test has however been noticed. With my  
>>
>> how do the other tests look? Is there any change in performance for
>> flashes that do not support RWW?
> 
> The current implementation takes care of not changing anything with the
> existing flashes, when I resend I'll provide all the logs you asked

yes, I saw. There are some ifs here and there, nothing scary, so I don't
expect any change in performance for the flashes without RWW support,
but it's always good to have a proof.
> for, plus another quick test without the RWW feature bit set.
> 

Cool, thanks! Cheers,
ta

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 7/8] mtd: spi-nor: Enhance locking to support reads while writes
  2023-03-27  9:29         ` Tudor Ambarus
@ 2023-03-28  8:22           ` Miquel Raynal
  -1 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-03-28  8:22 UTC (permalink / raw)
  To: Tudor Ambarus
  Cc: Richard Weinberger, Vignesh Raghavendra, Pratyush Yadav,
	Michael Walle, linux-mtd, Julien Su, Jaime Liao, Jaime Liao,
	Alvin Zhou, Thomas Petazzoni, Michal Simek, linux-arm-kernel

Hi Tudor,

tudor.ambarus@linaro.org wrote on Mon, 27 Mar 2023 10:29:03 +0100:

> On 3/24/23 17:41, Miquel Raynal wrote:
> > Hi Tudor,
> >   
> 
> Hi!
> 
> > tudor.ambarus@linaro.org wrote on Fri, 17 Mar 2023 05:59:08 +0000:
> >   
> >> Hi, Miquel,
> >>
> >> I find the overall idea good.  
> > 
> > Thanks a lot for the detailed review!
> >   
> >> On 2/1/23 11:36, Miquel Raynal wrote:  
> >>> 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 (common hardware limitation to limit costs)
> >>> 3#: Reads must remain serialized even though reads on different banks
> >>>     might occur at the same time.    
> >>
> >> 3# is unclear if one limits just at reading the commit message. Are the
> >> reads serialized per bank or per flash?  
> > 
> > Per flash.
> >   
> >> After reading the code, it looks like all the reads are serialized per
> >> flash regardless if it reads registers or memory. I assume you meant
> >> that crossing a bank boundary with a single read is fine.  
> > 
> > Yes, I will update that item to clarify.  
> 
> thanks!
> 
> >   
> >> But can you
> >> really read from bank 1 and bank 3 at the same time? The code doesn't
> >> take this into consideration.  
> > 
> > Yes this is taken into account and supported, a read can cross a bank
> > boundary.  
> 
> No, I meant that you can't do a read from bank 1 and while the first
> read is in progress, to start a second read from the 3rd bank and
> process both reads in parallel, reading from both banks at the same
> time. At least not with the current code, because you set
> rww.{ongoing_io, ongoing_rd} to true and the second read will wait.
> Cross boundary reads on successive banks should work with current code,
> yes. So what does the hw support?

Ok, sorry for the confusion. So, I think I remember a discussion where
I was told that this was not supported even though it would not be
extremely complex to support at a physical level ("just" by increasing
the current source). But IIRC right now this is not supported. Anyhow,
the main target of the RWW is to perform a read during a while, this is
very handy for performing eg. system updates besides reducing the
overall latency, but I don't think we want to bring even more
parallelism between reads. Actually the current implementation would
not work and a whole mtd I/O scheduler would be needed for that, which
is yet another task.


[...]

> >>> @@ -1087,7 +1157,81 @@ static void spi_nor_unprep(struct spi_nor *nor)
> >>>  		nor->controller_ops->unprepare(nor);
> >>>  }
> >>>  
> >>> +static void spi_nor_offset_to_banks(struct spi_nor *nor, loff_t start, size_t len,    
> >>
> >> pass directly the bank_size instead of the pointer to nor, you'll avoid
> >> the double dereference.  
> > 
> > Done
> >   
> >>  
> >>> +				    unsigned int *first, unsigned int *last)    
> >>
> >> unsigned long long *first, *last ?  
> > 
> > Actually I want these to remain unsigned int, the ULL suffix just mean
> > the input might be a 64-bit value, but it is quite common to treat the
> > output as 32-bit. Here we do not expect values greater than 4.  
> 
> Ok. Then maybe we should match how we define nbanks in NOR. Was it a u8?

Why not.

> 
> >   
> >>> +{
> >>> +	*first = DIV_ROUND_DOWN_ULL(start, nor->params->bank_size);
> >>> +	*last = DIV_ROUND_DOWN_ULL(start + len - 1, nor->params->bank_size);
> >>> +}
> >>> +

Thanks,
Miquèl

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

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 7/8] mtd: spi-nor: Enhance locking to support reads while writes
@ 2023-03-28  8:22           ` Miquel Raynal
  0 siblings, 0 replies; 48+ messages in thread
From: Miquel Raynal @ 2023-03-28  8:22 UTC (permalink / raw)
  To: Tudor Ambarus
  Cc: Richard Weinberger, Vignesh Raghavendra, Pratyush Yadav,
	Michael Walle, linux-mtd, Julien Su, Jaime Liao, Jaime Liao,
	Alvin Zhou, Thomas Petazzoni, Michal Simek, linux-arm-kernel

Hi Tudor,

tudor.ambarus@linaro.org wrote on Mon, 27 Mar 2023 10:29:03 +0100:

> On 3/24/23 17:41, Miquel Raynal wrote:
> > Hi Tudor,
> >   
> 
> Hi!
> 
> > tudor.ambarus@linaro.org wrote on Fri, 17 Mar 2023 05:59:08 +0000:
> >   
> >> Hi, Miquel,
> >>
> >> I find the overall idea good.  
> > 
> > Thanks a lot for the detailed review!
> >   
> >> On 2/1/23 11:36, Miquel Raynal wrote:  
> >>> 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 (common hardware limitation to limit costs)
> >>> 3#: Reads must remain serialized even though reads on different banks
> >>>     might occur at the same time.    
> >>
> >> 3# is unclear if one limits just at reading the commit message. Are the
> >> reads serialized per bank or per flash?  
> > 
> > Per flash.
> >   
> >> After reading the code, it looks like all the reads are serialized per
> >> flash regardless if it reads registers or memory. I assume you meant
> >> that crossing a bank boundary with a single read is fine.  
> > 
> > Yes, I will update that item to clarify.  
> 
> thanks!
> 
> >   
> >> But can you
> >> really read from bank 1 and bank 3 at the same time? The code doesn't
> >> take this into consideration.  
> > 
> > Yes this is taken into account and supported, a read can cross a bank
> > boundary.  
> 
> No, I meant that you can't do a read from bank 1 and while the first
> read is in progress, to start a second read from the 3rd bank and
> process both reads in parallel, reading from both banks at the same
> time. At least not with the current code, because you set
> rww.{ongoing_io, ongoing_rd} to true and the second read will wait.
> Cross boundary reads on successive banks should work with current code,
> yes. So what does the hw support?

Ok, sorry for the confusion. So, I think I remember a discussion where
I was told that this was not supported even though it would not be
extremely complex to support at a physical level ("just" by increasing
the current source). But IIRC right now this is not supported. Anyhow,
the main target of the RWW is to perform a read during a while, this is
very handy for performing eg. system updates besides reducing the
overall latency, but I don't think we want to bring even more
parallelism between reads. Actually the current implementation would
not work and a whole mtd I/O scheduler would be needed for that, which
is yet another task.


[...]

> >>> @@ -1087,7 +1157,81 @@ static void spi_nor_unprep(struct spi_nor *nor)
> >>>  		nor->controller_ops->unprepare(nor);
> >>>  }
> >>>  
> >>> +static void spi_nor_offset_to_banks(struct spi_nor *nor, loff_t start, size_t len,    
> >>
> >> pass directly the bank_size instead of the pointer to nor, you'll avoid
> >> the double dereference.  
> > 
> > Done
> >   
> >>  
> >>> +				    unsigned int *first, unsigned int *last)    
> >>
> >> unsigned long long *first, *last ?  
> > 
> > Actually I want these to remain unsigned int, the ULL suffix just mean
> > the input might be a 64-bit value, but it is quite common to treat the
> > output as 32-bit. Here we do not expect values greater than 4.  
> 
> Ok. Then maybe we should match how we define nbanks in NOR. Was it a u8?

Why not.

> 
> >   
> >>> +{
> >>> +	*first = DIV_ROUND_DOWN_ULL(start, nor->params->bank_size);
> >>> +	*last = DIV_ROUND_DOWN_ULL(start + len - 1, nor->params->bank_size);
> >>> +}
> >>> +

Thanks,
Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 48+ messages in thread

end of thread, other threads:[~2023-03-28  8:23 UTC | newest]

Thread overview: 48+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-02-01 11:35 [PATCH v4 0/8] mtd: spi-nor: read while write support Miquel Raynal
2023-02-01 11:35 ` Miquel Raynal
2023-02-01 11:35 ` [PATCH v4 1/8] mtd: spi-nor: Introduce the concept of bank Miquel Raynal
2023-02-01 11:35   ` Miquel Raynal
2023-03-17  3:36   ` Tudor Ambarus
2023-03-17  3:36     ` Tudor Ambarus
2023-02-01 11:35 ` [PATCH v4 2/8] mtd: spi-nor: Add a macro to define more banks Miquel Raynal
2023-02-01 11:35   ` Miquel Raynal
2023-02-01 11:35 ` [PATCH v4 3/8] mtd: spi-nor: Reorder the preparation vs locking steps Miquel Raynal
2023-02-01 11:35   ` Miquel Raynal
2023-03-17  3:39   ` Tudor Ambarus
2023-03-17  3:39     ` Tudor Ambarus
2023-03-17  3:51     ` Tudor Ambarus
2023-03-17  3:51       ` Tudor Ambarus
2023-03-24 15:28       ` Miquel Raynal
2023-03-24 15:28         ` Miquel Raynal
2023-02-01 11:35 ` [PATCH v4 4/8] mtd: spi-nor: Separate preparation and locking Miquel Raynal
2023-02-01 11:35   ` Miquel Raynal
2023-02-01 11:36 ` [PATCH v4 5/8] mtd: spi-nor: Prepare the introduction of a new locking mechanism Miquel Raynal
2023-02-01 11:36   ` Miquel Raynal
2023-02-01 11:36 ` [PATCH v4 6/8] mtd: spi-nor: Add a RWW flag Miquel Raynal
2023-02-01 11:36   ` Miquel Raynal
2023-03-17  3:20   ` Tudor Ambarus
2023-03-17  3:20     ` Tudor Ambarus
2023-02-01 11:36 ` [PATCH v4 7/8] mtd: spi-nor: Enhance locking to support reads while writes Miquel Raynal
2023-02-01 11:36   ` Miquel Raynal
2023-03-17  5:59   ` Tudor Ambarus
2023-03-17  5:59     ` Tudor Ambarus
2023-03-24 17:41     ` Miquel Raynal
2023-03-24 17:41       ` Miquel Raynal
2023-03-27  9:29       ` Tudor Ambarus
2023-03-27  9:29         ` Tudor Ambarus
2023-03-28  8:22         ` Miquel Raynal
2023-03-28  8:22           ` Miquel Raynal
2023-02-01 11:36 ` [PATCH v4 8/8] mtd: spi-nor: macronix: Add support for mx25uw51245g with RWW Miquel Raynal
2023-02-01 11:36   ` Miquel Raynal
2023-03-17  6:09   ` Tudor Ambarus
2023-03-17  6:09     ` Tudor Ambarus
2023-03-17  7:43     ` liao jaime
2023-03-17  7:43       ` liao jaime
2023-03-17  8:22       ` Tudor Ambarus
2023-03-17  8:22         ` Tudor Ambarus
2023-03-17  4:13 ` [PATCH v4 0/8] mtd: spi-nor: read while write support Tudor Ambarus
2023-03-17  4:13   ` Tudor Ambarus
2023-03-24 13:51   ` Miquel Raynal
2023-03-24 13:51     ` Miquel Raynal
2023-03-27  9:34     ` Tudor Ambarus
2023-03-27  9:34       ` Tudor Ambarus

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.