summaryrefslogtreecommitdiffstats
path: root/fs/notify
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2017-02-01 03:21:58 -0500
committerJan Kara <jack@suse.cz>2017-04-10 11:37:35 -0400
commit08991e83b7286635167bab40927665a90fb00d81 (patch)
treee82e62c0a92193ff3af83d3d1ea60f0430c5c118 /fs/notify
parent04662cab59fc3e8421fd7a0539d304d51d2750a4 (diff)
fsnotify: Free fsnotify_mark_connector when there is no mark attached
Currently we free fsnotify_mark_connector structure only when inode / vfsmount is getting freed. This can however impose noticeable memory overhead when marks get attached to inodes only temporarily. So free the connector structure once the last mark is detached from the object. Since notification infrastructure can be working with the connector under the protection of fsnotify_mark_srcu, we have to be careful and free the fsnotify_mark_connector only after SRCU period passes. 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/fsnotify.c9
-rw-r--r--fs/notify/fsnotify.h10
-rw-r--r--fs/notify/inode_mark.c2
-rw-r--r--fs/notify/mark.c152
-rw-r--r--fs/notify/vfsmount_mark.c2
5 files changed, 125 insertions, 50 deletions
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c
index eae621a18ac9..d512ef9f75fc 100644
--- a/fs/notify/fsnotify.c
+++ b/fs/notify/fsnotify.c
@@ -228,7 +228,8 @@ int fsnotify(struct inode *to_tell, __u32 mask, const void *data, int data_is,
228 228
229 if ((mask & FS_MODIFY) || 229 if ((mask & FS_MODIFY) ||
230 (test_mask & to_tell->i_fsnotify_mask)) { 230 (test_mask & to_tell->i_fsnotify_mask)) {
231 inode_conn = lockless_dereference(to_tell->i_fsnotify_marks); 231 inode_conn = srcu_dereference(to_tell->i_fsnotify_marks,
232 &fsnotify_mark_srcu);
232 if (inode_conn) 233 if (inode_conn)
233 inode_node = srcu_dereference(inode_conn->list.first, 234 inode_node = srcu_dereference(inode_conn->list.first,
234 &fsnotify_mark_srcu); 235 &fsnotify_mark_srcu);
@@ -236,11 +237,13 @@ int fsnotify(struct inode *to_tell, __u32 mask, const void *data, int data_is,
236 237
237 if (mnt && ((mask & FS_MODIFY) || 238 if (mnt && ((mask & FS_MODIFY) ||
238 (test_mask & mnt->mnt_fsnotify_mask))) { 239 (test_mask & mnt->mnt_fsnotify_mask))) {
239 inode_conn = lockless_dereference(to_tell->i_fsnotify_marks); 240 inode_conn = srcu_dereference(to_tell->i_fsnotify_marks,
241 &fsnotify_mark_srcu);
240 if (inode_conn) 242 if (inode_conn)
241 inode_node = srcu_dereference(inode_conn->list.first, 243 inode_node = srcu_dereference(inode_conn->list.first,
242 &fsnotify_mark_srcu); 244 &fsnotify_mark_srcu);
243 vfsmount_conn = lockless_dereference(mnt->mnt_fsnotify_marks); 245 vfsmount_conn = srcu_dereference(mnt->mnt_fsnotify_marks,
246 &fsnotify_mark_srcu);
244 if (vfsmount_conn) 247 if (vfsmount_conn)
245 vfsmount_node = srcu_dereference( 248 vfsmount_node = srcu_dereference(
246 vfsmount_conn->list.first, 249 vfsmount_conn->list.first,
diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h
index 510f027bdf0f..72050b75ca8c 100644
--- a/fs/notify/fsnotify.h
+++ b/fs/notify/fsnotify.h
@@ -20,19 +20,19 @@ extern int fsnotify_compare_groups(struct fsnotify_group *a,
20 20
21/* Find mark belonging to given group in the list of marks */ 21/* Find mark belonging to given group in the list of marks */
22extern struct fsnotify_mark *fsnotify_find_mark( 22extern struct fsnotify_mark *fsnotify_find_mark(
23 struct fsnotify_mark_connector *conn, 23 struct fsnotify_mark_connector __rcu **connp,
24 struct fsnotify_group *group); 24 struct fsnotify_group *group);
25/* Destroy all marks connected via given connector */ 25/* Destroy all marks connected via given connector */
26extern void fsnotify_destroy_marks(struct fsnotify_mark_connector *conn); 26extern void fsnotify_destroy_marks(struct fsnotify_mark_connector __rcu **connp);
27/* run the list of all marks associated with inode and destroy them */ 27/* run the list of all marks associated with inode and destroy them */
28static inline void fsnotify_clear_marks_by_inode(struct inode *inode) 28static inline void fsnotify_clear_marks_by_inode(struct inode *inode)
29{ 29{
30 fsnotify_destroy_marks(inode->i_fsnotify_marks); 30 fsnotify_destroy_marks(&inode->i_fsnotify_marks);
31} 31}
32/* run the list of all marks associated with vfsmount and destroy them */ 32/* run the list of all marks associated with vfsmount and destroy them */
33static inline void fsnotify_clear_marks_by_mount(struct vfsmount *mnt) 33static inline void fsnotify_clear_marks_by_mount(struct vfsmount *mnt)
34{ 34{
35 fsnotify_destroy_marks(real_mount(mnt)->mnt_fsnotify_marks); 35 fsnotify_destroy_marks(&real_mount(mnt)->mnt_fsnotify_marks);
36} 36}
37/* prepare for freeing all marks associated with given group */ 37/* prepare for freeing all marks associated with given group */
38extern void fsnotify_detach_group_marks(struct fsnotify_group *group); 38extern void fsnotify_detach_group_marks(struct fsnotify_group *group);
diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c
index 080b6d8b9973..b9370316727e 100644
--- a/fs/notify/inode_mark.c
+++ b/fs/notify/inode_mark.c
@@ -50,7 +50,7 @@ void fsnotify_clear_inode_marks_by_group(struct fsnotify_group *group)
50struct fsnotify_mark *fsnotify_find_inode_mark(struct fsnotify_group *group, 50struct fsnotify_mark *fsnotify_find_inode_mark(struct fsnotify_group *group,
51 struct inode *inode) 51 struct inode *inode)
52{ 52{
53 return fsnotify_find_mark(inode->i_fsnotify_marks, group); 53 return fsnotify_find_mark(&inode->i_fsnotify_marks, group);
54} 54}
55 55
56/** 56/**
diff --git a/fs/notify/mark.c b/fs/notify/mark.c
index bfb415d0d757..824095db5a3b 100644
--- a/fs/notify/mark.c
+++ b/fs/notify/mark.c
@@ -89,10 +89,14 @@ struct kmem_cache *fsnotify_mark_connector_cachep;
89 89
90static DEFINE_SPINLOCK(destroy_lock); 90static DEFINE_SPINLOCK(destroy_lock);
91static LIST_HEAD(destroy_list); 91static LIST_HEAD(destroy_list);
92static struct fsnotify_mark_connector *connector_destroy_list;
92 93
93static void fsnotify_mark_destroy_workfn(struct work_struct *work); 94static void fsnotify_mark_destroy_workfn(struct work_struct *work);
94static DECLARE_DELAYED_WORK(reaper_work, fsnotify_mark_destroy_workfn); 95static DECLARE_DELAYED_WORK(reaper_work, fsnotify_mark_destroy_workfn);
95 96
97static void fsnotify_connector_destroy_workfn(struct work_struct *work);
98static DECLARE_WORK(connector_reaper_work, fsnotify_connector_destroy_workfn);
99
96void fsnotify_get_mark(struct fsnotify_mark *mark) 100void fsnotify_get_mark(struct fsnotify_mark *mark)
97{ 101{
98 atomic_inc(&mark->refcnt); 102 atomic_inc(&mark->refcnt);
@@ -139,22 +143,73 @@ void fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)
139 __fsnotify_update_child_dentry_flags(conn->inode); 143 __fsnotify_update_child_dentry_flags(conn->inode);
140} 144}
141 145
146/* Free all connectors queued for freeing once SRCU period ends */
147static void fsnotify_connector_destroy_workfn(struct work_struct *work)
148{
149 struct fsnotify_mark_connector *conn, *free;
150
151 spin_lock(&destroy_lock);
152 conn = connector_destroy_list;
153 connector_destroy_list = NULL;
154 spin_unlock(&destroy_lock);
155
156 synchronize_srcu(&fsnotify_mark_srcu);
157 while (conn) {
158 free = conn;
159 conn = conn->destroy_next;
160 kmem_cache_free(fsnotify_mark_connector_cachep, free);
161 }
162}
163
164
165static struct inode *fsnotify_detach_connector_from_object(
166 struct fsnotify_mark_connector *conn)
167{
168 struct inode *inode = NULL;
169
170 if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE) {
171 inode = conn->inode;
172 rcu_assign_pointer(inode->i_fsnotify_marks, NULL);
173 inode->i_fsnotify_mask = 0;
174 conn->inode = NULL;
175 conn->flags &= ~FSNOTIFY_OBJ_TYPE_INODE;
176 } else if (conn->flags & FSNOTIFY_OBJ_TYPE_VFSMOUNT) {
177 rcu_assign_pointer(real_mount(conn->mnt)->mnt_fsnotify_marks,
178 NULL);
179 real_mount(conn->mnt)->mnt_fsnotify_mask = 0;
180 conn->mnt = NULL;
181 conn->flags &= ~FSNOTIFY_OBJ_TYPE_VFSMOUNT;
182 }
183
184 return inode;
185}
186
142static struct inode *fsnotify_detach_from_object(struct fsnotify_mark *mark) 187static struct inode *fsnotify_detach_from_object(struct fsnotify_mark *mark)
143{ 188{
144 struct fsnotify_mark_connector *conn; 189 struct fsnotify_mark_connector *conn;
145 struct inode *inode = NULL; 190 struct inode *inode = NULL;
191 bool free_conn = false;
146 192
147 conn = mark->connector; 193 conn = mark->connector;
148 spin_lock(&conn->lock); 194 spin_lock(&conn->lock);
149 hlist_del_init_rcu(&mark->obj_list); 195 hlist_del_init_rcu(&mark->obj_list);
150 if (hlist_empty(&conn->list)) { 196 if (hlist_empty(&conn->list)) {
151 if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE) 197 inode = fsnotify_detach_connector_from_object(conn);
152 inode = conn->inode; 198 free_conn = true;
199 } else {
200 __fsnotify_recalc_mask(conn);
153 } 201 }
154 __fsnotify_recalc_mask(conn);
155 mark->connector = NULL; 202 mark->connector = NULL;
156 spin_unlock(&conn->lock); 203 spin_unlock(&conn->lock);
157 204
205 if (free_conn) {
206 spin_lock(&destroy_lock);
207 conn->destroy_next = connector_destroy_list;
208 connector_destroy_list = conn;
209 spin_unlock(&destroy_lock);
210 queue_work(system_unbound_wq, &connector_reaper_work);
211 }
212
158 return inode; 213 return inode;
159} 214}
160 215
@@ -259,14 +314,6 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark,
259 fsnotify_free_mark(mark); 314 fsnotify_free_mark(mark);
260} 315}
261 316
262void fsnotify_connector_free(struct fsnotify_mark_connector **connp)
263{
264 if (*connp) {
265 kmem_cache_free(fsnotify_mark_connector_cachep, *connp);
266 *connp = NULL;
267 }
268}
269
270void fsnotify_set_mark_mask_locked(struct fsnotify_mark *mark, __u32 mask) 317void fsnotify_set_mark_mask_locked(struct fsnotify_mark *mark, __u32 mask)
271{ 318{
272 assert_spin_locked(&mark->lock); 319 assert_spin_locked(&mark->lock);
@@ -318,9 +365,9 @@ int fsnotify_compare_groups(struct fsnotify_group *a, struct fsnotify_group *b)
318} 365}
319 366
320static int fsnotify_attach_connector_to_object( 367static int fsnotify_attach_connector_to_object(
321 struct fsnotify_mark_connector **connp, 368 struct fsnotify_mark_connector __rcu **connp,
322 struct inode *inode, 369 struct inode *inode,
323 struct vfsmount *mnt) 370 struct vfsmount *mnt)
324{ 371{
325 struct fsnotify_mark_connector *conn; 372 struct fsnotify_mark_connector *conn;
326 373
@@ -331,7 +378,7 @@ static int fsnotify_attach_connector_to_object(
331 INIT_HLIST_HEAD(&conn->list); 378 INIT_HLIST_HEAD(&conn->list);
332 if (inode) { 379 if (inode) {
333 conn->flags = FSNOTIFY_OBJ_TYPE_INODE; 380 conn->flags = FSNOTIFY_OBJ_TYPE_INODE;
334 conn->inode = inode; 381 conn->inode = igrab(inode);
335 } else { 382 } else {
336 conn->flags = FSNOTIFY_OBJ_TYPE_VFSMOUNT; 383 conn->flags = FSNOTIFY_OBJ_TYPE_VFSMOUNT;
337 conn->mnt = mnt; 384 conn->mnt = mnt;
@@ -342,6 +389,8 @@ static int fsnotify_attach_connector_to_object(
342 */ 389 */
343 if (cmpxchg(connp, NULL, conn)) { 390 if (cmpxchg(connp, NULL, conn)) {
344 /* Someone else created list structure for us */ 391 /* Someone else created list structure for us */
392 if (inode)
393 iput(inode);
345 kmem_cache_free(fsnotify_mark_connector_cachep, conn); 394 kmem_cache_free(fsnotify_mark_connector_cachep, conn);
346 } 395 }
347 396
@@ -349,6 +398,34 @@ static int fsnotify_attach_connector_to_object(
349} 398}
350 399
351/* 400/*
401 * Get mark connector, make sure it is alive and return with its lock held.
402 * This is for users that get connector pointer from inode or mount. Users that
403 * hold reference to a mark on the list may directly lock connector->lock as
404 * they are sure list cannot go away under them.
405 */
406static struct fsnotify_mark_connector *fsnotify_grab_connector(
407 struct fsnotify_mark_connector __rcu **connp)
408{
409 struct fsnotify_mark_connector *conn;
410 int idx;
411
412 idx = srcu_read_lock(&fsnotify_mark_srcu);
413 conn = srcu_dereference(*connp, &fsnotify_mark_srcu);
414 if (!conn)
415 goto out;
416 spin_lock(&conn->lock);
417 if (!(conn->flags & (FSNOTIFY_OBJ_TYPE_INODE |
418 FSNOTIFY_OBJ_TYPE_VFSMOUNT))) {
419 spin_unlock(&conn->lock);
420 srcu_read_unlock(&fsnotify_mark_srcu, idx);
421 return NULL;
422 }
423out:
424 srcu_read_unlock(&fsnotify_mark_srcu, idx);
425 return conn;
426}
427
428/*
352 * Add mark into proper place in given list of marks. These marks may be used 429 * Add mark into proper place in given list of marks. These marks may be used
353 * for the fsnotify backend to determine which event types should be delivered 430 * for the fsnotify backend to determine which event types should be delivered
354 * to which group and for which inodes. These marks are ordered according to 431 * to which group and for which inodes. These marks are ordered according to
@@ -360,7 +437,7 @@ static int fsnotify_add_mark_list(struct fsnotify_mark *mark,
360{ 437{
361 struct fsnotify_mark *lmark, *last = NULL; 438 struct fsnotify_mark *lmark, *last = NULL;
362 struct fsnotify_mark_connector *conn; 439 struct fsnotify_mark_connector *conn;
363 struct fsnotify_mark_connector **connp; 440 struct fsnotify_mark_connector __rcu **connp;
364 int cmp; 441 int cmp;
365 int err = 0; 442 int err = 0;
366 443
@@ -370,21 +447,20 @@ static int fsnotify_add_mark_list(struct fsnotify_mark *mark,
370 connp = &inode->i_fsnotify_marks; 447 connp = &inode->i_fsnotify_marks;
371 else 448 else
372 connp = &real_mount(mnt)->mnt_fsnotify_marks; 449 connp = &real_mount(mnt)->mnt_fsnotify_marks;
373 450restart:
374 if (!*connp) { 451 spin_lock(&mark->lock);
452 conn = fsnotify_grab_connector(connp);
453 if (!conn) {
454 spin_unlock(&mark->lock);
375 err = fsnotify_attach_connector_to_object(connp, inode, mnt); 455 err = fsnotify_attach_connector_to_object(connp, inode, mnt);
376 if (err) 456 if (err)
377 return err; 457 return err;
458 goto restart;
378 } 459 }
379 spin_lock(&mark->lock);
380 conn = *connp;
381 spin_lock(&conn->lock);
382 460
383 /* is mark the first mark? */ 461 /* is mark the first mark? */
384 if (hlist_empty(&conn->list)) { 462 if (hlist_empty(&conn->list)) {
385 hlist_add_head_rcu(&mark->obj_list, &conn->list); 463 hlist_add_head_rcu(&mark->obj_list, &conn->list);
386 if (inode)
387 igrab(inode);
388 goto added; 464 goto added;
389 } 465 }
390 466
@@ -486,15 +562,17 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group *group,
486 * Given a list of marks, find the mark associated with given group. If found 562 * Given a list of marks, find the mark associated with given group. If found
487 * take a reference to that mark and return it, else return NULL. 563 * take a reference to that mark and return it, else return NULL.
488 */ 564 */
489struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_mark_connector *conn, 565struct fsnotify_mark *fsnotify_find_mark(
490 struct fsnotify_group *group) 566 struct fsnotify_mark_connector __rcu **connp,
567 struct fsnotify_group *group)
491{ 568{
569 struct fsnotify_mark_connector *conn;
492 struct fsnotify_mark *mark; 570 struct fsnotify_mark *mark;
493 571
572 conn = fsnotify_grab_connector(connp);
494 if (!conn) 573 if (!conn)
495 return NULL; 574 return NULL;
496 575
497 spin_lock(&conn->lock);
498 hlist_for_each_entry(mark, &conn->list, obj_list) { 576 hlist_for_each_entry(mark, &conn->list, obj_list) {
499 if (mark->group == group) { 577 if (mark->group == group) {
500 fsnotify_get_mark(mark); 578 fsnotify_get_mark(mark);
@@ -572,26 +650,20 @@ void fsnotify_detach_group_marks(struct fsnotify_group *group)
572 } 650 }
573} 651}
574 652
575void fsnotify_destroy_marks(struct fsnotify_mark_connector *conn) 653/* Destroy all marks attached to inode / vfsmount */
654void fsnotify_destroy_marks(struct fsnotify_mark_connector __rcu **connp)
576{ 655{
656 struct fsnotify_mark_connector *conn;
577 struct fsnotify_mark *mark; 657 struct fsnotify_mark *mark;
578 658
579 if (!conn) 659 while ((conn = fsnotify_grab_connector(connp))) {
580 return;
581
582 while (1) {
583 /* 660 /*
584 * We have to be careful since we can race with e.g. 661 * We have to be careful since we can race with e.g.
585 * fsnotify_clear_marks_by_group() and once we drop 'lock', 662 * fsnotify_clear_marks_by_group() and once we drop the list
586 * mark can get removed from the obj_list and destroyed. But 663 * lock, mark can get removed from the obj_list and destroyed.
587 * we are holding mark reference so mark cannot be freed and 664 * But we are holding mark reference so mark cannot be freed
588 * calling fsnotify_destroy_mark() more than once is fine. 665 * and calling fsnotify_destroy_mark() more than once is fine.
589 */ 666 */
590 spin_lock(&conn->lock);
591 if (hlist_empty(&conn->list)) {
592 spin_unlock(&conn->lock);
593 break;
594 }
595 mark = hlist_entry(conn->list.first, struct fsnotify_mark, 667 mark = hlist_entry(conn->list.first, struct fsnotify_mark,
596 obj_list); 668 obj_list);
597 fsnotify_get_mark(mark); 669 fsnotify_get_mark(mark);
diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c
index 26da5c209944..dd5f3fcbccfb 100644
--- a/fs/notify/vfsmount_mark.c
+++ b/fs/notify/vfsmount_mark.c
@@ -48,5 +48,5 @@ struct fsnotify_mark *fsnotify_find_vfsmount_mark(struct fsnotify_group *group,
48{ 48{
49 struct mount *m = real_mount(mnt); 49 struct mount *m = real_mount(mnt);
50 50
51 return fsnotify_find_mark(m->mnt_fsnotify_marks, group); 51 return fsnotify_find_mark(&m->mnt_fsnotify_marks, group);
52} 52}