162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Disk events - monitor disk events like media change and eject request. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/export.h> 662306a36Sopenharmony_ci#include <linux/moduleparam.h> 762306a36Sopenharmony_ci#include <linux/blkdev.h> 862306a36Sopenharmony_ci#include "blk.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_cistruct disk_events { 1162306a36Sopenharmony_ci struct list_head node; /* all disk_event's */ 1262306a36Sopenharmony_ci struct gendisk *disk; /* the associated disk */ 1362306a36Sopenharmony_ci spinlock_t lock; 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci struct mutex block_mutex; /* protects blocking */ 1662306a36Sopenharmony_ci int block; /* event blocking depth */ 1762306a36Sopenharmony_ci unsigned int pending; /* events already sent out */ 1862306a36Sopenharmony_ci unsigned int clearing; /* events being cleared */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci long poll_msecs; /* interval, -1 for default */ 2162306a36Sopenharmony_ci struct delayed_work dwork; 2262306a36Sopenharmony_ci}; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic const char *disk_events_strs[] = { 2562306a36Sopenharmony_ci [ilog2(DISK_EVENT_MEDIA_CHANGE)] = "media_change", 2662306a36Sopenharmony_ci [ilog2(DISK_EVENT_EJECT_REQUEST)] = "eject_request", 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic char *disk_uevents[] = { 3062306a36Sopenharmony_ci [ilog2(DISK_EVENT_MEDIA_CHANGE)] = "DISK_MEDIA_CHANGE=1", 3162306a36Sopenharmony_ci [ilog2(DISK_EVENT_EJECT_REQUEST)] = "DISK_EJECT_REQUEST=1", 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* list of all disk_events */ 3562306a36Sopenharmony_cistatic DEFINE_MUTEX(disk_events_mutex); 3662306a36Sopenharmony_cistatic LIST_HEAD(disk_events); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* disable in-kernel polling by default */ 3962306a36Sopenharmony_cistatic unsigned long disk_events_dfl_poll_msecs; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic unsigned long disk_events_poll_jiffies(struct gendisk *disk) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct disk_events *ev = disk->ev; 4462306a36Sopenharmony_ci long intv_msecs = 0; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* 4762306a36Sopenharmony_ci * If device-specific poll interval is set, always use it. If 4862306a36Sopenharmony_ci * the default is being used, poll if the POLL flag is set. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci if (ev->poll_msecs >= 0) 5162306a36Sopenharmony_ci intv_msecs = ev->poll_msecs; 5262306a36Sopenharmony_ci else if (disk->event_flags & DISK_EVENT_FLAG_POLL) 5362306a36Sopenharmony_ci intv_msecs = disk_events_dfl_poll_msecs; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return msecs_to_jiffies(intv_msecs); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/** 5962306a36Sopenharmony_ci * disk_block_events - block and flush disk event checking 6062306a36Sopenharmony_ci * @disk: disk to block events for 6162306a36Sopenharmony_ci * 6262306a36Sopenharmony_ci * On return from this function, it is guaranteed that event checking 6362306a36Sopenharmony_ci * isn't in progress and won't happen until unblocked by 6462306a36Sopenharmony_ci * disk_unblock_events(). Events blocking is counted and the actual 6562306a36Sopenharmony_ci * unblocking happens after the matching number of unblocks are done. 6662306a36Sopenharmony_ci * 6762306a36Sopenharmony_ci * Note that this intentionally does not block event checking from 6862306a36Sopenharmony_ci * disk_clear_events(). 6962306a36Sopenharmony_ci * 7062306a36Sopenharmony_ci * CONTEXT: 7162306a36Sopenharmony_ci * Might sleep. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_civoid disk_block_events(struct gendisk *disk) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct disk_events *ev = disk->ev; 7662306a36Sopenharmony_ci unsigned long flags; 7762306a36Sopenharmony_ci bool cancel; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (!ev) 8062306a36Sopenharmony_ci return; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* 8362306a36Sopenharmony_ci * Outer mutex ensures that the first blocker completes canceling 8462306a36Sopenharmony_ci * the event work before further blockers are allowed to finish. 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_ci mutex_lock(&ev->block_mutex); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci spin_lock_irqsave(&ev->lock, flags); 8962306a36Sopenharmony_ci cancel = !ev->block++; 9062306a36Sopenharmony_ci spin_unlock_irqrestore(&ev->lock, flags); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (cancel) 9362306a36Sopenharmony_ci cancel_delayed_work_sync(&disk->ev->dwork); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci mutex_unlock(&ev->block_mutex); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic void __disk_unblock_events(struct gendisk *disk, bool check_now) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct disk_events *ev = disk->ev; 10162306a36Sopenharmony_ci unsigned long intv; 10262306a36Sopenharmony_ci unsigned long flags; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci spin_lock_irqsave(&ev->lock, flags); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (WARN_ON_ONCE(ev->block <= 0)) 10762306a36Sopenharmony_ci goto out_unlock; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (--ev->block) 11062306a36Sopenharmony_ci goto out_unlock; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci intv = disk_events_poll_jiffies(disk); 11362306a36Sopenharmony_ci if (check_now) 11462306a36Sopenharmony_ci queue_delayed_work(system_freezable_power_efficient_wq, 11562306a36Sopenharmony_ci &ev->dwork, 0); 11662306a36Sopenharmony_ci else if (intv) 11762306a36Sopenharmony_ci queue_delayed_work(system_freezable_power_efficient_wq, 11862306a36Sopenharmony_ci &ev->dwork, intv); 11962306a36Sopenharmony_ciout_unlock: 12062306a36Sopenharmony_ci spin_unlock_irqrestore(&ev->lock, flags); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/** 12462306a36Sopenharmony_ci * disk_unblock_events - unblock disk event checking 12562306a36Sopenharmony_ci * @disk: disk to unblock events for 12662306a36Sopenharmony_ci * 12762306a36Sopenharmony_ci * Undo disk_block_events(). When the block count reaches zero, it 12862306a36Sopenharmony_ci * starts events polling if configured. 12962306a36Sopenharmony_ci * 13062306a36Sopenharmony_ci * CONTEXT: 13162306a36Sopenharmony_ci * Don't care. Safe to call from irq context. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_civoid disk_unblock_events(struct gendisk *disk) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci if (disk->ev) 13662306a36Sopenharmony_ci __disk_unblock_events(disk, false); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/** 14062306a36Sopenharmony_ci * disk_flush_events - schedule immediate event checking and flushing 14162306a36Sopenharmony_ci * @disk: disk to check and flush events for 14262306a36Sopenharmony_ci * @mask: events to flush 14362306a36Sopenharmony_ci * 14462306a36Sopenharmony_ci * Schedule immediate event checking on @disk if not blocked. Events in 14562306a36Sopenharmony_ci * @mask are scheduled to be cleared from the driver. Note that this 14662306a36Sopenharmony_ci * doesn't clear the events from @disk->ev. 14762306a36Sopenharmony_ci * 14862306a36Sopenharmony_ci * CONTEXT: 14962306a36Sopenharmony_ci * If @mask is non-zero must be called with disk->open_mutex held. 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_civoid disk_flush_events(struct gendisk *disk, unsigned int mask) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct disk_events *ev = disk->ev; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (!ev) 15662306a36Sopenharmony_ci return; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci spin_lock_irq(&ev->lock); 15962306a36Sopenharmony_ci ev->clearing |= mask; 16062306a36Sopenharmony_ci if (!ev->block) 16162306a36Sopenharmony_ci mod_delayed_work(system_freezable_power_efficient_wq, 16262306a36Sopenharmony_ci &ev->dwork, 0); 16362306a36Sopenharmony_ci spin_unlock_irq(&ev->lock); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/* 16762306a36Sopenharmony_ci * Tell userland about new events. Only the events listed in @disk->events are 16862306a36Sopenharmony_ci * reported, and only if DISK_EVENT_FLAG_UEVENT is set. Otherwise, events are 16962306a36Sopenharmony_ci * processed internally but never get reported to userland. 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_cistatic void disk_event_uevent(struct gendisk *disk, unsigned int events) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci char *envp[ARRAY_SIZE(disk_uevents) + 1] = { }; 17462306a36Sopenharmony_ci int nr_events = 0, i; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(disk_uevents); i++) 17762306a36Sopenharmony_ci if (events & disk->events & (1 << i)) 17862306a36Sopenharmony_ci envp[nr_events++] = disk_uevents[i]; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (nr_events) 18162306a36Sopenharmony_ci kobject_uevent_env(&disk_to_dev(disk)->kobj, KOBJ_CHANGE, envp); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic void disk_check_events(struct disk_events *ev, 18562306a36Sopenharmony_ci unsigned int *clearing_ptr) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct gendisk *disk = ev->disk; 18862306a36Sopenharmony_ci unsigned int clearing = *clearing_ptr; 18962306a36Sopenharmony_ci unsigned int events; 19062306a36Sopenharmony_ci unsigned long intv; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* check events */ 19362306a36Sopenharmony_ci events = disk->fops->check_events(disk, clearing); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* accumulate pending events and schedule next poll if necessary */ 19662306a36Sopenharmony_ci spin_lock_irq(&ev->lock); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci events &= ~ev->pending; 19962306a36Sopenharmony_ci ev->pending |= events; 20062306a36Sopenharmony_ci *clearing_ptr &= ~clearing; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci intv = disk_events_poll_jiffies(disk); 20362306a36Sopenharmony_ci if (!ev->block && intv) 20462306a36Sopenharmony_ci queue_delayed_work(system_freezable_power_efficient_wq, 20562306a36Sopenharmony_ci &ev->dwork, intv); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci spin_unlock_irq(&ev->lock); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (events & DISK_EVENT_MEDIA_CHANGE) 21062306a36Sopenharmony_ci inc_diskseq(disk); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (disk->event_flags & DISK_EVENT_FLAG_UEVENT) 21362306a36Sopenharmony_ci disk_event_uevent(disk, events); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci/** 21762306a36Sopenharmony_ci * disk_clear_events - synchronously check, clear and return pending events 21862306a36Sopenharmony_ci * @disk: disk to fetch and clear events from 21962306a36Sopenharmony_ci * @mask: mask of events to be fetched and cleared 22062306a36Sopenharmony_ci * 22162306a36Sopenharmony_ci * Disk events are synchronously checked and pending events in @mask 22262306a36Sopenharmony_ci * are cleared and returned. This ignores the block count. 22362306a36Sopenharmony_ci * 22462306a36Sopenharmony_ci * CONTEXT: 22562306a36Sopenharmony_ci * Might sleep. 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_cistatic unsigned int disk_clear_events(struct gendisk *disk, unsigned int mask) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct disk_events *ev = disk->ev; 23062306a36Sopenharmony_ci unsigned int pending; 23162306a36Sopenharmony_ci unsigned int clearing = mask; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (!ev) 23462306a36Sopenharmony_ci return 0; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci disk_block_events(disk); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* 23962306a36Sopenharmony_ci * store the union of mask and ev->clearing on the stack so that the 24062306a36Sopenharmony_ci * race with disk_flush_events does not cause ambiguity (ev->clearing 24162306a36Sopenharmony_ci * can still be modified even if events are blocked). 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_ci spin_lock_irq(&ev->lock); 24462306a36Sopenharmony_ci clearing |= ev->clearing; 24562306a36Sopenharmony_ci ev->clearing = 0; 24662306a36Sopenharmony_ci spin_unlock_irq(&ev->lock); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci disk_check_events(ev, &clearing); 24962306a36Sopenharmony_ci /* 25062306a36Sopenharmony_ci * if ev->clearing is not 0, the disk_flush_events got called in the 25162306a36Sopenharmony_ci * middle of this function, so we want to run the workfn without delay. 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_ci __disk_unblock_events(disk, ev->clearing ? true : false); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* then, fetch and clear pending events */ 25662306a36Sopenharmony_ci spin_lock_irq(&ev->lock); 25762306a36Sopenharmony_ci pending = ev->pending & mask; 25862306a36Sopenharmony_ci ev->pending &= ~mask; 25962306a36Sopenharmony_ci spin_unlock_irq(&ev->lock); 26062306a36Sopenharmony_ci WARN_ON_ONCE(clearing & mask); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return pending; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci/** 26662306a36Sopenharmony_ci * disk_check_media_change - check if a removable media has been changed 26762306a36Sopenharmony_ci * @disk: gendisk to check 26862306a36Sopenharmony_ci * 26962306a36Sopenharmony_ci * Check whether a removable media has been changed, and attempt to free all 27062306a36Sopenharmony_ci * dentries and inodes and invalidates all block device page cache entries in 27162306a36Sopenharmony_ci * that case. 27262306a36Sopenharmony_ci * 27362306a36Sopenharmony_ci * Returns %true if the media has changed, or %false if not. 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_cibool disk_check_media_change(struct gendisk *disk) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci unsigned int events; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci events = disk_clear_events(disk, DISK_EVENT_MEDIA_CHANGE | 28062306a36Sopenharmony_ci DISK_EVENT_EJECT_REQUEST); 28162306a36Sopenharmony_ci if (!(events & DISK_EVENT_MEDIA_CHANGE)) 28262306a36Sopenharmony_ci return false; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci bdev_mark_dead(disk->part0, true); 28562306a36Sopenharmony_ci set_bit(GD_NEED_PART_SCAN, &disk->state); 28662306a36Sopenharmony_ci return true; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ciEXPORT_SYMBOL(disk_check_media_change); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci/** 29162306a36Sopenharmony_ci * disk_force_media_change - force a media change event 29262306a36Sopenharmony_ci * @disk: the disk which will raise the event 29362306a36Sopenharmony_ci * 29462306a36Sopenharmony_ci * Should be called when the media changes for @disk. Generates a uevent 29562306a36Sopenharmony_ci * and attempts to free all dentries and inodes and invalidates all block 29662306a36Sopenharmony_ci * device page cache entries in that case. 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_civoid disk_force_media_change(struct gendisk *disk) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci disk_event_uevent(disk, DISK_EVENT_MEDIA_CHANGE); 30162306a36Sopenharmony_ci inc_diskseq(disk); 30262306a36Sopenharmony_ci bdev_mark_dead(disk->part0, true); 30362306a36Sopenharmony_ci set_bit(GD_NEED_PART_SCAN, &disk->state); 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(disk_force_media_change); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci/* 30862306a36Sopenharmony_ci * Separate this part out so that a different pointer for clearing_ptr can be 30962306a36Sopenharmony_ci * passed in for disk_clear_events. 31062306a36Sopenharmony_ci */ 31162306a36Sopenharmony_cistatic void disk_events_workfn(struct work_struct *work) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci struct delayed_work *dwork = to_delayed_work(work); 31462306a36Sopenharmony_ci struct disk_events *ev = container_of(dwork, struct disk_events, dwork); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci disk_check_events(ev, &ev->clearing); 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci/* 32062306a36Sopenharmony_ci * A disk events enabled device has the following sysfs nodes under 32162306a36Sopenharmony_ci * its /sys/block/X/ directory. 32262306a36Sopenharmony_ci * 32362306a36Sopenharmony_ci * events : list of all supported events 32462306a36Sopenharmony_ci * events_async : list of events which can be detected w/o polling 32562306a36Sopenharmony_ci * (always empty, only for backwards compatibility) 32662306a36Sopenharmony_ci * events_poll_msecs : polling interval, 0: disable, -1: system default 32762306a36Sopenharmony_ci */ 32862306a36Sopenharmony_cistatic ssize_t __disk_events_show(unsigned int events, char *buf) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci const char *delim = ""; 33162306a36Sopenharmony_ci ssize_t pos = 0; 33262306a36Sopenharmony_ci int i; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(disk_events_strs); i++) 33562306a36Sopenharmony_ci if (events & (1 << i)) { 33662306a36Sopenharmony_ci pos += sprintf(buf + pos, "%s%s", 33762306a36Sopenharmony_ci delim, disk_events_strs[i]); 33862306a36Sopenharmony_ci delim = " "; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci if (pos) 34162306a36Sopenharmony_ci pos += sprintf(buf + pos, "\n"); 34262306a36Sopenharmony_ci return pos; 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic ssize_t disk_events_show(struct device *dev, 34662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci struct gendisk *disk = dev_to_disk(dev); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (!(disk->event_flags & DISK_EVENT_FLAG_UEVENT)) 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci return __disk_events_show(disk->events, buf); 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic ssize_t disk_events_async_show(struct device *dev, 35662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci return 0; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic ssize_t disk_events_poll_msecs_show(struct device *dev, 36262306a36Sopenharmony_ci struct device_attribute *attr, 36362306a36Sopenharmony_ci char *buf) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci struct gendisk *disk = dev_to_disk(dev); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (!disk->ev) 36862306a36Sopenharmony_ci return sprintf(buf, "-1\n"); 36962306a36Sopenharmony_ci return sprintf(buf, "%ld\n", disk->ev->poll_msecs); 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic ssize_t disk_events_poll_msecs_store(struct device *dev, 37362306a36Sopenharmony_ci struct device_attribute *attr, 37462306a36Sopenharmony_ci const char *buf, size_t count) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct gendisk *disk = dev_to_disk(dev); 37762306a36Sopenharmony_ci long intv; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (!count || !sscanf(buf, "%ld", &intv)) 38062306a36Sopenharmony_ci return -EINVAL; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (intv < 0 && intv != -1) 38362306a36Sopenharmony_ci return -EINVAL; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (!disk->ev) 38662306a36Sopenharmony_ci return -ENODEV; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci disk_block_events(disk); 38962306a36Sopenharmony_ci disk->ev->poll_msecs = intv; 39062306a36Sopenharmony_ci __disk_unblock_events(disk, true); 39162306a36Sopenharmony_ci return count; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ciDEVICE_ATTR(events, 0444, disk_events_show, NULL); 39562306a36Sopenharmony_ciDEVICE_ATTR(events_async, 0444, disk_events_async_show, NULL); 39662306a36Sopenharmony_ciDEVICE_ATTR(events_poll_msecs, 0644, disk_events_poll_msecs_show, 39762306a36Sopenharmony_ci disk_events_poll_msecs_store); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci/* 40062306a36Sopenharmony_ci * The default polling interval can be specified by the kernel 40162306a36Sopenharmony_ci * parameter block.events_dfl_poll_msecs which defaults to 0 40262306a36Sopenharmony_ci * (disable). This can also be modified runtime by writing to 40362306a36Sopenharmony_ci * /sys/module/block/parameters/events_dfl_poll_msecs. 40462306a36Sopenharmony_ci */ 40562306a36Sopenharmony_cistatic int disk_events_set_dfl_poll_msecs(const char *val, 40662306a36Sopenharmony_ci const struct kernel_param *kp) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci struct disk_events *ev; 40962306a36Sopenharmony_ci int ret; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci ret = param_set_ulong(val, kp); 41262306a36Sopenharmony_ci if (ret < 0) 41362306a36Sopenharmony_ci return ret; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci mutex_lock(&disk_events_mutex); 41662306a36Sopenharmony_ci list_for_each_entry(ev, &disk_events, node) 41762306a36Sopenharmony_ci disk_flush_events(ev->disk, 0); 41862306a36Sopenharmony_ci mutex_unlock(&disk_events_mutex); 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic const struct kernel_param_ops disk_events_dfl_poll_msecs_param_ops = { 42362306a36Sopenharmony_ci .set = disk_events_set_dfl_poll_msecs, 42462306a36Sopenharmony_ci .get = param_get_ulong, 42562306a36Sopenharmony_ci}; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci#undef MODULE_PARAM_PREFIX 42862306a36Sopenharmony_ci#define MODULE_PARAM_PREFIX "block." 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cimodule_param_cb(events_dfl_poll_msecs, &disk_events_dfl_poll_msecs_param_ops, 43162306a36Sopenharmony_ci &disk_events_dfl_poll_msecs, 0644); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci/* 43462306a36Sopenharmony_ci * disk_{alloc|add|del|release}_events - initialize and destroy disk_events. 43562306a36Sopenharmony_ci */ 43662306a36Sopenharmony_ciint disk_alloc_events(struct gendisk *disk) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct disk_events *ev; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (!disk->fops->check_events || !disk->events) 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci ev = kzalloc(sizeof(*ev), GFP_KERNEL); 44462306a36Sopenharmony_ci if (!ev) { 44562306a36Sopenharmony_ci pr_warn("%s: failed to initialize events\n", disk->disk_name); 44662306a36Sopenharmony_ci return -ENOMEM; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci INIT_LIST_HEAD(&ev->node); 45062306a36Sopenharmony_ci ev->disk = disk; 45162306a36Sopenharmony_ci spin_lock_init(&ev->lock); 45262306a36Sopenharmony_ci mutex_init(&ev->block_mutex); 45362306a36Sopenharmony_ci ev->block = 1; 45462306a36Sopenharmony_ci ev->poll_msecs = -1; 45562306a36Sopenharmony_ci INIT_DELAYED_WORK(&ev->dwork, disk_events_workfn); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci disk->ev = ev; 45862306a36Sopenharmony_ci return 0; 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_civoid disk_add_events(struct gendisk *disk) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci if (!disk->ev) 46462306a36Sopenharmony_ci return; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci mutex_lock(&disk_events_mutex); 46762306a36Sopenharmony_ci list_add_tail(&disk->ev->node, &disk_events); 46862306a36Sopenharmony_ci mutex_unlock(&disk_events_mutex); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* 47162306a36Sopenharmony_ci * Block count is initialized to 1 and the following initial 47262306a36Sopenharmony_ci * unblock kicks it into action. 47362306a36Sopenharmony_ci */ 47462306a36Sopenharmony_ci __disk_unblock_events(disk, true); 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_civoid disk_del_events(struct gendisk *disk) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci if (disk->ev) { 48062306a36Sopenharmony_ci disk_block_events(disk); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci mutex_lock(&disk_events_mutex); 48362306a36Sopenharmony_ci list_del_init(&disk->ev->node); 48462306a36Sopenharmony_ci mutex_unlock(&disk_events_mutex); 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_civoid disk_release_events(struct gendisk *disk) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci /* the block count should be 1 from disk_del_events() */ 49162306a36Sopenharmony_ci WARN_ON_ONCE(disk->ev && disk->ev->block != 1); 49262306a36Sopenharmony_ci kfree(disk->ev); 49362306a36Sopenharmony_ci} 494