* [PATCH 0/2] spi: axi-spi-engine: performance improvements
@ 2024-02-06 20:31 David Lechner
2024-02-06 20:31 ` [PATCH 1/2] spi: axi-spi-engine: remove use of ida for sync id David Lechner
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: David Lechner @ 2024-02-06 20:31 UTC (permalink / raw)
To: linux-spi
Cc: David Lechner, Mark Brown, Michael Hennerich, Nuno Sá, linux-kernel
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
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH 1/2] spi: axi-spi-engine: remove use of ida for sync id 2024-02-06 20:31 [PATCH 0/2] spi: axi-spi-engine: performance improvements David Lechner @ 2024-02-06 20:31 ` David Lechner 2024-02-06 21:50 ` Christophe JAILLET 2024-02-07 9:40 ` Nuno Sá 2024-02-06 20:31 ` [PATCH 2/2] spi: axi-spi-engine: move msg finalization out of irq handler David Lechner 2024-02-08 16:33 ` [PATCH 0/2] spi: axi-spi-engine: performance improvements Mark Brown 2 siblings, 2 replies; 8+ messages in thread From: David Lechner @ 2024-02-06 20:31 UTC (permalink / raw) To: linux-spi Cc: David Lechner, Mark Brown, Michael Hennerich, Nuno Sá, linux-kernel 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 <dlechner@baylibre.com> --- 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 ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] spi: axi-spi-engine: remove use of ida for sync id 2024-02-06 20:31 ` [PATCH 1/2] spi: axi-spi-engine: remove use of ida for sync id David Lechner @ 2024-02-06 21:50 ` Christophe JAILLET 2024-02-07 14:09 ` David Lechner 2024-02-07 9:40 ` Nuno Sá 1 sibling, 1 reply; 8+ messages in thread From: Christophe JAILLET @ 2024-02-06 21:50 UTC (permalink / raw) To: dlechner; +Cc: broonie, linux-kernel, linux-spi, michael.hennerich, nuno.sa 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/w@public.gmane.org> > --- > 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; > > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] spi: axi-spi-engine: remove use of ida for sync id 2024-02-06 21:50 ` Christophe JAILLET @ 2024-02-07 14:09 ` David Lechner 0 siblings, 0 replies; 8+ messages in thread From: David Lechner @ 2024-02-07 14:09 UTC (permalink / raw) To: Christophe JAILLET Cc: broonie, linux-kernel, linux-spi, michael.hennerich, nuno.sa On Tue, Feb 6, 2024 at 3:50 PM Christophe JAILLET <christophe.jaillet@wanadoo.fr> 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. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] spi: axi-spi-engine: remove use of ida for sync id 2024-02-06 20:31 ` [PATCH 1/2] spi: axi-spi-engine: remove use of ida for sync id David Lechner 2024-02-06 21:50 ` Christophe JAILLET @ 2024-02-07 9:40 ` Nuno Sá 1 sibling, 0 replies; 8+ messages in thread From: Nuno Sá @ 2024-02-07 9:40 UTC (permalink / raw) To: David Lechner, linux-spi Cc: Mark Brown, Michael Hennerich, Nuno Sá, linux-kernel 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 <dlechner@baylibre.com> > --- With the removed header: Reviewed-by: Nuno Sa <nuno.sa@analog.com> (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; > > ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 2/2] spi: axi-spi-engine: move msg finalization out of irq handler 2024-02-06 20:31 [PATCH 0/2] spi: axi-spi-engine: performance improvements David Lechner 2024-02-06 20:31 ` [PATCH 1/2] spi: axi-spi-engine: remove use of ida for sync id David Lechner @ 2024-02-06 20:31 ` David Lechner 2024-02-07 9:33 ` Nuno Sá 2024-02-08 16:33 ` [PATCH 0/2] spi: axi-spi-engine: performance improvements Mark Brown 2 siblings, 1 reply; 8+ messages in thread From: David Lechner @ 2024-02-06 20:31 UTC (permalink / raw) To: linux-spi Cc: David Lechner, Mark Brown, Michael Hennerich, Nuno Sá, linux-kernel As a general principal, it is best to do as little as possible in an interrupt handler. This patch reworks the AXI SPI Engine driver to move timer_delete_sync() and spi_finalize_current_message() out of the interrupt handler. Instead, spi_finalize_current_message() is moved to the transfer_one_message function (similar to nearly all other SPI controllers). A completion is now used to wait for the sync interrupt that indicates that the message is complete. The watchdog timer is no longer needed since we can use the wait_for_completion_timeout() function to wait for the message to complete with the same effect. As a bonus, these changes also improve throughput of the SPI controller. For example, this was tested on a ZynqMP with a 80MHz SCLK reading 4 byte samples from an ADC. The max measured throughput increased from 26k to 28k samples per second. Signed-off-by: David Lechner <dlechner@baylibre.com> --- drivers/spi/spi-axi-spi-engine.c | 40 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c index 9cc602075c17..3c96aa9232b3 100644 --- a/drivers/spi/spi-axi-spi-engine.c +++ b/drivers/spi/spi-axi-spi-engine.c @@ -6,6 +6,7 @@ */ #include <linux/clk.h> +#include <linux/completion.h> #include <linux/fpga/adi-axi-common.h> #include <linux/idr.h> #include <linux/interrupt.h> @@ -14,7 +15,6 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> -#include <linux/timer.h> #define SPI_ENGINE_REG_RESET 0x40 @@ -110,9 +110,7 @@ struct spi_engine { spinlock_t lock; void __iomem *base; - struct timer_list watchdog_timer; - struct spi_controller *controller; - + struct completion msg_complete; unsigned int int_enable; }; @@ -484,11 +482,9 @@ static irqreturn_t spi_engine_irq(int irq, void *devid) if (pending & SPI_ENGINE_INT_SYNC && msg) { 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; - spi_finalize_current_message(host); - } + msg->status = 0; + msg->actual_length = msg->frame_length; + complete(&spi_engine->msg_complete); disable_int |= SPI_ENGINE_INT_SYNC; } } @@ -559,7 +555,7 @@ static int spi_engine_transfer_one_message(struct spi_controller *host, unsigned int int_enable = 0; unsigned long flags; - mod_timer(&spi_engine->watchdog_timer, jiffies + msecs_to_jiffies(5000)); + reinit_completion(&spi_engine->msg_complete); spin_lock_irqsave(&spi_engine->lock, flags); @@ -581,21 +577,16 @@ static int spi_engine_transfer_one_message(struct spi_controller *host, spi_engine->int_enable = int_enable; spin_unlock_irqrestore(&spi_engine->lock, flags); - return 0; -} - -static void spi_engine_timeout(struct timer_list *timer) -{ - struct spi_engine *spi_engine = from_timer(spi_engine, timer, watchdog_timer); - struct spi_controller *host = spi_engine->controller; - - if (WARN_ON(!host->cur_msg)) - return; + if (!wait_for_completion_timeout(&spi_engine->msg_complete, + msecs_to_jiffies(5000))) { + dev_err(&host->dev, + "Timeout occurred while waiting for transfer to complete. Hardware is probably broken.\n"); + msg->status = -ETIMEDOUT; + } - dev_err(&host->dev, - "Timeout occurred while waiting for transfer to complete. Hardware is probably broken.\n"); - host->cur_msg->status = -ETIMEDOUT; spi_finalize_current_message(host); + + return msg->status; } static void spi_engine_release_hw(void *p) @@ -626,8 +617,7 @@ static int spi_engine_probe(struct platform_device *pdev) spi_engine = spi_controller_get_devdata(host); spin_lock_init(&spi_engine->lock); - timer_setup(&spi_engine->watchdog_timer, spi_engine_timeout, TIMER_IRQSAFE); - spi_engine->controller = host; + init_completion(&spi_engine->msg_complete); spi_engine->clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk"); if (IS_ERR(spi_engine->clk)) -- 2.43.0 ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] spi: axi-spi-engine: move msg finalization out of irq handler 2024-02-06 20:31 ` [PATCH 2/2] spi: axi-spi-engine: move msg finalization out of irq handler David Lechner @ 2024-02-07 9:33 ` Nuno Sá 0 siblings, 0 replies; 8+ messages in thread From: Nuno Sá @ 2024-02-07 9:33 UTC (permalink / raw) To: David Lechner, linux-spi Cc: Mark Brown, Michael Hennerich, Nuno Sá, linux-kernel On Tue, 2024-02-06 at 14:31 -0600, David Lechner wrote: > As a general principal, it is best to do as little as possible in an > interrupt handler. This patch reworks the AXI SPI Engine driver to move > timer_delete_sync() and spi_finalize_current_message() out of the > interrupt handler. Instead, spi_finalize_current_message() is moved to > the transfer_one_message function (similar to nearly all other SPI > controllers). A completion is now used to wait for the sync interrupt > that indicates that the message is complete. The watchdog timer is no > longer needed since we can use the wait_for_completion_timeout() > function to wait for the message to complete with the same effect. > > As a bonus, these changes also improve throughput of the SPI controller. > For example, this was tested on a ZynqMP with a 80MHz SCLK reading 4 > byte samples from an ADC. The max measured throughput increased from > 26k to 28k samples per second. > > Signed-off-by: David Lechner <dlechner@baylibre.com> > --- Nice one... Reviewed-by: Nuno Sa <nuno.sa@analog.com> > drivers/spi/spi-axi-spi-engine.c | 40 +++++++++++++++------------------------ > - > 1 file changed, 15 insertions(+), 25 deletions(-) > > diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi- > engine.c > index 9cc602075c17..3c96aa9232b3 100644 > --- a/drivers/spi/spi-axi-spi-engine.c > +++ b/drivers/spi/spi-axi-spi-engine.c > @@ -6,6 +6,7 @@ > */ > > #include <linux/clk.h> > +#include <linux/completion.h> > #include <linux/fpga/adi-axi-common.h> > #include <linux/idr.h> > #include <linux/interrupt.h> > @@ -14,7 +15,6 @@ > #include <linux/module.h> > #include <linux/platform_device.h> > #include <linux/spi/spi.h> > -#include <linux/timer.h> > > #define SPI_ENGINE_REG_RESET 0x40 > > @@ -110,9 +110,7 @@ struct spi_engine { > spinlock_t lock; > > void __iomem *base; > - struct timer_list watchdog_timer; > - struct spi_controller *controller; > - > + struct completion msg_complete; > unsigned int int_enable; > }; > > @@ -484,11 +482,9 @@ static irqreturn_t spi_engine_irq(int irq, void *devid) > > if (pending & SPI_ENGINE_INT_SYNC && msg) { > 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; > - spi_finalize_current_message(host); > - } > + msg->status = 0; > + msg->actual_length = msg->frame_length; > + complete(&spi_engine->msg_complete); > disable_int |= SPI_ENGINE_INT_SYNC; > } > } > @@ -559,7 +555,7 @@ static int spi_engine_transfer_one_message(struct > spi_controller *host, > unsigned int int_enable = 0; > unsigned long flags; > > - mod_timer(&spi_engine->watchdog_timer, jiffies + > msecs_to_jiffies(5000)); > + reinit_completion(&spi_engine->msg_complete); > > spin_lock_irqsave(&spi_engine->lock, flags); > > @@ -581,21 +577,16 @@ static int spi_engine_transfer_one_message(struct > spi_controller *host, > spi_engine->int_enable = int_enable; > spin_unlock_irqrestore(&spi_engine->lock, flags); > > - return 0; > -} > - > -static void spi_engine_timeout(struct timer_list *timer) > -{ > - struct spi_engine *spi_engine = from_timer(spi_engine, timer, > watchdog_timer); > - struct spi_controller *host = spi_engine->controller; > - > - if (WARN_ON(!host->cur_msg)) > - return; > + if (!wait_for_completion_timeout(&spi_engine->msg_complete, > + msecs_to_jiffies(5000))) { > + dev_err(&host->dev, > + "Timeout occurred while waiting for transfer to > complete. Hardware is probably broken.\n"); > + msg->status = -ETIMEDOUT; > + } > > - dev_err(&host->dev, > - "Timeout occurred while waiting for transfer to complete. > Hardware is probably broken.\n"); > - host->cur_msg->status = -ETIMEDOUT; > spi_finalize_current_message(host); > + > + return msg->status; > } > > static void spi_engine_release_hw(void *p) > @@ -626,8 +617,7 @@ static int spi_engine_probe(struct platform_device *pdev) > spi_engine = spi_controller_get_devdata(host); > > spin_lock_init(&spi_engine->lock); > - timer_setup(&spi_engine->watchdog_timer, spi_engine_timeout, > TIMER_IRQSAFE); > - spi_engine->controller = host; > + init_completion(&spi_engine->msg_complete); > > spi_engine->clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk"); > if (IS_ERR(spi_engine->clk)) > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 0/2] spi: axi-spi-engine: performance improvements 2024-02-06 20:31 [PATCH 0/2] spi: axi-spi-engine: performance improvements David Lechner 2024-02-06 20:31 ` [PATCH 1/2] spi: axi-spi-engine: remove use of ida for sync id David Lechner 2024-02-06 20:31 ` [PATCH 2/2] spi: axi-spi-engine: move msg finalization out of irq handler David Lechner @ 2024-02-08 16:33 ` Mark Brown 2 siblings, 0 replies; 8+ messages in thread From: Mark Brown @ 2024-02-08 16:33 UTC (permalink / raw) To: linux-spi, David Lechner; +Cc: Michael Hennerich, Nuno Sá, linux-kernel 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 ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2024-02-08 16:33 UTC | newest] Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2024-02-06 20:31 [PATCH 0/2] spi: axi-spi-engine: performance improvements David Lechner 2024-02-06 20:31 ` [PATCH 1/2] spi: axi-spi-engine: remove use of ida for sync id David Lechner 2024-02-06 21:50 ` Christophe JAILLET 2024-02-07 14:09 ` David Lechner 2024-02-07 9:40 ` Nuno Sá 2024-02-06 20:31 ` [PATCH 2/2] spi: axi-spi-engine: move msg finalization out of irq handler David Lechner 2024-02-07 9:33 ` Nuno Sá 2024-02-08 16:33 ` [PATCH 0/2] spi: axi-spi-engine: performance improvements Mark Brown
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®