aboutsummaryrefslogtreecommitdiffstats
path: root/fs/notify/fsnotify.c
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/notify/fsnotify.c
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/notify/fsnotify.c')
-rw-r--r--fs/notify/fsnotify.c92
1 files changed, 70 insertions, 22 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