diff options
Diffstat (limited to 'fs/notify/notification.c')
-rw-r--r-- | fs/notify/notification.c | 73 |
1 files changed, 19 insertions, 54 deletions
diff --git a/fs/notify/notification.c b/fs/notify/notification.c index b34ce7ad0409..6dc96b35e4a7 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -104,7 +104,8 @@ struct fsnotify_event_holder *fsnotify_alloc_event_holder(void) void fsnotify_destroy_event_holder(struct fsnotify_event_holder *holder) { - kmem_cache_free(fsnotify_event_holder_cachep, holder); + if (holder) + kmem_cache_free(fsnotify_event_holder_cachep, holder); } /* @@ -129,53 +130,17 @@ struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struct fsnot } /* - * Check if 2 events contain the same information. We do not compare private data - * but at this moment that isn't a problem for any know fsnotify listeners. - */ -static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new) -{ - if ((old->mask == new->mask) && - (old->to_tell == new->to_tell) && - (old->data_type == new->data_type) && - (old->name_len == new->name_len)) { - switch (old->data_type) { - case (FSNOTIFY_EVENT_INODE): - /* remember, after old was put on the wait_q we aren't - * allowed to look at the inode any more, only thing - * left to check was if the file_name is the same */ - if (!old->name_len || - !strcmp(old->file_name, new->file_name)) - return true; - break; - case (FSNOTIFY_EVENT_PATH): - if ((old->path.mnt == new->path.mnt) && - (old->path.dentry == new->path.dentry)) - return true; - break; - case (FSNOTIFY_EVENT_NONE): - if (old->mask & FS_Q_OVERFLOW) - return true; - else if (old->mask & FS_IN_IGNORED) - return false; - return false; - }; - } - return false; -} - -/* * Add an event to the group notification queue. The group can later pull this * event off the queue to deal with. If the event is successfully added to the * group's notification queue, a reference is taken on event. */ int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, - struct fsnotify_event_private_data *priv) + struct fsnotify_event_private_data *priv, + int (*merge)(struct list_head *, struct fsnotify_event *)) { struct fsnotify_event_holder *holder = NULL; struct list_head *list = &group->notification_list; - struct fsnotify_event_holder *last_holder; - struct fsnotify_event *last_event; - int ret = 0; + int rc = 0; /* * There is one fsnotify_event_holder embedded inside each fsnotify_event. @@ -196,11 +161,23 @@ alloc_holder: if (group->q_len >= group->max_events) { event = q_overflow_event; - ret = -EOVERFLOW; + rc = -EOVERFLOW; /* sorry, no private data on the overflow event */ priv = NULL; } + if (!list_empty(list) && merge) { + int ret; + + ret = merge(list, event); + if (ret) { + mutex_unlock(&group->notification_mutex); + if (holder != &event->holder) + fsnotify_destroy_event_holder(holder); + return ret; + } + } + spin_lock(&event->lock); if (list_empty(&event->holder.event_list)) { @@ -215,18 +192,6 @@ alloc_holder: goto alloc_holder; } - if (!list_empty(list)) { - last_holder = list_entry(list->prev, struct fsnotify_event_holder, event_list); - last_event = last_holder->event; - if (event_compare(last_event, event)) { - spin_unlock(&event->lock); - mutex_unlock(&group->notification_mutex); - if (holder != &event->holder) - fsnotify_destroy_event_holder(holder); - return -EEXIST; - } - } - group->q_len++; holder->event = event; @@ -238,7 +203,7 @@ alloc_holder: mutex_unlock(&group->notification_mutex); wake_up(&group->notification_waitq); - return ret; + return rc; } /* |