Skip to content

Commit 5390813

Browse files
committed
firewire: ohci: operate IT/IR events in sleepable work process instead of tasklet softIRQ
This commit queues work item for IT/IR events at hardIRQ handler to operate the corresponding isochronous context. The work item is queued to any of worker-pools. The callback for either the implementation of unit protocol and user space clients is executed in sleepable work process context. The change could results in any errors of concurrent processing as well as sleep at atomic context. These errors are fixed by the following commits. Tested-by: Edmund Raile <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Takashi Sakamoto <[email protected]>
1 parent 4f55ad7 commit 5390813

File tree

1 file changed

+45
-10
lines changed

1 file changed

+45
-10
lines changed

drivers/firewire/ohci.c

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,6 +1182,47 @@ static void context_tasklet(unsigned long data)
11821182
}
11831183
}
11841184

1185+
static void ohci_isoc_context_work(struct work_struct *work)
1186+
{
1187+
struct fw_iso_context *base = container_of(work, struct fw_iso_context, work);
1188+
struct iso_context *isoc_ctx = container_of(base, struct iso_context, base);
1189+
struct context *ctx = &isoc_ctx->context;
1190+
struct descriptor *d, *last;
1191+
u32 address;
1192+
int z;
1193+
struct descriptor_buffer *desc;
1194+
1195+
desc = list_entry(ctx->buffer_list.next, struct descriptor_buffer, list);
1196+
last = ctx->last;
1197+
while (last->branch_address != 0) {
1198+
struct descriptor_buffer *old_desc = desc;
1199+
1200+
address = le32_to_cpu(last->branch_address);
1201+
z = address & 0xf;
1202+
address &= ~0xf;
1203+
ctx->current_bus = address;
1204+
1205+
// If the branch address points to a buffer outside of the current buffer, advance
1206+
// to the next buffer.
1207+
if (address < desc->buffer_bus || address >= desc->buffer_bus + desc->used)
1208+
desc = list_entry(desc->list.next, struct descriptor_buffer, list);
1209+
d = desc->buffer + (address - desc->buffer_bus) / sizeof(*d);
1210+
last = find_branch_descriptor(d, z);
1211+
1212+
if (!ctx->callback(ctx, d, last))
1213+
break;
1214+
1215+
if (old_desc != desc) {
1216+
// If we've advanced to the next buffer, move the previous buffer to the
1217+
// free list.
1218+
old_desc->used = 0;
1219+
guard(spinlock_irqsave)(&ctx->ohci->lock);
1220+
list_move_tail(&old_desc->list, &ctx->buffer_list);
1221+
}
1222+
ctx->last = last;
1223+
}
1224+
}
1225+
11851226
/*
11861227
* Allocate a new buffer and add it to the list of free buffers for this
11871228
* context. Must be called with ohci->lock held.
@@ -2242,8 +2283,7 @@ static irqreturn_t irq_handler(int irq, void *data)
22422283

22432284
while (iso_event) {
22442285
i = ffs(iso_event) - 1;
2245-
tasklet_schedule(
2246-
&ohci->ir_context_list[i].context.tasklet);
2286+
fw_iso_context_queue_work(&ohci->ir_context_list[i].base);
22472287
iso_event &= ~(1 << i);
22482288
}
22492289
}
@@ -2254,8 +2294,7 @@ static irqreturn_t irq_handler(int irq, void *data)
22542294

22552295
while (iso_event) {
22562296
i = ffs(iso_event) - 1;
2257-
tasklet_schedule(
2258-
&ohci->it_context_list[i].context.tasklet);
2297+
fw_iso_context_queue_work(&ohci->it_context_list[i].base);
22592298
iso_event &= ~(1 << i);
22602299
}
22612300
}
@@ -3130,6 +3169,7 @@ static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card,
31303169
ret = context_init(&ctx->context, ohci, regs, callback);
31313170
if (ret < 0)
31323171
goto out_with_header;
3172+
fw_iso_context_init_work(&ctx->base, ohci_isoc_context_work);
31333173

31343174
if (type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) {
31353175
set_multichannel_mask(ohci, 0);
@@ -3227,7 +3267,6 @@ static int ohci_stop_iso(struct fw_iso_context *base)
32273267
}
32283268
flush_writes(ohci);
32293269
context_stop(&ctx->context);
3230-
tasklet_kill(&ctx->context.tasklet);
32313270

32323271
return 0;
32333272
}
@@ -3584,10 +3623,8 @@ static int ohci_flush_iso_completions(struct fw_iso_context *base)
35843623
struct iso_context *ctx = container_of(base, struct iso_context, base);
35853624
int ret = 0;
35863625

3587-
tasklet_disable_in_atomic(&ctx->context.tasklet);
3588-
35893626
if (!test_and_set_bit_lock(0, &ctx->flushing_completions)) {
3590-
context_tasklet((unsigned long)&ctx->context);
3627+
ohci_isoc_context_work(&base->work);
35913628

35923629
switch (base->type) {
35933630
case FW_ISO_CONTEXT_TRANSMIT:
@@ -3607,8 +3644,6 @@ static int ohci_flush_iso_completions(struct fw_iso_context *base)
36073644
smp_mb__after_atomic();
36083645
}
36093646

3610-
tasklet_enable(&ctx->context.tasklet);
3611-
36123647
return ret;
36133648
}
36143649

0 commit comments

Comments
 (0)