summaryrefslogtreecommitdiffstats
path: root/fs/notify
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2017-02-01 02:19:43 -0500
committerJan Kara <jack@suse.cz>2017-04-10 11:37:35 -0400
commit04662cab59fc3e8421fd7a0539d304d51d2750a4 (patch)
treebde9d5a97ffbe63ea7366a4aab6be9e2402a2827 /fs/notify
parent2629718dd26f89e064dcdec6c8e5b9713502e1f8 (diff)
fsnotify: Lock object list with connector lock
So far list of marks attached to an object (inode / vfsmount) was protected by i_lock or mnt_root->d_lock. This dictates that the list must be empty before the object can be destroyed although the list is now anchored in the fsnotify_mark_connector structure. Protect the list by a spinlock in the fsnotify_mark_connector structure to decouple lifetime of a list of marks from a lifetime of the object. This also simplifies the code quite a bit since we don't have to differentiate between inode and vfsmount lists in quite a few places anymore. Reviewed-by: Miklos Szeredi <mszeredi@redhat.com> Reviewed-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs/notify')
-rw-r--r--fs/notify/mark.c90
1 files changed, 32 insertions, 58 deletions
diff --git a/fs/notify/mark.c b/fs/notify/mark.c
index b5b641a2b557..bfb415d0d757 100644
--- a/fs/notify/mark.c
+++ b/fs/notify/mark.c
@@ -33,7 +33,7 @@
33 * 33 *
34 * group->mark_mutex 34 * group->mark_mutex
35 * mark->lock 35 * mark->lock
36 * inode->i_lock 36 * mark->connector->lock
37 * 37 *
38 * group->mark_mutex protects the marks_list anchored inside a given group and 38 * group->mark_mutex protects the marks_list anchored inside a given group and
39 * each mark is hooked via the g_list. It also protects the groups private 39 * each mark is hooked via the g_list. It also protects the groups private
@@ -44,10 +44,12 @@
44 * is assigned to as well as the access to a reference of the inode/vfsmount 44 * is assigned to as well as the access to a reference of the inode/vfsmount
45 * that is being watched by the mark. 45 * that is being watched by the mark.
46 * 46 *
47 * inode->i_lock protects the i_fsnotify_marks list anchored inside a 47 * mark->connector->lock protects the list of marks anchored inside an
48 * given inode and each mark is hooked via the i_list. (and sorta the 48 * inode / vfsmount and each mark is hooked via the i_list.
49 * free_i_list)
50 * 49 *
50 * A list of notification marks relating to inode / mnt is contained in
51 * fsnotify_mark_connector. That structure is alive as long as there are any
52 * marks in the list and is also protected by fsnotify_mark_srcu.
51 * 53 *
52 * LIFETIME: 54 * LIFETIME:
53 * Inode marks survive between when they are added to an inode and when their 55 * Inode marks survive between when they are added to an inode and when their
@@ -110,8 +112,10 @@ static void __fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)
110 u32 new_mask = 0; 112 u32 new_mask = 0;
111 struct fsnotify_mark *mark; 113 struct fsnotify_mark *mark;
112 114
115 assert_spin_locked(&conn->lock);
113 hlist_for_each_entry(mark, &conn->list, obj_list) 116 hlist_for_each_entry(mark, &conn->list, obj_list)
114 new_mask |= mark->mask; 117 new_mask |= mark->mask;
118
115 if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE) 119 if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE)
116 conn->inode->i_fsnotify_mask = new_mask; 120 conn->inode->i_fsnotify_mask = new_mask;
117 else if (conn->flags & FSNOTIFY_OBJ_TYPE_VFSMOUNT) 121 else if (conn->flags & FSNOTIFY_OBJ_TYPE_VFSMOUNT)
@@ -128,31 +132,20 @@ void fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)
128 if (!conn) 132 if (!conn)
129 return; 133 return;
130 134
131 if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE) 135 spin_lock(&conn->lock);
132 spin_lock(&conn->inode->i_lock);
133 else
134 spin_lock(&conn->mnt->mnt_root->d_lock);
135 __fsnotify_recalc_mask(conn); 136 __fsnotify_recalc_mask(conn);
136 if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE) { 137 spin_unlock(&conn->lock);
137 spin_unlock(&conn->inode->i_lock); 138 if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE)
138 __fsnotify_update_child_dentry_flags(conn->inode); 139 __fsnotify_update_child_dentry_flags(conn->inode);
139 } else {
140 spin_unlock(&conn->mnt->mnt_root->d_lock);
141 }
142} 140}
143 141
144static struct inode *fsnotify_detach_from_object(struct fsnotify_mark *mark) 142static struct inode *fsnotify_detach_from_object(struct fsnotify_mark *mark)
145{ 143{
146 struct fsnotify_mark_connector *conn; 144 struct fsnotify_mark_connector *conn;
147 struct inode *inode = NULL; 145 struct inode *inode = NULL;
148 spinlock_t *lock;
149 146
150 conn = mark->connector; 147 conn = mark->connector;
151 if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE) 148 spin_lock(&conn->lock);
152 lock = &conn->inode->i_lock;
153 else
154 lock = &conn->mnt->mnt_root->d_lock;
155 spin_lock(lock);
156 hlist_del_init_rcu(&mark->obj_list); 149 hlist_del_init_rcu(&mark->obj_list);
157 if (hlist_empty(&conn->list)) { 150 if (hlist_empty(&conn->list)) {
158 if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE) 151 if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE)
@@ -160,7 +153,7 @@ static struct inode *fsnotify_detach_from_object(struct fsnotify_mark *mark)
160 } 153 }
161 __fsnotify_recalc_mask(conn); 154 __fsnotify_recalc_mask(conn);
162 mark->connector = NULL; 155 mark->connector = NULL;
163 spin_unlock(lock); 156 spin_unlock(&conn->lock);
164 157
165 return inode; 158 return inode;
166} 159}
@@ -326,7 +319,6 @@ int fsnotify_compare_groups(struct fsnotify_group *a, struct fsnotify_group *b)
326 319
327static int fsnotify_attach_connector_to_object( 320static int fsnotify_attach_connector_to_object(
328 struct fsnotify_mark_connector **connp, 321 struct fsnotify_mark_connector **connp,
329 spinlock_t *lock,
330 struct inode *inode, 322 struct inode *inode,
331 struct vfsmount *mnt) 323 struct vfsmount *mnt)
332{ 324{
@@ -335,6 +327,7 @@ static int fsnotify_attach_connector_to_object(
335 conn = kmem_cache_alloc(fsnotify_mark_connector_cachep, GFP_KERNEL); 327 conn = kmem_cache_alloc(fsnotify_mark_connector_cachep, GFP_KERNEL);
336 if (!conn) 328 if (!conn)
337 return -ENOMEM; 329 return -ENOMEM;
330 spin_lock_init(&conn->lock);
338 INIT_HLIST_HEAD(&conn->list); 331 INIT_HLIST_HEAD(&conn->list);
339 if (inode) { 332 if (inode) {
340 conn->flags = FSNOTIFY_OBJ_TYPE_INODE; 333 conn->flags = FSNOTIFY_OBJ_TYPE_INODE;
@@ -344,16 +337,13 @@ static int fsnotify_attach_connector_to_object(
344 conn->mnt = mnt; 337 conn->mnt = mnt;
345 } 338 }
346 /* 339 /*
347 * Make sure 'conn' initialization is visible. Matches 340 * cmpxchg() provides the barrier so that readers of *connp can see
348 * lockless_dereference() in fsnotify(). 341 * only initialized structure
349 */ 342 */
350 smp_wmb(); 343 if (cmpxchg(connp, NULL, conn)) {
351 spin_lock(lock); 344 /* Someone else created list structure for us */
352 if (!*connp)
353 *connp = conn;
354 else
355 kmem_cache_free(fsnotify_mark_connector_cachep, conn); 345 kmem_cache_free(fsnotify_mark_connector_cachep, conn);
356 spin_unlock(lock); 346 }
357 347
358 return 0; 348 return 0;
359} 349}
@@ -371,35 +361,30 @@ static int fsnotify_add_mark_list(struct fsnotify_mark *mark,
371 struct fsnotify_mark *lmark, *last = NULL; 361 struct fsnotify_mark *lmark, *last = NULL;
372 struct fsnotify_mark_connector *conn; 362 struct fsnotify_mark_connector *conn;
373 struct fsnotify_mark_connector **connp; 363 struct fsnotify_mark_connector **connp;
374 spinlock_t *lock;
375 int cmp; 364 int cmp;
376 int err = 0; 365 int err = 0;
377 366
378 if (WARN_ON(!inode && !mnt)) 367 if (WARN_ON(!inode && !mnt))
379 return -EINVAL; 368 return -EINVAL;
380 if (inode) { 369 if (inode)
381 connp = &inode->i_fsnotify_marks; 370 connp = &inode->i_fsnotify_marks;
382 lock = &inode->i_lock; 371 else
383 } else {
384 connp = &real_mount(mnt)->mnt_fsnotify_marks; 372 connp = &real_mount(mnt)->mnt_fsnotify_marks;
385 lock = &mnt->mnt_root->d_lock;
386 }
387 373
388 if (!*connp) { 374 if (!*connp) {
389 err = fsnotify_attach_connector_to_object(connp, lock, 375 err = fsnotify_attach_connector_to_object(connp, inode, mnt);
390 inode, mnt);
391 if (err) 376 if (err)
392 return err; 377 return err;
393 } 378 }
394 spin_lock(&mark->lock); 379 spin_lock(&mark->lock);
395 spin_lock(lock);
396 conn = *connp; 380 conn = *connp;
381 spin_lock(&conn->lock);
397 382
398 /* is mark the first mark? */ 383 /* is mark the first mark? */
399 if (hlist_empty(&conn->list)) { 384 if (hlist_empty(&conn->list)) {
400 hlist_add_head_rcu(&mark->obj_list, &conn->list); 385 hlist_add_head_rcu(&mark->obj_list, &conn->list);
401 if (inode) 386 if (inode)
402 __iget(inode); 387 igrab(inode);
403 goto added; 388 goto added;
404 } 389 }
405 390
@@ -425,7 +410,7 @@ static int fsnotify_add_mark_list(struct fsnotify_mark *mark,
425added: 410added:
426 mark->connector = conn; 411 mark->connector = conn;
427out_err: 412out_err:
428 spin_unlock(lock); 413 spin_unlock(&conn->lock);
429 spin_unlock(&mark->lock); 414 spin_unlock(&mark->lock);
430 return err; 415 return err;
431} 416}
@@ -449,7 +434,7 @@ int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
449 * LOCKING ORDER!!!! 434 * LOCKING ORDER!!!!
450 * group->mark_mutex 435 * group->mark_mutex
451 * mark->lock 436 * mark->lock
452 * inode->i_lock 437 * mark->connector->lock
453 */ 438 */
454 spin_lock(&mark->lock); 439 spin_lock(&mark->lock);
455 mark->flags |= FSNOTIFY_MARK_FLAG_ALIVE | FSNOTIFY_MARK_FLAG_ATTACHED; 440 mark->flags |= FSNOTIFY_MARK_FLAG_ALIVE | FSNOTIFY_MARK_FLAG_ATTACHED;
@@ -505,24 +490,19 @@ struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_mark_connector *conn,
505 struct fsnotify_group *group) 490 struct fsnotify_group *group)
506{ 491{
507 struct fsnotify_mark *mark; 492 struct fsnotify_mark *mark;
508 spinlock_t *lock;
509 493
510 if (!conn) 494 if (!conn)
511 return NULL; 495 return NULL;
512 496
513 if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE) 497 spin_lock(&conn->lock);
514 lock = &conn->inode->i_lock;
515 else
516 lock = &conn->mnt->mnt_root->d_lock;
517 spin_lock(lock);
518 hlist_for_each_entry(mark, &conn->list, obj_list) { 498 hlist_for_each_entry(mark, &conn->list, obj_list) {
519 if (mark->group == group) { 499 if (mark->group == group) {
520 fsnotify_get_mark(mark); 500 fsnotify_get_mark(mark);
521 spin_unlock(lock); 501 spin_unlock(&conn->lock);
522 return mark; 502 return mark;
523 } 503 }
524 } 504 }
525 spin_unlock(lock); 505 spin_unlock(&conn->lock);
526 return NULL; 506 return NULL;
527} 507}
528 508
@@ -595,16 +575,10 @@ void fsnotify_detach_group_marks(struct fsnotify_group *group)
595void fsnotify_destroy_marks(struct fsnotify_mark_connector *conn) 575void fsnotify_destroy_marks(struct fsnotify_mark_connector *conn)
596{ 576{
597 struct fsnotify_mark *mark; 577 struct fsnotify_mark *mark;
598 spinlock_t *lock;
599 578
600 if (!conn) 579 if (!conn)
601 return; 580 return;
602 581
603 if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE)
604 lock = &conn->inode->i_lock;
605 else
606 lock = &conn->mnt->mnt_root->d_lock;
607
608 while (1) { 582 while (1) {
609 /* 583 /*
610 * We have to be careful since we can race with e.g. 584 * We have to be careful since we can race with e.g.
@@ -613,15 +587,15 @@ void fsnotify_destroy_marks(struct fsnotify_mark_connector *conn)
613 * we are holding mark reference so mark cannot be freed and 587 * we are holding mark reference so mark cannot be freed and
614 * calling fsnotify_destroy_mark() more than once is fine. 588 * calling fsnotify_destroy_mark() more than once is fine.
615 */ 589 */
616 spin_lock(lock); 590 spin_lock(&conn->lock);
617 if (hlist_empty(&conn->list)) { 591 if (hlist_empty(&conn->list)) {
618 spin_unlock(lock); 592 spin_unlock(&conn->lock);
619 break; 593 break;
620 } 594 }
621 mark = hlist_entry(conn->list.first, struct fsnotify_mark, 595 mark = hlist_entry(conn->list.first, struct fsnotify_mark,
622 obj_list); 596 obj_list);
623 fsnotify_get_mark(mark); 597 fsnotify_get_mark(mark);
624 spin_unlock(lock); 598 spin_unlock(&conn->lock);
625 fsnotify_destroy_mark(mark, mark->group); 599 fsnotify_destroy_mark(mark, mark->group);
626 fsnotify_put_mark(mark); 600 fsnotify_put_mark(mark);
627 } 601 }