xref: /openbmc/linux/block/disk-events.c (revision eafdc5071351314702175a3cd083cf6f7eef6488)
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  *
269d5870edfSChristoph Hellwig  * Check whether a removable media has been changed, and attempt to free all
270d5870edfSChristoph Hellwig  * dentries and inodes and invalidates all block device page cache entries in
271d5870edfSChristoph Hellwig  * that case.
272d5870edfSChristoph Hellwig  *
273444aa2c5SChristoph Hellwig  * Returns %true if the media has changed, or %false if not.
274d5870edfSChristoph Hellwig  */
disk_check_media_change(struct gendisk * disk)275444aa2c5SChristoph Hellwig bool disk_check_media_change(struct gendisk *disk)
276d5870edfSChristoph Hellwig {
277d5870edfSChristoph Hellwig 	unsigned int events;
278d5870edfSChristoph Hellwig 
279444aa2c5SChristoph Hellwig 	events = disk_clear_events(disk, DISK_EVENT_MEDIA_CHANGE |
280d5870edfSChristoph Hellwig 				   DISK_EVENT_EJECT_REQUEST);
281d5870edfSChristoph Hellwig 	if (!(events & DISK_EVENT_MEDIA_CHANGE))
282d5870edfSChristoph Hellwig 		return false;
283d5870edfSChristoph Hellwig 
284*560e20e4SChristoph Hellwig 	bdev_mark_dead(disk->part0, true);
285444aa2c5SChristoph Hellwig 	set_bit(GD_NEED_PART_SCAN, &disk->state);
286d5870edfSChristoph Hellwig 	return true;
287d5870edfSChristoph Hellwig }
288444aa2c5SChristoph Hellwig EXPORT_SYMBOL(disk_check_media_change);
289d5870edfSChristoph Hellwig 
290e6138dc1SMatteo Croce /**
291e6138dc1SMatteo Croce  * disk_force_media_change - force a media change event
292e6138dc1SMatteo Croce  * @disk: the disk which will raise the event
293e6138dc1SMatteo Croce  *
294ab6860f6SChristoph Hellwig  * Should be called when the media changes for @disk.  Generates a uevent
295ab6860f6SChristoph Hellwig  * and attempts to free all dentries and inodes and invalidates all block
296e6138dc1SMatteo Croce  * device page cache entries in that case.
297e6138dc1SMatteo Croce  */
disk_force_media_change(struct gendisk * disk)298ab6860f6SChristoph Hellwig void disk_force_media_change(struct gendisk *disk)
299e6138dc1SMatteo Croce {
300ab6860f6SChristoph Hellwig 	disk_event_uevent(disk, DISK_EVENT_MEDIA_CHANGE);
301b90ecc03SDemi Marie Obenour 	inc_diskseq(disk);
302*560e20e4SChristoph Hellwig 	bdev_mark_dead(disk->part0, true);
303e6138dc1SMatteo Croce 	set_bit(GD_NEED_PART_SCAN, &disk->state);
304e6138dc1SMatteo Croce }
305e6138dc1SMatteo Croce EXPORT_SYMBOL_GPL(disk_force_media_change);
306e6138dc1SMatteo Croce 
307d5870edfSChristoph Hellwig /*
308d5870edfSChristoph Hellwig  * Separate this part out so that a different pointer for clearing_ptr can be
309d5870edfSChristoph Hellwig  * passed in for disk_clear_events.
310d5870edfSChristoph Hellwig  */
disk_events_workfn(struct work_struct * work)311d5870edfSChristoph Hellwig static void disk_events_workfn(struct work_struct *work)
312d5870edfSChristoph Hellwig {
313d5870edfSChristoph Hellwig 	struct delayed_work *dwork = to_delayed_work(work);
314d5870edfSChristoph Hellwig 	struct disk_events *ev = container_of(dwork, struct disk_events, dwork);
315d5870edfSChristoph Hellwig 
316d5870edfSChristoph Hellwig 	disk_check_events(ev, &ev->clearing);
317d5870edfSChristoph Hellwig }
318d5870edfSChristoph Hellwig 
319d5870edfSChristoph Hellwig /*
320d5870edfSChristoph Hellwig  * A disk events enabled device has the following sysfs nodes under
321d5870edfSChristoph Hellwig  * its /sys/block/X/ directory.
322d5870edfSChristoph Hellwig  *
323d5870edfSChristoph Hellwig  * events		: list of all supported events
324d5870edfSChristoph Hellwig  * events_async		: list of events which can be detected w/o polling
325d5870edfSChristoph Hellwig  *			  (always empty, only for backwards compatibility)
326d5870edfSChristoph Hellwig  * events_poll_msecs	: polling interval, 0: disable, -1: system default
327d5870edfSChristoph Hellwig  */
__disk_events_show(unsigned int events,char * buf)328d5870edfSChristoph Hellwig static ssize_t __disk_events_show(unsigned int events, char *buf)
329d5870edfSChristoph Hellwig {
330d5870edfSChristoph Hellwig 	const char *delim = "";
331d5870edfSChristoph Hellwig 	ssize_t pos = 0;
332d5870edfSChristoph Hellwig 	int i;
333d5870edfSChristoph Hellwig 
334d5870edfSChristoph Hellwig 	for (i = 0; i < ARRAY_SIZE(disk_events_strs); i++)
335d5870edfSChristoph Hellwig 		if (events & (1 << i)) {
336d5870edfSChristoph Hellwig 			pos += sprintf(buf + pos, "%s%s",
337d5870edfSChristoph Hellwig 				       delim, disk_events_strs[i]);
338d5870edfSChristoph Hellwig 			delim = " ";
339d5870edfSChristoph Hellwig 		}
340d5870edfSChristoph Hellwig 	if (pos)
341d5870edfSChristoph Hellwig 		pos += sprintf(buf + pos, "\n");
342d5870edfSChristoph Hellwig 	return pos;
343d5870edfSChristoph Hellwig }
344d5870edfSChristoph Hellwig 
disk_events_show(struct device * dev,struct device_attribute * attr,char * buf)345d5870edfSChristoph Hellwig static ssize_t disk_events_show(struct device *dev,
346d5870edfSChristoph Hellwig 				struct device_attribute *attr, char *buf)
347d5870edfSChristoph Hellwig {
348d5870edfSChristoph Hellwig 	struct gendisk *disk = dev_to_disk(dev);
349d5870edfSChristoph Hellwig 
350d5870edfSChristoph Hellwig 	if (!(disk->event_flags & DISK_EVENT_FLAG_UEVENT))
351d5870edfSChristoph Hellwig 		return 0;
352d5870edfSChristoph Hellwig 	return __disk_events_show(disk->events, buf);
353d5870edfSChristoph Hellwig }
354d5870edfSChristoph Hellwig 
disk_events_async_show(struct device * dev,struct device_attribute * attr,char * buf)355d5870edfSChristoph Hellwig static ssize_t disk_events_async_show(struct device *dev,
356d5870edfSChristoph Hellwig 				      struct device_attribute *attr, char *buf)
357d5870edfSChristoph Hellwig {
358d5870edfSChristoph Hellwig 	return 0;
359d5870edfSChristoph Hellwig }
360d5870edfSChristoph Hellwig 
disk_events_poll_msecs_show(struct device * dev,struct device_attribute * attr,char * buf)361d5870edfSChristoph Hellwig static ssize_t disk_events_poll_msecs_show(struct device *dev,
362d5870edfSChristoph Hellwig 					   struct device_attribute *attr,
363d5870edfSChristoph Hellwig 					   char *buf)
364d5870edfSChristoph Hellwig {
365d5870edfSChristoph Hellwig 	struct gendisk *disk = dev_to_disk(dev);
366d5870edfSChristoph Hellwig 
367d5870edfSChristoph Hellwig 	if (!disk->ev)
368d5870edfSChristoph Hellwig 		return sprintf(buf, "-1\n");
369d5870edfSChristoph Hellwig 	return sprintf(buf, "%ld\n", disk->ev->poll_msecs);
370d5870edfSChristoph Hellwig }
371d5870edfSChristoph Hellwig 
disk_events_poll_msecs_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)372d5870edfSChristoph Hellwig static ssize_t disk_events_poll_msecs_store(struct device *dev,
373d5870edfSChristoph Hellwig 					    struct device_attribute *attr,
374d5870edfSChristoph Hellwig 					    const char *buf, size_t count)
375d5870edfSChristoph Hellwig {
376d5870edfSChristoph Hellwig 	struct gendisk *disk = dev_to_disk(dev);
377d5870edfSChristoph Hellwig 	long intv;
378d5870edfSChristoph Hellwig 
379d5870edfSChristoph Hellwig 	if (!count || !sscanf(buf, "%ld", &intv))
380d5870edfSChristoph Hellwig 		return -EINVAL;
381d5870edfSChristoph Hellwig 
382d5870edfSChristoph Hellwig 	if (intv < 0 && intv != -1)
383d5870edfSChristoph Hellwig 		return -EINVAL;
384d5870edfSChristoph Hellwig 
385d5870edfSChristoph Hellwig 	if (!disk->ev)
386d5870edfSChristoph Hellwig 		return -ENODEV;
387d5870edfSChristoph Hellwig 
388d5870edfSChristoph Hellwig 	disk_block_events(disk);
389d5870edfSChristoph Hellwig 	disk->ev->poll_msecs = intv;
390d5870edfSChristoph Hellwig 	__disk_unblock_events(disk, true);
391d5870edfSChristoph Hellwig 	return count;
392d5870edfSChristoph Hellwig }
393d5870edfSChristoph Hellwig 
3942bc8cda5SChristoph Hellwig DEVICE_ATTR(events, 0444, disk_events_show, NULL);
3952bc8cda5SChristoph Hellwig DEVICE_ATTR(events_async, 0444, disk_events_async_show, NULL);
3962bc8cda5SChristoph Hellwig DEVICE_ATTR(events_poll_msecs, 0644, disk_events_poll_msecs_show,
397d5870edfSChristoph Hellwig 	    disk_events_poll_msecs_store);
398d5870edfSChristoph Hellwig 
399d5870edfSChristoph Hellwig /*
400d5870edfSChristoph Hellwig  * The default polling interval can be specified by the kernel
401d5870edfSChristoph Hellwig  * parameter block.events_dfl_poll_msecs which defaults to 0
402d5870edfSChristoph Hellwig  * (disable).  This can also be modified runtime by writing to
403d5870edfSChristoph Hellwig  * /sys/module/block/parameters/events_dfl_poll_msecs.
404d5870edfSChristoph Hellwig  */
disk_events_set_dfl_poll_msecs(const char * val,const struct kernel_param * kp)405d5870edfSChristoph Hellwig static int disk_events_set_dfl_poll_msecs(const char *val,
406d5870edfSChristoph Hellwig 					  const struct kernel_param *kp)
407d5870edfSChristoph Hellwig {
408d5870edfSChristoph Hellwig 	struct disk_events *ev;
409d5870edfSChristoph Hellwig 	int ret;
410d5870edfSChristoph Hellwig 
411d5870edfSChristoph Hellwig 	ret = param_set_ulong(val, kp);
412d5870edfSChristoph Hellwig 	if (ret < 0)
413d5870edfSChristoph Hellwig 		return ret;
414d5870edfSChristoph Hellwig 
415d5870edfSChristoph Hellwig 	mutex_lock(&disk_events_mutex);
416d5870edfSChristoph Hellwig 	list_for_each_entry(ev, &disk_events, node)
417d5870edfSChristoph Hellwig 		disk_flush_events(ev->disk, 0);
418d5870edfSChristoph Hellwig 	mutex_unlock(&disk_events_mutex);
419d5870edfSChristoph Hellwig 	return 0;
420d5870edfSChristoph Hellwig }
421d5870edfSChristoph Hellwig 
422d5870edfSChristoph Hellwig static const struct kernel_param_ops disk_events_dfl_poll_msecs_param_ops = {
423d5870edfSChristoph Hellwig 	.set	= disk_events_set_dfl_poll_msecs,
424d5870edfSChristoph Hellwig 	.get	= param_get_ulong,
425d5870edfSChristoph Hellwig };
426d5870edfSChristoph Hellwig 
427d5870edfSChristoph Hellwig #undef MODULE_PARAM_PREFIX
428d5870edfSChristoph Hellwig #define MODULE_PARAM_PREFIX	"block."
429d5870edfSChristoph Hellwig 
430d5870edfSChristoph Hellwig module_param_cb(events_dfl_poll_msecs, &disk_events_dfl_poll_msecs_param_ops,
431d5870edfSChristoph Hellwig 		&disk_events_dfl_poll_msecs, 0644);
432d5870edfSChristoph Hellwig 
433d5870edfSChristoph Hellwig /*
434d5870edfSChristoph Hellwig  * disk_{alloc|add|del|release}_events - initialize and destroy disk_events.
435d5870edfSChristoph Hellwig  */
disk_alloc_events(struct gendisk * disk)43692e7755eSLuis Chamberlain int disk_alloc_events(struct gendisk *disk)
437d5870edfSChristoph Hellwig {
438d5870edfSChristoph Hellwig 	struct disk_events *ev;
439d5870edfSChristoph Hellwig 
440d5870edfSChristoph Hellwig 	if (!disk->fops->check_events || !disk->events)
44192e7755eSLuis Chamberlain 		return 0;
442d5870edfSChristoph Hellwig 
443d5870edfSChristoph Hellwig 	ev = kzalloc(sizeof(*ev), GFP_KERNEL);
444d5870edfSChristoph Hellwig 	if (!ev) {
445d5870edfSChristoph Hellwig 		pr_warn("%s: failed to initialize events\n", disk->disk_name);
44692e7755eSLuis Chamberlain 		return -ENOMEM;
447d5870edfSChristoph Hellwig 	}
448d5870edfSChristoph Hellwig 
449d5870edfSChristoph Hellwig 	INIT_LIST_HEAD(&ev->node);
450d5870edfSChristoph Hellwig 	ev->disk = disk;
451d5870edfSChristoph Hellwig 	spin_lock_init(&ev->lock);
452d5870edfSChristoph Hellwig 	mutex_init(&ev->block_mutex);
453d5870edfSChristoph Hellwig 	ev->block = 1;
454d5870edfSChristoph Hellwig 	ev->poll_msecs = -1;
455d5870edfSChristoph Hellwig 	INIT_DELAYED_WORK(&ev->dwork, disk_events_workfn);
456d5870edfSChristoph Hellwig 
457d5870edfSChristoph Hellwig 	disk->ev = ev;
45892e7755eSLuis Chamberlain 	return 0;
459d5870edfSChristoph Hellwig }
460d5870edfSChristoph Hellwig 
disk_add_events(struct gendisk * disk)461d5870edfSChristoph Hellwig void disk_add_events(struct gendisk *disk)
462d5870edfSChristoph Hellwig {
463d5870edfSChristoph Hellwig 	if (!disk->ev)
464d5870edfSChristoph Hellwig 		return;
465d5870edfSChristoph Hellwig 
466d5870edfSChristoph Hellwig 	mutex_lock(&disk_events_mutex);
467d5870edfSChristoph Hellwig 	list_add_tail(&disk->ev->node, &disk_events);
468d5870edfSChristoph Hellwig 	mutex_unlock(&disk_events_mutex);
469d5870edfSChristoph Hellwig 
470d5870edfSChristoph Hellwig 	/*
471d5870edfSChristoph Hellwig 	 * Block count is initialized to 1 and the following initial
472d5870edfSChristoph Hellwig 	 * unblock kicks it into action.
473d5870edfSChristoph Hellwig 	 */
474d5870edfSChristoph Hellwig 	__disk_unblock_events(disk, true);
475d5870edfSChristoph Hellwig }
476d5870edfSChristoph Hellwig 
disk_del_events(struct gendisk * disk)477d5870edfSChristoph Hellwig void disk_del_events(struct gendisk *disk)
478d5870edfSChristoph Hellwig {
479d5870edfSChristoph Hellwig 	if (disk->ev) {
480d5870edfSChristoph Hellwig 		disk_block_events(disk);
481d5870edfSChristoph Hellwig 
482d5870edfSChristoph Hellwig 		mutex_lock(&disk_events_mutex);
483d5870edfSChristoph Hellwig 		list_del_init(&disk->ev->node);
484d5870edfSChristoph Hellwig 		mutex_unlock(&disk_events_mutex);
485d5870edfSChristoph Hellwig 	}
486d5870edfSChristoph Hellwig }
487d5870edfSChristoph Hellwig 
disk_release_events(struct gendisk * disk)488d5870edfSChristoph Hellwig void disk_release_events(struct gendisk *disk)
489d5870edfSChristoph Hellwig {
490d5870edfSChristoph Hellwig 	/* the block count should be 1 from disk_del_events() */
491d5870edfSChristoph Hellwig 	WARN_ON_ONCE(disk->ev && disk->ev->block != 1);
492d5870edfSChristoph Hellwig 	kfree(disk->ev);
493d5870edfSChristoph Hellwig }
494