diff options
author | Jens Axboe <jaxboe@fusionio.com> | 2011-01-13 08:47:54 -0500 |
---|---|---|
committer | Jens Axboe <jaxboe@fusionio.com> | 2011-01-13 08:47:54 -0500 |
commit | 81c5e2ae33c4b19e53966b427e33646bf6811830 (patch) | |
tree | a602a6dd100165c8948bfa713e6f0b422dcba5d8 /block | |
parent | 797a455d2c682476c3797dbfecf5bf84c1e3b9d3 (diff) | |
parent | fcc57045d53edc35bcce456e60ac4aa802712934 (diff) |
Merge branch 'for-2.6.38/event-handling' into for-2.6.38/core
Diffstat (limited to 'block')
-rw-r--r-- | block/genhd.c | 544 |
1 files changed, 516 insertions, 28 deletions
diff --git a/block/genhd.c b/block/genhd.c index 399d37ec7412..6a5b772aa201 100644 --- a/block/genhd.c +++ b/block/genhd.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/buffer_head.h> | 18 | #include <linux/buffer_head.h> |
19 | #include <linux/mutex.h> | 19 | #include <linux/mutex.h> |
20 | #include <linux/idr.h> | 20 | #include <linux/idr.h> |
21 | #include <linux/log2.h> | ||
21 | 22 | ||
22 | #include "blk.h" | 23 | #include "blk.h" |
23 | 24 | ||
@@ -35,6 +36,10 @@ static DEFINE_IDR(ext_devt_idr); | |||
35 | 36 | ||
36 | static struct device_type disk_type; | 37 | static struct device_type disk_type; |
37 | 38 | ||
39 | static void disk_add_events(struct gendisk *disk); | ||
40 | static void disk_del_events(struct gendisk *disk); | ||
41 | static void disk_release_events(struct gendisk *disk); | ||
42 | |||
38 | /** | 43 | /** |
39 | * disk_get_part - get partition | 44 | * disk_get_part - get partition |
40 | * @disk: disk to look partition from | 45 | * @disk: disk to look partition from |
@@ -502,6 +507,64 @@ static int exact_lock(dev_t devt, void *data) | |||
502 | return 0; | 507 | return 0; |
503 | } | 508 | } |
504 | 509 | ||
510 | void register_disk(struct gendisk *disk) | ||
511 | { | ||
512 | struct device *ddev = disk_to_dev(disk); | ||
513 | struct block_device *bdev; | ||
514 | struct disk_part_iter piter; | ||
515 | struct hd_struct *part; | ||
516 | int err; | ||
517 | |||
518 | ddev->parent = disk->driverfs_dev; | ||
519 | |||
520 | dev_set_name(ddev, disk->disk_name); | ||
521 | |||
522 | /* delay uevents, until we scanned partition table */ | ||
523 | dev_set_uevent_suppress(ddev, 1); | ||
524 | |||
525 | if (device_add(ddev)) | ||
526 | return; | ||
527 | if (!sysfs_deprecated) { | ||
528 | err = sysfs_create_link(block_depr, &ddev->kobj, | ||
529 | kobject_name(&ddev->kobj)); | ||
530 | if (err) { | ||
531 | device_del(ddev); | ||
532 | return; | ||
533 | } | ||
534 | } | ||
535 | disk->part0.holder_dir = kobject_create_and_add("holders", &ddev->kobj); | ||
536 | disk->slave_dir = kobject_create_and_add("slaves", &ddev->kobj); | ||
537 | |||
538 | /* No minors to use for partitions */ | ||
539 | if (!disk_partitionable(disk)) | ||
540 | goto exit; | ||
541 | |||
542 | /* No such device (e.g., media were just removed) */ | ||
543 | if (!get_capacity(disk)) | ||
544 | goto exit; | ||
545 | |||
546 | bdev = bdget_disk(disk, 0); | ||
547 | if (!bdev) | ||
548 | goto exit; | ||
549 | |||
550 | bdev->bd_invalidated = 1; | ||
551 | err = blkdev_get(bdev, FMODE_READ, NULL); | ||
552 | if (err < 0) | ||
553 | goto exit; | ||
554 | blkdev_put(bdev, FMODE_READ); | ||
555 | |||
556 | exit: | ||
557 | /* announce disk after possible partitions are created */ | ||
558 | dev_set_uevent_suppress(ddev, 0); | ||
559 | kobject_uevent(&ddev->kobj, KOBJ_ADD); | ||
560 | |||
561 | /* announce possible partitions */ | ||
562 | disk_part_iter_init(&piter, disk, 0); | ||
563 | while ((part = disk_part_iter_next(&piter))) | ||
564 | kobject_uevent(&part_to_dev(part)->kobj, KOBJ_ADD); | ||
565 | disk_part_iter_exit(&piter); | ||
566 | } | ||
567 | |||
505 | /** | 568 | /** |
506 | * add_disk - add partitioning information to kernel list | 569 | * add_disk - add partitioning information to kernel list |
507 | * @disk: per-device partitioning information | 570 | * @disk: per-device partitioning information |
@@ -551,18 +614,48 @@ void add_disk(struct gendisk *disk) | |||
551 | retval = sysfs_create_link(&disk_to_dev(disk)->kobj, &bdi->dev->kobj, | 614 | retval = sysfs_create_link(&disk_to_dev(disk)->kobj, &bdi->dev->kobj, |
552 | "bdi"); | 615 | "bdi"); |
553 | WARN_ON(retval); | 616 | WARN_ON(retval); |
554 | } | ||
555 | 617 | ||
618 | disk_add_events(disk); | ||
619 | } | ||
556 | EXPORT_SYMBOL(add_disk); | 620 | EXPORT_SYMBOL(add_disk); |
557 | EXPORT_SYMBOL(del_gendisk); /* in partitions/check.c */ | ||
558 | 621 | ||
559 | void unlink_gendisk(struct gendisk *disk) | 622 | void del_gendisk(struct gendisk *disk) |
560 | { | 623 | { |
624 | struct disk_part_iter piter; | ||
625 | struct hd_struct *part; | ||
626 | |||
627 | disk_del_events(disk); | ||
628 | |||
629 | /* invalidate stuff */ | ||
630 | disk_part_iter_init(&piter, disk, | ||
631 | DISK_PITER_INCL_EMPTY | DISK_PITER_REVERSE); | ||
632 | while ((part = disk_part_iter_next(&piter))) { | ||
633 | invalidate_partition(disk, part->partno); | ||
634 | delete_partition(disk, part->partno); | ||
635 | } | ||
636 | disk_part_iter_exit(&piter); | ||
637 | |||
638 | invalidate_partition(disk, 0); | ||
639 | blk_free_devt(disk_to_dev(disk)->devt); | ||
640 | set_capacity(disk, 0); | ||
641 | disk->flags &= ~GENHD_FL_UP; | ||
642 | |||
561 | sysfs_remove_link(&disk_to_dev(disk)->kobj, "bdi"); | 643 | sysfs_remove_link(&disk_to_dev(disk)->kobj, "bdi"); |
562 | bdi_unregister(&disk->queue->backing_dev_info); | 644 | bdi_unregister(&disk->queue->backing_dev_info); |
563 | blk_unregister_queue(disk); | 645 | blk_unregister_queue(disk); |
564 | blk_unregister_region(disk_devt(disk), disk->minors); | 646 | blk_unregister_region(disk_devt(disk), disk->minors); |
647 | |||
648 | part_stat_set_all(&disk->part0, 0); | ||
649 | disk->part0.stamp = 0; | ||
650 | |||
651 | kobject_put(disk->part0.holder_dir); | ||
652 | kobject_put(disk->slave_dir); | ||
653 | disk->driverfs_dev = NULL; | ||
654 | if (!sysfs_deprecated) | ||
655 | sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk))); | ||
656 | device_del(disk_to_dev(disk)); | ||
565 | } | 657 | } |
658 | EXPORT_SYMBOL(del_gendisk); | ||
566 | 659 | ||
567 | /** | 660 | /** |
568 | * get_gendisk - get partitioning information for a given device | 661 | * get_gendisk - get partitioning information for a given device |
@@ -1005,6 +1098,7 @@ static void disk_release(struct device *dev) | |||
1005 | { | 1098 | { |
1006 | struct gendisk *disk = dev_to_disk(dev); | 1099 | struct gendisk *disk = dev_to_disk(dev); |
1007 | 1100 | ||
1101 | disk_release_events(disk); | ||
1008 | kfree(disk->random); | 1102 | kfree(disk->random); |
1009 | disk_replace_part_tbl(disk, NULL); | 1103 | disk_replace_part_tbl(disk, NULL); |
1010 | free_part_stats(&disk->part0); | 1104 | free_part_stats(&disk->part0); |
@@ -1110,29 +1204,6 @@ static int __init proc_genhd_init(void) | |||
1110 | module_init(proc_genhd_init); | 1204 | module_init(proc_genhd_init); |
1111 | #endif /* CONFIG_PROC_FS */ | 1205 | #endif /* CONFIG_PROC_FS */ |
1112 | 1206 | ||
1113 | static void media_change_notify_thread(struct work_struct *work) | ||
1114 | { | ||
1115 | struct gendisk *gd = container_of(work, struct gendisk, async_notify); | ||
1116 | char event[] = "MEDIA_CHANGE=1"; | ||
1117 | char *envp[] = { event, NULL }; | ||
1118 | |||
1119 | /* | ||
1120 | * set enviroment vars to indicate which event this is for | ||
1121 | * so that user space will know to go check the media status. | ||
1122 | */ | ||
1123 | kobject_uevent_env(&disk_to_dev(gd)->kobj, KOBJ_CHANGE, envp); | ||
1124 | put_device(gd->driverfs_dev); | ||
1125 | } | ||
1126 | |||
1127 | #if 0 | ||
1128 | void genhd_media_change_notify(struct gendisk *disk) | ||
1129 | { | ||
1130 | get_device(disk->driverfs_dev); | ||
1131 | schedule_work(&disk->async_notify); | ||
1132 | } | ||
1133 | EXPORT_SYMBOL_GPL(genhd_media_change_notify); | ||
1134 | #endif /* 0 */ | ||
1135 | |||
1136 | dev_t blk_lookup_devt(const char *name, int partno) | 1207 | dev_t blk_lookup_devt(const char *name, int partno) |
1137 | { | 1208 | { |
1138 | dev_t devt = MKDEV(0, 0); | 1209 | dev_t devt = MKDEV(0, 0); |
@@ -1200,8 +1271,6 @@ struct gendisk *alloc_disk_node(int minors, int node_id) | |||
1200 | disk_to_dev(disk)->class = &block_class; | 1271 | disk_to_dev(disk)->class = &block_class; |
1201 | disk_to_dev(disk)->type = &disk_type; | 1272 | disk_to_dev(disk)->type = &disk_type; |
1202 | device_initialize(disk_to_dev(disk)); | 1273 | device_initialize(disk_to_dev(disk)); |
1203 | INIT_WORK(&disk->async_notify, | ||
1204 | media_change_notify_thread); | ||
1205 | } | 1274 | } |
1206 | return disk; | 1275 | return disk; |
1207 | } | 1276 | } |
@@ -1293,3 +1362,422 @@ int invalidate_partition(struct gendisk *disk, int partno) | |||
1293 | } | 1362 | } |
1294 | 1363 | ||
1295 | EXPORT_SYMBOL(invalidate_partition); | 1364 | EXPORT_SYMBOL(invalidate_partition); |
1365 | |||
1366 | /* | ||
1367 | * Disk events - monitor disk events like media change and eject request. | ||
1368 | */ | ||
1369 | struct disk_events { | ||
1370 | struct list_head node; /* all disk_event's */ | ||
1371 | struct gendisk *disk; /* the associated disk */ | ||
1372 | spinlock_t lock; | ||
1373 | |||
1374 | int block; /* event blocking depth */ | ||
1375 | unsigned int pending; /* events already sent out */ | ||
1376 | unsigned int clearing; /* events being cleared */ | ||
1377 | |||
1378 | long poll_msecs; /* interval, -1 for default */ | ||
1379 | struct delayed_work dwork; | ||
1380 | }; | ||
1381 | |||
1382 | static const char *disk_events_strs[] = { | ||
1383 | [ilog2(DISK_EVENT_MEDIA_CHANGE)] = "media_change", | ||
1384 | [ilog2(DISK_EVENT_EJECT_REQUEST)] = "eject_request", | ||
1385 | }; | ||
1386 | |||
1387 | static char *disk_uevents[] = { | ||
1388 | [ilog2(DISK_EVENT_MEDIA_CHANGE)] = "DISK_MEDIA_CHANGE=1", | ||
1389 | [ilog2(DISK_EVENT_EJECT_REQUEST)] = "DISK_EJECT_REQUEST=1", | ||
1390 | }; | ||
1391 | |||
1392 | /* list of all disk_events */ | ||
1393 | static DEFINE_MUTEX(disk_events_mutex); | ||
1394 | static LIST_HEAD(disk_events); | ||
1395 | |||
1396 | /* disable in-kernel polling by default */ | ||
1397 | static unsigned long disk_events_dfl_poll_msecs = 0; | ||
1398 | |||
1399 | static unsigned long disk_events_poll_jiffies(struct gendisk *disk) | ||
1400 | { | ||
1401 | struct disk_events *ev = disk->ev; | ||
1402 | long intv_msecs = 0; | ||
1403 | |||
1404 | /* | ||
1405 | * If device-specific poll interval is set, always use it. If | ||
1406 | * the default is being used, poll iff there are events which | ||
1407 | * can't be monitored asynchronously. | ||
1408 | */ | ||
1409 | if (ev->poll_msecs >= 0) | ||
1410 | intv_msecs = ev->poll_msecs; | ||
1411 | else if (disk->events & ~disk->async_events) | ||
1412 | intv_msecs = disk_events_dfl_poll_msecs; | ||
1413 | |||
1414 | return msecs_to_jiffies(intv_msecs); | ||
1415 | } | ||
1416 | |||
1417 | static void __disk_block_events(struct gendisk *disk, bool sync) | ||
1418 | { | ||
1419 | struct disk_events *ev = disk->ev; | ||
1420 | unsigned long flags; | ||
1421 | bool cancel; | ||
1422 | |||
1423 | spin_lock_irqsave(&ev->lock, flags); | ||
1424 | cancel = !ev->block++; | ||
1425 | spin_unlock_irqrestore(&ev->lock, flags); | ||
1426 | |||
1427 | if (cancel) { | ||
1428 | if (sync) | ||
1429 | cancel_delayed_work_sync(&disk->ev->dwork); | ||
1430 | else | ||
1431 | cancel_delayed_work(&disk->ev->dwork); | ||
1432 | } | ||
1433 | } | ||
1434 | |||
1435 | static void __disk_unblock_events(struct gendisk *disk, bool check_now) | ||
1436 | { | ||
1437 | struct disk_events *ev = disk->ev; | ||
1438 | unsigned long intv; | ||
1439 | unsigned long flags; | ||
1440 | |||
1441 | spin_lock_irqsave(&ev->lock, flags); | ||
1442 | |||
1443 | if (WARN_ON_ONCE(ev->block <= 0)) | ||
1444 | goto out_unlock; | ||
1445 | |||
1446 | if (--ev->block) | ||
1447 | goto out_unlock; | ||
1448 | |||
1449 | /* | ||
1450 | * Not exactly a latency critical operation, set poll timer | ||
1451 | * slack to 25% and kick event check. | ||
1452 | */ | ||
1453 | intv = disk_events_poll_jiffies(disk); | ||
1454 | set_timer_slack(&ev->dwork.timer, intv / 4); | ||
1455 | if (check_now) | ||
1456 | queue_delayed_work(system_nrt_wq, &ev->dwork, 0); | ||
1457 | else if (intv) | ||
1458 | queue_delayed_work(system_nrt_wq, &ev->dwork, intv); | ||
1459 | out_unlock: | ||
1460 | spin_unlock_irqrestore(&ev->lock, flags); | ||
1461 | } | ||
1462 | |||
1463 | /** | ||
1464 | * disk_block_events - block and flush disk event checking | ||
1465 | * @disk: disk to block events for | ||
1466 | * | ||
1467 | * On return from this function, it is guaranteed that event checking | ||
1468 | * isn't in progress and won't happen until unblocked by | ||
1469 | * disk_unblock_events(). Events blocking is counted and the actual | ||
1470 | * unblocking happens after the matching number of unblocks are done. | ||
1471 | * | ||
1472 | * Note that this intentionally does not block event checking from | ||
1473 | * disk_clear_events(). | ||
1474 | * | ||
1475 | * CONTEXT: | ||
1476 | * Might sleep. | ||
1477 | */ | ||
1478 | void disk_block_events(struct gendisk *disk) | ||
1479 | { | ||
1480 | if (disk->ev) | ||
1481 | __disk_block_events(disk, true); | ||
1482 | } | ||
1483 | |||
1484 | /** | ||
1485 | * disk_unblock_events - unblock disk event checking | ||
1486 | * @disk: disk to unblock events for | ||
1487 | * | ||
1488 | * Undo disk_block_events(). When the block count reaches zero, it | ||
1489 | * starts events polling if configured. | ||
1490 | * | ||
1491 | * CONTEXT: | ||
1492 | * Don't care. Safe to call from irq context. | ||
1493 | */ | ||
1494 | void disk_unblock_events(struct gendisk *disk) | ||
1495 | { | ||
1496 | if (disk->ev) | ||
1497 | __disk_unblock_events(disk, true); | ||
1498 | } | ||
1499 | |||
1500 | /** | ||
1501 | * disk_check_events - schedule immediate event checking | ||
1502 | * @disk: disk to check events for | ||
1503 | * | ||
1504 | * Schedule immediate event checking on @disk if not blocked. | ||
1505 | * | ||
1506 | * CONTEXT: | ||
1507 | * Don't care. Safe to call from irq context. | ||
1508 | */ | ||
1509 | void disk_check_events(struct gendisk *disk) | ||
1510 | { | ||
1511 | if (disk->ev) { | ||
1512 | __disk_block_events(disk, false); | ||
1513 | __disk_unblock_events(disk, true); | ||
1514 | } | ||
1515 | } | ||
1516 | EXPORT_SYMBOL_GPL(disk_check_events); | ||
1517 | |||
1518 | /** | ||
1519 | * disk_clear_events - synchronously check, clear and return pending events | ||
1520 | * @disk: disk to fetch and clear events from | ||
1521 | * @mask: mask of events to be fetched and clearted | ||
1522 | * | ||
1523 | * Disk events are synchronously checked and pending events in @mask | ||
1524 | * are cleared and returned. This ignores the block count. | ||
1525 | * | ||
1526 | * CONTEXT: | ||
1527 | * Might sleep. | ||
1528 | */ | ||
1529 | unsigned int disk_clear_events(struct gendisk *disk, unsigned int mask) | ||
1530 | { | ||
1531 | const struct block_device_operations *bdops = disk->fops; | ||
1532 | struct disk_events *ev = disk->ev; | ||
1533 | unsigned int pending; | ||
1534 | |||
1535 | if (!ev) { | ||
1536 | /* for drivers still using the old ->media_changed method */ | ||
1537 | if ((mask & DISK_EVENT_MEDIA_CHANGE) && | ||
1538 | bdops->media_changed && bdops->media_changed(disk)) | ||
1539 | return DISK_EVENT_MEDIA_CHANGE; | ||
1540 | return 0; | ||
1541 | } | ||
1542 | |||
1543 | /* tell the workfn about the events being cleared */ | ||
1544 | spin_lock_irq(&ev->lock); | ||
1545 | ev->clearing |= mask; | ||
1546 | spin_unlock_irq(&ev->lock); | ||
1547 | |||
1548 | /* uncondtionally schedule event check and wait for it to finish */ | ||
1549 | __disk_block_events(disk, true); | ||
1550 | queue_delayed_work(system_nrt_wq, &ev->dwork, 0); | ||
1551 | flush_delayed_work(&ev->dwork); | ||
1552 | __disk_unblock_events(disk, false); | ||
1553 | |||
1554 | /* then, fetch and clear pending events */ | ||
1555 | spin_lock_irq(&ev->lock); | ||
1556 | WARN_ON_ONCE(ev->clearing & mask); /* cleared by workfn */ | ||
1557 | pending = ev->pending & mask; | ||
1558 | ev->pending &= ~mask; | ||
1559 | spin_unlock_irq(&ev->lock); | ||
1560 | |||
1561 | return pending; | ||
1562 | } | ||
1563 | |||
1564 | static void disk_events_workfn(struct work_struct *work) | ||
1565 | { | ||
1566 | struct delayed_work *dwork = to_delayed_work(work); | ||
1567 | struct disk_events *ev = container_of(dwork, struct disk_events, dwork); | ||
1568 | struct gendisk *disk = ev->disk; | ||
1569 | char *envp[ARRAY_SIZE(disk_uevents) + 1] = { }; | ||
1570 | unsigned int clearing = ev->clearing; | ||
1571 | unsigned int events; | ||
1572 | unsigned long intv; | ||
1573 | int nr_events = 0, i; | ||
1574 | |||
1575 | /* check events */ | ||
1576 | events = disk->fops->check_events(disk, clearing); | ||
1577 | |||
1578 | /* accumulate pending events and schedule next poll if necessary */ | ||
1579 | spin_lock_irq(&ev->lock); | ||
1580 | |||
1581 | events &= ~ev->pending; | ||
1582 | ev->pending |= events; | ||
1583 | ev->clearing &= ~clearing; | ||
1584 | |||
1585 | intv = disk_events_poll_jiffies(disk); | ||
1586 | if (!ev->block && intv) | ||
1587 | queue_delayed_work(system_nrt_wq, &ev->dwork, intv); | ||
1588 | |||
1589 | spin_unlock_irq(&ev->lock); | ||
1590 | |||
1591 | /* tell userland about new events */ | ||
1592 | for (i = 0; i < ARRAY_SIZE(disk_uevents); i++) | ||
1593 | if (events & (1 << i)) | ||
1594 | envp[nr_events++] = disk_uevents[i]; | ||
1595 | |||
1596 | if (nr_events) | ||
1597 | kobject_uevent_env(&disk_to_dev(disk)->kobj, KOBJ_CHANGE, envp); | ||
1598 | } | ||
1599 | |||
1600 | /* | ||
1601 | * A disk events enabled device has the following sysfs nodes under | ||
1602 | * its /sys/block/X/ directory. | ||
1603 | * | ||
1604 | * events : list of all supported events | ||
1605 | * events_async : list of events which can be detected w/o polling | ||
1606 | * events_poll_msecs : polling interval, 0: disable, -1: system default | ||
1607 | */ | ||
1608 | static ssize_t __disk_events_show(unsigned int events, char *buf) | ||
1609 | { | ||
1610 | const char *delim = ""; | ||
1611 | ssize_t pos = 0; | ||
1612 | int i; | ||
1613 | |||
1614 | for (i = 0; i < ARRAY_SIZE(disk_events_strs); i++) | ||
1615 | if (events & (1 << i)) { | ||
1616 | pos += sprintf(buf + pos, "%s%s", | ||
1617 | delim, disk_events_strs[i]); | ||
1618 | delim = " "; | ||
1619 | } | ||
1620 | if (pos) | ||
1621 | pos += sprintf(buf + pos, "\n"); | ||
1622 | return pos; | ||
1623 | } | ||
1624 | |||
1625 | static ssize_t disk_events_show(struct device *dev, | ||
1626 | struct device_attribute *attr, char *buf) | ||
1627 | { | ||
1628 | struct gendisk *disk = dev_to_disk(dev); | ||
1629 | |||
1630 | return __disk_events_show(disk->events, buf); | ||
1631 | } | ||
1632 | |||
1633 | static ssize_t disk_events_async_show(struct device *dev, | ||
1634 | struct device_attribute *attr, char *buf) | ||
1635 | { | ||
1636 | struct gendisk *disk = dev_to_disk(dev); | ||
1637 | |||
1638 | return __disk_events_show(disk->async_events, buf); | ||
1639 | } | ||
1640 | |||
1641 | static ssize_t disk_events_poll_msecs_show(struct device *dev, | ||
1642 | struct device_attribute *attr, | ||
1643 | char *buf) | ||
1644 | { | ||
1645 | struct gendisk *disk = dev_to_disk(dev); | ||
1646 | |||
1647 | return sprintf(buf, "%ld\n", disk->ev->poll_msecs); | ||
1648 | } | ||
1649 | |||
1650 | static ssize_t disk_events_poll_msecs_store(struct device *dev, | ||
1651 | struct device_attribute *attr, | ||
1652 | const char *buf, size_t count) | ||
1653 | { | ||
1654 | struct gendisk *disk = dev_to_disk(dev); | ||
1655 | long intv; | ||
1656 | |||
1657 | if (!count || !sscanf(buf, "%ld", &intv)) | ||
1658 | return -EINVAL; | ||
1659 | |||
1660 | if (intv < 0 && intv != -1) | ||
1661 | return -EINVAL; | ||
1662 | |||
1663 | __disk_block_events(disk, true); | ||
1664 | disk->ev->poll_msecs = intv; | ||
1665 | __disk_unblock_events(disk, true); | ||
1666 | |||
1667 | return count; | ||
1668 | } | ||
1669 | |||
1670 | static const DEVICE_ATTR(events, S_IRUGO, disk_events_show, NULL); | ||
1671 | static const DEVICE_ATTR(events_async, S_IRUGO, disk_events_async_show, NULL); | ||
1672 | static const DEVICE_ATTR(events_poll_msecs, S_IRUGO|S_IWUSR, | ||
1673 | disk_events_poll_msecs_show, | ||
1674 | disk_events_poll_msecs_store); | ||
1675 | |||
1676 | static const struct attribute *disk_events_attrs[] = { | ||
1677 | &dev_attr_events.attr, | ||
1678 | &dev_attr_events_async.attr, | ||
1679 | &dev_attr_events_poll_msecs.attr, | ||
1680 | NULL, | ||
1681 | }; | ||
1682 | |||
1683 | /* | ||
1684 | * The default polling interval can be specified by the kernel | ||
1685 | * parameter block.events_dfl_poll_msecs which defaults to 0 | ||
1686 | * (disable). This can also be modified runtime by writing to | ||
1687 | * /sys/module/block/events_dfl_poll_msecs. | ||
1688 | */ | ||
1689 | static int disk_events_set_dfl_poll_msecs(const char *val, | ||
1690 | const struct kernel_param *kp) | ||
1691 | { | ||
1692 | struct disk_events *ev; | ||
1693 | int ret; | ||
1694 | |||
1695 | ret = param_set_ulong(val, kp); | ||
1696 | if (ret < 0) | ||
1697 | return ret; | ||
1698 | |||
1699 | mutex_lock(&disk_events_mutex); | ||
1700 | |||
1701 | list_for_each_entry(ev, &disk_events, node) | ||
1702 | disk_check_events(ev->disk); | ||
1703 | |||
1704 | mutex_unlock(&disk_events_mutex); | ||
1705 | |||
1706 | return 0; | ||
1707 | } | ||
1708 | |||
1709 | static const struct kernel_param_ops disk_events_dfl_poll_msecs_param_ops = { | ||
1710 | .set = disk_events_set_dfl_poll_msecs, | ||
1711 | .get = param_get_ulong, | ||
1712 | }; | ||
1713 | |||
1714 | #undef MODULE_PARAM_PREFIX | ||
1715 | #define MODULE_PARAM_PREFIX "block." | ||
1716 | |||
1717 | module_param_cb(events_dfl_poll_msecs, &disk_events_dfl_poll_msecs_param_ops, | ||
1718 | &disk_events_dfl_poll_msecs, 0644); | ||
1719 | |||
1720 | /* | ||
1721 | * disk_{add|del|release}_events - initialize and destroy disk_events. | ||
1722 | */ | ||
1723 | static void disk_add_events(struct gendisk *disk) | ||
1724 | { | ||
1725 | struct disk_events *ev; | ||
1726 | |||
1727 | if (!disk->fops->check_events || !(disk->events | disk->async_events)) | ||
1728 | return; | ||
1729 | |||
1730 | ev = kzalloc(sizeof(*ev), GFP_KERNEL); | ||
1731 | if (!ev) { | ||
1732 | pr_warn("%s: failed to initialize events\n", disk->disk_name); | ||
1733 | return; | ||
1734 | } | ||
1735 | |||
1736 | if (sysfs_create_files(&disk_to_dev(disk)->kobj, | ||
1737 | disk_events_attrs) < 0) { | ||
1738 | pr_warn("%s: failed to create sysfs files for events\n", | ||
1739 | disk->disk_name); | ||
1740 | kfree(ev); | ||
1741 | return; | ||
1742 | } | ||
1743 | |||
1744 | disk->ev = ev; | ||
1745 | |||
1746 | INIT_LIST_HEAD(&ev->node); | ||
1747 | ev->disk = disk; | ||
1748 | spin_lock_init(&ev->lock); | ||
1749 | ev->block = 1; | ||
1750 | ev->poll_msecs = -1; | ||
1751 | INIT_DELAYED_WORK(&ev->dwork, disk_events_workfn); | ||
1752 | |||
1753 | mutex_lock(&disk_events_mutex); | ||
1754 | list_add_tail(&ev->node, &disk_events); | ||
1755 | mutex_unlock(&disk_events_mutex); | ||
1756 | |||
1757 | /* | ||
1758 | * Block count is initialized to 1 and the following initial | ||
1759 | * unblock kicks it into action. | ||
1760 | */ | ||
1761 | __disk_unblock_events(disk, true); | ||
1762 | } | ||
1763 | |||
1764 | static void disk_del_events(struct gendisk *disk) | ||
1765 | { | ||
1766 | if (!disk->ev) | ||
1767 | return; | ||
1768 | |||
1769 | __disk_block_events(disk, true); | ||
1770 | |||
1771 | mutex_lock(&disk_events_mutex); | ||
1772 | list_del_init(&disk->ev->node); | ||
1773 | mutex_unlock(&disk_events_mutex); | ||
1774 | |||
1775 | sysfs_remove_files(&disk_to_dev(disk)->kobj, disk_events_attrs); | ||
1776 | } | ||
1777 | |||
1778 | static void disk_release_events(struct gendisk *disk) | ||
1779 | { | ||
1780 | /* the block count should be 1 from disk_del_events() */ | ||
1781 | WARN_ON_ONCE(disk->ev && disk->ev->block != 1); | ||
1782 | kfree(disk->ev); | ||
1783 | } | ||