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 | |
| 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>
| -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); |
