diff options
Diffstat (limited to 'fs/notify/mark.c')
-rw-r--r-- | fs/notify/mark.c | 53 |
1 files changed, 39 insertions, 14 deletions
diff --git a/fs/notify/mark.c b/fs/notify/mark.c index cfcbf114676e..7115c5d7d373 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c | |||
@@ -91,7 +91,14 @@ | |||
91 | #include <linux/fsnotify_backend.h> | 91 | #include <linux/fsnotify_backend.h> |
92 | #include "fsnotify.h" | 92 | #include "fsnotify.h" |
93 | 93 | ||
94 | #define FSNOTIFY_REAPER_DELAY (1) /* 1 jiffy */ | ||
95 | |||
94 | struct srcu_struct fsnotify_mark_srcu; | 96 | struct srcu_struct fsnotify_mark_srcu; |
97 | static DEFINE_SPINLOCK(destroy_lock); | ||
98 | static LIST_HEAD(destroy_list); | ||
99 | |||
100 | static void fsnotify_mark_destroy(struct work_struct *work); | ||
101 | static DECLARE_DELAYED_WORK(reaper_work, fsnotify_mark_destroy); | ||
95 | 102 | ||
96 | void fsnotify_get_mark(struct fsnotify_mark *mark) | 103 | void fsnotify_get_mark(struct fsnotify_mark *mark) |
97 | { | 104 | { |
@@ -165,19 +172,10 @@ void fsnotify_detach_mark(struct fsnotify_mark *mark) | |||
165 | atomic_dec(&group->num_marks); | 172 | atomic_dec(&group->num_marks); |
166 | } | 173 | } |
167 | 174 | ||
168 | static void | ||
169 | fsnotify_mark_free_rcu(struct rcu_head *rcu) | ||
170 | { | ||
171 | struct fsnotify_mark *mark; | ||
172 | |||
173 | mark = container_of(rcu, struct fsnotify_mark, g_rcu); | ||
174 | fsnotify_put_mark(mark); | ||
175 | } | ||
176 | |||
177 | /* | 175 | /* |
178 | * Free fsnotify mark. The freeing is actually happening from a call_srcu | 176 | * Free fsnotify mark. The freeing is actually happening from a kthread which |
179 | * callback. Caller must have a reference to the mark or be protected by | 177 | * first waits for srcu period end. Caller must have a reference to the mark |
180 | * fsnotify_mark_srcu. | 178 | * or be protected by fsnotify_mark_srcu. |
181 | */ | 179 | */ |
182 | void fsnotify_free_mark(struct fsnotify_mark *mark) | 180 | void fsnotify_free_mark(struct fsnotify_mark *mark) |
183 | { | 181 | { |
@@ -192,7 +190,11 @@ void fsnotify_free_mark(struct fsnotify_mark *mark) | |||
192 | mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE; | 190 | mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE; |
193 | spin_unlock(&mark->lock); | 191 | spin_unlock(&mark->lock); |
194 | 192 | ||
195 | call_srcu(&fsnotify_mark_srcu, &mark->g_rcu, fsnotify_mark_free_rcu); | 193 | spin_lock(&destroy_lock); |
194 | list_add(&mark->g_list, &destroy_list); | ||
195 | spin_unlock(&destroy_lock); | ||
196 | queue_delayed_work(system_unbound_wq, &reaper_work, | ||
197 | FSNOTIFY_REAPER_DELAY); | ||
196 | 198 | ||
197 | /* | 199 | /* |
198 | * Some groups like to know that marks are being freed. This is a | 200 | * Some groups like to know that marks are being freed. This is a |
@@ -388,7 +390,12 @@ err: | |||
388 | 390 | ||
389 | spin_unlock(&mark->lock); | 391 | spin_unlock(&mark->lock); |
390 | 392 | ||
391 | call_srcu(&fsnotify_mark_srcu, &mark->g_rcu, fsnotify_mark_free_rcu); | 393 | spin_lock(&destroy_lock); |
394 | list_add(&mark->g_list, &destroy_list); | ||
395 | spin_unlock(&destroy_lock); | ||
396 | queue_delayed_work(system_unbound_wq, &reaper_work, | ||
397 | FSNOTIFY_REAPER_DELAY); | ||
398 | |||
392 | return ret; | 399 | return ret; |
393 | } | 400 | } |
394 | 401 | ||
@@ -491,3 +498,21 @@ void fsnotify_init_mark(struct fsnotify_mark *mark, | |||
491 | atomic_set(&mark->refcnt, 1); | 498 | atomic_set(&mark->refcnt, 1); |
492 | mark->free_mark = free_mark; | 499 | mark->free_mark = free_mark; |
493 | } | 500 | } |
501 | |||
502 | static void fsnotify_mark_destroy(struct work_struct *work) | ||
503 | { | ||
504 | struct fsnotify_mark *mark, *next; | ||
505 | struct list_head private_destroy_list; | ||
506 | |||
507 | spin_lock(&destroy_lock); | ||
508 | /* exchange the list head */ | ||
509 | list_replace_init(&destroy_list, &private_destroy_list); | ||
510 | spin_unlock(&destroy_lock); | ||
511 | |||
512 | synchronize_srcu(&fsnotify_mark_srcu); | ||
513 | |||
514 | list_for_each_entry_safe(mark, next, &private_destroy_list, g_list) { | ||
515 | list_del_init(&mark->g_list); | ||
516 | fsnotify_put_mark(mark); | ||
517 | } | ||
518 | } | ||