aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Kara <jack@suse.com>2015-09-04 18:43:12 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2015-09-04 19:54:41 -0400
commit4712e722f91457e60723b9cef6265a74290efba9 (patch)
treeb6b4bf48018d23c180cb7407ea1ff58402664e5c
parent925d1132a03e33cb8f29a0057300d023b4f1be23 (diff)
fsnotify: get rid of fsnotify_destroy_mark_locked()
fsnotify_destroy_mark_locked() is subtle to use because it temporarily releases group->mark_mutex. To avoid future problems with this function, split it into two. fsnotify_detach_mark() is the part that needs group->mark_mutex and fsnotify_free_mark() is the part that must be called outside of group->mark_mutex. This way it's much clearer what's going on and we also avoid some pointless acquisitions of group->mark_mutex. Signed-off-by: Jan Kara <jack@suse.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/notify/dnotify/dnotify.c14
-rw-r--r--fs/notify/fanotify/fanotify_user.c8
-rw-r--r--fs/notify/mark.c73
-rw-r--r--include/linux/fsnotify_backend.h7
4 files changed, 61 insertions, 41 deletions
diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c
index 44523f4a6084..6faaf710e563 100644
--- a/fs/notify/dnotify/dnotify.c
+++ b/fs/notify/dnotify/dnotify.c
@@ -154,6 +154,7 @@ void dnotify_flush(struct file *filp, fl_owner_t id)
154 struct dnotify_struct *dn; 154 struct dnotify_struct *dn;
155 struct dnotify_struct **prev; 155 struct dnotify_struct **prev;
156 struct inode *inode; 156 struct inode *inode;
157 bool free = false;
157 158
158 inode = file_inode(filp); 159 inode = file_inode(filp);
159 if (!S_ISDIR(inode->i_mode)) 160 if (!S_ISDIR(inode->i_mode))
@@ -182,11 +183,15 @@ void dnotify_flush(struct file *filp, fl_owner_t id)
182 183
183 /* nothing else could have found us thanks to the dnotify_groups 184 /* nothing else could have found us thanks to the dnotify_groups
184 mark_mutex */ 185 mark_mutex */
185 if (dn_mark->dn == NULL) 186 if (dn_mark->dn == NULL) {
186 fsnotify_destroy_mark_locked(fsn_mark, dnotify_group); 187 fsnotify_detach_mark(fsn_mark);
188 free = true;
189 }
187 190
188 mutex_unlock(&dnotify_group->mark_mutex); 191 mutex_unlock(&dnotify_group->mark_mutex);
189 192
193 if (free)
194 fsnotify_free_mark(fsn_mark);
190 fsnotify_put_mark(fsn_mark); 195 fsnotify_put_mark(fsn_mark);
191} 196}
192 197
@@ -362,9 +367,10 @@ out:
362 spin_unlock(&fsn_mark->lock); 367 spin_unlock(&fsn_mark->lock);
363 368
364 if (destroy) 369 if (destroy)
365 fsnotify_destroy_mark_locked(fsn_mark, dnotify_group); 370 fsnotify_detach_mark(fsn_mark);
366
367 mutex_unlock(&dnotify_group->mark_mutex); 371 mutex_unlock(&dnotify_group->mark_mutex);
372 if (destroy)
373 fsnotify_free_mark(fsn_mark);
368 fsnotify_put_mark(fsn_mark); 374 fsnotify_put_mark(fsn_mark);
369out_err: 375out_err:
370 if (new_fsn_mark) 376 if (new_fsn_mark)
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index cf275500a665..8e8e6bcd1d43 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -529,8 +529,10 @@ static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group,
529 removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags, 529 removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags,
530 &destroy_mark); 530 &destroy_mark);
531 if (destroy_mark) 531 if (destroy_mark)
532 fsnotify_destroy_mark_locked(fsn_mark, group); 532 fsnotify_detach_mark(fsn_mark);
533 mutex_unlock(&group->mark_mutex); 533 mutex_unlock(&group->mark_mutex);
534 if (destroy_mark)
535 fsnotify_free_mark(fsn_mark);
534 536
535 fsnotify_put_mark(fsn_mark); 537 fsnotify_put_mark(fsn_mark);
536 if (removed & real_mount(mnt)->mnt_fsnotify_mask) 538 if (removed & real_mount(mnt)->mnt_fsnotify_mask)
@@ -557,8 +559,10 @@ static int fanotify_remove_inode_mark(struct fsnotify_group *group,
557 removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags, 559 removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags,
558 &destroy_mark); 560 &destroy_mark);
559 if (destroy_mark) 561 if (destroy_mark)
560 fsnotify_destroy_mark_locked(fsn_mark, group); 562 fsnotify_detach_mark(fsn_mark);
561 mutex_unlock(&group->mark_mutex); 563 mutex_unlock(&group->mark_mutex);
564 if (destroy_mark)
565 fsnotify_free_mark(fsn_mark);
562 566
563 /* matches the fsnotify_find_inode_mark() */ 567 /* matches the fsnotify_find_inode_mark() */
564 fsnotify_put_mark(fsn_mark); 568 fsnotify_put_mark(fsn_mark);
diff --git a/fs/notify/mark.c b/fs/notify/mark.c
index 3b2d1ba41e7b..fc0df4442f7b 100644
--- a/fs/notify/mark.c
+++ b/fs/notify/mark.c
@@ -122,26 +122,27 @@ u32 fsnotify_recalc_mask(struct hlist_head *head)
122} 122}
123 123
124/* 124/*
125 * Any time a mark is getting freed we end up here. 125 * Remove mark from inode / vfsmount list, group list, drop inode reference
126 * The caller had better be holding a reference to this mark so we don't actually 126 * if we got one.
127 * do the final put under the mark->lock 127 *
128 * Must be called with group->mark_mutex held.
128 */ 129 */
129void fsnotify_destroy_mark_locked(struct fsnotify_mark *mark, 130void fsnotify_detach_mark(struct fsnotify_mark *mark)
130 struct fsnotify_group *group)
131{ 131{
132 struct inode *inode = NULL; 132 struct inode *inode = NULL;
133 struct fsnotify_group *group = mark->group;
133 134
134 BUG_ON(!mutex_is_locked(&group->mark_mutex)); 135 BUG_ON(!mutex_is_locked(&group->mark_mutex));
135 136
136 spin_lock(&mark->lock); 137 spin_lock(&mark->lock);
137 138
138 /* something else already called this function on this mark */ 139 /* something else already called this function on this mark */
139 if (!(mark->flags & FSNOTIFY_MARK_FLAG_ALIVE)) { 140 if (!(mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED)) {
140 spin_unlock(&mark->lock); 141 spin_unlock(&mark->lock);
141 return; 142 return;
142 } 143 }
143 144
144 mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE; 145 mark->flags &= ~FSNOTIFY_MARK_FLAG_ATTACHED;
145 146
146 if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) { 147 if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) {
147 inode = mark->inode; 148 inode = mark->inode;
@@ -150,6 +151,12 @@ void fsnotify_destroy_mark_locked(struct fsnotify_mark *mark,
150 fsnotify_destroy_vfsmount_mark(mark); 151 fsnotify_destroy_vfsmount_mark(mark);
151 else 152 else
152 BUG(); 153 BUG();
154 /*
155 * Note that we didn't update flags telling whether inode cares about
156 * what's happening with children. We update these flags from
157 * __fsnotify_parent() lazily when next event happens on one of our
158 * children.
159 */
153 160
154 list_del_init(&mark->g_list); 161 list_del_init(&mark->g_list);
155 162
@@ -157,18 +164,32 @@ void fsnotify_destroy_mark_locked(struct fsnotify_mark *mark,
157 164
158 if (inode && (mark->flags & FSNOTIFY_MARK_FLAG_OBJECT_PINNED)) 165 if (inode && (mark->flags & FSNOTIFY_MARK_FLAG_OBJECT_PINNED))
159 iput(inode); 166 iput(inode);
160 /* release lock temporarily */ 167
161 mutex_unlock(&group->mark_mutex); 168 atomic_dec(&group->num_marks);
169}
170
171/*
172 * Free fsnotify mark. The freeing is actually happening from a kthread which
173 * first waits for srcu period end. Caller must have a reference to the mark
174 * or be protected by fsnotify_mark_srcu.
175 */
176void fsnotify_free_mark(struct fsnotify_mark *mark)
177{
178 struct fsnotify_group *group = mark->group;
179
180 spin_lock(&mark->lock);
181 /* something else already called this function on this mark */
182 if (!(mark->flags & FSNOTIFY_MARK_FLAG_ALIVE)) {
183 spin_unlock(&mark->lock);
184 return;
185 }
186 mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE;
187 spin_unlock(&mark->lock);
162 188
163 spin_lock(&destroy_lock); 189 spin_lock(&destroy_lock);
164 list_add(&mark->g_list, &destroy_list); 190 list_add(&mark->g_list, &destroy_list);
165 spin_unlock(&destroy_lock); 191 spin_unlock(&destroy_lock);
166 wake_up(&destroy_waitq); 192 wake_up(&destroy_waitq);
167 /*
168 * We don't necessarily have a ref on mark from caller so the above destroy
169 * may have actually freed it, unless this group provides a 'freeing_mark'
170 * function which must be holding a reference.
171 */
172 193
173 /* 194 /*
174 * Some groups like to know that marks are being freed. This is a 195 * Some groups like to know that marks are being freed. This is a
@@ -177,30 +198,15 @@ void fsnotify_destroy_mark_locked(struct fsnotify_mark *mark,
177 */ 198 */
178 if (group->ops->freeing_mark) 199 if (group->ops->freeing_mark)
179 group->ops->freeing_mark(mark, group); 200 group->ops->freeing_mark(mark, group);
180
181 /*
182 * __fsnotify_update_child_dentry_flags(inode);
183 *
184 * I really want to call that, but we can't, we have no idea if the inode
185 * still exists the second we drop the mark->lock.
186 *
187 * The next time an event arrive to this inode from one of it's children
188 * __fsnotify_parent will see that the inode doesn't care about it's
189 * children and will update all of these flags then. So really this
190 * is just a lazy update (and could be a perf win...)
191 */
192
193 atomic_dec(&group->num_marks);
194
195 mutex_lock_nested(&group->mark_mutex, SINGLE_DEPTH_NESTING);
196} 201}
197 202
198void fsnotify_destroy_mark(struct fsnotify_mark *mark, 203void fsnotify_destroy_mark(struct fsnotify_mark *mark,
199 struct fsnotify_group *group) 204 struct fsnotify_group *group)
200{ 205{
201 mutex_lock_nested(&group->mark_mutex, SINGLE_DEPTH_NESTING); 206 mutex_lock_nested(&group->mark_mutex, SINGLE_DEPTH_NESTING);
202 fsnotify_destroy_mark_locked(mark, group); 207 fsnotify_detach_mark(mark);
203 mutex_unlock(&group->mark_mutex); 208 mutex_unlock(&group->mark_mutex);
209 fsnotify_free_mark(mark);
204} 210}
205 211
206void fsnotify_destroy_marks(struct hlist_head *head, spinlock_t *lock) 212void fsnotify_destroy_marks(struct hlist_head *head, spinlock_t *lock)
@@ -342,7 +348,7 @@ int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
342 * inode->i_lock 348 * inode->i_lock
343 */ 349 */
344 spin_lock(&mark->lock); 350 spin_lock(&mark->lock);
345 mark->flags |= FSNOTIFY_MARK_FLAG_ALIVE; 351 mark->flags |= FSNOTIFY_MARK_FLAG_ALIVE | FSNOTIFY_MARK_FLAG_ATTACHED;
346 352
347 fsnotify_get_group(group); 353 fsnotify_get_group(group);
348 mark->group = group; 354 mark->group = group;
@@ -448,8 +454,9 @@ void fsnotify_clear_marks_by_group_flags(struct fsnotify_group *group,
448 } 454 }
449 mark = list_first_entry(&to_free, struct fsnotify_mark, g_list); 455 mark = list_first_entry(&to_free, struct fsnotify_mark, g_list);
450 fsnotify_get_mark(mark); 456 fsnotify_get_mark(mark);
451 fsnotify_destroy_mark_locked(mark, group); 457 fsnotify_detach_mark(mark);
452 mutex_unlock(&group->mark_mutex); 458 mutex_unlock(&group->mark_mutex);
459 fsnotify_free_mark(mark);
453 fsnotify_put_mark(mark); 460 fsnotify_put_mark(mark);
454 } 461 }
455} 462}
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index f044fe30e8c3..e0727d77feaf 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -236,6 +236,7 @@ struct fsnotify_mark {
236#define FSNOTIFY_MARK_FLAG_OBJECT_PINNED 0x04 236#define FSNOTIFY_MARK_FLAG_OBJECT_PINNED 0x04
237#define FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY 0x08 237#define FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY 0x08
238#define FSNOTIFY_MARK_FLAG_ALIVE 0x10 238#define FSNOTIFY_MARK_FLAG_ALIVE 0x10
239#define FSNOTIFY_MARK_FLAG_ATTACHED 0x20
239 unsigned int flags; /* flags [mark->lock] */ 240 unsigned int flags; /* flags [mark->lock] */
240 void (*free_mark)(struct fsnotify_mark *mark); /* called on final put+free */ 241 void (*free_mark)(struct fsnotify_mark *mark); /* called on final put+free */
241}; 242};
@@ -353,8 +354,10 @@ extern int fsnotify_add_mark_locked(struct fsnotify_mark *mark, struct fsnotify_
353/* given a group and a mark, flag mark to be freed when all references are dropped */ 354/* given a group and a mark, flag mark to be freed when all references are dropped */
354extern void fsnotify_destroy_mark(struct fsnotify_mark *mark, 355extern void fsnotify_destroy_mark(struct fsnotify_mark *mark,
355 struct fsnotify_group *group); 356 struct fsnotify_group *group);
356extern void fsnotify_destroy_mark_locked(struct fsnotify_mark *mark, 357/* detach mark from inode / mount list, group list, drop inode reference */
357 struct fsnotify_group *group); 358extern void fsnotify_detach_mark(struct fsnotify_mark *mark);
359/* free mark */
360extern void fsnotify_free_mark(struct fsnotify_mark *mark);
358/* run all the marks in a group, and clear all of the vfsmount marks */ 361/* run all the marks in a group, and clear all of the vfsmount marks */
359extern void fsnotify_clear_vfsmount_marks_by_group(struct fsnotify_group *group); 362extern void fsnotify_clear_vfsmount_marks_by_group(struct fsnotify_group *group);
360/* run all the marks in a group, and clear all of the inode marks */ 363/* run all the marks in a group, and clear all of the inode marks */