aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorEric Paris <eparis@redhat.com>2009-12-17 21:24:23 -0500
committerEric Paris <eparis@redhat.com>2010-07-28 09:58:52 -0400
commit7131485a93679ff9a543b74df280cfd119eb03ca (patch)
treed5d635cb37f77ef7ff8942e15de4c45f3fded940 /fs
parent4ca763523e040dc61191d4866a82981a5d30a4e9 (diff)
fsnotify: mount point listeners list and global mask
currently all of the notification systems implemented select which inodes they care about and receive messages only about those inodes (or the children of those inodes.) This patch begins to flesh out fsnotify support for the concept of listeners that want to hear notification for an inode accessed below a given monut point. This patch implements a second list of fsnotify groups to hold these types of groups and a second global mask to hold the events of interest for this type of group. The reason we want a second group list and mask is because the inode based notification should_send_event support which makes each group look for a mark on the given inode. With one nfsmount listener that means that every group would have to take the inode->i_lock, look for their mark, not find one, and return for every operation. By seperating vfsmount from inode listeners only when there is a inode listener will the inode groups have to look for their mark and take the inode lock. vfsmount listeners will have to grab the lock and look for a mark but there should be fewer of them, and one vfsmount listener won't cause the i_lock to be grabbed and released for every fsnotify group on every io operation. Signed-off-by: Eric Paris <eparis@redhat.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/notify/fsnotify.c92
-rw-r--r--fs/notify/fsnotify.h6
-rw-r--r--fs/notify/group.c33
-rw-r--r--fs/notify/inode_mark.c7
4 files changed, 112 insertions, 26 deletions
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c
index 23b5cfbeed50..a61aaa710825 100644
--- a/fs/notify/fsnotify.c
+++ b/fs/notify/fsnotify.c
@@ -21,6 +21,7 @@
21#include <linux/gfp.h> 21#include <linux/gfp.h>
22#include <linux/init.h> 22#include <linux/init.h>
23#include <linux/module.h> 23#include <linux/module.h>
24#include <linux/mount.h>
24#include <linux/srcu.h> 25#include <linux/srcu.h>
25 26
26#include <linux/fsnotify_backend.h> 27#include <linux/fsnotify_backend.h>
@@ -134,6 +135,45 @@ void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask)
134} 135}
135EXPORT_SYMBOL_GPL(__fsnotify_parent); 136EXPORT_SYMBOL_GPL(__fsnotify_parent);
136 137
138static void send_to_group(__u32 mask,
139 struct fsnotify_group *group,
140 void *data, int data_is, const char *file_name,
141 u32 cookie, struct fsnotify_event **event,
142 struct inode *to_tell)
143{
144 if (!group->ops->should_send_event(group, to_tell, mask,
145 data, data_is))
146 return;
147 if (!*event) {
148 *event = fsnotify_create_event(to_tell, mask, data,
149 data_is, file_name,
150 cookie, GFP_KERNEL);
151 /*
152 * shit, we OOM'd and now we can't tell, maybe
153 * someday someone else will want to do something
154 * here
155 */
156 if (!*event)
157 return;
158 }
159 group->ops->handle_event(group, *event);
160}
161
162static bool needed_by_vfsmount(__u32 test_mask, void *data, int data_is)
163{
164 struct path *path;
165
166 if (data_is == FSNOTIFY_EVENT_PATH)
167 path = (struct path *)data;
168 else if (data_is == FSNOTIFY_EVENT_FILE)
169 path = &((struct file *)data)->f_path;
170 else
171 return false;
172
173 /* hook in this when mnt->mnt_fsnotify_mask is defined */
174 /* return (test_mask & path->mnt->mnt_fsnotify_mask); */
175 return false;
176}
137/* 177/*
138 * This is the main call to fsnotify. The VFS calls into hook specific functions 178 * This is the main call to fsnotify. The VFS calls into hook specific functions
139 * in linux/fsnotify.h. Those functions then in turn call here. Here will call 179 * in linux/fsnotify.h. Those functions then in turn call here. Here will call
@@ -148,38 +188,46 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const
148 /* global tests shouldn't care about events on child only the specific event */ 188 /* global tests shouldn't care about events on child only the specific event */
149 __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); 189 __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD);
150 190
151 if (list_empty(&fsnotify_inode_groups)) 191 /* if no fsnotify listeners, nothing to do */
152 return; 192 if (list_empty(&fsnotify_inode_groups) &&
193 list_empty(&fsnotify_vfsmount_groups))
194 return;
195
196 /* if none of the directed listeners or vfsmount listeners care */
197 if (!(test_mask & fsnotify_inode_mask) &&
198 !(test_mask & fsnotify_vfsmount_mask))
199 return;
200
201 /* if this inode's directed listeners don't care and nothing on the vfsmount
202 * listeners list cares, nothing to do */
203 if (!(test_mask & to_tell->i_fsnotify_mask) &&
204 !needed_by_vfsmount(test_mask, data, data_is))
205 return;
153 206
154 if (!(test_mask & fsnotify_inode_mask))
155 return;
156
157 if (!(test_mask & to_tell->i_fsnotify_mask))
158 return;
159 /* 207 /*
160 * SRCU!! the groups list is very very much read only and the path is 208 * SRCU!! the groups list is very very much read only and the path is
161 * very hot. The VAST majority of events are not going to need to do 209 * very hot. The VAST majority of events are not going to need to do
162 * anything other than walk the list so it's crazy to pre-allocate. 210 * anything other than walk the list so it's crazy to pre-allocate.
163 */ 211 */
164 idx = srcu_read_lock(&fsnotify_grp_srcu); 212 idx = srcu_read_lock(&fsnotify_grp_srcu);
165 list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) { 213
166 if (test_mask & group->mask) { 214 if (test_mask & to_tell->i_fsnotify_mask) {
167 if (!group->ops->should_send_event(group, to_tell, mask, 215 list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) {
168 data, data_is)) 216 if (test_mask & group->mask) {
169 continue; 217 send_to_group(mask, group, data, data_is,
170 if (!event) { 218 file_name, cookie, &event, to_tell);
171 event = fsnotify_create_event(to_tell, mask, data,
172 data_is, file_name, cookie,
173 GFP_KERNEL);
174 /* shit, we OOM'd and now we can't tell, maybe
175 * someday someone else will want to do something
176 * here */
177 if (!event)
178 break;
179 } 219 }
180 group->ops->handle_event(group, event);
181 } 220 }
182 } 221 }
222 if (needed_by_vfsmount(test_mask, data, data_is)) {
223 list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) {
224 if (test_mask & group->mask) {
225 send_to_group(mask, group, data, data_is,
226 file_name, cookie, &event, to_tell);
227 }
228 }
229 }
230
183 srcu_read_unlock(&fsnotify_grp_srcu, idx); 231 srcu_read_unlock(&fsnotify_grp_srcu, idx);
184 /* 232 /*
185 * fsnotify_create_event() took a reference so the event can't be cleaned 233 * fsnotify_create_event() took a reference so the event can't be cleaned
diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h
index 5bd22412017f..2ba59158969f 100644
--- a/fs/notify/fsnotify.h
+++ b/fs/notify/fsnotify.h
@@ -10,14 +10,20 @@
10extern struct srcu_struct fsnotify_grp_srcu; 10extern struct srcu_struct fsnotify_grp_srcu;
11/* all groups which receive inode fsnotify events */ 11/* all groups which receive inode fsnotify events */
12extern struct list_head fsnotify_inode_groups; 12extern struct list_head fsnotify_inode_groups;
13/* all groups which receive vfsmount fsnotify events */
14extern struct list_head fsnotify_vfsmount_groups;
13/* all bitwise OR of all event types (FS_*) for all fsnotify_inode_groups */ 15/* all bitwise OR of all event types (FS_*) for all fsnotify_inode_groups */
14extern __u32 fsnotify_inode_mask; 16extern __u32 fsnotify_inode_mask;
17/* all bitwise OR of all event types (FS_*) for all fsnotify_vfsmount_groups */
18extern __u32 fsnotify_vfsmount_mask;
15 19
16/* destroy all events sitting in this groups notification queue */ 20/* destroy all events sitting in this groups notification queue */
17extern void fsnotify_flush_notify(struct fsnotify_group *group); 21extern void fsnotify_flush_notify(struct fsnotify_group *group);
18 22
19/* add a group to the inode group list */ 23/* add a group to the inode group list */
20extern void fsnotify_add_inode_group(struct fsnotify_group *group); 24extern void fsnotify_add_inode_group(struct fsnotify_group *group);
25/* add a group to the vfsmount group list */
26extern void fsnotify_add_vfsmount_group(struct fsnotify_group *group);
21/* final kfree of a group */ 27/* final kfree of a group */
22extern void fsnotify_final_destroy_group(struct fsnotify_group *group); 28extern void fsnotify_final_destroy_group(struct fsnotify_group *group);
23 29
diff --git a/fs/notify/group.c b/fs/notify/group.c
index 34fccbd2809c..aa4654fe6ec2 100644
--- a/fs/notify/group.c
+++ b/fs/notify/group.c
@@ -32,10 +32,14 @@
32static DEFINE_MUTEX(fsnotify_grp_mutex); 32static DEFINE_MUTEX(fsnotify_grp_mutex);
33/* protects reads while running the fsnotify_groups list */ 33/* protects reads while running the fsnotify_groups list */
34struct srcu_struct fsnotify_grp_srcu; 34struct srcu_struct fsnotify_grp_srcu;
35/* all groups registered to receive filesystem notifications */ 35/* all groups registered to receive inode filesystem notifications */
36LIST_HEAD(fsnotify_inode_groups); 36LIST_HEAD(fsnotify_inode_groups);
37/* all groups registered to receive mount point filesystem notifications */
38LIST_HEAD(fsnotify_vfsmount_groups);
37/* bitwise OR of all events (FS_*) interesting to some group on this system */ 39/* bitwise OR of all events (FS_*) interesting to some group on this system */
38__u32 fsnotify_inode_mask; 40__u32 fsnotify_inode_mask;
41/* bitwise OR of all events (FS_*) interesting to some group on this system */
42__u32 fsnotify_vfsmount_mask;
39 43
40/* 44/*
41 * When a new group registers or changes it's set of interesting events 45 * When a new group registers or changes it's set of interesting events
@@ -44,14 +48,20 @@ __u32 fsnotify_inode_mask;
44void fsnotify_recalc_global_mask(void) 48void fsnotify_recalc_global_mask(void)
45{ 49{
46 struct fsnotify_group *group; 50 struct fsnotify_group *group;
47 __u32 mask = 0; 51 __u32 inode_mask = 0;
52 __u32 vfsmount_mask = 0;
48 int idx; 53 int idx;
49 54
50 idx = srcu_read_lock(&fsnotify_grp_srcu); 55 idx = srcu_read_lock(&fsnotify_grp_srcu);
51 list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) 56 list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list)
52 mask |= group->mask; 57 inode_mask |= group->mask;
58 list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list)
59 vfsmount_mask |= group->mask;
60
53 srcu_read_unlock(&fsnotify_grp_srcu, idx); 61 srcu_read_unlock(&fsnotify_grp_srcu, idx);
54 fsnotify_inode_mask = mask; 62
63 fsnotify_inode_mask = inode_mask;
64 fsnotify_vfsmount_mask = vfsmount_mask;
55} 65}
56 66
57/* 67/*
@@ -77,6 +87,17 @@ void fsnotify_recalc_group_mask(struct fsnotify_group *group)
77 fsnotify_recalc_global_mask(); 87 fsnotify_recalc_global_mask();
78} 88}
79 89
90void fsnotify_add_vfsmount_group(struct fsnotify_group *group)
91{
92 mutex_lock(&fsnotify_grp_mutex);
93
94 if (!group->on_vfsmount_group_list)
95 list_add_tail_rcu(&group->vfsmount_group_list, &fsnotify_vfsmount_groups);
96 group->on_vfsmount_group_list = 1;
97
98 mutex_unlock(&fsnotify_grp_mutex);
99}
100
80void fsnotify_add_inode_group(struct fsnotify_group *group) 101void fsnotify_add_inode_group(struct fsnotify_group *group)
81{ 102{
82 mutex_lock(&fsnotify_grp_mutex); 103 mutex_lock(&fsnotify_grp_mutex);
@@ -132,6 +153,9 @@ static void __fsnotify_evict_group(struct fsnotify_group *group)
132 if (group->on_inode_group_list) 153 if (group->on_inode_group_list)
133 list_del_rcu(&group->inode_group_list); 154 list_del_rcu(&group->inode_group_list);
134 group->on_inode_group_list = 0; 155 group->on_inode_group_list = 0;
156 if (group->on_vfsmount_group_list)
157 list_del_rcu(&group->vfsmount_group_list);
158 group->on_vfsmount_group_list = 0;
135} 159}
136 160
137/* 161/*
@@ -197,6 +221,7 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops)
197 group->max_events = UINT_MAX; 221 group->max_events = UINT_MAX;
198 222
199 INIT_LIST_HEAD(&group->inode_group_list); 223 INIT_LIST_HEAD(&group->inode_group_list);
224 INIT_LIST_HEAD(&group->vfsmount_group_list);
200 225
201 spin_lock_init(&group->mark_lock); 226 spin_lock_init(&group->mark_lock);
202 INIT_LIST_HEAD(&group->mark_entries); 227 INIT_LIST_HEAD(&group->mark_entries);
diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c
index a3230c485531..beffebb64627 100644
--- a/fs/notify/inode_mark.c
+++ b/fs/notify/inode_mark.c
@@ -328,6 +328,13 @@ int fsnotify_add_mark(struct fsnotify_mark_entry *entry,
328 */ 328 */
329 if (unlikely(list_empty(&group->inode_group_list))) 329 if (unlikely(list_empty(&group->inode_group_list)))
330 fsnotify_add_inode_group(group); 330 fsnotify_add_inode_group(group);
331 /*
332 * XXX This is where we could also do the fsnotify_add_vfsmount_group
333 * if we are setting and vfsmount mark....
334
335 if (unlikely(list_empty(&group->vfsmount_group_list)))
336 fsnotify_add_vfsmount_group(group);
337 */
331 338
332 /* 339 /*
333 * LOCKING ORDER!!!! 340 * LOCKING ORDER!!!!