diff options
Diffstat (limited to 'fs/notify/notification.c')
-rw-r--r-- | fs/notify/notification.c | 411 |
1 files changed, 411 insertions, 0 deletions
diff --git a/fs/notify/notification.c b/fs/notify/notification.c new file mode 100644 index 000000000000..959b73e756fd --- /dev/null +++ b/fs/notify/notification.c | |||
@@ -0,0 +1,411 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 Red Hat, Inc., Eric Paris <eparis@redhat.com> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2, or (at your option) | ||
7 | * any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; see the file COPYING. If not, write to | ||
16 | * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
17 | */ | ||
18 | |||
19 | /* | ||
20 | * Basic idea behind the notification queue: An fsnotify group (like inotify) | ||
21 | * sends the userspace notification about events asyncronously some time after | ||
22 | * the event happened. When inotify gets an event it will need to add that | ||
23 | * event to the group notify queue. Since a single event might need to be on | ||
24 | * multiple group's notification queues we can't add the event directly to each | ||
25 | * queue and instead add a small "event_holder" to each queue. This event_holder | ||
26 | * has a pointer back to the original event. Since the majority of events are | ||
27 | * going to end up on one, and only one, notification queue we embed one | ||
28 | * event_holder into each event. This means we have a single allocation instead | ||
29 | * of always needing two. If the embedded event_holder is already in use by | ||
30 | * another group a new event_holder (from fsnotify_event_holder_cachep) will be | ||
31 | * allocated and used. | ||
32 | */ | ||
33 | |||
34 | #include <linux/fs.h> | ||
35 | #include <linux/init.h> | ||
36 | #include <linux/kernel.h> | ||
37 | #include <linux/list.h> | ||
38 | #include <linux/module.h> | ||
39 | #include <linux/mount.h> | ||
40 | #include <linux/mutex.h> | ||
41 | #include <linux/namei.h> | ||
42 | #include <linux/path.h> | ||
43 | #include <linux/slab.h> | ||
44 | #include <linux/spinlock.h> | ||
45 | |||
46 | #include <asm/atomic.h> | ||
47 | |||
48 | #include <linux/fsnotify_backend.h> | ||
49 | #include "fsnotify.h" | ||
50 | |||
51 | static struct kmem_cache *fsnotify_event_cachep; | ||
52 | static struct kmem_cache *fsnotify_event_holder_cachep; | ||
53 | /* | ||
54 | * This is a magic event we send when the q is too full. Since it doesn't | ||
55 | * hold real event information we just keep one system wide and use it any time | ||
56 | * it is needed. It's refcnt is set 1 at kernel init time and will never | ||
57 | * get set to 0 so it will never get 'freed' | ||
58 | */ | ||
59 | static struct fsnotify_event q_overflow_event; | ||
60 | static atomic_t fsnotify_sync_cookie = ATOMIC_INIT(0); | ||
61 | |||
62 | /** | ||
63 | * fsnotify_get_cookie - return a unique cookie for use in synchronizing events. | ||
64 | * Called from fsnotify_move, which is inlined into filesystem modules. | ||
65 | */ | ||
66 | u32 fsnotify_get_cookie(void) | ||
67 | { | ||
68 | return atomic_inc_return(&fsnotify_sync_cookie); | ||
69 | } | ||
70 | EXPORT_SYMBOL_GPL(fsnotify_get_cookie); | ||
71 | |||
72 | /* return true if the notify queue is empty, false otherwise */ | ||
73 | bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group) | ||
74 | { | ||
75 | BUG_ON(!mutex_is_locked(&group->notification_mutex)); | ||
76 | return list_empty(&group->notification_list) ? true : false; | ||
77 | } | ||
78 | |||
79 | void fsnotify_get_event(struct fsnotify_event *event) | ||
80 | { | ||
81 | atomic_inc(&event->refcnt); | ||
82 | } | ||
83 | |||
84 | void fsnotify_put_event(struct fsnotify_event *event) | ||
85 | { | ||
86 | if (!event) | ||
87 | return; | ||
88 | |||
89 | if (atomic_dec_and_test(&event->refcnt)) { | ||
90 | if (event->data_type == FSNOTIFY_EVENT_PATH) | ||
91 | path_put(&event->path); | ||
92 | |||
93 | BUG_ON(!list_empty(&event->private_data_list)); | ||
94 | |||
95 | kfree(event->file_name); | ||
96 | kmem_cache_free(fsnotify_event_cachep, event); | ||
97 | } | ||
98 | } | ||
99 | |||
100 | struct fsnotify_event_holder *fsnotify_alloc_event_holder(void) | ||
101 | { | ||
102 | return kmem_cache_alloc(fsnotify_event_holder_cachep, GFP_KERNEL); | ||
103 | } | ||
104 | |||
105 | void fsnotify_destroy_event_holder(struct fsnotify_event_holder *holder) | ||
106 | { | ||
107 | kmem_cache_free(fsnotify_event_holder_cachep, holder); | ||
108 | } | ||
109 | |||
110 | /* | ||
111 | * Find the private data that the group previously attached to this event when | ||
112 | * the group added the event to the notification queue (fsnotify_add_notify_event) | ||
113 | */ | ||
114 | struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struct fsnotify_group *group, struct fsnotify_event *event) | ||
115 | { | ||
116 | struct fsnotify_event_private_data *lpriv; | ||
117 | struct fsnotify_event_private_data *priv = NULL; | ||
118 | |||
119 | assert_spin_locked(&event->lock); | ||
120 | |||
121 | list_for_each_entry(lpriv, &event->private_data_list, event_list) { | ||
122 | if (lpriv->group == group) { | ||
123 | priv = lpriv; | ||
124 | list_del(&priv->event_list); | ||
125 | break; | ||
126 | } | ||
127 | } | ||
128 | return priv; | ||
129 | } | ||
130 | |||
131 | /* | ||
132 | * Check if 2 events contain the same information. We do not compare private data | ||
133 | * but at this moment that isn't a problem for any know fsnotify listeners. | ||
134 | */ | ||
135 | static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new) | ||
136 | { | ||
137 | if ((old->mask == new->mask) && | ||
138 | (old->to_tell == new->to_tell) && | ||
139 | (old->data_type == new->data_type)) { | ||
140 | switch (old->data_type) { | ||
141 | case (FSNOTIFY_EVENT_INODE): | ||
142 | if (old->inode == new->inode) | ||
143 | return true; | ||
144 | break; | ||
145 | case (FSNOTIFY_EVENT_PATH): | ||
146 | if ((old->path.mnt == new->path.mnt) && | ||
147 | (old->path.dentry == new->path.dentry)) | ||
148 | return true; | ||
149 | case (FSNOTIFY_EVENT_NONE): | ||
150 | return true; | ||
151 | }; | ||
152 | } | ||
153 | return false; | ||
154 | } | ||
155 | |||
156 | /* | ||
157 | * Add an event to the group notification queue. The group can later pull this | ||
158 | * event off the queue to deal with. If the event is successfully added to the | ||
159 | * group's notification queue, a reference is taken on event. | ||
160 | */ | ||
161 | int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, | ||
162 | struct fsnotify_event_private_data *priv) | ||
163 | { | ||
164 | struct fsnotify_event_holder *holder = NULL; | ||
165 | struct list_head *list = &group->notification_list; | ||
166 | struct fsnotify_event_holder *last_holder; | ||
167 | struct fsnotify_event *last_event; | ||
168 | |||
169 | /* easy to tell if priv was attached to the event */ | ||
170 | INIT_LIST_HEAD(&priv->event_list); | ||
171 | |||
172 | /* | ||
173 | * There is one fsnotify_event_holder embedded inside each fsnotify_event. | ||
174 | * Check if we expect to be able to use that holder. If not alloc a new | ||
175 | * holder. | ||
176 | * For the overflow event it's possible that something will use the in | ||
177 | * event holder before we get the lock so we may need to jump back and | ||
178 | * alloc a new holder, this can't happen for most events... | ||
179 | */ | ||
180 | if (!list_empty(&event->holder.event_list)) { | ||
181 | alloc_holder: | ||
182 | holder = fsnotify_alloc_event_holder(); | ||
183 | if (!holder) | ||
184 | return -ENOMEM; | ||
185 | } | ||
186 | |||
187 | mutex_lock(&group->notification_mutex); | ||
188 | |||
189 | if (group->q_len >= group->max_events) { | ||
190 | event = &q_overflow_event; | ||
191 | /* sorry, no private data on the overflow event */ | ||
192 | priv = NULL; | ||
193 | } | ||
194 | |||
195 | spin_lock(&event->lock); | ||
196 | |||
197 | if (list_empty(&event->holder.event_list)) { | ||
198 | if (unlikely(holder)) | ||
199 | fsnotify_destroy_event_holder(holder); | ||
200 | holder = &event->holder; | ||
201 | } else if (unlikely(!holder)) { | ||
202 | /* between the time we checked above and got the lock the in | ||
203 | * event holder was used, go back and get a new one */ | ||
204 | spin_unlock(&event->lock); | ||
205 | mutex_unlock(&group->notification_mutex); | ||
206 | goto alloc_holder; | ||
207 | } | ||
208 | |||
209 | if (!list_empty(list)) { | ||
210 | last_holder = list_entry(list->prev, struct fsnotify_event_holder, event_list); | ||
211 | last_event = last_holder->event; | ||
212 | if (event_compare(last_event, event)) { | ||
213 | spin_unlock(&event->lock); | ||
214 | mutex_unlock(&group->notification_mutex); | ||
215 | if (holder != &event->holder) | ||
216 | fsnotify_destroy_event_holder(holder); | ||
217 | return -EEXIST; | ||
218 | } | ||
219 | } | ||
220 | |||
221 | group->q_len++; | ||
222 | holder->event = event; | ||
223 | |||
224 | fsnotify_get_event(event); | ||
225 | list_add_tail(&holder->event_list, list); | ||
226 | if (priv) | ||
227 | list_add_tail(&priv->event_list, &event->private_data_list); | ||
228 | spin_unlock(&event->lock); | ||
229 | mutex_unlock(&group->notification_mutex); | ||
230 | |||
231 | wake_up(&group->notification_waitq); | ||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | /* | ||
236 | * Remove and return the first event from the notification list. There is a | ||
237 | * reference held on this event since it was on the list. It is the responsibility | ||
238 | * of the caller to drop this reference. | ||
239 | */ | ||
240 | struct fsnotify_event *fsnotify_remove_notify_event(struct fsnotify_group *group) | ||
241 | { | ||
242 | struct fsnotify_event *event; | ||
243 | struct fsnotify_event_holder *holder; | ||
244 | |||
245 | BUG_ON(!mutex_is_locked(&group->notification_mutex)); | ||
246 | |||
247 | holder = list_first_entry(&group->notification_list, struct fsnotify_event_holder, event_list); | ||
248 | |||
249 | event = holder->event; | ||
250 | |||
251 | spin_lock(&event->lock); | ||
252 | holder->event = NULL; | ||
253 | list_del_init(&holder->event_list); | ||
254 | spin_unlock(&event->lock); | ||
255 | |||
256 | /* event == holder means we are referenced through the in event holder */ | ||
257 | if (holder != &event->holder) | ||
258 | fsnotify_destroy_event_holder(holder); | ||
259 | |||
260 | group->q_len--; | ||
261 | |||
262 | return event; | ||
263 | } | ||
264 | |||
265 | /* | ||
266 | * This will not remove the event, that must be done with fsnotify_remove_notify_event() | ||
267 | */ | ||
268 | struct fsnotify_event *fsnotify_peek_notify_event(struct fsnotify_group *group) | ||
269 | { | ||
270 | struct fsnotify_event *event; | ||
271 | struct fsnotify_event_holder *holder; | ||
272 | |||
273 | BUG_ON(!mutex_is_locked(&group->notification_mutex)); | ||
274 | |||
275 | holder = list_first_entry(&group->notification_list, struct fsnotify_event_holder, event_list); | ||
276 | event = holder->event; | ||
277 | |||
278 | return event; | ||
279 | } | ||
280 | |||
281 | /* | ||
282 | * Called when a group is being torn down to clean up any outstanding | ||
283 | * event notifications. | ||
284 | */ | ||
285 | void fsnotify_flush_notify(struct fsnotify_group *group) | ||
286 | { | ||
287 | struct fsnotify_event *event; | ||
288 | struct fsnotify_event_private_data *priv; | ||
289 | |||
290 | mutex_lock(&group->notification_mutex); | ||
291 | while (!fsnotify_notify_queue_is_empty(group)) { | ||
292 | event = fsnotify_remove_notify_event(group); | ||
293 | /* if they don't implement free_event_priv they better not have attached any */ | ||
294 | if (group->ops->free_event_priv) { | ||
295 | spin_lock(&event->lock); | ||
296 | priv = fsnotify_remove_priv_from_event(group, event); | ||
297 | spin_unlock(&event->lock); | ||
298 | if (priv) | ||
299 | group->ops->free_event_priv(priv); | ||
300 | } | ||
301 | fsnotify_put_event(event); /* matches fsnotify_add_notify_event */ | ||
302 | } | ||
303 | mutex_unlock(&group->notification_mutex); | ||
304 | } | ||
305 | |||
306 | static void initialize_event(struct fsnotify_event *event) | ||
307 | { | ||
308 | event->holder.event = NULL; | ||
309 | INIT_LIST_HEAD(&event->holder.event_list); | ||
310 | atomic_set(&event->refcnt, 1); | ||
311 | |||
312 | spin_lock_init(&event->lock); | ||
313 | |||
314 | event->path.dentry = NULL; | ||
315 | event->path.mnt = NULL; | ||
316 | event->inode = NULL; | ||
317 | event->data_type = FSNOTIFY_EVENT_NONE; | ||
318 | |||
319 | INIT_LIST_HEAD(&event->private_data_list); | ||
320 | |||
321 | event->to_tell = NULL; | ||
322 | |||
323 | event->file_name = NULL; | ||
324 | event->name_len = 0; | ||
325 | |||
326 | event->sync_cookie = 0; | ||
327 | } | ||
328 | |||
329 | /* | ||
330 | * fsnotify_create_event - Allocate a new event which will be sent to each | ||
331 | * group's handle_event function if the group was interested in this | ||
332 | * particular event. | ||
333 | * | ||
334 | * @to_tell the inode which is supposed to receive the event (sometimes a | ||
335 | * parent of the inode to which the event happened. | ||
336 | * @mask what actually happened. | ||
337 | * @data pointer to the object which was actually affected | ||
338 | * @data_type flag indication if the data is a file, path, inode, nothing... | ||
339 | * @name the filename, if available | ||
340 | */ | ||
341 | struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, void *data, | ||
342 | int data_type, const char *name, u32 cookie) | ||
343 | { | ||
344 | struct fsnotify_event *event; | ||
345 | |||
346 | event = kmem_cache_alloc(fsnotify_event_cachep, GFP_KERNEL); | ||
347 | if (!event) | ||
348 | return NULL; | ||
349 | |||
350 | initialize_event(event); | ||
351 | |||
352 | if (name) { | ||
353 | event->file_name = kstrdup(name, GFP_KERNEL); | ||
354 | if (!event->file_name) { | ||
355 | kmem_cache_free(fsnotify_event_cachep, event); | ||
356 | return NULL; | ||
357 | } | ||
358 | event->name_len = strlen(event->file_name); | ||
359 | } | ||
360 | |||
361 | event->sync_cookie = cookie; | ||
362 | event->to_tell = to_tell; | ||
363 | |||
364 | switch (data_type) { | ||
365 | case FSNOTIFY_EVENT_FILE: { | ||
366 | struct file *file = data; | ||
367 | struct path *path = &file->f_path; | ||
368 | event->path.dentry = path->dentry; | ||
369 | event->path.mnt = path->mnt; | ||
370 | path_get(&event->path); | ||
371 | event->data_type = FSNOTIFY_EVENT_PATH; | ||
372 | break; | ||
373 | } | ||
374 | case FSNOTIFY_EVENT_PATH: { | ||
375 | struct path *path = data; | ||
376 | event->path.dentry = path->dentry; | ||
377 | event->path.mnt = path->mnt; | ||
378 | path_get(&event->path); | ||
379 | event->data_type = FSNOTIFY_EVENT_PATH; | ||
380 | break; | ||
381 | } | ||
382 | case FSNOTIFY_EVENT_INODE: | ||
383 | event->inode = data; | ||
384 | event->data_type = FSNOTIFY_EVENT_INODE; | ||
385 | break; | ||
386 | case FSNOTIFY_EVENT_NONE: | ||
387 | event->inode = NULL; | ||
388 | event->path.dentry = NULL; | ||
389 | event->path.mnt = NULL; | ||
390 | break; | ||
391 | default: | ||
392 | BUG(); | ||
393 | } | ||
394 | |||
395 | event->mask = mask; | ||
396 | |||
397 | return event; | ||
398 | } | ||
399 | |||
400 | __init int fsnotify_notification_init(void) | ||
401 | { | ||
402 | fsnotify_event_cachep = KMEM_CACHE(fsnotify_event, SLAB_PANIC); | ||
403 | fsnotify_event_holder_cachep = KMEM_CACHE(fsnotify_event_holder, SLAB_PANIC); | ||
404 | |||
405 | initialize_event(&q_overflow_event); | ||
406 | q_overflow_event.mask = FS_Q_OVERFLOW; | ||
407 | |||
408 | return 0; | ||
409 | } | ||
410 | subsys_initcall(fsnotify_notification_init); | ||
411 | |||