aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/notify/inotify/inotify_fsnotify.c56
-rw-r--r--fs/notify/inotify/inotify_user.c2
-rw-r--r--fs/notify/notification.c73
-rw-r--r--include/linux/fsnotify_backend.h6
4 files changed, 79 insertions, 58 deletions
diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c
index 1f33234cc308..0a0f5d0f0d0a 100644
--- a/fs/notify/inotify/inotify_fsnotify.c
+++ b/fs/notify/inotify/inotify_fsnotify.c
@@ -32,6 +32,60 @@
32 32
33#include "inotify.h" 33#include "inotify.h"
34 34
35/*
36 * Check if 2 events contain the same information. We do not compare private data
37 * but at this moment that isn't a problem for any know fsnotify listeners.
38 */
39static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new)
40{
41 if ((old->mask == new->mask) &&
42 (old->to_tell == new->to_tell) &&
43 (old->data_type == new->data_type) &&
44 (old->name_len == new->name_len)) {
45 switch (old->data_type) {
46 case (FSNOTIFY_EVENT_INODE):
47 /* remember, after old was put on the wait_q we aren't
48 * allowed to look at the inode any more, only thing
49 * left to check was if the file_name is the same */
50 if (!old->name_len ||
51 !strcmp(old->file_name, new->file_name))
52 return true;
53 break;
54 case (FSNOTIFY_EVENT_PATH):
55 if ((old->path.mnt == new->path.mnt) &&
56 (old->path.dentry == new->path.dentry))
57 return true;
58 break;
59 case (FSNOTIFY_EVENT_NONE):
60 if (old->mask & FS_Q_OVERFLOW)
61 return true;
62 else if (old->mask & FS_IN_IGNORED)
63 return false;
64 return true;
65 };
66 }
67 return false;
68}
69
70static int inotify_merge(struct list_head *list, struct fsnotify_event *event)
71{
72 struct fsnotify_event_holder *last_holder;
73 struct fsnotify_event *last_event;
74 int ret = 0;
75
76 /* and the list better be locked by something too */
77 spin_lock(&event->lock);
78
79 last_holder = list_entry(list->prev, struct fsnotify_event_holder, event_list);
80 last_event = last_holder->event;
81 if (event_compare(last_event, event))
82 ret = -EEXIST;
83
84 spin_unlock(&event->lock);
85
86 return ret;
87}
88
35static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) 89static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event)
36{ 90{
37 struct fsnotify_mark_entry *entry; 91 struct fsnotify_mark_entry *entry;
@@ -62,7 +116,7 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev
62 fsn_event_priv->group = group; 116 fsn_event_priv->group = group;
63 event_priv->wd = wd; 117 event_priv->wd = wd;
64 118
65 ret = fsnotify_add_notify_event(group, event, fsn_event_priv); 119 ret = fsnotify_add_notify_event(group, event, fsn_event_priv, inotify_merge);
66 if (ret) { 120 if (ret) {
67 inotify_free_event_priv(fsn_event_priv); 121 inotify_free_event_priv(fsn_event_priv);
68 /* EEXIST says we tail matched, EOVERFLOW isn't something 122 /* EEXIST says we tail matched, EOVERFLOW isn't something
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c
index f2b542479e91..cbe16df326f8 100644
--- a/fs/notify/inotify/inotify_user.c
+++ b/fs/notify/inotify/inotify_user.c
@@ -531,7 +531,7 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark_entry *entry,
531 fsn_event_priv->group = group; 531 fsn_event_priv->group = group;
532 event_priv->wd = ientry->wd; 532 event_priv->wd = ientry->wd;
533 533
534 ret = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv); 534 ret = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv, NULL);
535 if (ret) 535 if (ret)
536 inotify_free_event_priv(fsn_event_priv); 536 inotify_free_event_priv(fsn_event_priv);
537 537
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)
104 104
105void fsnotify_destroy_event_holder(struct fsnotify_event_holder *holder) 105void fsnotify_destroy_event_holder(struct fsnotify_event_holder *holder)
106{ 106{
107 kmem_cache_free(fsnotify_event_holder_cachep, holder); 107 if (holder)
108 kmem_cache_free(fsnotify_event_holder_cachep, holder);
108} 109}
109 110
110/* 111/*
@@ -129,53 +130,17 @@ struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struct fsnot
129} 130}
130 131
131/* 132/*
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 */
135static 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 (old->name_len == new->name_len)) {
141 switch (old->data_type) {
142 case (FSNOTIFY_EVENT_INODE):
143 /* remember, after old was put on the wait_q we aren't
144 * allowed to look at the inode any more, only thing
145 * left to check was if the file_name is the same */
146 if (!old->name_len ||
147 !strcmp(old->file_name, new->file_name))
148 return true;
149 break;
150 case (FSNOTIFY_EVENT_PATH):
151 if ((old->path.mnt == new->path.mnt) &&
152 (old->path.dentry == new->path.dentry))
153 return true;
154 break;
155 case (FSNOTIFY_EVENT_NONE):
156 if (old->mask & FS_Q_OVERFLOW)
157 return true;
158 else if (old->mask & FS_IN_IGNORED)
159 return false;
160 return false;
161 };
162 }
163 return false;
164}
165
166/*
167 * Add an event to the group notification queue. The group can later pull this 133 * Add an event to the group notification queue. The group can later pull this
168 * event off the queue to deal with. If the event is successfully added to the 134 * event off the queue to deal with. If the event is successfully added to the
169 * group's notification queue, a reference is taken on event. 135 * group's notification queue, a reference is taken on event.
170 */ 136 */
171int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, 137int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event,
172 struct fsnotify_event_private_data *priv) 138 struct fsnotify_event_private_data *priv,
139 int (*merge)(struct list_head *, struct fsnotify_event *))
173{ 140{
174 struct fsnotify_event_holder *holder = NULL; 141 struct fsnotify_event_holder *holder = NULL;
175 struct list_head *list = &group->notification_list; 142 struct list_head *list = &group->notification_list;
176 struct fsnotify_event_holder *last_holder; 143 int rc = 0;
177 struct fsnotify_event *last_event;
178 int ret = 0;
179 144
180 /* 145 /*
181 * There is one fsnotify_event_holder embedded inside each fsnotify_event. 146 * There is one fsnotify_event_holder embedded inside each fsnotify_event.
@@ -196,11 +161,23 @@ alloc_holder:
196 161
197 if (group->q_len >= group->max_events) { 162 if (group->q_len >= group->max_events) {
198 event = q_overflow_event; 163 event = q_overflow_event;
199 ret = -EOVERFLOW; 164 rc = -EOVERFLOW;
200 /* sorry, no private data on the overflow event */ 165 /* sorry, no private data on the overflow event */
201 priv = NULL; 166 priv = NULL;
202 } 167 }
203 168
169 if (!list_empty(list) && merge) {
170 int ret;
171
172 ret = merge(list, event);
173 if (ret) {
174 mutex_unlock(&group->notification_mutex);
175 if (holder != &event->holder)
176 fsnotify_destroy_event_holder(holder);
177 return ret;
178 }
179 }
180
204 spin_lock(&event->lock); 181 spin_lock(&event->lock);
205 182
206 if (list_empty(&event->holder.event_list)) { 183 if (list_empty(&event->holder.event_list)) {
@@ -215,18 +192,6 @@ alloc_holder:
215 goto alloc_holder; 192 goto alloc_holder;
216 } 193 }
217 194
218 if (!list_empty(list)) {
219 last_holder = list_entry(list->prev, struct fsnotify_event_holder, event_list);
220 last_event = last_holder->event;
221 if (event_compare(last_event, event)) {
222 spin_unlock(&event->lock);
223 mutex_unlock(&group->notification_mutex);
224 if (holder != &event->holder)
225 fsnotify_destroy_event_holder(holder);
226 return -EEXIST;
227 }
228 }
229
230 group->q_len++; 195 group->q_len++;
231 holder->event = event; 196 holder->event = event;
232 197
@@ -238,7 +203,7 @@ alloc_holder:
238 mutex_unlock(&group->notification_mutex); 203 mutex_unlock(&group->notification_mutex);
239 204
240 wake_up(&group->notification_waitq); 205 wake_up(&group->notification_waitq);
241 return ret; 206 return rc;
242} 207}
243 208
244/* 209/*
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index 0e0c2b76b067..25789d45fad8 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -328,8 +328,10 @@ extern struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struc
328 struct fsnotify_event *event); 328 struct fsnotify_event *event);
329 329
330/* attach the event to the group notification queue */ 330/* attach the event to the group notification queue */
331extern int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, 331extern int fsnotify_add_notify_event(struct fsnotify_group *group,
332 struct fsnotify_event_private_data *priv); 332 struct fsnotify_event *event,
333 struct fsnotify_event_private_data *priv,
334 int (*merge)(struct list_head *, struct fsnotify_event *));
333/* true if the group notification queue is empty */ 335/* true if the group notification queue is empty */
334extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group); 336extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group);
335/* return, but do not dequeue the first event on the notification queue */ 337/* return, but do not dequeue the first event on the notification queue */