aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/notify/group.c28
-rw-r--r--fs/notify/inotify/inotify_fsnotify.c2
-rw-r--r--fs/notify/inotify/inotify_user.c1
-rw-r--r--fs/notify/mark.c24
4 files changed, 27 insertions, 28 deletions
diff --git a/fs/notify/group.c b/fs/notify/group.c
index 1d57c35f1043..354044c47e23 100644
--- a/fs/notify/group.c
+++ b/fs/notify/group.c
@@ -33,9 +33,6 @@
33 */ 33 */
34void fsnotify_final_destroy_group(struct fsnotify_group *group) 34void fsnotify_final_destroy_group(struct fsnotify_group *group)
35{ 35{
36 /* clear the notification queue of all events */
37 fsnotify_flush_notify(group);
38
39 if (group->ops->free_group_priv) 36 if (group->ops->free_group_priv)
40 group->ops->free_group_priv(group); 37 group->ops->free_group_priv(group);
41 38
@@ -43,12 +40,10 @@ void fsnotify_final_destroy_group(struct fsnotify_group *group)
43} 40}
44 41
45/* 42/*
46 * Trying to get rid of a group. We need to first get rid of any outstanding 43 * Trying to get rid of a group. Remove all marks, flush all events and release
47 * allocations and then free the group. Remember that fsnotify_clear_marks_by_group 44 * the group reference.
48 * could miss marks that are being freed by inode and those marks could still 45 * Note that another thread calling fsnotify_clear_marks_by_group() may still
49 * hold a reference to this group (via group->num_marks) If we get into that 46 * hold a ref to the group.
50 * situtation, the fsnotify_final_destroy_group will get called when that final
51 * mark is freed.
52 */ 47 */
53void fsnotify_destroy_group(struct fsnotify_group *group) 48void fsnotify_destroy_group(struct fsnotify_group *group)
54{ 49{
@@ -57,9 +52,10 @@ void fsnotify_destroy_group(struct fsnotify_group *group)
57 52
58 synchronize_srcu(&fsnotify_mark_srcu); 53 synchronize_srcu(&fsnotify_mark_srcu);
59 54
60 /* past the point of no return, matches the initial value of 1 */ 55 /* clear the notification queue of all events */
61 if (atomic_dec_and_test(&group->num_marks)) 56 fsnotify_flush_notify(group);
62 fsnotify_final_destroy_group(group); 57
58 fsnotify_put_group(group);
63} 59}
64 60
65/* 61/*
@@ -76,7 +72,7 @@ void fsnotify_get_group(struct fsnotify_group *group)
76void fsnotify_put_group(struct fsnotify_group *group) 72void fsnotify_put_group(struct fsnotify_group *group)
77{ 73{
78 if (atomic_dec_and_test(&group->refcnt)) 74 if (atomic_dec_and_test(&group->refcnt))
79 fsnotify_destroy_group(group); 75 fsnotify_final_destroy_group(group);
80} 76}
81 77
82/* 78/*
@@ -92,11 +88,7 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops)
92 88
93 /* set to 0 when there a no external references to this group */ 89 /* set to 0 when there a no external references to this group */
94 atomic_set(&group->refcnt, 1); 90 atomic_set(&group->refcnt, 1);
95 /* 91 atomic_set(&group->num_marks, 0);
96 * hits 0 when there are no external references AND no marks for
97 * this group
98 */
99 atomic_set(&group->num_marks, 1);
100 92
101 mutex_init(&group->notification_mutex); 93 mutex_init(&group->notification_mutex);
102 INIT_LIST_HEAD(&group->notification_list); 94 INIT_LIST_HEAD(&group->notification_list);
diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c
index e3cbd746f64a..74977fbf5aae 100644
--- a/fs/notify/inotify/inotify_fsnotify.c
+++ b/fs/notify/inotify/inotify_fsnotify.c
@@ -118,6 +118,7 @@ static int inotify_handle_event(struct fsnotify_group *group,
118 118
119 fsn_event_priv = &event_priv->fsnotify_event_priv_data; 119 fsn_event_priv = &event_priv->fsnotify_event_priv_data;
120 120
121 fsnotify_get_group(group);
121 fsn_event_priv->group = group; 122 fsn_event_priv->group = group;
122 event_priv->wd = wd; 123 event_priv->wd = wd;
123 124
@@ -210,6 +211,7 @@ void inotify_free_event_priv(struct fsnotify_event_private_data *fsn_event_priv)
210 event_priv = container_of(fsn_event_priv, struct inotify_event_private_data, 211 event_priv = container_of(fsn_event_priv, struct inotify_event_private_data,
211 fsnotify_event_priv_data); 212 fsnotify_event_priv_data);
212 213
214 fsnotify_put_group(fsn_event_priv->group);
213 kmem_cache_free(event_priv_cachep, event_priv); 215 kmem_cache_free(event_priv_cachep, event_priv);
214} 216}
215 217
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c
index dbafbfc8ceca..246250f1db7a 100644
--- a/fs/notify/inotify/inotify_user.c
+++ b/fs/notify/inotify/inotify_user.c
@@ -531,6 +531,7 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark,
531 531
532 fsn_event_priv = &event_priv->fsnotify_event_priv_data; 532 fsn_event_priv = &event_priv->fsnotify_event_priv_data;
533 533
534 fsnotify_get_group(group);
534 fsn_event_priv->group = group; 535 fsn_event_priv->group = group;
535 event_priv->wd = i_mark->wd; 536 event_priv->wd = i_mark->wd;
536 537
diff --git a/fs/notify/mark.c b/fs/notify/mark.c
index f104d565b682..3c7a1699df3d 100644
--- a/fs/notify/mark.c
+++ b/fs/notify/mark.c
@@ -109,8 +109,11 @@ void fsnotify_get_mark(struct fsnotify_mark *mark)
109 109
110void fsnotify_put_mark(struct fsnotify_mark *mark) 110void fsnotify_put_mark(struct fsnotify_mark *mark)
111{ 111{
112 if (atomic_dec_and_test(&mark->refcnt)) 112 if (atomic_dec_and_test(&mark->refcnt)) {
113 if (mark->group)
114 fsnotify_put_group(mark->group);
113 mark->free_mark(mark); 115 mark->free_mark(mark);
116 }
114} 117}
115 118
116/* 119/*
@@ -125,12 +128,13 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark)
125 128
126 spin_lock(&mark->lock); 129 spin_lock(&mark->lock);
127 130
131 fsnotify_get_group(mark->group);
128 group = mark->group; 132 group = mark->group;
129 133
130 /* something else already called this function on this mark */ 134 /* something else already called this function on this mark */
131 if (!(mark->flags & FSNOTIFY_MARK_FLAG_ALIVE)) { 135 if (!(mark->flags & FSNOTIFY_MARK_FLAG_ALIVE)) {
132 spin_unlock(&mark->lock); 136 spin_unlock(&mark->lock);
133 return; 137 goto put_group;
134 } 138 }
135 139
136 mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE; 140 mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE;
@@ -177,19 +181,15 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark)
177 181
178 if (inode && (mark->flags & FSNOTIFY_MARK_FLAG_OBJECT_PINNED)) 182 if (inode && (mark->flags & FSNOTIFY_MARK_FLAG_OBJECT_PINNED))
179 iput(inode); 183 iput(inode);
180
181 /* 184 /*
182 * We don't necessarily have a ref on mark from caller so the above iput 185 * We don't necessarily have a ref on mark from caller so the above iput
183 * may have already destroyed it. Don't touch from now on. 186 * may have already destroyed it. Don't touch from now on.
184 */ 187 */
185 188
186 /* 189 atomic_dec(&group->num_marks);
187 * it's possible that this group tried to destroy itself, but this 190
188 * this mark was simultaneously being freed by inode. If that's the 191put_group:
189 * case, we finish freeing the group here. 192 fsnotify_put_group(group);
190 */
191 if (unlikely(atomic_dec_and_test(&group->num_marks)))
192 fsnotify_final_destroy_group(group);
193} 193}
194 194
195void fsnotify_set_mark_mask_locked(struct fsnotify_mark *mark, __u32 mask) 195void fsnotify_set_mark_mask_locked(struct fsnotify_mark *mark, __u32 mask)
@@ -234,6 +234,7 @@ int fsnotify_add_mark(struct fsnotify_mark *mark,
234 234
235 mark->flags |= FSNOTIFY_MARK_FLAG_ALIVE; 235 mark->flags |= FSNOTIFY_MARK_FLAG_ALIVE;
236 236
237 fsnotify_get_group(group);
237 mark->group = group; 238 mark->group = group;
238 list_add(&mark->g_list, &group->marks_list); 239 list_add(&mark->g_list, &group->marks_list);
239 atomic_inc(&group->num_marks); 240 atomic_inc(&group->num_marks);
@@ -265,6 +266,7 @@ int fsnotify_add_mark(struct fsnotify_mark *mark,
265err: 266err:
266 mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE; 267 mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE;
267 list_del_init(&mark->g_list); 268 list_del_init(&mark->g_list);
269 fsnotify_put_group(group);
268 mark->group = NULL; 270 mark->group = NULL;
269 atomic_dec(&group->num_marks); 271 atomic_dec(&group->num_marks);
270 272
@@ -317,6 +319,8 @@ void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *ol
317 assert_spin_locked(&old->lock); 319 assert_spin_locked(&old->lock);
318 new->i.inode = old->i.inode; 320 new->i.inode = old->i.inode;
319 new->m.mnt = old->m.mnt; 321 new->m.mnt = old->m.mnt;
322 if (old->group)
323 fsnotify_get_group(old->group);
320 new->group = old->group; 324 new->group = old->group;
321 new->mask = old->mask; 325 new->mask = old->mask;
322 new->free_mark = old->free_mark; 326 new->free_mark = old->free_mark;