aboutsummaryrefslogtreecommitdiffstats
path: root/fs/notify/fsnotify.c
diff options
context:
space:
mode:
authorEric Paris <eparis@redhat.com>2010-07-28 10:18:39 -0400
committerEric Paris <eparis@redhat.com>2010-07-28 10:18:54 -0400
commit613a807fe7c793ceb7d6f059773527a5a6c84a96 (patch)
treed217dbde37c8c0a145c36dea24e7afe5fe5e5ff0 /fs/notify/fsnotify.c
parent84a5b68e8da1490906c11129756490a556ae2c19 (diff)
fsnotify: walk the inode and vfsmount lists simultaneously
We currently walk the list of marks on an inode followed by the list of marks on the vfsmount. These are in order (by the memory address of the group) so lets walk them both together. Eventually we can pass both the inode mark and the vfsmount mark to helpers simultaneously. Signed-off-by: Eric Paris <eparis@redhat.com>
Diffstat (limited to 'fs/notify/fsnotify.c')
-rw-r--r--fs/notify/fsnotify.c134
1 files changed, 84 insertions, 50 deletions
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c
index 0bb4aeb8e00f..cdaa51cb698c 100644
--- a/fs/notify/fsnotify.c
+++ b/fs/notify/fsnotify.c
@@ -140,19 +140,31 @@ void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask)
140} 140}
141EXPORT_SYMBOL_GPL(__fsnotify_parent); 141EXPORT_SYMBOL_GPL(__fsnotify_parent);
142 142
143static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, 143static int send_to_group(struct inode *to_tell, struct vfsmount *mnt,
144 struct vfsmount *mnt, struct fsnotify_mark *mark, 144 struct fsnotify_mark *mark,
145 __u32 mask, void *data, int data_is, u32 cookie, 145 __u32 mask, void *data,
146 int data_is, u32 cookie,
146 const unsigned char *file_name, 147 const unsigned char *file_name,
147 struct fsnotify_event **event) 148 struct fsnotify_event **event)
148{ 149{
150 struct fsnotify_group *group = mark->group;
151 __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD);
152
149 pr_debug("%s: group=%p to_tell=%p mnt=%p mark=%p mask=%x data=%p" 153 pr_debug("%s: group=%p to_tell=%p mnt=%p mark=%p mask=%x data=%p"
150 " data_is=%d cookie=%d event=%p\n", __func__, group, to_tell, 154 " data_is=%d cookie=%d event=%p\n", __func__, group, to_tell,
151 mnt, mark, mask, data, data_is, cookie, *event); 155 mnt, mark, mask, data, data_is, cookie, *event);
152 156
157 if ((mask & FS_MODIFY) &&
158 !(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
159 mark->ignored_mask = 0;
160
161 if (!(test_mask & mark->mask & ~mark->ignored_mask))
162 return 0;
163
153 if (group->ops->should_send_event(group, to_tell, mnt, mark, mask, 164 if (group->ops->should_send_event(group, to_tell, mnt, mark, mask,
154 data, data_is) == false) 165 data, data_is) == false)
155 return 0; 166 return 0;
167
156 if (!*event) { 168 if (!*event) {
157 *event = fsnotify_create_event(to_tell, mask, data, 169 *event = fsnotify_create_event(to_tell, mask, data,
158 data_is, file_name, 170 data_is, file_name,
@@ -172,67 +184,89 @@ static int send_to_group(struct fsnotify_group *group, struct inode *to_tell,
172int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, 184int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
173 const unsigned char *file_name, u32 cookie) 185 const unsigned char *file_name, u32 cookie)
174{ 186{
175 struct fsnotify_mark *mark; 187 struct hlist_node *inode_node, *vfsmount_node;
176 struct fsnotify_group *group; 188 struct fsnotify_mark *inode_mark = NULL, *vfsmount_mark = NULL;
189 struct fsnotify_group *inode_group, *vfsmount_group;
177 struct fsnotify_event *event = NULL; 190 struct fsnotify_event *event = NULL;
178 struct hlist_node *node; 191 struct vfsmount *mnt;
179 struct vfsmount *mnt = NULL;
180 int idx, ret = 0; 192 int idx, ret = 0;
193 bool used_inode = false, used_vfsmount = false;
181 /* global tests shouldn't care about events on child only the specific event */ 194 /* global tests shouldn't care about events on child only the specific event */
182 __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); 195 __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD);
183 196
184 if (data_is == FSNOTIFY_EVENT_FILE) 197 if (data_is == FSNOTIFY_EVENT_FILE)
185 mnt = ((struct file *)data)->f_path.mnt; 198 mnt = ((struct file *)data)->f_path.mnt;
199 else
200 mnt = NULL;
201
202 /*
203 * if this is a modify event we may need to clear the ignored masks
204 * otherwise return if neither the inode nor the vfsmount care about
205 * this type of event.
206 */
207 if (!(mask & FS_MODIFY) &&
208 !(test_mask & to_tell->i_fsnotify_mask) &&
209 !(mnt && test_mask & mnt->mnt_fsnotify_mask))
210 return 0;
186 211
187 idx = srcu_read_lock(&fsnotify_mark_srcu); 212 idx = srcu_read_lock(&fsnotify_mark_srcu);
188 213
189 if ((test_mask & to_tell->i_fsnotify_mask) || (mask & FS_MODIFY)) { 214 if ((mask & FS_MODIFY) ||
190 hlist_for_each_entry_rcu(mark, node, &to_tell->i_fsnotify_marks, i.i_list) { 215 (test_mask & to_tell->i_fsnotify_mask))
191 216 inode_node = to_tell->i_fsnotify_marks.first;
192 pr_debug("%s: inode_loop: mark=%p mark->mask=%x mark->ignored_mask=%x\n", 217 else
193 __func__, mark, mark->mask, mark->ignored_mask); 218 inode_node = NULL;
194 219
195 if ((mask & FS_MODIFY) && 220 if (mnt) {
196 !(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) 221 if ((mask & FS_MODIFY) ||
197 mark->ignored_mask = 0; 222 (test_mask & mnt->mnt_fsnotify_mask))
198 223 vfsmount_node = mnt->mnt_fsnotify_marks.first;
199 if (test_mask & mark->mask & ~mark->ignored_mask) { 224 else
200 group = mark->group; 225 vfsmount_node = NULL;
201 if (!group) 226 } else {
202 continue; 227 mnt = NULL;
203 ret = send_to_group(group, to_tell, NULL, mark, mask, 228 vfsmount_node = NULL;
204 data, data_is, cookie, file_name,
205 &event);
206 if (ret)
207 goto out;
208 }
209 }
210 } 229 }
211 230
212 if (mnt && ((test_mask & mnt->mnt_fsnotify_mask) || 231 while (inode_node || vfsmount_node) {
213 (mask & FS_MODIFY))) { 232 if (inode_node) {
214 hlist_for_each_entry_rcu(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { 233 inode_mark = hlist_entry(srcu_dereference(inode_node, &fsnotify_mark_srcu),
215 234 struct fsnotify_mark, i.i_list);
216 pr_debug("%s: mnt_loop: mark=%p mark->mask=%x mark->ignored_mask=%x\n", 235 inode_group = inode_mark->group;
217 __func__, mark, mark->mask, mark->ignored_mask); 236 } else
218 237 inode_group = (void *)-1;
219 if ((mask & FS_MODIFY) && 238
220 !(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) 239 if (vfsmount_node) {
221 mark->ignored_mask = 0; 240 vfsmount_mark = hlist_entry(srcu_dereference(vfsmount_node, &fsnotify_mark_srcu),
222 241 struct fsnotify_mark, m.m_list);
223 if (test_mask & mark->mask & ~mark->ignored_mask) { 242 vfsmount_group = vfsmount_mark->group;
224 group = mark->group; 243 } else
225 if (!group) 244 vfsmount_group = (void *)-1;
226 continue; 245
227 ret = send_to_group(group, to_tell, mnt, mark, mask, 246 if (inode_group < vfsmount_group) {
228 data, data_is, cookie, file_name, 247 /* handle inode */
229 &event); 248 send_to_group(to_tell, NULL, inode_mark, mask, data,
230 if (ret) 249 data_is, cookie, file_name, &event);
231 goto out; 250 used_inode = true;
232 } 251 } else if (vfsmount_group < inode_group) {
252 send_to_group(to_tell, mnt, vfsmount_mark, mask, data,
253 data_is, cookie, file_name, &event);
254 used_vfsmount = true;
255 } else {
256 send_to_group(to_tell, mnt, vfsmount_mark, mask, data,
257 data_is, cookie, file_name, &event);
258 used_vfsmount = true;
259 send_to_group(to_tell, NULL, inode_mark, mask, data,
260 data_is, cookie, file_name, &event);
261 used_inode = true;
233 } 262 }
263
264 if (used_inode)
265 inode_node = inode_node->next;
266 if (used_vfsmount)
267 vfsmount_node = vfsmount_node->next;
234 } 268 }
235out: 269
236 srcu_read_unlock(&fsnotify_mark_srcu, idx); 270 srcu_read_unlock(&fsnotify_mark_srcu, idx);
237 /* 271 /*
238 * fsnotify_create_event() took a reference so the event can't be cleaned 272 * fsnotify_create_event() took a reference so the event can't be cleaned