aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/notify/inode_mark.c40
-rw-r--r--fs/notify/vfsmount_mark.c40
2 files changed, 55 insertions, 25 deletions
diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c
index 0c0a48b1659f..83ce6db34039 100644
--- a/fs/notify/inode_mark.c
+++ b/fs/notify/inode_mark.c
@@ -174,15 +174,17 @@ void fsnotify_set_inode_mark_mask_locked(struct fsnotify_mark *mark,
174} 174}
175 175
176/* 176/*
177 * Attach an initialized mark to a given group and inode. 177 * Attach an initialized mark to a given inode.
178 * These marks may be used for the fsnotify backend to determine which 178 * These marks may be used for the fsnotify backend to determine which
179 * event types should be delivered to which group and for which inodes. 179 * event types should be delivered to which group and for which inodes. These
180 * marks are ordered according to the group's location in memory.
180 */ 181 */
181int fsnotify_add_inode_mark(struct fsnotify_mark *mark, 182int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
182 struct fsnotify_group *group, struct inode *inode, 183 struct fsnotify_group *group, struct inode *inode,
183 int allow_dups) 184 int allow_dups)
184{ 185{
185 struct fsnotify_mark *lmark = NULL; 186 struct fsnotify_mark *lmark;
187 struct hlist_node *node, *last = NULL;
186 int ret = 0; 188 int ret = 0;
187 189
188 mark->flags = FSNOTIFY_MARK_FLAG_INODE; 190 mark->flags = FSNOTIFY_MARK_FLAG_INODE;
@@ -192,21 +194,37 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
192 194
193 spin_lock(&inode->i_lock); 195 spin_lock(&inode->i_lock);
194 196
195 if (!allow_dups) 197 mark->i.inode = inode;
196 lmark = fsnotify_find_inode_mark_locked(group, inode);
197 if (!lmark) {
198 mark->i.inode = inode;
199 198
199 /* is mark the first mark? */
200 if (hlist_empty(&inode->i_fsnotify_marks)) {
200 hlist_add_head(&mark->i.i_list, &inode->i_fsnotify_marks); 201 hlist_add_head(&mark->i.i_list, &inode->i_fsnotify_marks);
202 goto out;
203 }
204
205 /* should mark be in the middle of the current list? */
206 hlist_for_each_entry(lmark, node, &inode->i_fsnotify_marks, i.i_list) {
207 last = node;
208
209 if ((lmark->group == group) && !allow_dups) {
210 ret = -EEXIST;
211 goto out;
212 }
201 213
202 fsnotify_recalc_inode_mask_locked(inode); 214 if (mark->group < lmark->group)
215 continue;
216
217 hlist_add_before(&mark->i.i_list, &lmark->i.i_list);
218 goto out;
203 } 219 }
204 220
221 BUG_ON(last == NULL);
222 /* mark should be the last entry. last is the current last entry */
223 hlist_add_after(last, &mark->i.i_list);
224out:
225 fsnotify_recalc_inode_mask_locked(inode);
205 spin_unlock(&inode->i_lock); 226 spin_unlock(&inode->i_lock);
206 227
207 if (lmark)
208 ret = -EEXIST;
209
210 return ret; 228 return ret;
211} 229}
212 230
diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c
index ec580a25d293..c4b3f14d2530 100644
--- a/fs/notify/vfsmount_mark.c
+++ b/fs/notify/vfsmount_mark.c
@@ -141,34 +141,46 @@ int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark,
141 struct fsnotify_group *group, struct vfsmount *mnt, 141 struct fsnotify_group *group, struct vfsmount *mnt,
142 int allow_dups) 142 int allow_dups)
143{ 143{
144 struct fsnotify_mark *lmark = NULL; 144 struct fsnotify_mark *lmark;
145 struct hlist_node *node, *last = NULL;
145 int ret = 0; 146 int ret = 0;
146 147
147 mark->flags = FSNOTIFY_MARK_FLAG_VFSMOUNT; 148 mark->flags = FSNOTIFY_MARK_FLAG_VFSMOUNT;
148 149
149 /*
150 * LOCKING ORDER!!!!
151 * mark->lock
152 * group->mark_lock
153 * mnt->mnt_root->d_lock
154 */
155 assert_spin_locked(&mark->lock); 150 assert_spin_locked(&mark->lock);
156 assert_spin_locked(&group->mark_lock); 151 assert_spin_locked(&group->mark_lock);
157 152
158 spin_lock(&mnt->mnt_root->d_lock); 153 spin_lock(&mnt->mnt_root->d_lock);
159 154
160 if (!allow_dups) 155 mark->m.mnt = mnt;
161 lmark = fsnotify_find_vfsmount_mark_locked(group, mnt);
162 if (!lmark) {
163 mark->m.mnt = mnt;
164 156
157 /* is mark the first mark? */
158 if (hlist_empty(&mnt->mnt_fsnotify_marks)) {
165 hlist_add_head(&mark->m.m_list, &mnt->mnt_fsnotify_marks); 159 hlist_add_head(&mark->m.m_list, &mnt->mnt_fsnotify_marks);
160 goto out;
161 }
162
163 /* should mark be in the middle of the current list? */
164 hlist_for_each_entry(lmark, node, &mnt->mnt_fsnotify_marks, m.m_list) {
165 last = node;
166
167 if ((lmark->group == group) && !allow_dups) {
168 ret = -EEXIST;
169 goto out;
170 }
171
172 if (mark->group < lmark->group)
173 continue;
166 174
167 fsnotify_recalc_vfsmount_mask_locked(mnt); 175 hlist_add_before(&mark->m.m_list, &lmark->m.m_list);
168 } else { 176 goto out;
169 ret = -EEXIST;
170 } 177 }
171 178
179 BUG_ON(last == NULL);
180 /* mark should be the last entry. last is the current last entry */
181 hlist_add_after(last, &mark->m.m_list);
182out:
183 fsnotify_recalc_vfsmount_mask_locked(mnt);
172 spin_unlock(&mnt->mnt_root->d_lock); 184 spin_unlock(&mnt->mnt_root->d_lock);
173 185
174 return ret; 186 return ret;