aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Paris <eparis@redhat.com>2009-12-17 21:24:21 -0500
committerEric Paris <eparis@redhat.com>2010-07-28 09:58:49 -0400
commit74766bbfa99adf8cb8119df6121851edba21c9d9 (patch)
tree724dbee0d1e7794401e2d3bdd3c17fd2ce3e4635
parent28c60e37f874dcbb93c4afc839ba5e4911c4f4bc (diff)
fsnotify: per group notification queue merge types
inotify only wishes to merge a new event with the last event on the notification fifo. fanotify is willing to merge any events including by means of bitwise OR masks of multiple events together. This patch moves the inotify event merging logic out of the generic fsnotify notification.c and into the inotify code. This allows each use of fsnotify to provide their own merge functionality. Signed-off-by: Eric Paris <eparis@redhat.com>
-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 */