diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-02-25 16:18:57 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-02-25 16:18:57 -0500 |
| commit | 6dba6ecba7d937e9b04b46f6cdff25e574f64857 (patch) | |
| tree | afb2187012074240ad38fa6dcac6279588868d3b | |
| parent | e4cc60cbdc4fd1d1050824937079bed2a4108864 (diff) | |
| parent | da87ca4d4ca101f177fffd84f1f0a5e4c0343557 (diff) | |
Merge tag 'dmaengine-fixes-3.14-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/djbw/dmaengine
Pull dmaengine fixes from Dan Williams:
"Fix tasklet lifetime management in the ioat driver causing ksoftirqd
to spin indefinitely.
References:
https://lkml.org/lkml/2014/1/27/282
https://lkml.org/lkml/2014/2/19/672"
* tag 'dmaengine-fixes-3.14-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/djbw/dmaengine:
ioat: fix tasklet tear down
| -rw-r--r-- | drivers/dma/ioat/dma.c | 52 | ||||
| -rw-r--r-- | drivers/dma/ioat/dma.h | 1 | ||||
| -rw-r--r-- | drivers/dma/ioat/dma_v2.c | 11 | ||||
| -rw-r--r-- | drivers/dma/ioat/dma_v3.c | 3 |
4 files changed, 54 insertions, 13 deletions
diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index 87529181efcc..4e3549a16132 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c | |||
| @@ -77,7 +77,8 @@ static irqreturn_t ioat_dma_do_interrupt(int irq, void *data) | |||
| 77 | attnstatus = readl(instance->reg_base + IOAT_ATTNSTATUS_OFFSET); | 77 | attnstatus = readl(instance->reg_base + IOAT_ATTNSTATUS_OFFSET); |
| 78 | for_each_set_bit(bit, &attnstatus, BITS_PER_LONG) { | 78 | for_each_set_bit(bit, &attnstatus, BITS_PER_LONG) { |
| 79 | chan = ioat_chan_by_index(instance, bit); | 79 | chan = ioat_chan_by_index(instance, bit); |
| 80 | tasklet_schedule(&chan->cleanup_task); | 80 | if (test_bit(IOAT_RUN, &chan->state)) |
| 81 | tasklet_schedule(&chan->cleanup_task); | ||
| 81 | } | 82 | } |
| 82 | 83 | ||
| 83 | writeb(intrctrl, instance->reg_base + IOAT_INTRCTRL_OFFSET); | 84 | writeb(intrctrl, instance->reg_base + IOAT_INTRCTRL_OFFSET); |
| @@ -93,7 +94,8 @@ static irqreturn_t ioat_dma_do_interrupt_msix(int irq, void *data) | |||
| 93 | { | 94 | { |
| 94 | struct ioat_chan_common *chan = data; | 95 | struct ioat_chan_common *chan = data; |
| 95 | 96 | ||
| 96 | tasklet_schedule(&chan->cleanup_task); | 97 | if (test_bit(IOAT_RUN, &chan->state)) |
| 98 | tasklet_schedule(&chan->cleanup_task); | ||
| 97 | 99 | ||
| 98 | return IRQ_HANDLED; | 100 | return IRQ_HANDLED; |
| 99 | } | 101 | } |
| @@ -116,7 +118,6 @@ void ioat_init_channel(struct ioatdma_device *device, struct ioat_chan_common *c | |||
| 116 | chan->timer.function = device->timer_fn; | 118 | chan->timer.function = device->timer_fn; |
| 117 | chan->timer.data = data; | 119 | chan->timer.data = data; |
| 118 | tasklet_init(&chan->cleanup_task, device->cleanup_fn, data); | 120 | tasklet_init(&chan->cleanup_task, device->cleanup_fn, data); |
| 119 | tasklet_disable(&chan->cleanup_task); | ||
| 120 | } | 121 | } |
| 121 | 122 | ||
| 122 | /** | 123 | /** |
| @@ -354,13 +355,49 @@ static int ioat1_dma_alloc_chan_resources(struct dma_chan *c) | |||
| 354 | writel(((u64) chan->completion_dma) >> 32, | 355 | writel(((u64) chan->completion_dma) >> 32, |
| 355 | chan->reg_base + IOAT_CHANCMP_OFFSET_HIGH); | 356 | chan->reg_base + IOAT_CHANCMP_OFFSET_HIGH); |
| 356 | 357 | ||
| 357 | tasklet_enable(&chan->cleanup_task); | 358 | set_bit(IOAT_RUN, &chan->state); |
| 358 | ioat1_dma_start_null_desc(ioat); /* give chain to dma device */ | 359 | ioat1_dma_start_null_desc(ioat); /* give chain to dma device */ |
| 359 | dev_dbg(to_dev(chan), "%s: allocated %d descriptors\n", | 360 | dev_dbg(to_dev(chan), "%s: allocated %d descriptors\n", |
| 360 | __func__, ioat->desccount); | 361 | __func__, ioat->desccount); |
| 361 | return ioat->desccount; | 362 | return ioat->desccount; |
| 362 | } | 363 | } |
| 363 | 364 | ||
| 365 | void ioat_stop(struct ioat_chan_common *chan) | ||
| 366 | { | ||
| 367 | struct ioatdma_device *device = chan->device; | ||
| 368 | struct pci_dev *pdev = device->pdev; | ||
| 369 | int chan_id = chan_num(chan); | ||
| 370 | struct msix_entry *msix; | ||
| 371 | |||
| 372 | /* 1/ stop irq from firing tasklets | ||
| 373 | * 2/ stop the tasklet from re-arming irqs | ||
| 374 | */ | ||
| 375 | clear_bit(IOAT_RUN, &chan->state); | ||
| 376 | |||
| 377 | /* flush inflight interrupts */ | ||
| 378 | switch (device->irq_mode) { | ||
| 379 | case IOAT_MSIX: | ||
| 380 | msix = &device->msix_entries[chan_id]; | ||
| 381 | synchronize_irq(msix->vector); | ||
| 382 | break; | ||
| 383 | case IOAT_MSI: | ||
| 384 | case IOAT_INTX: | ||
| 385 | synchronize_irq(pdev->irq); | ||
| 386 | break; | ||
| 387 | default: | ||
| 388 | break; | ||
| 389 | } | ||
| 390 | |||
| 391 | /* flush inflight timers */ | ||
| 392 | del_timer_sync(&chan->timer); | ||
| 393 | |||
| 394 | /* flush inflight tasklet runs */ | ||
| 395 | tasklet_kill(&chan->cleanup_task); | ||
| 396 | |||
| 397 | /* final cleanup now that everything is quiesced and can't re-arm */ | ||
| 398 | device->cleanup_fn((unsigned long) &chan->common); | ||
| 399 | } | ||
| 400 | |||
| 364 | /** | 401 | /** |
| 365 | * ioat1_dma_free_chan_resources - release all the descriptors | 402 | * ioat1_dma_free_chan_resources - release all the descriptors |
| 366 | * @chan: the channel to be cleaned | 403 | * @chan: the channel to be cleaned |
| @@ -379,9 +416,7 @@ static void ioat1_dma_free_chan_resources(struct dma_chan *c) | |||
| 379 | if (ioat->desccount == 0) | 416 | if (ioat->desccount == 0) |
| 380 | return; | 417 | return; |
| 381 | 418 | ||
| 382 | tasklet_disable(&chan->cleanup_task); | 419 | ioat_stop(chan); |
| 383 | del_timer_sync(&chan->timer); | ||
| 384 | ioat1_cleanup(ioat); | ||
| 385 | 420 | ||
| 386 | /* Delay 100ms after reset to allow internal DMA logic to quiesce | 421 | /* Delay 100ms after reset to allow internal DMA logic to quiesce |
| 387 | * before removing DMA descriptor resources. | 422 | * before removing DMA descriptor resources. |
| @@ -526,8 +561,11 @@ ioat1_dma_prep_memcpy(struct dma_chan *c, dma_addr_t dma_dest, | |||
| 526 | static void ioat1_cleanup_event(unsigned long data) | 561 | static void ioat1_cleanup_event(unsigned long data) |
| 527 | { | 562 | { |
| 528 | struct ioat_dma_chan *ioat = to_ioat_chan((void *) data); | 563 | struct ioat_dma_chan *ioat = to_ioat_chan((void *) data); |
| 564 | struct ioat_chan_common *chan = &ioat->base; | ||
| 529 | 565 | ||
| 530 | ioat1_cleanup(ioat); | 566 | ioat1_cleanup(ioat); |
| 567 | if (!test_bit(IOAT_RUN, &chan->state)) | ||
| 568 | return; | ||
| 531 | writew(IOAT_CHANCTRL_RUN, ioat->base.reg_base + IOAT_CHANCTRL_OFFSET); | 569 | writew(IOAT_CHANCTRL_RUN, ioat->base.reg_base + IOAT_CHANCTRL_OFFSET); |
| 532 | } | 570 | } |
| 533 | 571 | ||
diff --git a/drivers/dma/ioat/dma.h b/drivers/dma/ioat/dma.h index 11fb877ddca9..e982f00a9843 100644 --- a/drivers/dma/ioat/dma.h +++ b/drivers/dma/ioat/dma.h | |||
| @@ -356,6 +356,7 @@ bool ioat_cleanup_preamble(struct ioat_chan_common *chan, | |||
| 356 | void ioat_kobject_add(struct ioatdma_device *device, struct kobj_type *type); | 356 | void ioat_kobject_add(struct ioatdma_device *device, struct kobj_type *type); |
| 357 | void ioat_kobject_del(struct ioatdma_device *device); | 357 | void ioat_kobject_del(struct ioatdma_device *device); |
| 358 | int ioat_dma_setup_interrupts(struct ioatdma_device *device); | 358 | int ioat_dma_setup_interrupts(struct ioatdma_device *device); |
| 359 | void ioat_stop(struct ioat_chan_common *chan); | ||
| 359 | extern const struct sysfs_ops ioat_sysfs_ops; | 360 | extern const struct sysfs_ops ioat_sysfs_ops; |
| 360 | extern struct ioat_sysfs_entry ioat_version_attr; | 361 | extern struct ioat_sysfs_entry ioat_version_attr; |
| 361 | extern struct ioat_sysfs_entry ioat_cap_attr; | 362 | extern struct ioat_sysfs_entry ioat_cap_attr; |
diff --git a/drivers/dma/ioat/dma_v2.c b/drivers/dma/ioat/dma_v2.c index 5d3affe7e976..8d1058085eeb 100644 --- a/drivers/dma/ioat/dma_v2.c +++ b/drivers/dma/ioat/dma_v2.c | |||
| @@ -190,8 +190,11 @@ static void ioat2_cleanup(struct ioat2_dma_chan *ioat) | |||
| 190 | void ioat2_cleanup_event(unsigned long data) | 190 | void ioat2_cleanup_event(unsigned long data) |
| 191 | { | 191 | { |
| 192 | struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data); | 192 | struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data); |
| 193 | struct ioat_chan_common *chan = &ioat->base; | ||
| 193 | 194 | ||
| 194 | ioat2_cleanup(ioat); | 195 | ioat2_cleanup(ioat); |
| 196 | if (!test_bit(IOAT_RUN, &chan->state)) | ||
| 197 | return; | ||
| 195 | writew(IOAT_CHANCTRL_RUN, ioat->base.reg_base + IOAT_CHANCTRL_OFFSET); | 198 | writew(IOAT_CHANCTRL_RUN, ioat->base.reg_base + IOAT_CHANCTRL_OFFSET); |
| 196 | } | 199 | } |
| 197 | 200 | ||
| @@ -553,10 +556,10 @@ int ioat2_alloc_chan_resources(struct dma_chan *c) | |||
| 553 | ioat->issued = 0; | 556 | ioat->issued = 0; |
| 554 | ioat->tail = 0; | 557 | ioat->tail = 0; |
| 555 | ioat->alloc_order = order; | 558 | ioat->alloc_order = order; |
| 559 | set_bit(IOAT_RUN, &chan->state); | ||
| 556 | spin_unlock_bh(&ioat->prep_lock); | 560 | spin_unlock_bh(&ioat->prep_lock); |
| 557 | spin_unlock_bh(&chan->cleanup_lock); | 561 | spin_unlock_bh(&chan->cleanup_lock); |
| 558 | 562 | ||
| 559 | tasklet_enable(&chan->cleanup_task); | ||
| 560 | ioat2_start_null_desc(ioat); | 563 | ioat2_start_null_desc(ioat); |
| 561 | 564 | ||
| 562 | /* check that we got off the ground */ | 565 | /* check that we got off the ground */ |
| @@ -566,7 +569,6 @@ int ioat2_alloc_chan_resources(struct dma_chan *c) | |||
| 566 | } while (i++ < 20 && !is_ioat_active(status) && !is_ioat_idle(status)); | 569 | } while (i++ < 20 && !is_ioat_active(status) && !is_ioat_idle(status)); |
| 567 | 570 | ||
| 568 | if (is_ioat_active(status) || is_ioat_idle(status)) { | 571 | if (is_ioat_active(status) || is_ioat_idle(status)) { |
| 569 | set_bit(IOAT_RUN, &chan->state); | ||
| 570 | return 1 << ioat->alloc_order; | 572 | return 1 << ioat->alloc_order; |
| 571 | } else { | 573 | } else { |
| 572 | u32 chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); | 574 | u32 chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); |
| @@ -809,11 +811,8 @@ void ioat2_free_chan_resources(struct dma_chan *c) | |||
| 809 | if (!ioat->ring) | 811 | if (!ioat->ring) |
| 810 | return; | 812 | return; |
| 811 | 813 | ||
| 812 | tasklet_disable(&chan->cleanup_task); | 814 | ioat_stop(chan); |
| 813 | del_timer_sync(&chan->timer); | ||
| 814 | device->cleanup_fn((unsigned long) c); | ||
| 815 | device->reset_hw(chan); | 815 | device->reset_hw(chan); |
| 816 | clear_bit(IOAT_RUN, &chan->state); | ||
| 817 | 816 | ||
| 818 | spin_lock_bh(&chan->cleanup_lock); | 817 | spin_lock_bh(&chan->cleanup_lock); |
| 819 | spin_lock_bh(&ioat->prep_lock); | 818 | spin_lock_bh(&ioat->prep_lock); |
diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index 820817e97e62..b9b38a1cf92f 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c | |||
| @@ -464,8 +464,11 @@ static void ioat3_cleanup(struct ioat2_dma_chan *ioat) | |||
| 464 | static void ioat3_cleanup_event(unsigned long data) | 464 | static void ioat3_cleanup_event(unsigned long data) |
| 465 | { | 465 | { |
| 466 | struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data); | 466 | struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data); |
| 467 | struct ioat_chan_common *chan = &ioat->base; | ||
| 467 | 468 | ||
| 468 | ioat3_cleanup(ioat); | 469 | ioat3_cleanup(ioat); |
| 470 | if (!test_bit(IOAT_RUN, &chan->state)) | ||
| 471 | return; | ||
| 469 | writew(IOAT_CHANCTRL_RUN, ioat->base.reg_base + IOAT_CHANCTRL_OFFSET); | 472 | writew(IOAT_CHANCTRL_RUN, ioat->base.reg_base + IOAT_CHANCTRL_OFFSET); |
| 470 | } | 473 | } |
| 471 | 474 | ||
