2024-02-06 20:33:48

by David Lechner

[permalink] [raw]
Subject: [PATCH 0/2] spi: axi-spi-engine: performance improvements

While researching potential performance improvements in the core SPI
code, we found a few low-hanging opportunities for improvements in the
AXI SPI Engine driver.

---
David Lechner (2):
spi: axi-spi-engine: remove use of ida for sync id
spi: axi-spi-engine: move msg finalization out of irq handler

drivers/spi/spi-axi-spi-engine.c | 67 +++++++++++++---------------------------
1 file changed, 21 insertions(+), 46 deletions(-)
---
base-commit: 80fa6a033ac8c395a3de4840e204638e92b8b371
change-id: 20240206-axi-spi-engine-round-2-1-bb73990abac3


2024-02-06 20:33:50

by David Lechner

[permalink] [raw]
Subject: [PATCH 1/2] spi: axi-spi-engine: remove use of ida for sync id

Profiling has shown that ida_alloc_range() accounts for about 10% of the
time spent in spi_sync() when using the AXI SPI Engine controller. This
call is used to create a unique id for each SPI message to match to an
IRQ when the message is complete.

Since the core SPI code serializes messages in a message queue, we can
only have one message in flight at a time, namely host->cur_msg. This
means that we can use a fixed value instead of a unique id for each
message since there can never be more than one message pending at a
time.

This patch removes the use of ida for the sync id and replaces it with a
constant value. This simplifies the driver and improves performance.

Signed-off-by: David Lechner <[email protected]>
---
drivers/spi/spi-axi-spi-engine.c | 27 ++++++---------------------
1 file changed, 6 insertions(+), 21 deletions(-)

diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c
index 6b0c72bf3395..9cc602075c17 100644
--- a/drivers/spi/spi-axi-spi-engine.c
+++ b/drivers/spi/spi-axi-spi-engine.c
@@ -57,6 +57,9 @@
#define SPI_ENGINE_TRANSFER_WRITE 0x1
#define SPI_ENGINE_TRANSFER_READ 0x2

+/* Arbitrary sync ID for use by host->cur_msg */
+#define AXI_SPI_ENGINE_CUR_MSG_SYNC_ID 0x1
+
#define SPI_ENGINE_CMD(inst, arg1, arg2) \
(((inst) << 12) | ((arg1) << 8) | (arg2))

@@ -98,8 +101,6 @@ struct spi_engine_message_state {
unsigned int rx_length;
/** @rx_buf: Bytes not yet written to the RX FIFO. */
uint8_t *rx_buf;
- /** @sync_id: ID to correlate SYNC interrupts with this message. */
- u8 sync_id;
};

