xref: /linux/block/disk-events.c (revision 6e57236e)
1d5870edfSChristoph Hellwig // SPDX-License-Identifier: GPL-2.0
2d5870edfSChristoph Hellwig /*
3d5870edfSChristoph Hellwig  * Disk events - monitor disk events like media change and eject request.
4d5870edfSChristoph Hellwig  */
5d5870edfSChristoph Hellwig #include <linux/export.h>
6d5870edfSChristoph Hellwig #include <linux/moduleparam.h>
7322cbb50SChristoph Hellwig #include <linux/blkdev.h>
8d5870edfSChristoph Hellwig #include "blk.h"
9d5870edfSChristoph Hellwig 
10d5870edfSChristoph Hellwig struct disk_events {
11d5870edfSChristoph Hellwig 	struct list_head	node;		/* all disk_event's */
12d5870edfSChristoph Hellwig 	struct gendisk		*disk;		/* the associated disk */
13d5870edfSChristoph Hellwig 	spinlock_t		lock;
14d5870edfSChristoph Hellwig 
15d5870edfSChristoph Hellwig 	struct mutex		block_mutex;	/* protects blocking */
16d5870edfSChristoph Hellwig 	int			block;		/* event blocking depth */
17d5870edfSChristoph Hellwig 	unsigned int		pending;	/* events already sent out */
18d5870edfSChristoph Hellwig 	unsigned int		clearing;	/* events being cleared */
19d5870edfSChristoph Hellwig 
20d5870edfSChristoph Hellwig 	long			poll_msecs;	/* interval, -1 for default */
21d5870edfSChristoph Hellwig 	struct delayed_work	dwork;
22d5870edfSChristoph Hellwig };
23d5870edfSChristoph Hellwig 
24d5870edfSChristoph Hellwig static const char *disk_events_strs[] = {
25d5870edfSChristoph Hellwig 	[ilog2(DISK_EVENT_MEDIA_CHANGE)]	= "media_change",
26d5870edfSChristoph Hellwig 	[ilog2(DISK_EVENT_EJECT_REQUEST)]	= "eject_request",
27d5870edfSChristoph Hellwig };
28d5870edfSChristoph Hellwig 
29d5870edfSChristoph Hellwig static char *disk_uevents[] = {
30d5870edfSChristoph Hellwig 	[ilog2(DISK_EVENT_MEDIA_CHANGE)]	= "DISK_MEDIA_CHANGE=1",
31d5870edfSChristoph Hellwig 	[ilog2(DISK_EVENT_EJECT_REQUEST)]	= "DISK_EJECT_REQUEST=1",
32d5870edfSChristoph Hellwig };
33d5870edfSChristoph Hellwig 
34d5870edfSChristoph Hellwig /* list of all disk_events */
35d5870edfSChristoph Hellwig static DEFINE_MUTEX(disk_events_mutex);
36d5870edfSChristoph Hellwig static LIST_HEAD(disk_events);
37d5870edfSChristoph Hellwig 
38d5870edfSChristoph Hellwig /* disable in-kernel polling by default */
39d5870edfSChristoph Hellwig static unsigned long disk_events_dfl_poll_msecs;
40d5870edfSChristoph Hellwig 
disk_events_poll_jiffies(struct gendisk * disk)41d5870edfSChristoph Hellwig static unsigned long disk_events_poll_jiffies(struct gendisk *disk)
42d5870edfSChristoph Hellwig {
43d5870edfSChristoph Hellwig 	struct disk_events *ev = disk->ev;
44d5870edfSChristoph Hellwig 	long intv_msecs = 0;
45d5870edfSChristoph Hellwig 
46d5870edfSChristoph Hellwig 	/*
47d5870edfSChristoph Hellwig 	 * If device-specific poll interval is set, always use it.  If
48d5870edfSChristoph Hellwig 	 * the default is being used, poll if the POLL flag is set.
49d5870edfSChristoph Hellwig 	 */
50d5870edfSChristoph Hellwig 	if (ev->poll_msecs >= 0)
51d5870edfSChristoph Hellwig 		intv_msecs = ev->poll_msecs;
52d5870edfSChristoph Hellwig 	else if (disk->event_flags & DISK_EVENT_FLAG_POLL)
53d5870edfSChristoph Hellwig 		intv_msecs = disk_events_dfl_poll_msecs;
54d5870edfSChristoph Hellwig 
55d5870edfSChristoph Hellwig 	return msecs_to_jiffies(intv_msecs);
56d5870edfSChristoph Hellwig }
57d5870edfSChristoph Hellwig 
58d5870edfSChristoph Hellwig /**
59d5870edfSChristoph Hellwig  * disk_block_events - block and flush disk event checking
60d5870edfSChristoph Hellwig  * @disk: disk to block events for
61d5870edfSChristoph Hellwig  *
62d5870edfSChristoph Hellwig  * On return from this function, it is guaranteed that event checking
63d5870edfSChristoph Hellwig  * isn't in progress and won't happen until unblocked by
64d5870edfSChristoph Hellwig  * disk_unblock_events().  Events blocking is counted and the actual
65d5870edfSChristoph Hellwig  * unblocking happens after the matching number of unblocks are done.
66d5870edfSChristoph Hellwig  *
67d5870edfSChristoph Hellwig  * Note that this intentionally does not block event checking from
68d5870edfSChristoph Hellwig  * disk_clear_events().
69d5870edfSChristoph Hellwig  *
70d5870edfSChristoph Hellwig  * CONTEXT:
71d5870edfSChristoph Hellwig  * Might sleep.
72d5870edfSChristoph Hellwig  */
disk_block_events(struct gendisk * disk)73d5870edfSChristoph Hellwig void disk_block_events(struct gendisk *disk)
74d5870edfSChristoph Hellwig {
75d5870edfSChristoph Hellwig 	struct disk_events *ev = disk->ev;
76d5870edfSChristoph Hellwig 	unsigned long flags;
77d5870edfSChristoph Hellwig 	bool cancel;
78d5870edfSChristoph Hellwig 
79d5870edfSChristoph Hellwig 	if (!ev)
80d5870edfSChristoph Hellwig 		return;
81d5870edfSChristoph Hellwig 
82d5870edfSChristoph Hellwig 	/*
83d5870edfSChristoph Hellwig 	 * Outer mutex ensures that the first blocker completes canceling
84d5870edfSChristoph Hellwig 	 * the event work before further blockers are allowed to finish.
85d5870edfSChristoph Hellwig 	 */
86d5870edfSChristoph Hellwig 	mutex_lock(&ev->block_mutex);
87d5870edfSChristoph Hellwig 
88d5870edfSChristoph Hellwig 	spin_lock_irqsave(&ev->lock, flags);
89d5870edfSChristoph Hellwig 	cancel = !ev->block++;
90d5870edfSChristoph Hellwig 	spin_unlock_irqrestore(&ev->lock, flags);
91d5870edfSChristoph Hellwig 
92d5870edfSChristoph Hellwig 	if (cancel)
93d5870edfSChristoph Hellwig 		cancel_delayed_work_sync(&disk->ev->dwork);
94d5870edfSChristoph Hellwig 
95d5870edfSChristoph Hellwig 	mutex_unlock(&ev->block_mutex);
96d5870edfSChristoph Hellwig }
97d5870edfSChristoph Hellwig 
__disk_unblock_events(struct gendisk * disk,bool check_now)98d5870edfSChristoph Hellwig static void __disk_unblock_events(struct gendisk *disk, bool check_now)
99d5870edfSChristoph Hellwig {
100d5870edfSChristoph Hellwig 	struct disk_events *ev = disk->ev;
101d5870edfSChristoph Hellwig 	unsigned long intv;
102d5870edfSChristoph Hellwig 	unsigned long flags;
103d5870edfSChristoph Hellwig 
104d5870edfSChristoph Hellwig 	spin_lock_irqsave(&ev->lock, flags);
105d5870edfSChristoph Hellwig 
106d5870edfSChristoph Hellwig 	if (WARN_ON_ONCE(ev->block <= 0))
107d5870edfSChristoph Hellwig 		goto out_unlock;
108d5870edfSChristoph Hellwig 
109d5870edfSChristoph Hellwig 	if (--ev->block)
110d5870edfSChristoph Hellwig 		goto out_unlock;
111d5870edfSChristoph Hellwig 
112d5870edfSChristoph Hellwig 	intv = disk_events_poll_jiffies(disk);
113d5870edfSChristoph Hellwig 	if (check_now)
114d5870edfSChristoph Hellwig 		queue_delayed_work(system_freezable_power_efficient_wq,
115d5870edfSChristoph Hellwig 				&ev->dwork, 0);
116d5870edfSChristoph Hellwig 	else if (intv)
117d5870edfSChristoph Hellwig 		queue_delayed_work(system_freezable_power_efficient_wq,
118d5870edfSChristoph Hellwig 				&ev->dwork, intv);
119d5870edfSChristoph Hellwig out_unlock:
120d5870edfSChristoph Hellwig 	spin_unlock_irqrestore(&ev->lock, flags);
121d5870edfSChristoph Hellwig }
122d5870edfSChristoph Hellwig 
123d5870edfSChristoph Hellwig /**
124d5870edfSChristoph Hellwig  * disk_unblock_events - unblock disk event checking
125d5870edfSChristoph Hellwig  * @disk: disk to unblock events for
126d5870edfSChristoph Hellwig  *
127d5870edfSChristoph Hellwig  * Undo disk_block_events().  When the block count reaches zero, it
128d5870edfSChristoph Hellwig  * starts events polling if configured.
129d5870edfSChristoph Hellwig  *
130d5870edfSChristoph Hellwig  * CONTEXT:
131d5870edfSChristoph Hellwig  * Don't care.  Safe to call from irq context.
132d5870edfSChristoph Hellwig  */
disk_unblock_events(struct gendisk * disk)133d5870edfSChristoph Hellwig void disk_unblock_events(struct gendisk *disk)
134d5870edfSChristoph Hellwig {
135d5870edfSChristoph Hellwig 	if (disk->ev)
136d5870edfSChristoph Hellwig 		__disk_unblock_events(disk, false);
137d5870edfSChristoph Hellwig }
138d5870edfSChristoph Hellwig 
139d5870edfSChristoph Hellwig /**
140d5870edfSChristoph Hellwig  * disk_flush_events - schedule immediate event checking and flushing
141d5870edfSChristoph Hellwig  * @disk: disk to check and flush events for
142d5870edfSChristoph Hellwig  * @mask: events to flush
143d5870edfSChristoph Hellwig  *
144d5870edfSChristoph Hellwig  * Schedule immediate event checking on @disk if not blocked.  Events in
145d5870edfSChristoph Hellwig  * @mask are scheduled to be cleared from the driver.  Note that this
146d5870edfSChristoph Hellwig  * doesn't clear the events from @disk->ev.
147d5870edfSChristoph Hellwig  *
148d5870edfSChristoph Hellwig  * CONTEXT:
149d5870edfSChristoph Hellwig  * If @mask is non-zero must be called with disk->open_mutex held.
150d5870edfSChristoph Hellwig  */
disk_flush_events(struct gendisk * disk,unsigned int mask)151d5870edfSChristoph Hellwig void disk_flush_events(struct gendisk *disk, unsigned int mask)
152d5870edfSChristoph Hellwig {
153d5870edfSChristoph Hellwig 	struct disk_events *ev = disk->ev;
154d5870edfSChristoph Hellwig 
155d5870edfSChristoph Hellwig 	if (!ev)
156d5870edfSChristoph Hellwig 		return;
157d5870edfSChristoph Hellwig 
158d5870edfSChristoph Hellwig 	spin_lock_irq(&ev->lock);
159d5870edfSChristoph Hellwig 	ev->clearing |= mask;
160d5870edfSChristoph Hellwig 	if (!ev->block)
161d5870edfSChristoph Hellwig 		mod_delayed_work(system_freezable_power_efficient_wq,
162d5870edfSChristoph Hellwig 				&ev->dwork, 0);
163d5870edfSChristoph Hellwig 	spin_unlock_irq(&ev->lock);
164d5870edfSChristoph Hellwig }
165d5870edfSChristoph Hellwig 
166e6138dc1SMatteo Croce /*
167e6138dc1SMatteo Croce  * Tell userland about new events.  Only the events listed in @disk->events are
168e6138dc1SMatteo Croce  * reported, and only if DISK_EVENT_FLAG_UEVENT is set.  Otherwise, events are
169e6138dc1SMatteo Croce  * processed internally but never get reported to userland.
170e6138dc1SMatteo Croce  */
disk_event_uevent(struct gendisk * disk,unsigned int events)171e6138dc1SMatteo Croce static void disk_event_uevent(struct gendisk *disk, unsigned int events)
172e6138dc1SMatteo Croce {
173e6138dc1SMatteo Croce 	char *envp[ARRAY_SIZE(disk_uevents) + 1] = { };
174e6138dc1SMatteo Croce 	int nr_events = 0, i;
175e6138dc1SMatteo Croce 
176e6138dc1SMatteo Croce 	for (i = 0; i < ARRAY_SIZE(disk_uevents); i++)
177e6138dc1SMatteo Croce 		if (events & disk->events & (1 << i))
178e6138dc1SMatteo Croce 			envp[nr_events++] = disk_uevents[i];
179e6138dc1SMatteo Croce 
180e6138dc1SMatteo Croce 	if (nr_events)
181e6138dc1SMatteo Croce 		kobject_uevent_env(&disk_to_dev(disk)->kobj, KOBJ_CHANGE, envp);
182e6138dc1SMatteo Croce }
183e6138dc1SMatteo Croce 
disk_check_events(struct disk_events * ev,unsigned int * clearing_ptr)184d5870edfSChristoph Hellwig static void disk_check_events(struct disk_events *ev,
185d5870edfSChristoph Hellwig 			      unsigned int *clearing_ptr)
186d5870edfSChristoph Hellwig {
187d5870edfSChristoph Hellwig 	struct gendisk *disk = ev->disk;
188d5870edfSChristoph Hellwig 	unsigned int clearing = *clearing_ptr;
189d5870edfSChristoph Hellwig 	unsigned int events;
190d5870edfSChristoph Hellwig 	unsigned long intv;
191d5870edfSChristoph Hellwig 
192d5870edfSChristoph Hellwig 	/* check events */
193d5870edfSChristoph Hellwig 	events = disk->fops->check_events(disk, clearing);
194d5870edfSChristoph Hellwig 
195d5870edfSChristoph Hellwig 	/* accumulate pending events and schedule next poll if necessary */
196d5870edfSChristoph Hellwig 	spin_lock_irq(&ev->lock);
197d5870edfSChristoph Hellwig 
198d5870edfSChristoph Hellwig 	events &= ~ev->pending;
199d5870edfSChristoph Hellwig 	ev->pending |= events;
200d5870edfSChristoph Hellwig 	*clearing_ptr &= ~clearing;
201d5870edfSChristoph Hellwig 
202d5870edfSChristoph Hellwig 	intv = disk_events_poll_jiffies(disk);
203d5870edfSChristoph Hellwig 	if (!ev->block && intv)
204d5870edfSChristoph Hellwig 		queue_delayed_work(system_freezable_power_efficient_wq,
205d5870edfSChristoph Hellwig 				&ev->dwork, intv);
206d5870edfSChristoph Hellwig 
207d5870edfSChristoph Hellwig 	spin_unlock_irq(&ev->lock);
208d5870edfSChristoph Hellwig 
209cf179948SMatteo Croce 	if (events & DISK_EVENT_MEDIA_CHANGE)
210cf179948SMatteo Croce 		inc_diskseq(disk);
211cf179948SMatteo Croce 
212e6138dc1SMatteo Croce 	if (disk->event_flags & DISK_EVENT_FLAG_UEVENT)
213e6138dc1SMatteo Croce 		disk_event_uevent(disk, events);
214d5870edfSChristoph Hellwig }
215d5870edfSChristoph Hellwig 
216d5870edfSChristoph Hellwig /**
217d5870edfSChristoph Hellwig  * disk_clear_events - synchronously check, clear and return pending events
218d5870edfSChristoph Hellwig  * @disk: disk to fetch and clear events from
219d5870edfSChristoph Hellwig  * @mask: mask of events to be fetched and cleared
220d5870edfSChristoph Hellwig  *
221d5870edfSChristoph Hellwig  * Disk events are synchronously checked and pending events in @mask
222d5870edfSChristoph Hellwig  * are cleared and returned.  This ignores the block count.
223d5870edfSChristoph Hellwig  *
224d5870edfSChristoph Hellwig  * CONTEXT:
225d5870edfSChristoph Hellwig  * Might sleep.
226d5870edfSChristoph Hellwig  */
disk_clear_events(struct gendisk * disk,unsigned int mask)227d5870edfSChristoph Hellwig static unsigned int disk_clear_events(struct gendisk *disk, unsigned int mask)
228d5870edfSChristoph Hellwig {
229d5870edfSChristoph Hellwig 	struct disk_events *ev = disk->ev;
230d5870edfSChristoph Hellwig 	unsigned int pending;
231d5870edfSChristoph Hellwig 	unsigned int clearing = mask;
232d5870edfSChristoph Hellwig 
233d5870edfSChristoph Hellwig 	if (!ev)
234d5870edfSChristoph Hellwig 		return 0;
235d5870edfSChristoph Hellwig 
236d5870edfSChristoph Hellwig 	disk_block_events(disk);
237d5870edfSChristoph Hellwig 
238d5870edfSChristoph Hellwig 	/*
239d5870edfSChristoph Hellwig 	 * store the union of mask and ev->clearing on the stack so that the
240d5870edfSChristoph Hellwig 	 * race with disk_flush_events does not cause ambiguity (ev->clearing
241d5870edfSChristoph Hellwig 	 * can still be modified even if events are blocked).
242d5870edfSChristoph Hellwig 	 */
243d5870edfSChristoph Hellwig 	spin_lock_irq(&ev->lock);
244d5870edfSChristoph Hellwig 	clearing |= ev->clearing;
245d5870edfSChristoph Hellwig 	ev->clearing = 0;
246d5870edfSChristoph Hellwig 	spin_unlock_irq(&ev->lock);
247d5870edfSChristoph Hellwig 
248d5870edfSChristoph Hellwig 	disk_check_events(ev, &clearing);
249d5870edfSChristoph Hellwig 	/*
250d5870edfSChristoph Hellwig 	 * if ev->clearing is not 0, the disk_flush_events got called in the
251d5870edfSChristoph Hellwig 	 * middle of this function, so we want to run the workfn without delay.
252d5870edfSChristoph Hellwig 	 */
253d5870edfSChristoph Hellwig 	__disk_unblock_events(disk, ev->clearing ? true : false);
254d5870edfSChristoph Hellwig 
255d5870edfSChristoph Hellwig 	/* then, fetch and clear pending events */
256d5870edfSChristoph Hellwig 	spin_lock_irq(&ev->lock);
257d5870edfSChristoph Hellwig 	pending = ev->pending & mask;
258d5870edfSChristoph Hellwig 	ev->pending &= ~mask;
259d5870edfSChristoph Hellwig 	spin_unlock_irq(&ev->lock);
260d5870edfSChristoph Hellwig 	WARN_ON_ONCE(clearing & mask);
261d5870edfSChristoph Hellwig 
262d5870edfSChristoph Hellwig 	return pending;
263d5870edfSChristoph Hellwig }
264d5870edfSChristoph Hellwig 
265d5870edfSChristoph Hellwig /**
266444aa2c5SChristoph Hellwig  * disk_check_media_change - check if a removable media has been changed
267444aa2c5SChristoph Hellwig  * @disk: gendisk to check
268d5870edfSChristoph Hellwig  *
269*6e57236eSChristoph Hellwig  * Returns %true and marks the disk for a partition rescan whether a removable
270*6e57236eSChristoph Hellwig  * media has been changed, and %false if the media did not change.
271d5870edfSChristoph Hellwig  */
disk_check_media_change(struct gendisk * disk)272444aa2c5SChristoph Hellwig bool disk_check_media_change(struct gendisk *disk)
273d5870edfSChristoph Hellwig {
274d5870edfSChristoph Hellwig 	unsigned int events;
275d5870edfSChristoph Hellwig 
276444aa2c5SChristoph Hellwig 	events = disk_clear_events(disk, DISK_EVENT_MEDIA_CHANGE |
277d5870edfSChristoph Hellwig 				   DISK_EVENT_EJECT_REQUEST);
278*6e57236eSChristoph Hellwig 	if (events & DISK_EVENT_MEDIA_CHANGE) {
279444aa2c5SChristoph Hellwig 		set_bit(GD_NEED_PART_SCAN, &disk->state);
280d5870edfSChristoph Hellwig 		return true;
281d5870edfSChristoph Hellwig 	}
282*6e57236eSChristoph Hellwig 	return false;
283*6e57236eSChristoph Hellwig }
284444aa2c5SChristoph Hellwig EXPORT_SYMBOL(disk_check_media_change);
285d5870edfSChristoph Hellwig 
286e6138dc1SMatteo Croce /**
287e6138dc1SMatteo Croce  * disk_force_media_change - force a media change event
288e6138dc1SMatteo Croce  * @disk: the disk which will raise the event
289e6138dc1SMatteo Croce  *
290ab6860f6SChristoph Hellwig  * Should be called when the media changes for @disk.  Generates a uevent
291ab6860f6SChristoph Hellwig  * and attempts to free all dentries and inodes and invalidates all block
292e6138dc1SMatteo Croce  * device page cache entries in that case.
293e6138dc1SMatteo Croce  */
disk_force_media_change(struct gendisk * disk)294ab6860f6SChristoph Hellwig void disk_force_media_change(struct gendisk *disk)
295e6138dc1SMatteo Croce {
296ab6860f6SChristoph Hellwig 	disk_event_uevent(disk, DISK_EVENT_MEDIA_CHANGE);
297b90ecc03SDemi Marie Obenour 	inc_diskseq(disk);
298560e20e4SChristoph Hellwig 	bdev_mark_dead(disk->part0, true);
299e6138dc1SMatteo Croce 	set_bit(GD_NEED_PART_SCAN, &disk->state);
300e6138dc1SMatteo Croce }
301e6138dc1SMatteo Croce EXPORT_SYMBOL_GPL(disk_force_media_change);
302e6138dc1SMatteo Croce 
303d5870edfSChristoph Hellwig /*
304d5870edfSChristoph Hellwig  * Separate this part out so that a different pointer for clearing_ptr can be
305d5870edfSChristoph Hellwig  * passed in for disk_clear_events.
306d5870edfSChristoph Hellwig  */
disk_events_workfn(struct work_struct * work)307d5870edfSChristoph Hellwig static void disk_events_workfn(struct work_struct *work)
308d5870edfSChristoph Hellwig {
309d5870edfSChristoph Hellwig 	struct delayed_work *dwork = to_delayed_work(work);
310d5870edfSChristoph Hellwig 	struct disk_events *ev = container_of(dwork, struct disk_events, dwork);
311d5870edfSChristoph Hellwig 
312d5870edfSChristoph Hellwig 	disk_check_events(ev, &ev->clearing);
313d5870edfSChristoph Hellwig }
314d5870edfSChristoph Hellwig 
315d5870edfSChristoph Hellwig /*
316d5870edfSChristoph Hellwig  * A disk events enabled device has the following sysfs nodes under
317d5870edfSChristoph Hellwig  * its /sys/block/X/ directory.
318d5870edfSChristoph Hellwig  *
319d5870edfSChristoph Hellwig  * events		: list of all supported events
320d5870edfSChristoph Hellwig  * events_async		: list of events which can be detected w/o polling
321d5870edfSChristoph Hellwig  *			  (always empty, only for backwards compatibility)
322d5870edfSChristoph Hellwig  * events_poll_msecs	: polling interval, 0: disable, -1: system default
323d5870edfSChristoph Hellwig  */
__disk_events_show(unsigned int events,char * buf)324d5870edfSChristoph Hellwig static ssize_t __disk_events_show(unsigned int events, char *buf)
325d5870edfSChristoph Hellwig {
326d5870edfSChristoph Hellwig 	const char *delim = "";
327d5870edfSChristoph Hellwig 	ssize_t pos = 0;
328d5870edfSChristoph Hellwig 	int i;
329d5870edfSChristoph Hellwig 
330d5870edfSChristoph Hellwig 	for (i = 0; i < ARRAY_SIZE(disk_events_strs); i++)
331d5870edfSChristoph Hellwig 		if (events & (1 << i)) {
332d5870edfSChristoph Hellwig 			pos += sprintf(buf + pos, "%s%s",
333d5870edfSChristoph Hellwig 				       delim, disk_events_strs[i]);
334d5870edfSChristoph Hellwig 			delim = " ";
335d5870edfSChristoph Hellwig 		}
336d5870edfSChristoph Hellwig 	if (pos)
337d5870edfSChristoph Hellwig 		pos += sprintf(buf + pos, "\n");
338d5870edfSChristoph Hellwig 	return pos;
339d5870edfSChristoph Hellwig }
340d5870edfSChristoph Hellwig 
disk_events_show(struct device * dev,struct device_attribute * attr,char * buf)341d5870edfSChristoph Hellwig static ssize_t disk_events_show(struct device *dev,
342d5870edfSChristoph Hellwig 				struct device_attribute *attr, char *buf)
343d5870edfSChristoph Hellwig {
344d5870edfSChristoph Hellwig 	struct gendisk *disk = dev_to_disk(dev);
345d5870edfSChristoph Hellwig 
346d5870edfSChristoph Hellwig 	if (!(disk->event_flags & DISK_EVENT_FLAG_UEVENT))
347d5870edfSChristoph Hellwig 		return 0;
348d5870edfSChristoph Hellwig 	return __disk_events_show(disk->events, buf);
349d5870edfSChristoph Hellwig }
350d5870edfSChristoph Hellwig 
disk_events_async_show(struct device * dev,struct device_attribute * attr,char * buf)351d5870edfSChristoph Hellwig static ssize_t disk_events_async_show(struct device *dev,
352d5870edfSChristoph Hellwig 				      struct device_attribute *attr, char *buf)
353d5870edfSChristoph Hellwig {
354d5870edfSChristoph Hellwig 	return 0;
355d5870edfSChristoph Hellwig }
356d5870edfSChristoph Hellwig 
disk_events_poll_msecs_show(struct device * dev,struct device_attribute * attr,char * buf)357d5870edfSChristoph Hellwig static ssize_t disk_events_poll_msecs_show(struct device *dev,
358d5870edfSChristoph Hellwig 					   struct device_attribute *attr,
359d5870edfSChristoph Hellwig 					   char *buf)
360d5870edfSChristoph Hellwig {
361d5870edfSChristoph Hellwig 	struct gendisk *disk = dev_to_disk(dev);
362d5870edfSChristoph Hellwig 
363d5870edfSChristoph Hellwig 	if (!disk->ev)
364d5870edfSChristoph Hellwig 		return sprintf(buf, "-1\n");
365d5870edfSChristoph Hellwig 	return sprintf(buf, "%ld\n", disk->ev->poll_msecs);
366d5870edfSChristoph Hellwig }
367d5870edfSChristoph Hellwig 
disk_events_poll_msecs_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)368d5870edfSChristoph Hellwig static ssize_t disk_events_poll_msecs_store(struct device *dev,
369d5870edfSChristoph Hellwig 					    struct device_attribute *attr,
370d5870edfSChristoph Hellwig 					    const char *buf, size_t count)
371d5870edfSChristoph Hellwig {
372d5870edfSChristoph Hellwig 	struct gendisk *disk = dev_to_disk(dev);
373d5870edfSChristoph Hellwig 	long intv;
374d5870edfSChristoph Hellwig 
375d5870edfSChristoph Hellwig 	if (!count || !sscanf(buf, "%ld", &intv))
376d5870edfSChristoph Hellwig 		return -EINVAL;
377d5870edfSChristoph Hellwig 
378d5870edfSChristoph Hellwig 	if (intv < 0 && intv != -1)
379d5870edfSChristoph Hellwig 		return -EINVAL;
380d5870edfSChristoph Hellwig 
381d5870edfSChristoph Hellwig 	if (!disk->ev)
382d5870edfSChristoph Hellwig 		return -ENODEV;
383d5870edfSChristoph Hellwig 
384d5870edfSChristoph Hellwig 	disk_block_events(disk);
385d5870edfSChristoph Hellwig 	disk->ev->poll_msecs = intv;
386d5870edfSChristoph Hellwig 	__disk_unblock_events(disk, true);
387d5870edfSChristoph Hellwig 	return count;
388d5870edfSChristoph Hellwig }
389d5870edfSChristoph Hellwig 
3902bc8cda5SChristoph Hellwig DEVICE_ATTR(events, 0444, disk_events_show, NULL);
3912bc8cda5SChristoph Hellwig DEVICE_ATTR(events_async, 0444, disk_events_async_show, NULL);
3922bc8cda5SChristoph Hellwig DEVICE_ATTR(events_poll_msecs, 0644, disk_events_poll_msecs_show,
393d5870edfSChristoph Hellwig 	    disk_events_poll_msecs_store);
394d5870edfSChristoph Hellwig 
395d5870edfSChristoph Hellwig /*
396d5870edfSChristoph Hellwig  * The default polling interval can be specified by the kernel
397d5870edfSChristoph Hellwig  * parameter block.events_dfl_poll_msecs which defaults to 0
398d5870edfSChristoph Hellwig  * (disable).  This can also be modified runtime by writing to
399d5870edfSChristoph Hellwig  * /sys/module/block/parameters/events_dfl_poll_msecs.
400d5870edfSChristoph Hellwig  */
disk_events_set_dfl_poll_msecs(const char * val,const struct kernel_param * kp)401d5870edfSChristoph Hellwig static int disk_events_set_dfl_poll_msecs(const char *val,
402d5870edfSChristoph Hellwig 					  const struct kernel_param *kp)
403d5870edfSChristoph Hellwig {
404d5870edfSChristoph Hellwig 	struct disk_events *ev;
405d5870edfSChristoph Hellwig 	int ret;
406d5870edfSChristoph Hellwig 
407d5870edfSChristoph Hellwig 	ret = param_set_ulong(val, kp);
408d5870edfSChristoph Hellwig 	if (ret < 0)
409d5870edfSChristoph Hellwig 		return ret;
410d5870edfSChristoph Hellwig 
411d5870edfSChristoph Hellwig 	mutex_lock(&disk_events_mutex);
412d5870edfSChristoph Hellwig 	list_for_each_entry(ev, &disk_events, node)
413d5870edfSChristoph Hellwig 		disk_flush_events(ev->disk, 0);
414d5870edfSChristoph Hellwig 	mutex_unlock(&disk_events_mutex);
415d5870edfSChristoph Hellwig 	return 0;
416d5870edfSChristoph Hellwig }
417d5870edfSChristoph Hellwig 
418d5870edfSChristoph Hellwig static const struct kernel_param_ops disk_events_dfl_poll_msecs_param_ops = {
419d5870edfSChristoph Hellwig 	.set	= disk_events_set_dfl_poll_msecs,
420d5870edfSChristoph Hellwig 	.get	= param_get_ulong,
421d5870edfSChristoph Hellwig };
422d5870edfSChristoph Hellwig 
423d5870edfSChristoph Hellwig #undef MODULE_PARAM_PREFIX
424d5870edfSChristoph Hellwig #define MODULE_PARAM_PREFIX	"block."
425d5870edfSChristoph Hellwig 
426d5870edfSChristoph Hellwig module_param_cb(events_dfl_poll_msecs, &disk_events_dfl_poll_msecs_param_ops,
427d5870edfSChristoph Hellwig 		&disk_events_dfl_poll_msecs, 0644);
428d5870edfSChristoph Hellwig 
429d5870edfSChristoph Hellwig /*
430d5870edfSChristoph Hellwig  * disk_{alloc|add|del|release}_events - initialize and destroy disk_events.
431d5870edfSChristoph Hellwig  */
disk_alloc_events(struct gendisk * disk)43292e7755eSLuis Chamberlain int disk_alloc_events(struct gendisk *disk)
433d5870edfSChristoph Hellwig {
434d5870edfSChristoph Hellwig 	struct disk_events *ev;
435d5870edfSChristoph Hellwig 
436d5870edfSChristoph Hellwig 	if (!disk->fops->check_events || !disk->events)
43792e7755eSLuis Chamberlain 		return 0;
438d5870edfSChristoph Hellwig 
439d5870edfSChristoph Hellwig 	ev = kzalloc(sizeof(*ev), GFP_KERNEL);
440d5870edfSChristoph Hellwig 	if (!ev) {
441d5870edfSChristoph Hellwig 		pr_warn("%s: failed to initialize events\n", disk->disk_name);
44292e7755eSLuis Chamberlain 		return -ENOMEM;
443d5870edfSChristoph Hellwig 	}
444d5870edfSChristoph Hellwig 
445d5870edfSChristoph Hellwig 	INIT_LIST_HEAD(&ev->node);
446d5870edfSChristoph Hellwig 	ev->disk = disk;
447d5870edfSChristoph Hellwig 	spin_lock_init(&ev->lock);
448d5870edfSChristoph Hellwig 	mutex_init(&ev->block_mutex);
449d5870edfSChristoph Hellwig 	ev->block = 1;
450d5870edfSChristoph Hellwig 	ev->poll_msecs = -1;
451d5870edfSChristoph Hellwig 	INIT_DELAYED_WORK(&ev->dwork, disk_events_workfn);
452d5870edfSChristoph Hellwig 
453d5870edfSChristoph Hellwig 	disk->ev = ev;
45492e7755eSLuis Chamberlain 	return 0;
455d5870edfSChristoph Hellwig }
456d5870edfSChristoph Hellwig 
disk_add_events(struct gendisk * disk)457d5870edfSChristoph Hellwig void disk_add_events(struct gendisk *disk)
458d5870edfSChristoph Hellwig {
459d5870edfSChristoph Hellwig 	if (!disk->ev)
460d5870edfSChristoph Hellwig 		return;
461d5870edfSChristoph Hellwig 
462d5870edfSChristoph Hellwig 	mutex_lock(&disk_events_mutex);
463d5870edfSChristoph Hellwig 	list_add_tail(&disk->ev->node, &disk_events);
464d5870edfSChristoph Hellwig 	mutex_unlock(&disk_events_mutex);
465d5870edfSChristoph Hellwig 
466d5870edfSChristoph Hellwig 	/*
467d5870edfSChristoph Hellwig 	 * Block count is initialized to 1 and the following initial
468d5870edfSChristoph Hellwig 	 * unblock kicks it into action.
469d5870edfSChristoph Hellwig 	 */
470d5870edfSChristoph Hellwig 	__disk_unblock_events(disk, true);
471d5870edfSChristoph Hellwig }
472d5870edfSChristoph Hellwig 
disk_del_events(struct gendisk * disk)473d5870edfSChristoph Hellwig void disk_del_events(struct gendisk *disk)
474d5870edfSChristoph Hellwig {
475d5870edfSChristoph Hellwig 	if (disk->ev) {
476d5870edfSChristoph Hellwig 		disk_block_events(disk);
477d5870edfSChristoph Hellwig 
478d5870edfSChristoph Hellwig 		mutex_lock(&disk_events_mutex);
479d5870edfSChristoph Hellwig 		list_del_init(&disk->ev->node);
480d5870edfSChristoph Hellwig 		mutex_unlock(&disk_events_mutex);
481d5870edfSChristoph Hellwig 	}
482d5870edfSChristoph Hellwig }
483d5870edfSChristoph Hellwig 
disk_release_events(struct gendisk * disk)484d5870edfSChristoph Hellwig void disk_release_events(struct gendisk *disk)
485d5870edfSChristoph Hellwig {
486d5870edfSChristoph Hellwig 	/* the block count should be 1 from disk_del_events() */
487d5870edfSChristoph Hellwig 	WARN_ON_ONCE(disk->ev && disk->ev->block != 1);
488d5870edfSChristoph Hellwig 	kfree(disk->ev);
489d5870edfSChristoph Hellwig }
490