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