struct spi_engine {
@@ -109,7 +110,6 @@ struct spi_engine {
spinlock_t lock;

void __iomem *base;
- struct ida sync_ida;
struct timer_list watchdog_timer;
struct spi_controller *controller;

@@ -483,9 +483,7 @@ static irqreturn_t spi_engine_irq(int irq, void *devid)
}

if (pending & SPI_ENGINE_INT_SYNC && msg) {
- struct spi_engine_message_state *st = msg->state;
-
- if (completed_id == st->sync_id) {
+ if (completed_id == AXI_SPI_ENGINE_CUR_MSG_SYNC_ID) {
if (timer_delete_sync(&spi_engine->watchdog_timer)) {
msg->status = 0;
msg->actual_length = msg->frame_length;
@@ -510,10 +508,8 @@ static int spi_engine_prepare_message(struct spi_controller *host,
struct spi_message *msg)
{
struct spi_engine_program p_dry, *p;
- struct spi_engine *spi_engine = spi_controller_get_devdata(host);
struct spi_engine_message_state *st;
size_t size;
- int ret;

st = kzalloc(sizeof(*st), GFP_KERNEL);
if (!st)
@@ -531,18 +527,10 @@ static int spi_engine_prepare_message(struct spi_controller *host,
return -ENOMEM;
}

- ret = ida_alloc_range(&spi_engine->sync_ida, 0, U8_MAX, GFP_KERNEL);
- if (ret < 0) {
- kfree(p);
- kfree(st);
- return ret;
- }
-
- st->sync_id = ret;
-
spi_engine_compile_message(msg, false, p);

- spi_engine_program_add_cmd(p, false, SPI_ENGINE_CMD_SYNC(st->sync_id));
+ spi_engine_program_add_cmd(p, false, SPI_ENGINE_CMD_SYNC(
+ AXI_SPI_ENGINE_CUR_MSG_SYNC_ID));

st->p = p;
st->cmd_buf = p->instructions;
@@ -555,10 +543,8 @@ static int spi_engine_prepare_message(struct spi_controller *host,
static int spi_engine_unprepare_message(struct spi_controller *host,
struct spi_message *msg)
{
- struct spi_engine *spi_engine = spi_controller_get_devdata(host);
struct spi_engine_message_state *st = msg->state;

- ida_free(&spi_engine->sync_ida, st->sync_id);
kfree(st->p);
kfree(st);

@@ -640,7 +626,6 @@ static int spi_engine_probe(struct platform_device *pdev)
spi_engine = spi_controller_get_devdata(host);

spin_lock_init(&spi_engine->lock);
- ida_init(&spi_engine->sync_ida);
timer_setup(&spi_engine->watchdog_timer, spi_engine_timeout, TIMER_IRQSAFE);
spi_engine->controller = host;


--
2.43.0


2024-02-06 21:50:54

by Christophe JAILLET

[permalink] [raw]
Subject: Re: [PATCH 1/2] spi: axi-spi-engine: remove use of ida for sync id

Le 06/02/2024 à 21:31, David Lechner a écrit :
> Profiling has shown that ida_alloc_range() accounts for about 10% of the
> time spent in spi_sync() when using the AXI SPI Engine controller. This
> call is used to create a unique id for each SPI message to match to an
> IRQ when the message is complete.
>
> Since the core SPI code serializes messages in a message queue, we can
> only have one message in flight at a time, namely host->cur_msg. This
> means that we can use a fixed value instead of a unique id for each
> message since there can never be more than one message pending at a
> time.
>
> This patch removes the use of ida...

So, maybe #include <linux/idr.h> can be removed as well?
(untested)



Also, even if unrelated to your changes, spi_engine_prepare_message()
could use struct_size() in:

size = sizeof(*p->instructions) * (p_dry.length + 1);
p = kzalloc(sizeof(*p) + size, GFP_KERNEL);

-->
p = kzalloc(struct_size(p, instructions, p_dry.length + 1, GFP_KERNEL);

which can be a little safer and less verbose.

CJ

> ...for the sync id and replaces it with a
> constant value. This simplifies the driver and improves performance.
>
> Signed-off-by: David Lechner <dlechner-rdvid1DuHRBWk0Htik3J/[email protected]>
> ---
> drivers/spi/spi-axi-spi-engine.c | 27 ++++++---------------------
> 1 file changed, 6 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c
> index 6b0c72bf3395..9cc602075c17 100644
> --- a/drivers/spi/spi-axi-spi-engine.c
> +++ b/drivers/spi/spi-axi-spi-engine.c
> @@ -57,6 +57,9 @@
> #define SPI_ENGINE_TRANSFER_WRITE 0x1
> #define SPI_ENGINE_TRANSFER_READ 0x2
>
> +/* Arbitrary sync ID for use by host->cur_msg */
> +#define AXI_SPI_ENGINE_CUR_MSG_SYNC_ID 0x1
> +
> #define SPI_ENGINE_CMD(inst, arg1, arg2) \
> (((inst) << 12) | ((arg1) << 8) | (arg2))
>
> @@ -98,8 +101,6 @@ struct spi_engine_message_state {
> unsigned int rx_length;
> /** @rx_buf: Bytes not yet written to the RX FIFO. */
> uint8_t *rx_buf;
> - /** @sync_id: ID to correlate SYNC interrupts with this message. */
> - u8 sync_id;
> };
>
> struct spi_engine {
> @@ -109,7 +110,6 @@ struct spi_engine {
> spinlock_t lock;
>
> void __iomem *base;
> - struct ida sync_ida;
> struct timer_list watchdog_timer;
> struct spi_controller *controller;
>
> @@ -483,9 +483,7 @@ static irqreturn_t spi_engine_irq(int irq, void *devid)
> }
>
> if (pending & SPI_ENGINE_INT_SYNC && msg) {
> - struct spi_engine_message_state *st = msg->state;
> -
> - if (completed_id == st->sync_id) {
> + if (completed_id == AXI_SPI_ENGINE_CUR_MSG_SYNC_ID) {
> if (timer_delete_sync(&spi_engine->watchdog_timer)) {
> msg->status = 0;
> msg->actual_length = msg->frame_length;
> @@ -510,10 +508,8 @@ static int spi_engine_prepare_message(struct spi_controller *host,
> struct spi_message *msg)
> {
> struct spi_engine_program p_dry, *p;
> - struct spi_engine *spi_engine = spi_controller_get_devdata(host);
> struct spi_engine_message_state *st;
> size_t size;
> - int ret;
>
> st = kzalloc(sizeof(*st), GFP_KERNEL);
> if (!st)
> @@ -531,18 +527,10 @@ static int spi_engine_prepare_message(struct spi_controller *host,
> return -ENOMEM;
> }
>
> - ret = ida_alloc_range(&spi_engine->sync_ida, 0, U8_MAX, GFP_KERNEL);
> - if (ret < 0) {
> - kfree(p);
> - kfree(st);
> - return ret;
> - }
> -
> - st->sync_id = ret;
> -
> spi_engine_compile_message(msg, false, p);
>
> - spi_engine_program_add_cmd(p, false, SPI_ENGINE_CMD_SYNC(st->sync_id));
> + spi_engine_program_add_cmd(p, false, SPI_ENGINE_CMD_SYNC(
> + AXI_SPI_ENGINE_CUR_MSG_SYNC_ID));
>
> st->p = p;
> st->cmd_buf = p->instructions;
> @@ -555,10 +543,8 @@ static int spi_engine_prepare_message(struct spi_controller *host,
> static int spi_engine_unprepare_message(struct spi_controller *host,
> struct spi_message *msg)
> {
> - struct spi_engine *spi_engine = spi_controller_get_devdata(host);
> struct spi_engine_message_state *st = msg->state;
>
> - ida_free(&spi_engine->sync_ida, st->sync_id);
> kfree(st->p);
> kfree(st);
>
> @@ -640,7 +626,6 @@ static int spi_engine_probe(struct platform_device *pdev)
> spi_engine = spi_controller_get_devdata(host);
>
> spin_lock_init(&spi_engine->lock);
> - ida_init(&spi_engine->sync_ida);
> timer_setup(&spi_engine->watchdog_timer, spi_engine_timeout, TIMER_IRQSAFE);
> spi_engine->controller = host;
>
>


2024-02-07 09:41:01

by Nuno Sá

[permalink] [raw]
Subject: Re: [PATCH 1/2] spi: axi-spi-engine: remove use of ida for sync id

On Tue, 2024-02-06 at 14:31 -0600, David Lechner wrote:
> Profiling has shown that ida_alloc_range() accounts for about 10% of the
> time spent in spi_sync() when using the AXI SPI Engine controller. This
> call is used to create a unique id for each SPI message to match to an
> IRQ when the message is complete.
>
> Since the core SPI code serializes messages in a message queue, we can
> only have one message in flight at a time, namely host->cur_msg. This
> means that we can use a fixed value instead of a unique id for each
> message since there can never be more than one message pending at a
> time.
>
> This patch removes the use of ida for the sync id and replaces it with a
> constant value. This simplifies the driver and improves performance.
>
> Signed-off-by: David Lechner <[email protected]>
> ---

With the removed header:

Reviewed-by: Nuno Sa <[email protected]>

(Christophe suggestion is also pretty good but I would likely do it in a
different patch - maybe we could even annotate the flex array with __counted_by)

- Nuno Sá

>  drivers/spi/spi-axi-spi-engine.c | 27 ++++++---------------------
>  1 file changed, 6 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-
> engine.c
> index 6b0c72bf3395..9cc602075c17 100644
> --- a/drivers/spi/spi-axi-spi-engine.c
> +++ b/drivers/spi/spi-axi-spi-engine.c
> @@ -57,6 +57,9 @@
>  #define SPI_ENGINE_TRANSFER_WRITE 0x1
>  #define SPI_ENGINE_TRANSFER_READ 0x2
>  
> +/* Arbitrary sync ID for use by host->cur_msg */
> +#define AXI_SPI_ENGINE_CUR_MSG_SYNC_ID 0x1
> +
>  #define SPI_ENGINE_CMD(inst, arg1, arg2) \
>   (((inst) << 12) | ((arg1) << 8) | (arg2))
>  
> @@ -98,8 +101,6 @@ struct spi_engine_message_state {
>   unsigned int rx_length;
>   /** @rx_buf: Bytes not yet written to the RX FIFO. */
>   uint8_t *rx_buf;
> - /** @sync_id: ID to correlate SYNC interrupts with this message. */
> - u8 sync_id;
>  };
>  
>  struct spi_engine {
> @@ -109,7 +110,6 @@ struct spi_engine {
>   spinlock_t lock;
>  
>   void __iomem *base;
> - struct ida sync_ida;
>   struct timer_list watchdog_timer;
>   struct spi_controller *controller;
>  
> @@ -483,9 +483,7 @@ static irqreturn_t spi_engine_irq(int irq, void *devid)
>   }
>  
>   if (pending & SPI_ENGINE_INT_SYNC && msg) {
> - struct spi_engine_message_state *st = msg->state;
> -
> - if (completed_id == st->sync_id) {
> + if (completed_id == AXI_SPI_ENGINE_CUR_MSG_SYNC_ID) {
>   if (timer_delete_sync(&spi_engine->watchdog_timer)) {
>   msg->status = 0;
>   msg->actual_length = msg->frame_length;
> @@ -510,10 +508,8 @@ static int spi_engine_prepare_message(struct
> spi_controller *host,
>         struct spi_message *msg)
>  {
>   struct spi_engine_program p_dry, *p;
> - struct spi_engine *spi_engine = spi_controller_get_devdata(host);
>   struct spi_engine_message_state *st;
>   size_t size;
> - int ret;
>  
>   st = kzalloc(sizeof(*st), GFP_KERNEL);
>   if (!st)
> @@ -531,18 +527,10 @@ static int spi_engine_prepare_message(struct
> spi_controller *host,
>   return -ENOMEM;
>   }
>  
> - ret = ida_alloc_range(&spi_engine->sync_ida, 0, U8_MAX, GFP_KERNEL);
> - if (ret < 0) {
> - kfree(p);
> - kfree(st);
> - return ret;
> - }
> -
> - st->sync_id = ret;
> -
>   spi_engine_compile_message(msg, false, p);
>  
> - spi_engine_program_add_cmd(p, false, SPI_ENGINE_CMD_SYNC(st-
> >sync_id));
> + spi_engine_program_add_cmd(p, false, SPI_ENGINE_CMD_SYNC(
> + AXI_SPI_ENGINE_CUR_MSG_SYNC_I
> D));
>  
>   st->p = p;
>   st->cmd_buf = p->instructions;
> @@ -555,10 +543,8 @@ static int spi_engine_prepare_message(struct
> spi_controller *host,
>  static int spi_engine_unprepare_message(struct spi_controller *host,
>   struct spi_message *msg)
>  {
> - struct spi_engine *spi_engine = spi_controller_get_devdata(host);
>   struct spi_engine_message_state *st = msg->state;
>  
> - ida_free(&spi_engine->sync_ida, st->sync_id);
>   kfree(st->p);
>   kfree(st);
>  
> @@ -640,7 +626,6 @@ static int spi_engine_probe(struct platform_device *pdev)
>   spi_engine = spi_controller_get_devdata(host);
>  
>   spin_lock_init(&spi_engine->lock);
> - ida_init(&spi_engine->sync_ida);
>   timer_setup(&spi_engine->watchdog_timer, spi_engine_timeout,
> TIMER_IRQSAFE);
>   spi_engine->controller = host;
>  
>


2024-02-07 14:10:10

by David Lechner

[permalink] [raw]
Subject: Re: [PATCH 1/2] spi: axi-spi-engine: remove use of ida for sync id

On Tue, Feb 6, 2024 at 3:50 PM Christophe JAILLET
<[email protected]> wrote:
>
> Le 06/02/2024 à 21:31, David Lechner a écrit :
> > Profiling has shown that ida_alloc_range() accounts for about 10% of the
> > time spent in spi_sync() when using the AXI SPI Engine controller. This
> > call is used to create a unique id for each SPI message to match to an
> > IRQ when the message is complete.
> >
> > Since the core SPI code serializes messages in a message queue, we can
> > only have one message in flight at a time, namely host->cur_msg. This
> > means that we can use a fixed value instead of a unique id for each
> > message since there can never be more than one message pending at a
> > time.
> >
> > This patch removes the use of ida...
>
> So, maybe #include <linux/idr.h> can be removed as well?
> (untested)
>

Yes it should be removed.

>
>
> Also, even if unrelated to your changes, spi_engine_prepare_message()
> could use struct_size() in:
>
> size = sizeof(*p->instructions) * (p_dry.length + 1);
> p = kzalloc(sizeof(*p) + size, GFP_KERNEL);
>
> -->
> p = kzalloc(struct_size(p, instructions, p_dry.length + 1, GFP_KERNEL);
>
> which can be a little safer and less verbose.

Thanks for the suggestion. I will consider it for a separate patch in
the future.

2024-02-08 16:41:26

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH 0/2] spi: axi-spi-engine: performance improvements

On Tue, 06 Feb 2024 14:31:26 -0600, David Lechner wrote:
> While researching potential performance improvements in the core SPI
> code, we found a few low-hanging opportunities for improvements in the
> AXI SPI Engine driver.
>

Applied to

https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-next

Thanks!

[1/2] spi: axi-spi-engine: remove use of ida for sync id
commit: 531860e12da76a444e0ecfd37a9d786e7986957a
[2/2] spi: axi-spi-engine: move msg finalization out of irq handler
commit: abb4b46c43689dd1f4d80c41e49127ca0ede75b3

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark