aboutsummaryrefslogtreecommitdiffstats
path: root/fs/notify
diff options
context:
space:
mode:
authorLino Sanfilippo <LinoSanfilippo@gmx.de>2011-06-14 11:29:47 -0400
committerEric Paris <eparis@redhat.com>2012-12-11 13:29:44 -0500
commit23e964c284ca0a767b80a30482bd53b059d30391 (patch)
tree43d3cd8c5836a3c43f853366bce51f621773dedc /fs/notify
parent986129520479d689962a42c31acdeaf854ac91f5 (diff)
fsnotify: use reference counting for groups
Get a group ref for each mark that is added to the groups list and release that ref when the mark is freed in fsnotify_put_mark(). We also use get a group reference for duplicated marks and for private event data. Now we dont free a group any more when the number of marks becomes 0 but when the groups ref count does. Since this will only happen when all marks are removed from a groups mark list, we dont have to set the groups number of marks to 1 at group creation. Beside clearing all marks in fsnotify_destroy_group() we do also flush the groups event queue. This is since events may hold references to groups (due to private event data) and we have to put those references first before we get a chance to put the final ref, which will result in a call to fsnotify_final_destroy_group(). Signed-off-by: Lino Sanfilippo <LinoSanfilippo@gmx.de> Signed-off-by: Eric Paris <eparis@redhat.com>
Diffstat (limited to 'fs/notify')
-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;