diff options
Diffstat (limited to 'fs/notify/group.c')
-rw-r--r-- | fs/notify/group.c | 182 |
1 files changed, 16 insertions, 166 deletions
diff --git a/fs/notify/group.c b/fs/notify/group.c index 0e1677144bc5..d309f38449cb 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c | |||
@@ -28,64 +28,6 @@ | |||
28 | 28 | ||
29 | #include <asm/atomic.h> | 29 | #include <asm/atomic.h> |
30 | 30 | ||
31 | /* protects writes to fsnotify_groups and fsnotify_mask */ | ||
32 | static DEFINE_MUTEX(fsnotify_grp_mutex); | ||
33 | /* protects reads while running the fsnotify_groups list */ | ||
34 | struct srcu_struct fsnotify_grp_srcu; | ||
35 | /* all groups registered to receive filesystem notifications */ | ||
36 | LIST_HEAD(fsnotify_groups); | ||
37 | /* bitwise OR of all events (FS_*) interesting to some group on this system */ | ||
38 | __u32 fsnotify_mask; | ||
39 | |||
40 | /* | ||
41 | * When a new group registers or changes it's set of interesting events | ||
42 | * this function updates the fsnotify_mask to contain all interesting events | ||
43 | */ | ||
44 | void fsnotify_recalc_global_mask(void) | ||
45 | { | ||
46 | struct fsnotify_group *group; | ||
47 | __u32 mask = 0; | ||
48 | int idx; | ||
49 | |||
50 | idx = srcu_read_lock(&fsnotify_grp_srcu); | ||
51 | list_for_each_entry_rcu(group, &fsnotify_groups, group_list) | ||
52 | mask |= group->mask; | ||
53 | srcu_read_unlock(&fsnotify_grp_srcu, idx); | ||
54 | fsnotify_mask = mask; | ||
55 | } | ||
56 | |||
57 | /* | ||
58 | * Update the group->mask by running all of the marks associated with this | ||
59 | * group and finding the bitwise | of all of the mark->mask. If we change | ||
60 | * the group->mask we need to update the global mask of events interesting | ||
61 | * to the system. | ||
62 | */ | ||
63 | void fsnotify_recalc_group_mask(struct fsnotify_group *group) | ||
64 | { | ||
65 | __u32 mask = 0; | ||
66 | __u32 old_mask = group->mask; | ||
67 | struct fsnotify_mark_entry *entry; | ||
68 | |||
69 | spin_lock(&group->mark_lock); | ||
70 | list_for_each_entry(entry, &group->mark_entries, g_list) | ||
71 | mask |= entry->mask; | ||
72 | spin_unlock(&group->mark_lock); | ||
73 | |||
74 | group->mask = mask; | ||
75 | |||
76 | if (old_mask != mask) | ||
77 | fsnotify_recalc_global_mask(); | ||
78 | } | ||
79 | |||
80 | /* | ||
81 | * Take a reference to a group so things found under the fsnotify_grp_mutex | ||
82 | * can't get freed under us | ||
83 | */ | ||
84 | static void fsnotify_get_group(struct fsnotify_group *group) | ||
85 | { | ||
86 | atomic_inc(&group->refcnt); | ||
87 | } | ||
88 | |||
89 | /* | 31 | /* |
90 | * Final freeing of a group | 32 | * Final freeing of a group |
91 | */ | 33 | */ |
@@ -110,145 +52,53 @@ void fsnotify_final_destroy_group(struct fsnotify_group *group) | |||
110 | */ | 52 | */ |
111 | static void fsnotify_destroy_group(struct fsnotify_group *group) | 53 | static void fsnotify_destroy_group(struct fsnotify_group *group) |
112 | { | 54 | { |
113 | /* clear all inode mark entries for this group */ | 55 | /* clear all inode marks for this group */ |
114 | fsnotify_clear_marks_by_group(group); | 56 | fsnotify_clear_marks_by_group(group); |
115 | 57 | ||
58 | synchronize_srcu(&fsnotify_mark_srcu); | ||
59 | |||
116 | /* past the point of no return, matches the initial value of 1 */ | 60 | /* past the point of no return, matches the initial value of 1 */ |
117 | if (atomic_dec_and_test(&group->num_marks)) | 61 | if (atomic_dec_and_test(&group->num_marks)) |
118 | fsnotify_final_destroy_group(group); | 62 | fsnotify_final_destroy_group(group); |
119 | } | 63 | } |
120 | 64 | ||
121 | /* | 65 | /* |
122 | * Remove this group from the global list of groups that will get events | ||
123 | * this can be done even if there are still references and things still using | ||
124 | * this group. This just stops the group from getting new events. | ||
125 | */ | ||
126 | static void __fsnotify_evict_group(struct fsnotify_group *group) | ||
127 | { | ||
128 | BUG_ON(!mutex_is_locked(&fsnotify_grp_mutex)); | ||
129 | |||
130 | if (group->on_group_list) | ||
131 | list_del_rcu(&group->group_list); | ||
132 | group->on_group_list = 0; | ||
133 | } | ||
134 | |||
135 | /* | ||
136 | * Called when a group is no longer interested in getting events. This can be | ||
137 | * used if a group is misbehaving or if for some reason a group should no longer | ||
138 | * get any filesystem events. | ||
139 | */ | ||
140 | void fsnotify_evict_group(struct fsnotify_group *group) | ||
141 | { | ||
142 | mutex_lock(&fsnotify_grp_mutex); | ||
143 | __fsnotify_evict_group(group); | ||
144 | mutex_unlock(&fsnotify_grp_mutex); | ||
145 | } | ||
146 | |||
147 | /* | ||
148 | * Drop a reference to a group. Free it if it's through. | 66 | * Drop a reference to a group. Free it if it's through. |
149 | */ | 67 | */ |
150 | void fsnotify_put_group(struct fsnotify_group *group) | 68 | void fsnotify_put_group(struct fsnotify_group *group) |
151 | { | 69 | { |
152 | if (!atomic_dec_and_mutex_lock(&group->refcnt, &fsnotify_grp_mutex)) | 70 | if (atomic_dec_and_test(&group->refcnt)) |
153 | return; | 71 | fsnotify_destroy_group(group); |
154 | |||
155 | /* | ||
156 | * OK, now we know that there's no other users *and* we hold mutex, | ||
157 | * so no new references will appear | ||
158 | */ | ||
159 | __fsnotify_evict_group(group); | ||
160 | |||
161 | /* | ||
162 | * now it's off the list, so the only thing we might care about is | ||
163 | * srcu access.... | ||
164 | */ | ||
165 | mutex_unlock(&fsnotify_grp_mutex); | ||
166 | synchronize_srcu(&fsnotify_grp_srcu); | ||
167 | |||
168 | /* and now it is really dead. _Nothing_ could be seeing it */ | ||
169 | fsnotify_recalc_global_mask(); | ||
170 | fsnotify_destroy_group(group); | ||
171 | } | ||
172 | |||
173 | /* | ||
174 | * Simply run the fsnotify_groups list and find a group which matches | ||
175 | * the given parameters. If a group is found we take a reference to that | ||
176 | * group. | ||
177 | */ | ||
178 | static struct fsnotify_group *fsnotify_find_group(unsigned int group_num, __u32 mask, | ||
179 | const struct fsnotify_ops *ops) | ||
180 | { | ||
181 | struct fsnotify_group *group_iter; | ||
182 | struct fsnotify_group *group = NULL; | ||
183 | |||
184 | BUG_ON(!mutex_is_locked(&fsnotify_grp_mutex)); | ||
185 | |||
186 | list_for_each_entry_rcu(group_iter, &fsnotify_groups, group_list) { | ||
187 | if (group_iter->group_num == group_num) { | ||
188 | if ((group_iter->mask == mask) && | ||
189 | (group_iter->ops == ops)) { | ||
190 | fsnotify_get_group(group_iter); | ||
191 | group = group_iter; | ||
192 | } else | ||
193 | group = ERR_PTR(-EEXIST); | ||
194 | } | ||
195 | } | ||
196 | return group; | ||
197 | } | 72 | } |
198 | 73 | ||
199 | /* | 74 | /* |
200 | * Either finds an existing group which matches the group_num, mask, and ops or | 75 | * Create a new fsnotify_group and hold a reference for the group returned. |
201 | * creates a new group and adds it to the global group list. In either case we | ||
202 | * take a reference for the group returned. | ||
203 | */ | 76 | */ |
204 | struct fsnotify_group *fsnotify_obtain_group(unsigned int group_num, __u32 mask, | 77 | struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) |
205 | const struct fsnotify_ops *ops) | ||
206 | { | 78 | { |
207 | struct fsnotify_group *group, *tgroup; | 79 | struct fsnotify_group *group; |
208 | 80 | ||
209 | /* very low use, simpler locking if we just always alloc */ | 81 | group = kzalloc(sizeof(struct fsnotify_group), GFP_KERNEL); |
210 | group = kmalloc(sizeof(struct fsnotify_group), GFP_KERNEL); | ||
211 | if (!group) | 82 | if (!group) |
212 | return ERR_PTR(-ENOMEM); | 83 | return ERR_PTR(-ENOMEM); |
213 | 84 | ||
85 | /* set to 0 when there a no external references to this group */ | ||
214 | atomic_set(&group->refcnt, 1); | 86 | atomic_set(&group->refcnt, 1); |
215 | 87 | /* | |
216 | group->on_group_list = 0; | 88 | * hits 0 when there are no external references AND no marks for |
217 | group->group_num = group_num; | 89 | * this group |
218 | group->mask = mask; | 90 | */ |
91 | atomic_set(&group->num_marks, 1); | ||
219 | 92 | ||
220 | mutex_init(&group->notification_mutex); | 93 | mutex_init(&group->notification_mutex); |
221 | INIT_LIST_HEAD(&group->notification_list); | 94 | INIT_LIST_HEAD(&group->notification_list); |
222 | init_waitqueue_head(&group->notification_waitq); | 95 | init_waitqueue_head(&group->notification_waitq); |
223 | group->q_len = 0; | ||
224 | group->max_events = UINT_MAX; | 96 | group->max_events = UINT_MAX; |
225 | 97 | ||
226 | spin_lock_init(&group->mark_lock); | 98 | spin_lock_init(&group->mark_lock); |
227 | atomic_set(&group->num_marks, 0); | 99 | INIT_LIST_HEAD(&group->marks_list); |
228 | INIT_LIST_HEAD(&group->mark_entries); | ||
229 | 100 | ||
230 | group->ops = ops; | 101 | group->ops = ops; |
231 | 102 | ||
232 | mutex_lock(&fsnotify_grp_mutex); | ||
233 | tgroup = fsnotify_find_group(group_num, mask, ops); | ||
234 | if (tgroup) { | ||
235 | /* group already exists */ | ||
236 | mutex_unlock(&fsnotify_grp_mutex); | ||
237 | /* destroy the new one we made */ | ||
238 | fsnotify_put_group(group); | ||
239 | return tgroup; | ||
240 | } | ||
241 | |||
242 | /* group not found, add a new one */ | ||
243 | list_add_rcu(&group->group_list, &fsnotify_groups); | ||
244 | group->on_group_list = 1; | ||
245 | /* being on the fsnotify_groups list holds one num_marks */ | ||
246 | atomic_inc(&group->num_marks); | ||
247 | |||
248 | mutex_unlock(&fsnotify_grp_mutex); | ||
249 | |||
250 | if (mask) | ||
251 | fsnotify_recalc_global_mask(); | ||
252 | |||
253 | return group; | 103 | return group; |
254 | } | 104 | } |