diff options
author | Louis Rilling <Louis.Rilling@kerlabs.com> | 2008-06-16 13:00:58 -0400 |
---|---|---|
committer | Mark Fasheh <mfasheh@suse.com> | 2008-07-14 16:57:15 -0400 |
commit | 6f61076406251626be39651d114fac412b1e0c39 (patch) | |
tree | d7adb1de212cfb7ead490b448bf75ce3b3b91c9c /fs | |
parent | fe9f387740ac7cb3b7c2fffa76807e997e6c6292 (diff) |
configfs: Introduce configfs_dirent_lock
This patch introduces configfs_dirent_lock spinlock to protect configfs_dirent
traversals against linkage mutations (add/del/move). This will allow
configfs_detach_prep() to avoid locking i_mutexes.
Locking rules for configfs_dirent linkage mutations are the same plus the
requirement of taking configfs_dirent_lock. For configfs_dirent walking, one can
either take appropriate i_mutex as before, or take configfs_dirent_lock.
The spinlock could actually be a mutex, but the critical sections are either
O(1) or should not be too long (default groups walking in last patch).
ChangeLog:
- Clarify the comment on configfs_dirent_lock usage
- Move sd->s_element init before linking the new dirent
- In lseek(), do not release configfs_dirent_lock before the dirent is
relinked.
Signed-off-by: Louis Rilling <Louis.Rilling@kerlabs.com>
Signed-off-by: Joel Becker <joel.becker@oracle.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/configfs/configfs_internal.h | 3 | ||||
-rw-r--r-- | fs/configfs/dir.c | 28 | ||||
-rw-r--r-- | fs/configfs/inode.c | 2 | ||||
-rw-r--r-- | fs/configfs/symlink.c | 2 |
4 files changed, 34 insertions, 1 deletions
diff --git a/fs/configfs/configfs_internal.h b/fs/configfs/configfs_internal.h index cca98609aa7f..5a33b58e66da 100644 --- a/fs/configfs/configfs_internal.h +++ b/fs/configfs/configfs_internal.h | |||
@@ -26,6 +26,7 @@ | |||
26 | 26 | ||
27 | #include <linux/slab.h> | 27 | #include <linux/slab.h> |
28 | #include <linux/list.h> | 28 | #include <linux/list.h> |
29 | #include <linux/spinlock.h> | ||
29 | 30 | ||
30 | struct configfs_dirent { | 31 | struct configfs_dirent { |
31 | atomic_t s_count; | 32 | atomic_t s_count; |
@@ -49,6 +50,8 @@ struct configfs_dirent { | |||
49 | #define CONFIGFS_USET_DROPPING 0x0100 | 50 | #define CONFIGFS_USET_DROPPING 0x0100 |
50 | #define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR) | 51 | #define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR) |
51 | 52 | ||
53 | extern spinlock_t configfs_dirent_lock; | ||
54 | |||
52 | extern struct vfsmount * configfs_mount; | 55 | extern struct vfsmount * configfs_mount; |
53 | extern struct kmem_cache *configfs_dir_cachep; | 56 | extern struct kmem_cache *configfs_dir_cachep; |
54 | 57 | ||
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index a48dc7dd8765..2619f485bc3d 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c | |||
@@ -35,6 +35,14 @@ | |||
35 | #include "configfs_internal.h" | 35 | #include "configfs_internal.h" |
36 | 36 | ||
37 | DECLARE_RWSEM(configfs_rename_sem); | 37 | DECLARE_RWSEM(configfs_rename_sem); |
38 | /* | ||
39 | * Protects mutations of configfs_dirent linkage together with proper i_mutex | ||
40 | * Mutators of configfs_dirent linkage must *both* have the proper inode locked | ||
41 | * and configfs_dirent_lock locked, in that order. | ||
42 | * This allows one to safely traverse configfs_dirent trees without having to | ||
43 | * lock inodes. | ||
44 | */ | ||
45 | DEFINE_SPINLOCK(configfs_dirent_lock); | ||
38 | 46 | ||
39 | static void configfs_d_iput(struct dentry * dentry, | 47 | static void configfs_d_iput(struct dentry * dentry, |
40 | struct inode * inode) | 48 | struct inode * inode) |
@@ -79,8 +87,10 @@ static struct configfs_dirent *configfs_new_dirent(struct configfs_dirent * pare | |||
79 | atomic_set(&sd->s_count, 1); | 87 | atomic_set(&sd->s_count, 1); |
80 | INIT_LIST_HEAD(&sd->s_links); | 88 | INIT_LIST_HEAD(&sd->s_links); |
81 | INIT_LIST_HEAD(&sd->s_children); | 89 | INIT_LIST_HEAD(&sd->s_children); |
82 | list_add(&sd->s_sibling, &parent_sd->s_children); | ||
83 | sd->s_element = element; | 90 | sd->s_element = element; |
91 | spin_lock(&configfs_dirent_lock); | ||
92 | list_add(&sd->s_sibling, &parent_sd->s_children); | ||
93 | spin_unlock(&configfs_dirent_lock); | ||
84 | 94 | ||
85 | return sd; | 95 | return sd; |
86 | } | 96 | } |
@@ -173,7 +183,9 @@ static int create_dir(struct config_item * k, struct dentry * p, | |||
173 | } else { | 183 | } else { |
174 | struct configfs_dirent *sd = d->d_fsdata; | 184 | struct configfs_dirent *sd = d->d_fsdata; |
175 | if (sd) { | 185 | if (sd) { |
186 | spin_lock(&configfs_dirent_lock); | ||
176 | list_del_init(&sd->s_sibling); | 187 | list_del_init(&sd->s_sibling); |
188 | spin_unlock(&configfs_dirent_lock); | ||
177 | configfs_put(sd); | 189 | configfs_put(sd); |
178 | } | 190 | } |
179 | } | 191 | } |
@@ -224,7 +236,9 @@ int configfs_create_link(struct configfs_symlink *sl, | |||
224 | else { | 236 | else { |
225 | struct configfs_dirent *sd = dentry->d_fsdata; | 237 | struct configfs_dirent *sd = dentry->d_fsdata; |
226 | if (sd) { | 238 | if (sd) { |
239 | spin_lock(&configfs_dirent_lock); | ||
227 | list_del_init(&sd->s_sibling); | 240 | list_del_init(&sd->s_sibling); |
241 | spin_unlock(&configfs_dirent_lock); | ||
228 | configfs_put(sd); | 242 | configfs_put(sd); |
229 | } | 243 | } |
230 | } | 244 | } |
@@ -238,7 +252,9 @@ static void remove_dir(struct dentry * d) | |||
238 | struct configfs_dirent * sd; | 252 | struct configfs_dirent * sd; |
239 | 253 | ||
240 | sd = d->d_fsdata; | 254 | sd = d->d_fsdata; |
255 | spin_lock(&configfs_dirent_lock); | ||
241 | list_del_init(&sd->s_sibling); | 256 | list_del_init(&sd->s_sibling); |
257 | spin_unlock(&configfs_dirent_lock); | ||
242 | configfs_put(sd); | 258 | configfs_put(sd); |
243 | if (d->d_inode) | 259 | if (d->d_inode) |
244 | simple_rmdir(parent->d_inode,d); | 260 | simple_rmdir(parent->d_inode,d); |
@@ -410,7 +426,9 @@ static void detach_attrs(struct config_item * item) | |||
410 | list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) { | 426 | list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) { |
411 | if (!sd->s_element || !(sd->s_type & CONFIGFS_NOT_PINNED)) | 427 | if (!sd->s_element || !(sd->s_type & CONFIGFS_NOT_PINNED)) |
412 | continue; | 428 | continue; |
429 | spin_lock(&configfs_dirent_lock); | ||
413 | list_del_init(&sd->s_sibling); | 430 | list_del_init(&sd->s_sibling); |
431 | spin_unlock(&configfs_dirent_lock); | ||
414 | configfs_drop_dentry(sd, dentry); | 432 | configfs_drop_dentry(sd, dentry); |
415 | configfs_put(sd); | 433 | configfs_put(sd); |
416 | } | 434 | } |
@@ -1268,7 +1286,9 @@ static int configfs_dir_close(struct inode *inode, struct file *file) | |||
1268 | struct configfs_dirent * cursor = file->private_data; | 1286 | struct configfs_dirent * cursor = file->private_data; |
1269 | 1287 | ||
1270 | mutex_lock(&dentry->d_inode->i_mutex); | 1288 | mutex_lock(&dentry->d_inode->i_mutex); |
1289 | spin_lock(&configfs_dirent_lock); | ||
1271 | list_del_init(&cursor->s_sibling); | 1290 | list_del_init(&cursor->s_sibling); |
1291 | spin_unlock(&configfs_dirent_lock); | ||
1272 | mutex_unlock(&dentry->d_inode->i_mutex); | 1292 | mutex_unlock(&dentry->d_inode->i_mutex); |
1273 | 1293 | ||
1274 | release_configfs_dirent(cursor); | 1294 | release_configfs_dirent(cursor); |
@@ -1308,7 +1328,9 @@ static int configfs_readdir(struct file * filp, void * dirent, filldir_t filldir | |||
1308 | /* fallthrough */ | 1328 | /* fallthrough */ |
1309 | default: | 1329 | default: |
1310 | if (filp->f_pos == 2) { | 1330 | if (filp->f_pos == 2) { |
1331 | spin_lock(&configfs_dirent_lock); | ||
1311 | list_move(q, &parent_sd->s_children); | 1332 | list_move(q, &parent_sd->s_children); |
1333 | spin_unlock(&configfs_dirent_lock); | ||
1312 | } | 1334 | } |
1313 | for (p=q->next; p!= &parent_sd->s_children; p=p->next) { | 1335 | for (p=q->next; p!= &parent_sd->s_children; p=p->next) { |
1314 | struct configfs_dirent *next; | 1336 | struct configfs_dirent *next; |
@@ -1331,7 +1353,9 @@ static int configfs_readdir(struct file * filp, void * dirent, filldir_t filldir | |||
1331 | dt_type(next)) < 0) | 1353 | dt_type(next)) < 0) |
1332 | return 0; | 1354 | return 0; |
1333 | 1355 | ||
1356 | spin_lock(&configfs_dirent_lock); | ||
1334 | list_move(q, p); | 1357 | list_move(q, p); |
1358 | spin_unlock(&configfs_dirent_lock); | ||
1335 | p = q; | 1359 | p = q; |
1336 | filp->f_pos++; | 1360 | filp->f_pos++; |
1337 | } | 1361 | } |
@@ -1362,6 +1386,7 @@ static loff_t configfs_dir_lseek(struct file * file, loff_t offset, int origin) | |||
1362 | struct list_head *p; | 1386 | struct list_head *p; |
1363 | loff_t n = file->f_pos - 2; | 1387 | loff_t n = file->f_pos - 2; |
1364 | 1388 | ||
1389 | spin_lock(&configfs_dirent_lock); | ||
1365 | list_del(&cursor->s_sibling); | 1390 | list_del(&cursor->s_sibling); |
1366 | p = sd->s_children.next; | 1391 | p = sd->s_children.next; |
1367 | while (n && p != &sd->s_children) { | 1392 | while (n && p != &sd->s_children) { |
@@ -1373,6 +1398,7 @@ static loff_t configfs_dir_lseek(struct file * file, loff_t offset, int origin) | |||
1373 | p = p->next; | 1398 | p = p->next; |
1374 | } | 1399 | } |
1375 | list_add_tail(&cursor->s_sibling, p); | 1400 | list_add_tail(&cursor->s_sibling, p); |
1401 | spin_unlock(&configfs_dirent_lock); | ||
1376 | } | 1402 | } |
1377 | } | 1403 | } |
1378 | mutex_unlock(&dentry->d_inode->i_mutex); | 1404 | mutex_unlock(&dentry->d_inode->i_mutex); |
diff --git a/fs/configfs/inode.c b/fs/configfs/inode.c index b9a1d810346d..4803ccc94480 100644 --- a/fs/configfs/inode.c +++ b/fs/configfs/inode.c | |||
@@ -247,7 +247,9 @@ void configfs_hash_and_remove(struct dentry * dir, const char * name) | |||
247 | if (!sd->s_element) | 247 | if (!sd->s_element) |
248 | continue; | 248 | continue; |
249 | if (!strcmp(configfs_get_name(sd), name)) { | 249 | if (!strcmp(configfs_get_name(sd), name)) { |
250 | spin_lock(&configfs_dirent_lock); | ||
250 | list_del_init(&sd->s_sibling); | 251 | list_del_init(&sd->s_sibling); |
252 | spin_unlock(&configfs_dirent_lock); | ||
251 | configfs_drop_dentry(sd, dir); | 253 | configfs_drop_dentry(sd, dir); |
252 | configfs_put(sd); | 254 | configfs_put(sd); |
253 | break; | 255 | break; |
diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c index 2a731ef5f305..676c84c416da 100644 --- a/fs/configfs/symlink.c +++ b/fs/configfs/symlink.c | |||
@@ -169,7 +169,9 @@ int configfs_unlink(struct inode *dir, struct dentry *dentry) | |||
169 | parent_item = configfs_get_config_item(dentry->d_parent); | 169 | parent_item = configfs_get_config_item(dentry->d_parent); |
170 | type = parent_item->ci_type; | 170 | type = parent_item->ci_type; |
171 | 171 | ||
172 | spin_lock(&configfs_dirent_lock); | ||
172 | list_del_init(&sd->s_sibling); | 173 | list_del_init(&sd->s_sibling); |
174 | spin_unlock(&configfs_dirent_lock); | ||
173 | configfs_drop_dentry(sd, dentry->d_parent); | 175 | configfs_drop_dentry(sd, dentry->d_parent); |
174 | dput(dentry); | 176 | dput(dentry); |
175 | configfs_put(sd); | 177 | configfs_put(sd); |