diff options
Diffstat (limited to 'block')
-rw-r--r-- | block/genhd.c | 42 |
1 files changed, 32 insertions, 10 deletions
diff --git a/block/genhd.c b/block/genhd.c index 9a289d7c84bb..3993ebf4135f 100644 --- a/block/genhd.c +++ b/block/genhd.c | |||
@@ -35,6 +35,8 @@ static DEFINE_IDR(ext_devt_idr); | |||
35 | 35 | ||
36 | static struct device_type disk_type; | 36 | static struct device_type disk_type; |
37 | 37 | ||
38 | static void disk_check_events(struct disk_events *ev, | ||
39 | unsigned int *clearing_ptr); | ||
38 | static void disk_alloc_events(struct gendisk *disk); | 40 | static void disk_alloc_events(struct gendisk *disk); |
39 | static void disk_add_events(struct gendisk *disk); | 41 | static void disk_add_events(struct gendisk *disk); |
40 | static void disk_del_events(struct gendisk *disk); | 42 | static void disk_del_events(struct gendisk *disk); |
@@ -1549,6 +1551,7 @@ unsigned int disk_clear_events(struct gendisk *disk, unsigned int mask) | |||
1549 | const struct block_device_operations *bdops = disk->fops; | 1551 | const struct block_device_operations *bdops = disk->fops; |
1550 | struct disk_events *ev = disk->ev; | 1552 | struct disk_events *ev = disk->ev; |
1551 | unsigned int pending; | 1553 | unsigned int pending; |
1554 | unsigned int clearing = mask; | ||
1552 | 1555 | ||
1553 | if (!ev) { | 1556 | if (!ev) { |
1554 | /* for drivers still using the old ->media_changed method */ | 1557 | /* for drivers still using the old ->media_changed method */ |
@@ -1558,34 +1561,53 @@ unsigned int disk_clear_events(struct gendisk *disk, unsigned int mask) | |||
1558 | return 0; | 1561 | return 0; |
1559 | } | 1562 | } |
1560 | 1563 | ||
1561 | /* tell the workfn about the events being cleared */ | 1564 | disk_block_events(disk); |
1565 | |||
1566 | /* | ||
1567 | * store the union of mask and ev->clearing on the stack so that the | ||
1568 | * race with disk_flush_events does not cause ambiguity (ev->clearing | ||
1569 | * can still be modified even if events are blocked). | ||
1570 | */ | ||
1562 | spin_lock_irq(&ev->lock); | 1571 | spin_lock_irq(&ev->lock); |
1563 | ev->clearing |= mask; | 1572 | clearing |= ev->clearing; |
1573 | ev->clearing = 0; | ||
1564 | spin_unlock_irq(&ev->lock); | 1574 | spin_unlock_irq(&ev->lock); |
1565 | 1575 | ||
1566 | /* uncondtionally schedule event check and wait for it to finish */ | 1576 | disk_check_events(ev, &clearing); |
1567 | disk_block_events(disk); | 1577 | /* |
1568 | queue_delayed_work(system_freezable_wq, &ev->dwork, 0); | 1578 | * if ev->clearing is not 0, the disk_flush_events got called in the |
1569 | flush_delayed_work(&ev->dwork); | 1579 | * middle of this function, so we want to run the workfn without delay. |
1570 | __disk_unblock_events(disk, false); | 1580 | */ |
1581 | __disk_unblock_events(disk, ev->clearing ? true : false); | ||
1571 | 1582 | ||
1572 | /* then, fetch and clear pending events */ | 1583 | /* then, fetch and clear pending events */ |
1573 | spin_lock_irq(&ev->lock); | 1584 | spin_lock_irq(&ev->lock); |
1574 | WARN_ON_ONCE(ev->clearing & mask); /* cleared by workfn */ | ||
1575 | pending = ev->pending & mask; | 1585 | pending = ev->pending & mask; |
1576 | ev->pending &= ~mask; | 1586 | ev->pending &= ~mask; |
1577 | spin_unlock_irq(&ev->lock); | 1587 | spin_unlock_irq(&ev->lock); |
1588 | WARN_ON_ONCE(clearing & mask); | ||
1578 | 1589 | ||
1579 | return pending; | 1590 | return pending; |
1580 | } | 1591 | } |
1581 | 1592 | ||
1593 | /* | ||
1594 | * Separate this part out so that a different pointer for clearing_ptr can be | ||
1595 | * passed in for disk_clear_events. | ||
1596 | */ | ||
1582 | static void disk_events_workfn(struct work_struct *work) | 1597 | static void disk_events_workfn(struct work_struct *work) |
1583 | { | 1598 | { |
1584 | struct delayed_work *dwork = to_delayed_work(work); | 1599 | struct delayed_work *dwork = to_delayed_work(work); |
1585 | struct disk_events *ev = container_of(dwork, struct disk_events, dwork); | 1600 | struct disk_events *ev = container_of(dwork, struct disk_events, dwork); |
1601 | |||
1602 | disk_check_events(ev, &ev->clearing); | ||
1603 | } | ||
1604 | |||
1605 | static void disk_check_events(struct disk_events *ev, | ||
1606 | unsigned int *clearing_ptr) | ||
1607 | { | ||
1586 | struct gendisk *disk = ev->disk; | 1608 | struct gendisk *disk = ev->disk; |
1587 | char *envp[ARRAY_SIZE(disk_uevents) + 1] = { }; | 1609 | char *envp[ARRAY_SIZE(disk_uevents) + 1] = { }; |
1588 | unsigned int clearing = ev->clearing; | 1610 | unsigned int clearing = *clearing_ptr; |
1589 | unsigned int events; | 1611 | unsigned int events; |
1590 | unsigned long intv; | 1612 | unsigned long intv; |
1591 | int nr_events = 0, i; | 1613 | int nr_events = 0, i; |
@@ -1598,7 +1620,7 @@ static void disk_events_workfn(struct work_struct *work) | |||
1598 | 1620 | ||
1599 | events &= ~ev->pending; | 1621 | events &= ~ev->pending; |
1600 | ev->pending |= events; | 1622 | ev->pending |= events; |
1601 | ev->clearing &= ~clearing; | 1623 | *clearing_ptr &= ~clearing; |
1602 | 1624 | ||
1603 | intv = disk_events_poll_jiffies(disk); | 1625 | intv = disk_events_poll_jiffies(disk); |
1604 | if (!ev->block && intv) | 1626 | if (!ev->block && intv) |