aboutsummaryrefslogtreecommitdiffstats
path: root/fs/notify
diff options
context:
space:
mode:
authorEric Paris <eparis@redhat.com>2009-05-21 17:01:37 -0400
committerEric Paris <eparis@redhat.com>2009-06-11 14:57:53 -0400
commita2d8bc6cb4a3024661baf877242f123787d0c054 (patch)
tree4ff3f93877a8992d5383c14fb6012ab9b1954660 /fs/notify
parent3c5119c05d624f95f4967d16b38c9624b816bdb9 (diff)
fsnotify: generic notification queue and waitq
inotify needs to do asyc notification in which event information is stored on a queue until the listener is ready to receive it. This patch implements a generic notification queue for inotify (and later fanotify) to store events to be sent at a later time. Signed-off-by: Eric Paris <eparis@redhat.com> Acked-by: Al Viro <viro@zeniv.linux.org.uk> Cc: Christoph Hellwig <hch@lst.de>
Diffstat (limited to 'fs/notify')
-rw-r--r--fs/notify/fsnotify.h9
-rw-r--r--fs/notify/group.c9
-rw-r--r--fs/notify/notification.c230
3 files changed, 241 insertions, 7 deletions
diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h
index 83b8ec0a8ec2..4dc240824b2d 100644
--- a/fs/notify/fsnotify.h
+++ b/fs/notify/fsnotify.h
@@ -13,8 +13,12 @@ extern struct list_head fsnotify_groups;
13/* all bitwise OR of all event types (FS_*) for all fsnotify_groups */ 13/* all bitwise OR of all event types (FS_*) for all fsnotify_groups */
14extern __u32 fsnotify_mask; 14extern __u32 fsnotify_mask;
15 15
16/* destroy all events sitting in this groups notification queue */
17extern void fsnotify_flush_notify(struct fsnotify_group *group);
18
16/* final kfree of a group */ 19/* final kfree of a group */
17extern void fsnotify_final_destroy_group(struct fsnotify_group *group); 20extern void fsnotify_final_destroy_group(struct fsnotify_group *group);
21
18/* run the list of all marks associated with inode and flag them to be freed */ 22/* run the list of all marks associated with inode and flag them to be freed */
19extern void fsnotify_clear_marks_by_inode(struct inode *inode); 23extern void fsnotify_clear_marks_by_inode(struct inode *inode);
20/* 24/*
@@ -22,4 +26,9 @@ extern void fsnotify_clear_marks_by_inode(struct inode *inode);
22 * about events that happen to its children. 26 * about events that happen to its children.
23 */ 27 */
24extern void __fsnotify_update_child_dentry_flags(struct inode *inode); 28extern void __fsnotify_update_child_dentry_flags(struct inode *inode);
29
30/* allocate and destroy and event holder to attach events to notification/access queues */
31extern struct fsnotify_event_holder *fsnotify_alloc_event_holder(void);
32extern void fsnotify_destroy_event_holder(struct fsnotify_event_holder *holder);
33
25#endif /* __FS_NOTIFY_FSNOTIFY_H_ */ 34#endif /* __FS_NOTIFY_FSNOTIFY_H_ */
diff --git a/fs/notify/group.c b/fs/notify/group.c
index a29d2fa67927..0e1677144bc5 100644
--- a/fs/notify/group.c
+++ b/fs/notify/group.c
@@ -91,6 +91,9 @@ static void fsnotify_get_group(struct fsnotify_group *group)
91 */ 91 */
92void fsnotify_final_destroy_group(struct fsnotify_group *group) 92void fsnotify_final_destroy_group(struct fsnotify_group *group)
93{ 93{
94 /* clear the notification queue of all events */
95 fsnotify_flush_notify(group);
96
94 if (group->ops->free_group_priv) 97 if (group->ops->free_group_priv)
95 group->ops->free_group_priv(group); 98 group->ops->free_group_priv(group);
96 99
@@ -214,6 +217,12 @@ struct fsnotify_group *fsnotify_obtain_group(unsigned int group_num, __u32 mask,
214 group->group_num = group_num; 217 group->group_num = group_num;
215 group->mask = mask; 218 group->mask = mask;
216 219
220 mutex_init(&group->notification_mutex);
221 INIT_LIST_HEAD(&group->notification_list);
222 init_waitqueue_head(&group->notification_waitq);
223 group->q_len = 0;
224 group->max_events = UINT_MAX;
225
217 spin_lock_init(&group->mark_lock); 226 spin_lock_init(&group->mark_lock);
218 atomic_set(&group->num_marks, 0); 227 atomic_set(&group->num_marks, 0);
219 INIT_LIST_HEAD(&group->mark_entries); 228 INIT_LIST_HEAD(&group->mark_entries);
diff --git a/fs/notify/notification.c b/fs/notify/notification.c
index b8e9a87f8f58..dddecc74e63d 100644
--- a/fs/notify/notification.c
+++ b/fs/notify/notification.c
@@ -16,6 +16,21 @@
16 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 16 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
17 */ 17 */
18 18
19/*
20 * Basic idea behind the notification queue: An fsnotify group (like inotify)
21 * sends the userspace notification about events asyncronously some time after
22 * the event happened. When inotify gets an event it will need to add that
23 * event to the group notify queue. Since a single event might need to be on
24 * multiple group's notification queues we can't add the event directly to each
25 * queue and instead add a small "event_holder" to each queue. This event_holder
26 * has a pointer back to the original event. Since the majority of events are
27 * going to end up on one, and only one, notification queue we embed one
28 * event_holder into each event. This means we have a single allocation instead
29 * of always needing two. If the embedded event_holder is already in use by
30 * another group a new event_holder (from fsnotify_event_holder_cachep) will be
31 * allocated and used.
32 */
33
19#include <linux/fs.h> 34#include <linux/fs.h>
20#include <linux/init.h> 35#include <linux/init.h>
21#include <linux/kernel.h> 36#include <linux/kernel.h>
@@ -33,6 +48,21 @@
33#include "fsnotify.h" 48#include "fsnotify.h"
34 49
35static struct kmem_cache *fsnotify_event_cachep; 50static struct kmem_cache *fsnotify_event_cachep;
51static struct kmem_cache *fsnotify_event_holder_cachep;
52/*
53 * This is a magic event we send when the q is too full. Since it doesn't
54 * hold real event information we just keep one system wide and use it any time
55 * it is needed. It's refcnt is set 1 at kernel init time and will never
56 * get set to 0 so it will never get 'freed'
57 */
58static struct fsnotify_event q_overflow_event;
59
60/* return true if the notify queue is empty, false otherwise */
61bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group)
62{
63 BUG_ON(!mutex_is_locked(&group->notification_mutex));
64 return list_empty(&group->notification_list) ? true : false;
65}
36 66
37void fsnotify_get_event(struct fsnotify_event *event) 67void fsnotify_get_event(struct fsnotify_event *event)
38{ 68{
@@ -52,19 +82,176 @@ void fsnotify_put_event(struct fsnotify_event *event)
52 } 82 }
53} 83}
54 84
85struct fsnotify_event_holder *fsnotify_alloc_event_holder(void)
86{
87 return kmem_cache_alloc(fsnotify_event_holder_cachep, GFP_KERNEL);
88}
89
90void fsnotify_destroy_event_holder(struct fsnotify_event_holder *holder)
91{
92 kmem_cache_free(fsnotify_event_holder_cachep, holder);
93}
94
95/*
96 * check if 2 events contain the same information.
97 */
98static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new)
99{
100 if ((old->mask == new->mask) &&
101 (old->to_tell == new->to_tell) &&
102 (old->data_type == new->data_type)) {
103 switch (old->data_type) {
104 case (FSNOTIFY_EVENT_INODE):
105 if (old->inode == new->inode)
106 return true;
107 break;
108 case (FSNOTIFY_EVENT_PATH):
109 if ((old->path.mnt == new->path.mnt) &&
110 (old->path.dentry == new->path.dentry))
111 return true;
112 case (FSNOTIFY_EVENT_NONE):
113 return true;
114 };
115 }
116 return false;
117}
118
55/* 119/*
56 * Allocate a new event which will be sent to each group's handle_event function 120 * Add an event to the group notification queue. The group can later pull this
57 * if the group was interested in this particular event. 121 * event off the queue to deal with. If the event is successfully added to the
122 * group's notification queue, a reference is taken on event.
58 */ 123 */
59struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, 124int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event)
60 void *data, int data_type) 125{
126 struct fsnotify_event_holder *holder = NULL;
127 struct list_head *list = &group->notification_list;
128 struct fsnotify_event_holder *last_holder;
129 struct fsnotify_event *last_event;
130
131 /*
132 * There is one fsnotify_event_holder embedded inside each fsnotify_event.
133 * Check if we expect to be able to use that holder. If not alloc a new
134 * holder.
135 * For the overflow event it's possible that something will use the in
136 * event holder before we get the lock so we may need to jump back and
137 * alloc a new holder, this can't happen for most events...
138 */
139 if (!list_empty(&event->holder.event_list)) {
140alloc_holder:
141 holder = fsnotify_alloc_event_holder();
142 if (!holder)
143 return -ENOMEM;
144 }
145
146 mutex_lock(&group->notification_mutex);
147
148 if (group->q_len >= group->max_events)
149 event = &q_overflow_event;
150
151 spin_lock(&event->lock);
152
153 if (list_empty(&event->holder.event_list)) {
154 if (unlikely(holder))
155 fsnotify_destroy_event_holder(holder);
156 holder = &event->holder;
157 } else if (unlikely(!holder)) {
158 /* between the time we checked above and got the lock the in
159 * event holder was used, go back and get a new one */
160 spin_unlock(&event->lock);
161 mutex_unlock(&group->notification_mutex);
162 goto alloc_holder;
163 }
164
165 if (!list_empty(list)) {
166 last_holder = list_entry(list->prev, struct fsnotify_event_holder, event_list);
167 last_event = last_holder->event;
168 if (event_compare(last_event, event)) {
169 spin_unlock(&event->lock);
170 mutex_unlock(&group->notification_mutex);
171 if (holder != &event->holder)
172 fsnotify_destroy_event_holder(holder);
173 return 0;
174 }
175 }
176
177 group->q_len++;
178 holder->event = event;
179
180 fsnotify_get_event(event);
181 list_add_tail(&holder->event_list, list);
182 spin_unlock(&event->lock);
183 mutex_unlock(&group->notification_mutex);
184
185 wake_up(&group->notification_waitq);
186 return 0;
187}
188
189/*
190 * Remove and return the first event from the notification list. There is a
191 * reference held on this event since it was on the list. It is the responsibility
192 * of the caller to drop this reference.
193 */
194struct fsnotify_event *fsnotify_remove_notify_event(struct fsnotify_group *group)
61{ 195{
62 struct fsnotify_event *event; 196 struct fsnotify_event *event;
197 struct fsnotify_event_holder *holder;
63 198
64 event = kmem_cache_alloc(fsnotify_event_cachep, GFP_KERNEL); 199 BUG_ON(!mutex_is_locked(&group->notification_mutex));
65 if (!event)
66 return NULL;
67 200
201 holder = list_first_entry(&group->notification_list, struct fsnotify_event_holder, event_list);
202
203 event = holder->event;
204
205 spin_lock(&event->lock);
206 holder->event = NULL;
207 list_del_init(&holder->event_list);
208 spin_unlock(&event->lock);
209
210 /* event == holder means we are referenced through the in event holder */
211 if (holder != &event->holder)
212 fsnotify_destroy_event_holder(holder);
213
214 group->q_len--;
215
216 return event;
217}
218
219/*
220 * This will not remove the event, that must be done with fsnotify_remove_notify_event()
221 */
222struct fsnotify_event *fsnotify_peek_notify_event(struct fsnotify_group *group)
223{
224 struct fsnotify_event *event;
225 struct fsnotify_event_holder *holder;
226
227 BUG_ON(!mutex_is_locked(&group->notification_mutex));
228
229 holder = list_first_entry(&group->notification_list, struct fsnotify_event_holder, event_list);
230 event = holder->event;
231
232 return event;
233}
234
235/*
236 * Called when a group is being torn down to clean up any outstanding
237 * event notifications.
238 */
239void fsnotify_flush_notify(struct fsnotify_group *group)
240{
241 struct fsnotify_event *event;
242
243 mutex_lock(&group->notification_mutex);
244 while (!fsnotify_notify_queue_is_empty(group)) {
245 event = fsnotify_remove_notify_event(group);
246 fsnotify_put_event(event); /* matches fsnotify_add_notify_event */
247 }
248 mutex_unlock(&group->notification_mutex);
249}
250
251static void initialize_event(struct fsnotify_event *event)
252{
253 event->holder.event = NULL;
254 INIT_LIST_HEAD(&event->holder.event_list);
68 atomic_set(&event->refcnt, 1); 255 atomic_set(&event->refcnt, 1);
69 256
70 spin_lock_init(&event->lock); 257 spin_lock_init(&event->lock);
@@ -72,7 +259,32 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask,
72 event->path.dentry = NULL; 259 event->path.dentry = NULL;
73 event->path.mnt = NULL; 260 event->path.mnt = NULL;
74 event->inode = NULL; 261 event->inode = NULL;
262 event->data_type = FSNOTIFY_EVENT_NONE;
75 263
264 event->to_tell = NULL;
265}
266
267/*
268 * fsnotify_create_event - Allocate a new event which will be sent to each
269 * group's handle_event function if the group was interested in this
270 * particular event.
271 *
272 * @to_tell the inode which is supposed to receive the event (sometimes a
273 * parent of the inode to which the event happened.
274 * @mask what actually happened.
275 * @data pointer to the object which was actually affected
276 * @data_type flag indication if the data is a file, path, inode, nothing...
277 */
278struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask,
279 void *data, int data_type)
280{
281 struct fsnotify_event *event;
282
283 event = kmem_cache_alloc(fsnotify_event_cachep, GFP_KERNEL);
284 if (!event)
285 return NULL;
286
287 initialize_event(event);
76 event->to_tell = to_tell; 288 event->to_tell = to_tell;
77 289
78 switch (data_type) { 290 switch (data_type) {
@@ -114,6 +326,10 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask,
114__init int fsnotify_notification_init(void) 326__init int fsnotify_notification_init(void)
115{ 327{
116 fsnotify_event_cachep = KMEM_CACHE(fsnotify_event, SLAB_PANIC); 328 fsnotify_event_cachep = KMEM_CACHE(fsnotify_event, SLAB_PANIC);
329 fsnotify_event_holder_cachep = KMEM_CACHE(fsnotify_event_holder, SLAB_PANIC);
330
331 initialize_event(&q_overflow_event);
332 q_overflow_event.mask = FS_Q_OVERFLOW;
117 333
118 return 0; 334 return 0;
119} 335}