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