aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/notify/fsnotify.h9
-rw-r--r--fs/notify/group.c9
-rw-r--r--fs/notify/notification.c230
-rw-r--r--include/linux/fsnotify_backend.h37
4 files changed, 278 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}
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index 9ea800e840f1..15f8f82a5c57 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -119,6 +119,13 @@ struct fsnotify_group {
119 119
120 const struct fsnotify_ops *ops; /* how this group handles things */ 120 const struct fsnotify_ops *ops; /* how this group handles things */
121 121
122 /* needed to send notification to userspace */
123 struct mutex notification_mutex; /* protect the notification_list */
124 struct list_head notification_list; /* list of event_holder this group needs to send to userspace */
125 wait_queue_head_t notification_waitq; /* read() on the notification file blocks on this waitq */
126 unsigned int q_len; /* events on the queue */
127 unsigned int max_events; /* maximum events allowed on the list */
128
122 /* stores all fastapth entries assoc with this group so they can be cleaned on unregister */ 129 /* stores all fastapth entries assoc with this group so they can be cleaned on unregister */
123 spinlock_t mark_lock; /* protect mark_entries list */ 130 spinlock_t mark_lock; /* protect mark_entries list */
124 atomic_t num_marks; /* 1 for each mark entry and 1 for not being 131 atomic_t num_marks; /* 1 for each mark entry and 1 for not being
@@ -136,11 +143,32 @@ struct fsnotify_group {
136}; 143};
137 144
138/* 145/*
146 * A single event can be queued in multiple group->notification_lists.
147 *
148 * each group->notification_list will point to an event_holder which in turns points
149 * to the actual event that needs to be sent to userspace.
150 *
151 * Seemed cheaper to create a refcnt'd event and a small holder for every group
152 * than create a different event for every group
153 *
154 */
155struct fsnotify_event_holder {
156 struct fsnotify_event *event;
157 struct list_head event_list;
158};
159
160/*
139 * all of the information about the original object we want to now send to 161 * all of the information about the original object we want to now send to
140 * a group. If you want to carry more info from the accessing task to the 162 * a group. If you want to carry more info from the accessing task to the
141 * listener this structure is where you need to be adding fields. 163 * listener this structure is where you need to be adding fields.
142 */ 164 */
143struct fsnotify_event { 165struct fsnotify_event {
166 /*
167 * If we create an event we are also likely going to need a holder
168 * to link to a group. So embed one holder in the event. Means only
169 * one allocation for the common case where we only have one group
170 */
171 struct fsnotify_event_holder holder;
144 spinlock_t lock; /* protection for the associated event_holder and private_list */ 172 spinlock_t lock; /* protection for the associated event_holder and private_list */
145 /* to_tell may ONLY be dereferenced during handle_event(). */ 173 /* to_tell may ONLY be dereferenced during handle_event(). */
146 struct inode *to_tell; /* either the inode the event happened to or its parent */ 174 struct inode *to_tell; /* either the inode the event happened to or its parent */
@@ -264,6 +292,15 @@ extern void fsnotify_put_event(struct fsnotify_event *event);
264extern struct fsnotify_event_private_data *fsnotify_get_priv_from_event(struct fsnotify_group *group, 292extern struct fsnotify_event_private_data *fsnotify_get_priv_from_event(struct fsnotify_group *group,
265 struct fsnotify_event *event); 293 struct fsnotify_event *event);
266 294
295/* attach the event to the group notification queue */
296extern int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event);
297/* true if the group notification queue is empty */
298extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group);
299/* return, but do not dequeue the first event on the notification queue */
300extern struct fsnotify_event *fsnotify_peek_notify_event(struct fsnotify_group *group);
301/* reutnr AND dequeue the first event on the notification queue */
302extern struct fsnotify_event *fsnotify_remove_notify_event(struct fsnotify_group *group);
303
267/* functions used to manipulate the marks attached to inodes */ 304/* functions used to manipulate the marks attached to inodes */
268 305
269/* run all marks associated with an inode and update inode->i_fsnotify_mask */ 306/* run all marks associated with an inode and update inode->i_fsnotify_mask */