Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

firewire: ohci: use workqueue to handle events of AT request/response contexts

This commit adds a work item to handle events of 1394 OHCI AT
request/response contexts, and queues the item to the specific
workqueue. The call of struct fw_packet.callbaqck() is done in the
workqueue when receiving acknowledgement to the asynchronous packet
transferred to remote node.

Link: https://lore.kernel.org/r/20250615133253.433057-4-o-takashi@sakamocchi.jp
Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>

+35 -20
+2 -2
drivers/firewire/net.c
··· 1007 1007 1008 1008 spin_lock_irqsave(&dev->lock, flags); 1009 1009 1010 - /* If the AT tasklet already ran, we may be last user. */ 1010 + /* If the AT work item already ran, we may be last user. */ 1011 1011 free = (ptask->outstanding_pkts == 0 && !ptask->enqueued); 1012 1012 if (!free) 1013 1013 ptask->enqueued = true; ··· 1026 1026 1027 1027 spin_lock_irqsave(&dev->lock, flags); 1028 1028 1029 - /* If the AT tasklet already ran, we may be last user. */ 1029 + /* If the AT work item already ran, we may be last user. */ 1030 1030 free = (ptask->outstanding_pkts == 0 && !ptask->enqueued); 1031 1031 if (!free) 1032 1032 ptask->enqueued = true;
+24 -16
drivers/firewire/ohci.c
··· 158 158 159 159 descriptor_callback_t callback; 160 160 161 - struct tasklet_struct tasklet; 161 + struct work_struct work; 162 162 }; 163 163 164 164 struct iso_context { ··· 1176 1176 } 1177 1177 } 1178 1178 1179 - static void context_tasklet(unsigned long data) 1179 + static void ohci_at_context_work(struct work_struct *work) 1180 1180 { 1181 - struct context *ctx = (struct context *) data; 1181 + struct context *ctx = from_work(ctx, work, work); 1182 1182 1183 1183 context_retire_descriptors(ctx); 1184 1184 } ··· 1243 1243 ctx->buffer_tail = list_entry(ctx->buffer_list.next, 1244 1244 struct descriptor_buffer, list); 1245 1245 1246 - tasklet_init(&ctx->tasklet, context_tasklet, (unsigned long)ctx); 1247 1246 ctx->callback = callback; 1248 1247 1249 1248 /* ··· 1523 1524 1524 1525 static void at_context_flush(struct context *ctx) 1525 1526 { 1526 - tasklet_disable(&ctx->tasklet); 1527 + // Avoid dead lock due to programming mistake. 1528 + if (WARN_ON_ONCE(current_work() == &ctx->work)) 1529 + return; 1527 1530 1528 - ctx->flushing = true; 1529 - context_tasklet((unsigned long)ctx); 1530 - ctx->flushing = false; 1531 + disable_work_sync(&ctx->work); 1531 1532 1532 - tasklet_enable(&ctx->tasklet); 1533 + WRITE_ONCE(ctx->flushing, true); 1534 + ohci_at_context_work(&ctx->work); 1535 + WRITE_ONCE(ctx->flushing, false); 1536 + 1537 + enable_work(&ctx->work); 1533 1538 } 1534 1539 1535 1540 static int handle_at_packet(struct context *context, ··· 1545 1542 struct fw_ohci *ohci = context->ohci; 1546 1543 int evt; 1547 1544 1548 - if (last->transfer_status == 0 && !context->flushing) 1545 + if (last->transfer_status == 0 && !READ_ONCE(context->flushing)) 1549 1546 /* This descriptor isn't done yet, stop iteration. */ 1550 1547 return 0; 1551 1548 ··· 1579 1576 break; 1580 1577 1581 1578 case OHCI1394_evt_missing_ack: 1582 - if (context->flushing) 1579 + if (READ_ONCE(context->flushing)) 1583 1580 packet->ack = RCODE_GENERATION; 1584 1581 else { 1585 1582 /* ··· 1601 1598 break; 1602 1599 1603 1600 case OHCI1394_evt_no_status: 1604 - if (context->flushing) { 1601 + if (READ_ONCE(context->flushing)) { 1605 1602 packet->ack = RCODE_GENERATION; 1606 1603 break; 1607 1604 } ··· 2242 2239 queue_work(ohci->card.async_wq, &ohci->ar_response_ctx.work); 2243 2240 2244 2241 if (event & OHCI1394_reqTxComplete) 2245 - tasklet_schedule(&ohci->at_request_ctx.tasklet); 2242 + queue_work(ohci->card.async_wq, &ohci->at_request_ctx.work); 2246 2243 2247 2244 if (event & OHCI1394_respTxComplete) 2248 - tasklet_schedule(&ohci->at_response_ctx.tasklet); 2245 + queue_work(ohci->card.async_wq, &ohci->at_response_ctx.work); 2249 2246 2250 2247 if (event & OHCI1394_isochRx) { 2251 2248 iso_event = reg_read(ohci, OHCI1394_IsoRecvIntEventClear); ··· 2687 2684 struct driver_data *driver_data = packet->driver_data; 2688 2685 int ret = -ENOENT; 2689 2686 2690 - tasklet_disable_in_atomic(&ctx->tasklet); 2687 + // Avoid dead lock due to programming mistake. 2688 + if (WARN_ON_ONCE(current_work() == &ctx->work)) 2689 + return 0; 2690 + disable_work_sync(&ctx->work); 2691 2691 2692 2692 if (packet->ack != 0) 2693 2693 goto out; ··· 2709 2703 packet->callback(packet, &ohci->card, packet->ack); 2710 2704 ret = 0; 2711 2705 out: 2712 - tasklet_enable(&ctx->tasklet); 2706 + enable_work(&ctx->work); 2713 2707 2714 2708 return ret; 2715 2709 } ··· 3771 3765 OHCI1394_AsReqTrContextControlSet, handle_at_packet); 3772 3766 if (err < 0) 3773 3767 return err; 3768 + INIT_WORK(&ohci->at_request_ctx.work, ohci_at_context_work); 3774 3769 3775 3770 err = context_init(&ohci->at_response_ctx, ohci, 3776 3771 OHCI1394_AsRspTrContextControlSet, handle_at_packet); 3777 3772 if (err < 0) 3778 3773 return err; 3774 + INIT_WORK(&ohci->at_response_ctx.work, ohci_at_context_work); 3779 3775 3780 3776 reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, ~0); 3781 3777 ohci->ir_context_channels = ~0ULL;
+9 -2
include/linux/firewire.h
··· 308 308 * For successful transmission, the status code is the ack received 309 309 * from the destination. Otherwise it is one of the juju-specific 310 310 * rcodes: RCODE_SEND_ERROR, _CANCELLED, _BUSY, _GENERATION, _NO_ACK. 311 - * The callback can be called from tasklet context and thus 312 - * must never block. 311 + * The callback can be called from workqueue and thus must never block. 313 312 */ 314 313 fw_packet_callback_t callback; 315 314 int ack; ··· 381 382 * 382 383 * A variation of __fw_send_request() to generate callback for response subaction without time 383 384 * stamp. 385 + * 386 + * The callback is invoked in the workqueue context in most cases. However, if an error is detected 387 + * before queueing or the destination address refers to the local node, it is invoked in the 388 + * current context instead. 384 389 */ 385 390 static inline void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode, 386 391 int destination_id, int generation, int speed, ··· 414 411 * @callback_data: data to be passed to the transaction completion callback 415 412 * 416 413 * A variation of __fw_send_request() to generate callback for response subaction with time stamp. 414 + * 415 + * The callback is invoked in the workqueue context in most cases. However, if an error is detected 416 + * before queueing or the destination address refers to the local node, it is invoked in the current 417 + * context instead. 417 418 */ 418 419 static inline void fw_send_request_with_tstamp(struct fw_card *card, struct fw_transaction *t, 419 420 int tcode, int destination_id, int generation, int speed, unsigned long long offset,