From c6fb88a5270f4ba24859f5ecb61cfc731ef16300 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 4 Sep 2024 21:51:50 +0900 Subject: firewire: core: allocate workqueue to handle isochronous contexts in card This commit adds a workqueue dedicated for isochronous context processing. The workqueue is allocated per instance of fw_card structure to satisfy the following characteristics descending from 1394 OHCI specification: In 1394 OHCI specification, memory pages are reserved to each isochronous context dedicated to DMA transmission. It allows to operate these per-context pages concurrently. Software can schedule hardware interrupt for several isochronous context to the same cycle, thus WQ_UNBOUND is specified. Additionally, it is sleepable to operate the content of pages, thus WQ_BH is not used. The isochronous context delivers the packets with time stamp, thus WQ_HIGHPRI is specified for semi real-time data such as IEC 61883-1/6 protocol implemented by ALSA firewire stack. The isochronous context is not used by the implementation of SCSI over IEEE1394 protocol (sbp2), thus WQ_MEM_RECLAIM is not specified. It is useful for users to adjust cpu affinity of the workqueue depending on their work loads, thus WQ_SYS is specified to expose the attributes to user space. Tested-by: Edmund Raile Link: https://lore.kernel.org/r/20240904125155.461886-2-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- include/linux/firewire.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 1cca14cf5652..10e135d60824 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -134,6 +134,8 @@ struct fw_card { __be32 topology_map[(CSR_TOPOLOGY_MAP_END - CSR_TOPOLOGY_MAP) / 4]; __be32 maint_utility_register; + + struct workqueue_struct *isoc_wq; }; static inline struct fw_card *fw_card_get(struct fw_card *card) -- cgit From 4f55ad754d6b7b6fa4ebcfd8c50f7e53e661a78e Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 4 Sep 2024 21:51:51 +0900 Subject: firewire: core: add local API to queue work item to workqueue specific to isochronous contexts In the previous commit, the workqueue is added per the instance of fw_card structure for isochronous contexts. The workqueue is designed to be used by the implementation of fw_card_driver structure underlying the fw_card. This commit adds some local APIs to be used by the implementation. Tested-by: Edmund Raile Link: https://lore.kernel.org/r/20240904125155.461886-3-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-iso.c | 30 ++++++++++++++++++++++++++++-- drivers/firewire/core.h | 10 ++++++++++ include/linux/firewire.h | 1 + 3 files changed, 39 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index 101433b8bb51..af76fa1823f1 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -211,21 +211,47 @@ EXPORT_SYMBOL(fw_iso_context_queue_flush); int fw_iso_context_flush_completions(struct fw_iso_context *ctx) { + int err; + trace_isoc_outbound_flush_completions(ctx); trace_isoc_inbound_single_flush_completions(ctx); trace_isoc_inbound_multiple_flush_completions(ctx); - return ctx->card->driver->flush_iso_completions(ctx); + might_sleep(); + + // Avoid dead lock due to programming mistake. + if (WARN_ON(current_work() == &ctx->work)) + return 0; + + disable_work_sync(&ctx->work); + + err = ctx->card->driver->flush_iso_completions(ctx); + + enable_work(&ctx->work); + + return err; } EXPORT_SYMBOL(fw_iso_context_flush_completions); int fw_iso_context_stop(struct fw_iso_context *ctx) { + int err; + trace_isoc_outbound_stop(ctx); trace_isoc_inbound_single_stop(ctx); trace_isoc_inbound_multiple_stop(ctx); - return ctx->card->driver->stop_iso(ctx); + might_sleep(); + + // Avoid dead lock due to programming mistake. + if (WARN_ON(current_work() == &ctx->work)) + return 0; + + err = ctx->card->driver->stop_iso(ctx); + + cancel_work_sync(&ctx->work); + + return err; } EXPORT_SYMBOL(fw_iso_context_stop); diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 96ae366889e0..2874f316156a 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -159,6 +159,16 @@ int fw_iso_buffer_alloc(struct fw_iso_buffer *buffer, int page_count); int fw_iso_buffer_map_dma(struct fw_iso_buffer *buffer, struct fw_card *card, enum dma_data_direction direction); +static inline void fw_iso_context_init_work(struct fw_iso_context *ctx, work_func_t func) +{ + INIT_WORK(&ctx->work, func); +} + +static inline void fw_iso_context_queue_work(struct fw_iso_context *ctx) +{ + queue_work(ctx->card->isoc_wq, &ctx->work); +} + /* -topology */ diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 10e135d60824..72f497b61739 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -511,6 +511,7 @@ union fw_iso_callback { struct fw_iso_context { struct fw_card *card; + struct work_struct work; int type; int channel; int speed; -- cgit From 446216bd8e5d4a3ffc9738f6adffc98fbfdcc582 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 8 Sep 2024 13:05:48 +0900 Subject: firewire: core: expose kernel API to schedule work item to process isochronous context In packet-per-buffer mode for isochronous context of 1394 OHCI, software can schedule hardIRQ to the buffer in which the content of isochronous packet is processed. The actual behaviour is different between isochronous receive (IR) and transmit (IT) contexts in respect to isochronous cycle in which the hardIRQ occurs. In IR context, the hardIRQ occurs when the buffer is filled actually by the content of received packet. If there are any isochronous cycles in which the packet transmission is skipped, it is postponed to generate the hardIRQ in respect to the isochronous cycle. In IT context, software can schedule the content of packet every isochronous cycle including skipping, therefore the hardIRQ occurs in the isochronous cycle to which the software scheduled. ALSA firewire stack uses the packet-per-buffer mode for both IR/IT contexts. To process time stamp per packet (or per sample in some cases) steadily for media clock recovery against unexpected transmission skips, it uses an IT context to operate all of isochronous contexts by calls of fw_iso_context_flush_completions() in the bottom-half of hardIRQ for the IT context. Although it looks well to handle all of isochronous contexts in a single bottom-half context, it relatively takes longer time. In the future code integration (not yet), it is possible to apply parallelism method to process these context. In the case, it is useful to allow unit drivers to schedule work items to process these isochronous contexts. As a preparation, this commit exposes fw_iso_context_schedule_flush_completions() as a kernel API available by unit drivers. It is renamed from fw_iso_context_queue_work() since it is a counter part of fw_iso_context_flush_completions(). Link: https://lore.kernel.org/r/20240908040549.75304-2-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- Documentation/driver-api/firewire.rst | 2 ++ drivers/firewire/core.h | 5 ----- drivers/firewire/ohci.c | 4 ++-- include/linux/firewire.h | 17 +++++++++++++++++ 4 files changed, 21 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/Documentation/driver-api/firewire.rst b/Documentation/driver-api/firewire.rst index d3cfa73cbb2b..28a32410f7d2 100644 --- a/Documentation/driver-api/firewire.rst +++ b/Documentation/driver-api/firewire.rst @@ -43,6 +43,8 @@ Firewire core transaction interfaces Firewire Isochronous I/O interfaces =================================== +.. kernel-doc:: include/linux/firewire.h + :functions: fw_iso_context_schedule_flush_completions .. kernel-doc:: drivers/firewire/core-iso.c :export: diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 2874f316156a..0ae2c84ecafe 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -164,11 +164,6 @@ static inline void fw_iso_context_init_work(struct fw_iso_context *ctx, work_fun INIT_WORK(&ctx->work, func); } -static inline void fw_iso_context_queue_work(struct fw_iso_context *ctx) -{ - queue_work(ctx->card->isoc_wq, &ctx->work); -} - /* -topology */ diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index d0b1fccc450f..3a911cfb5ff3 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -2283,7 +2283,7 @@ static irqreturn_t irq_handler(int irq, void *data) while (iso_event) { i = ffs(iso_event) - 1; - fw_iso_context_queue_work(&ohci->ir_context_list[i].base); + fw_iso_context_schedule_flush_completions(&ohci->ir_context_list[i].base); iso_event &= ~(1 << i); } } @@ -2294,7 +2294,7 @@ static irqreturn_t irq_handler(int irq, void *data) while (iso_event) { i = ffs(iso_event) - 1; - fw_iso_context_queue_work(&ohci->it_context_list[i].base); + fw_iso_context_schedule_flush_completions(&ohci->it_context_list[i].base); iso_event &= ~(1 << i); } } diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 72f497b61739..f815d12deda0 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -531,6 +531,23 @@ int fw_iso_context_queue(struct fw_iso_context *ctx, unsigned long payload); void fw_iso_context_queue_flush(struct fw_iso_context *ctx); int fw_iso_context_flush_completions(struct fw_iso_context *ctx); + +/** + * fw_iso_context_schedule_flush_completions() - schedule work item to process isochronous context. + * @ctx: the isochronous context + * + * Schedule a work item on workqueue to process the isochronous context. The registered callback + * function is called in the worker if some packets have been already transferred since the last + * time. If it is required to process the context in the current context, + * fw_iso_context_flush_completions() is available instead. + * + * Context: Any context. + */ +static inline void fw_iso_context_schedule_flush_completions(struct fw_iso_context *ctx) +{ + queue_work(ctx->card->isoc_wq, &ctx->work); +} + int fw_iso_context_start(struct fw_iso_context *ctx, int cycle, int sync, int tags); int fw_iso_context_stop(struct fw_iso_context *ctx); -- cgit From f877f1d81b2ffcac341ff62ae076d7ad5ba83f46 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 9 Sep 2024 23:00:18 +0900 Subject: firewire: core: use mutex to coordinate concurrent calls to flush completions In current implementation, test_and_set_bit_lock() is used to mediate concurrent calls of ohci_flush_iso_completions(). However, the ad-hoc usage of atomic operations is not preferable. This commit uses mutex_trylock() as the similar operations. The core function is responsible for the mediation, instead of 1394 OHCI driver. Link: https://lore.kernel.org/r/20240909140018.65289-3-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-iso.c | 11 +++++++++-- drivers/firewire/ohci.c | 37 ++++++++++++++----------------------- include/linux/firewire.h | 1 + 3 files changed, 24 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index 9f41c78878ad..1405d2e9cb2c 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -157,6 +157,7 @@ struct fw_iso_context *fw_iso_context_create(struct fw_card *card, ctx->callback.sc = callback; ctx->callback_data = callback_data; INIT_WORK(&ctx->work, flush_completions_work); + mutex_init(&ctx->flushing_completions_mutex); trace_isoc_outbound_allocate(ctx, channel, speed); trace_isoc_inbound_single_allocate(ctx, channel, header_size); @@ -173,6 +174,8 @@ void fw_iso_context_destroy(struct fw_iso_context *ctx) trace_isoc_inbound_multiple_destroy(ctx); ctx->card->driver->free_iso_context(ctx); + + mutex_destroy(&ctx->flushing_completions_mutex); } EXPORT_SYMBOL(fw_iso_context_destroy); @@ -226,7 +229,7 @@ EXPORT_SYMBOL(fw_iso_context_queue_flush); * to process the context asynchronously, fw_iso_context_schedule_flush_completions() is available * instead. * - * Context: Process context. + * Context: Process context due to mutex_trylock(). */ int fw_iso_context_flush_completions(struct fw_iso_context *ctx) { @@ -234,7 +237,11 @@ int fw_iso_context_flush_completions(struct fw_iso_context *ctx) trace_isoc_inbound_single_flush_completions(ctx); trace_isoc_inbound_multiple_flush_completions(ctx); - return ctx->card->driver->flush_iso_completions(ctx); + scoped_cond_guard(mutex_try, /* nothing to do */, &ctx->flushing_completions_mutex) { + return ctx->card->driver->flush_iso_completions(ctx); + } + + return 0; } EXPORT_SYMBOL(fw_iso_context_flush_completions); diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 02ff0363d3ad..b182998a77f4 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -166,7 +166,6 @@ struct iso_context { struct context context; void *header; size_t header_length; - unsigned long flushing_completions; u32 mc_buffer_bus; u16 mc_completed; u16 last_timestamp; @@ -3579,31 +3578,23 @@ static void ohci_flush_queue_iso(struct fw_iso_context *base) static int ohci_flush_iso_completions(struct fw_iso_context *base) { struct iso_context *ctx = container_of(base, struct iso_context, base); - int ret = 0; - if (!test_and_set_bit_lock(0, &ctx->flushing_completions)) { - // Note that tasklet softIRQ is not used to process isochronous context anymore. - context_tasklet((unsigned long)&ctx->context); + // Note that tasklet softIRQ is not used to process isochronous context anymore. + context_tasklet((unsigned long)&ctx->context); - switch (base->type) { - case FW_ISO_CONTEXT_TRANSMIT: - case FW_ISO_CONTEXT_RECEIVE: - if (ctx->header_length != 0) - flush_iso_completions(ctx, FW_ISO_CONTEXT_COMPLETIONS_CAUSE_FLUSH); - break; - case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: - if (ctx->mc_completed != 0) - flush_ir_buffer_fill(ctx); - break; - default: - ret = -ENOSYS; - } - - clear_bit_unlock(0, &ctx->flushing_completions); - smp_mb__after_atomic(); + switch (base->type) { + case FW_ISO_CONTEXT_TRANSMIT: + case FW_ISO_CONTEXT_RECEIVE: + if (ctx->header_length != 0) + flush_iso_completions(ctx, FW_ISO_CONTEXT_COMPLETIONS_CAUSE_FLUSH); + return 0; + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + if (ctx->mc_completed != 0) + flush_ir_buffer_fill(ctx); + return 0; + default: + return -ENOSYS; } - - return ret; } static const struct fw_card_driver ohci_driver = { diff --git a/include/linux/firewire.h b/include/linux/firewire.h index f815d12deda0..19e8c5f9537c 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -512,6 +512,7 @@ union fw_iso_callback { struct fw_iso_context { struct fw_card *card; struct work_struct work; + struct mutex flushing_completions_mutex; int type; int channel; int speed; -- cgit From c45b9a07b6392fa224ca76b89f24dae1046eef09 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 12 Sep 2024 22:30:34 +0900 Subject: Revert "firewire: core: use mutex to coordinate concurrent calls to flush completions" This reverts commit d9605d67562505e27dcc0f71af418118d3db91e5, since this commit is on the following reverted changes. Link: https://lore.kernel.org/r/20240912133038.238786-2-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-iso.c | 11 ++--------- drivers/firewire/ohci.c | 37 +++++++++++++++++++++++-------------- include/linux/firewire.h | 1 - 3 files changed, 25 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index 1405d2e9cb2c..9f41c78878ad 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -157,7 +157,6 @@ struct fw_iso_context *fw_iso_context_create(struct fw_card *card, ctx->callback.sc = callback; ctx->callback_data = callback_data; INIT_WORK(&ctx->work, flush_completions_work); - mutex_init(&ctx->flushing_completions_mutex); trace_isoc_outbound_allocate(ctx, channel, speed); trace_isoc_inbound_single_allocate(ctx, channel, header_size); @@ -174,8 +173,6 @@ void fw_iso_context_destroy(struct fw_iso_context *ctx) trace_isoc_inbound_multiple_destroy(ctx); ctx->card->driver->free_iso_context(ctx); - - mutex_destroy(&ctx->flushing_completions_mutex); } EXPORT_SYMBOL(fw_iso_context_destroy); @@ -229,7 +226,7 @@ EXPORT_SYMBOL(fw_iso_context_queue_flush); * to process the context asynchronously, fw_iso_context_schedule_flush_completions() is available * instead. * - * Context: Process context due to mutex_trylock(). + * Context: Process context. */ int fw_iso_context_flush_completions(struct fw_iso_context *ctx) { @@ -237,11 +234,7 @@ int fw_iso_context_flush_completions(struct fw_iso_context *ctx) trace_isoc_inbound_single_flush_completions(ctx); trace_isoc_inbound_multiple_flush_completions(ctx); - scoped_cond_guard(mutex_try, /* nothing to do */, &ctx->flushing_completions_mutex) { - return ctx->card->driver->flush_iso_completions(ctx); - } - - return 0; + return ctx->card->driver->flush_iso_completions(ctx); } EXPORT_SYMBOL(fw_iso_context_flush_completions); diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index b182998a77f4..02ff0363d3ad 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -166,6 +166,7 @@ struct iso_context { struct context context; void *header; size_t header_length; + unsigned long flushing_completions; u32 mc_buffer_bus; u16 mc_completed; u16 last_timestamp; @@ -3578,23 +3579,31 @@ static void ohci_flush_queue_iso(struct fw_iso_context *base) static int ohci_flush_iso_completions(struct fw_iso_context *base) { struct iso_context *ctx = container_of(base, struct iso_context, base); + int ret = 0; - // Note that tasklet softIRQ is not used to process isochronous context anymore. - context_tasklet((unsigned long)&ctx->context); + if (!test_and_set_bit_lock(0, &ctx->flushing_completions)) { + // Note that tasklet softIRQ is not used to process isochronous context anymore. + context_tasklet((unsigned long)&ctx->context); - switch (base->type) { - case FW_ISO_CONTEXT_TRANSMIT: - case FW_ISO_CONTEXT_RECEIVE: - if (ctx->header_length != 0) - flush_iso_completions(ctx, FW_ISO_CONTEXT_COMPLETIONS_CAUSE_FLUSH); - return 0; - case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: - if (ctx->mc_completed != 0) - flush_ir_buffer_fill(ctx); - return 0; - default: - return -ENOSYS; + switch (base->type) { + case FW_ISO_CONTEXT_TRANSMIT: + case FW_ISO_CONTEXT_RECEIVE: + if (ctx->header_length != 0) + flush_iso_completions(ctx, FW_ISO_CONTEXT_COMPLETIONS_CAUSE_FLUSH); + break; + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + if (ctx->mc_completed != 0) + flush_ir_buffer_fill(ctx); + break; + default: + ret = -ENOSYS; + } + + clear_bit_unlock(0, &ctx->flushing_completions); + smp_mb__after_atomic(); } + + return ret; } static const struct fw_card_driver ohci_driver = { diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 19e8c5f9537c..f815d12deda0 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -512,7 +512,6 @@ union fw_iso_callback { struct fw_iso_context { struct fw_card *card; struct work_struct work; - struct mutex flushing_completions_mutex; int type; int channel; int speed; -- cgit From 4010cb1efda08ec6fd02ec5db9da909322ef352e Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 12 Sep 2024 22:30:37 +0900 Subject: firewire: core: update documentation of kernel APIs for flushing completions There is a slight difference between fw_iso_context_flush_completions() and fw_iso_context_schedule_flush_completions(). This commit updates the documentations for them. Link: https://lore.kernel.org/r/20240912133038.238786-5-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-iso.c | 9 ++++++--- include/linux/firewire.h | 8 +++++--- 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index f2394f3ed194..a67493862c85 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -214,9 +214,12 @@ EXPORT_SYMBOL(fw_iso_context_queue_flush); * @ctx: the isochronous context * * Process the isochronous context in the current process context. The registered callback function - * is called if some packets have been already transferred since the last time. If it is required - * to process the context asynchronously, fw_iso_context_schedule_flush_completions() is available - * instead. + * is called when a queued packet buffer with the interrupt flag is completed, either after + * transmission in the IT context or after being filled in the IR context. Additionally, the + * callback function is also called for the packet buffer completed at last. Furthermore, the + * callback function is called as well when the header buffer in the context becomes full. If it is + * required to process the context asynchronously, fw_iso_context_schedule_flush_completions() is + * available instead. * * Context: Process context. May sleep due to disable_work_sync(). */ diff --git a/include/linux/firewire.h b/include/linux/firewire.h index f815d12deda0..b632eec3ab52 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -537,9 +537,11 @@ int fw_iso_context_flush_completions(struct fw_iso_context *ctx); * @ctx: the isochronous context * * Schedule a work item on workqueue to process the isochronous context. The registered callback - * function is called in the worker if some packets have been already transferred since the last - * time. If it is required to process the context in the current context, - * fw_iso_context_flush_completions() is available instead. + * function is called by the worker when a queued packet buffer with the interrupt flag is + * completed, either after transmission in the IT context or after being filled in the IR context. + * The callback function is also called when the header buffer in the context becomes full, If it + * is required to process the context in the current context, fw_iso_context_flush_completions() is + * available instead. * * Context: Any context. */ -- cgit