aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/notify/notification.c56
-rw-r--r--include/linux/fsnotify_backend.h2
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 */
294int 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)) {
309alloc_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
290struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event) 346struct 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... */
367extern struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event); 367extern struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event);
368extern int fsnotify_replace_event(struct fsnotify_event_holder *old_holder,
369 struct fsnotify_event *new_event);
368 370
369#else 371#else
370 372