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