diff options
author | Tejun Heo <tj@kernel.org> | 2011-07-01 10:17:47 -0400 |
---|---|---|
committer | Jens Axboe <jaxboe@fusionio.com> | 2011-07-01 10:17:47 -0400 |
commit | 85ef06d1d252f6a2e73b678591ab71caad4667bb (patch) | |
tree | dc5cbc4f2e54f5b5b3f8653a595799e5fe0f2f18 | |
parent | 04bf7869ca0fd12009aee301cac2264a36df4d98 (diff) |
block: flush MEDIA_CHANGE from drivers on close(2)
Currently, only open(2) is defined as the 'clearing' point. It has
two roles - first, it's an acknowledgement from userland indicating
that the event has been received and kernel can clear pending states
and proceed to generate more events. Secondly, it's passed on to
device drivers as a hint indicating that a synchronization point has
been reached and it might want to take a deeper look at the device.
The latter currently is only used by sr which uses two different
mechanisms - GET_EVENT_MEDIA_STATUS_NOTIFICATION and TEST_UNIT_READY
to discover events, where the former is lighter weight and safe to be
used repeatedly but may not provide full coverage. Among other
things, GET_EVENT can't detect media removal while TUR can.
This patch makes close(2) - blkdev_put() - indicate clearing hint for
MEDIA_CHANGE to drivers. disk_check_events() is renamed to
disk_flush_events() and updated to take @mask for events to flush
which is or'd to ev->clearing and will be passed to the driver on the
next ->check_events() invocation.
This change makes sr generate MEDIA_CHANGE when media is ejected from
userland - e.g. with eject(1).
Note: Given the current usage, it seems @clearing hint is needlessly
complex. disk_clear_events() can simply clear all events and the hint
can be boolean @flush.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
-rw-r--r-- | block/genhd.c | 22 | ||||
-rw-r--r-- | fs/block_dev.c | 23 | ||||
-rw-r--r-- | include/linux/genhd.h | 2 |
3 files changed, 27 insertions, 20 deletions
diff --git a/block/genhd.c b/block/genhd.c index 82d97c3594a8..fbd7605a5865 100644 --- a/block/genhd.c +++ b/block/genhd.c | |||
@@ -1500,30 +1500,32 @@ void disk_unblock_events(struct gendisk *disk) | |||
1500 | } | 1500 | } |
1501 | 1501 | ||
1502 | /** | 1502 | /** |
1503 | * disk_check_events - schedule immediate event checking | 1503 | * disk_flush_events - schedule immediate event checking and flushing |
1504 | * @disk: disk to check events for | 1504 | * @disk: disk to check and flush events for |
1505 | * @mask: events to flush | ||
1505 | * | 1506 | * |
1506 | * Schedule immediate event checking on @disk if not blocked. | 1507 | * Schedule immediate event checking on @disk if not blocked. Events in |
1508 | * @mask are scheduled to be cleared from the driver. Note that this | ||
1509 | * doesn't clear the events from @disk->ev. | ||
1507 | * | 1510 | * |
1508 | * CONTEXT: | 1511 | * CONTEXT: |
1509 | * Don't care. Safe to call from irq context. | 1512 | * If @mask is non-zero must be called with bdev->bd_mutex held. |
1510 | */ | 1513 | */ |
1511 | void disk_check_events(struct gendisk *disk) | 1514 | void disk_flush_events(struct gendisk *disk, unsigned int mask) |
1512 | { | 1515 | { |
1513 | struct disk_events *ev = disk->ev; | 1516 | struct disk_events *ev = disk->ev; |
1514 | unsigned long flags; | ||
1515 | 1517 | ||
1516 | if (!ev) | 1518 | if (!ev) |
1517 | return; | 1519 | return; |
1518 | 1520 | ||
1519 | spin_lock_irqsave(&ev->lock, flags); | 1521 | spin_lock_irq(&ev->lock); |
1522 | ev->clearing |= mask; | ||
1520 | if (!ev->block) { | 1523 | if (!ev->block) { |
1521 | cancel_delayed_work(&ev->dwork); | 1524 | cancel_delayed_work(&ev->dwork); |
1522 | queue_delayed_work(system_nrt_wq, &ev->dwork, 0); | 1525 | queue_delayed_work(system_nrt_wq, &ev->dwork, 0); |
1523 | } | 1526 | } |
1524 | spin_unlock_irqrestore(&ev->lock, flags); | 1527 | spin_unlock_irq(&ev->lock); |
1525 | } | 1528 | } |
1526 | EXPORT_SYMBOL_GPL(disk_check_events); | ||
1527 | 1529 | ||
1528 | /** | 1530 | /** |
1529 | * disk_clear_events - synchronously check, clear and return pending events | 1531 | * disk_clear_events - synchronously check, clear and return pending events |
@@ -1713,7 +1715,7 @@ static int disk_events_set_dfl_poll_msecs(const char *val, | |||
1713 | mutex_lock(&disk_events_mutex); | 1715 | mutex_lock(&disk_events_mutex); |
1714 | 1716 | ||
1715 | list_for_each_entry(ev, &disk_events, node) | 1717 | list_for_each_entry(ev, &disk_events, node) |
1716 | disk_check_events(ev->disk); | 1718 | disk_flush_events(ev->disk, 0); |
1717 | 1719 | ||
1718 | mutex_unlock(&disk_events_mutex); | 1720 | mutex_unlock(&disk_events_mutex); |
1719 | 1721 | ||
diff --git a/fs/block_dev.c b/fs/block_dev.c index 610e8e0b04b8..6ff7f070016f 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c | |||
@@ -1447,6 +1447,8 @@ static int __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part) | |||
1447 | 1447 | ||
1448 | int blkdev_put(struct block_device *bdev, fmode_t mode) | 1448 | int blkdev_put(struct block_device *bdev, fmode_t mode) |
1449 | { | 1449 | { |
1450 | mutex_lock(&bdev->bd_mutex); | ||
1451 | |||
1450 | if (mode & FMODE_EXCL) { | 1452 | if (mode & FMODE_EXCL) { |
1451 | bool bdev_free; | 1453 | bool bdev_free; |
1452 | 1454 | ||
@@ -1455,7 +1457,6 @@ int blkdev_put(struct block_device *bdev, fmode_t mode) | |||
1455 | * are protected with bdev_lock. bd_mutex is to | 1457 | * are protected with bdev_lock. bd_mutex is to |
1456 | * synchronize disk_holder unlinking. | 1458 | * synchronize disk_holder unlinking. |
1457 | */ | 1459 | */ |
1458 | mutex_lock(&bdev->bd_mutex); | ||
1459 | spin_lock(&bdev_lock); | 1460 | spin_lock(&bdev_lock); |
1460 | 1461 | ||
1461 | WARN_ON_ONCE(--bdev->bd_holders < 0); | 1462 | WARN_ON_ONCE(--bdev->bd_holders < 0); |
@@ -1473,17 +1474,21 @@ int blkdev_put(struct block_device *bdev, fmode_t mode) | |||
1473 | * If this was the last claim, remove holder link and | 1474 | * If this was the last claim, remove holder link and |
1474 | * unblock evpoll if it was a write holder. | 1475 | * unblock evpoll if it was a write holder. |
1475 | */ | 1476 | */ |
1476 | if (bdev_free) { | 1477 | if (bdev_free && bdev->bd_write_holder) { |
1477 | if (bdev->bd_write_holder) { | 1478 | disk_unblock_events(bdev->bd_disk); |
1478 | disk_unblock_events(bdev->bd_disk); | 1479 | bdev->bd_write_holder = false; |
1479 | disk_check_events(bdev->bd_disk); | ||
1480 | bdev->bd_write_holder = false; | ||
1481 | } | ||
1482 | } | 1480 | } |
1483 | |||
1484 | mutex_unlock(&bdev->bd_mutex); | ||
1485 | } | 1481 | } |
1486 | 1482 | ||
1483 | /* | ||
1484 | * Trigger event checking and tell drivers to flush MEDIA_CHANGE | ||
1485 | * event. This is to ensure detection of media removal commanded | ||
1486 | * from userland - e.g. eject(1). | ||
1487 | */ | ||
1488 | disk_flush_events(bdev->bd_disk, DISK_EVENT_MEDIA_CHANGE); | ||
1489 | |||
1490 | mutex_unlock(&bdev->bd_mutex); | ||
1491 | |||
1487 | return __blkdev_put(bdev, mode, 0); | 1492 | return __blkdev_put(bdev, mode, 0); |
1488 | } | 1493 | } |
1489 | EXPORT_SYMBOL(blkdev_put); | 1494 | EXPORT_SYMBOL(blkdev_put); |
diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 300d7582006e..02fa4697a0e5 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h | |||
@@ -420,7 +420,7 @@ static inline int get_disk_ro(struct gendisk *disk) | |||
420 | 420 | ||
421 | extern void disk_block_events(struct gendisk *disk); | 421 | extern void disk_block_events(struct gendisk *disk); |
422 | extern void disk_unblock_events(struct gendisk *disk); | 422 | extern void disk_unblock_events(struct gendisk *disk); |
423 | extern void disk_check_events(struct gendisk *disk); | 423 | extern void disk_flush_events(struct gendisk *disk, unsigned int mask); |
424 | extern unsigned int disk_clear_events(struct gendisk *disk, unsigned int mask); | 424 | extern unsigned int disk_clear_events(struct gendisk *disk, unsigned int mask); |
425 | 425 | ||
426 | /* drivers/char/random.c */ | 426 | /* drivers/char/random.c */ |