diff options
-rw-r--r-- | fs/notify/notification.c | 56 | ||||
-rw-r--r-- | include/linux/fsnotify_backend.h | 2 |
2 files changed, 58 insertions, 0 deletions
diff --git a/fs/notify/notification.c b/fs/notify/notification.c index bc9470c7ece7..b493c378445f 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c | |||
@@ -287,6 +287,62 @@ static void initialize_event(struct fsnotify_event *event) | |||
287 | INIT_LIST_HEAD(&event->private_data_list); | 287 | INIT_LIST_HEAD(&event->private_data_list); |
288 | } | 288 | } |
289 | 289 | ||
290 | /* | ||
291 | * Caller damn well better be holding whatever mutex is protecting the | ||
292 | * old_holder->event_list. | ||
293 | */ | ||
294 | int fsnotify_replace_event(struct fsnotify_event_holder *old_holder, | ||
295 | struct fsnotify_event *new_event) | ||
296 | { | ||
297 | struct fsnotify_event *old_event = old_holder->event; | ||
298 | struct fsnotify_event_holder *new_holder = NULL; | ||
299 | |||
300 | /* | ||
301 | * There is one fsnotify_event_holder embedded inside each fsnotify_event. | ||
302 | * Check if we expect to be able to use that holder. If not alloc a new | ||
303 | * holder. | ||
304 | * For the overflow event it's possible that something will use the in | ||
305 | * event holder before we get the lock so we may need to jump back and | ||
306 | * alloc a new holder, this can't happen for most events... | ||
307 | */ | ||
308 | if (!list_empty(&new_event->holder.event_list)) { | ||
309 | alloc_holder: | ||
310 | new_holder = fsnotify_alloc_event_holder(); | ||
311 | if (!new_holder) | ||
312 | return -ENOMEM; | ||
313 | } | ||
314 | |||
315 | spin_lock(&old_event->lock); | ||
316 | spin_lock(&new_event->lock); | ||
317 | |||
318 | if (list_empty(&new_event->holder.event_list)) { | ||
319 | if (unlikely(new_holder)) | ||
320 | fsnotify_destroy_event_holder(new_holder); | ||
321 | new_holder = &new_event->holder; | ||
322 | } else if (unlikely(!new_holder)) { | ||
323 | /* between the time we checked above and got the lock the in | ||
324 | * event holder was used, go back and get a new one */ | ||
325 | spin_unlock(&new_event->lock); | ||
326 | spin_unlock(&old_event->lock); | ||
327 | goto alloc_holder; | ||
328 | } | ||
329 | |||
330 | new_holder->event = new_event; | ||
331 | list_replace_init(&old_holder->event_list, &new_holder->event_list); | ||
332 | |||
333 | spin_unlock(&new_event->lock); | ||
334 | spin_unlock(&old_event->lock); | ||
335 | |||
336 | /* event == holder means we are referenced through the in event holder */ | ||
337 | if (old_holder != &old_event->holder) | ||
338 | fsnotify_destroy_event_holder(old_holder); | ||
339 | |||
340 | fsnotify_get_event(new_event); /* on the list take reference */ | ||
341 | fsnotify_put_event(old_event); /* off the list, drop reference */ | ||
342 | |||
343 | return 0; | ||
344 | } | ||
345 | |||
290 | struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event) | 346 | struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event) |
291 | { | 347 | { |
292 | struct fsnotify_event *event; | 348 | struct fsnotify_event *event; |
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 3a7fff235539..427f6ffab127 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h | |||
@@ -365,6 +365,8 @@ extern struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 | |||
365 | 365 | ||
366 | /* fanotify likes to change events after they are on lists... */ | 366 | /* fanotify likes to change events after they are on lists... */ |
367 | extern struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event); | 367 | extern struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event); |
368 | extern int fsnotify_replace_event(struct fsnotify_event_holder *old_holder, | ||
369 | struct fsnotify_event *new_event); | ||
368 | 370 | ||
369 | #else | 371 | #else |
370 | 372 | ||