aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorLouis Rilling <Louis.Rilling@kerlabs.com>2008-06-16 13:00:58 -0400
committerMark Fasheh <mfasheh@suse.com>2008-07-14 16:57:15 -0400
commit6f61076406251626be39651d114fac412b1e0c39 (patch)
treed7adb1de212cfb7ead490b448bf75ce3b3b91c9c /fs
parentfe9f387740ac7cb3b7c2fffa76807e997e6c6292 (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.h3
-rw-r--r--fs/configfs/dir.c28
-rw-r--r--fs/configfs/inode.c2
-rw-r--r--fs/configfs/symlink.c2
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
30struct configfs_dirent { 31struct 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
53extern spinlock_t configfs_dirent_lock;
54
52extern struct vfsmount * configfs_mount; 55extern struct vfsmount * configfs_mount;
53extern struct kmem_cache *configfs_dir_cachep; 56extern 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
37DECLARE_RWSEM(configfs_rename_sem); 37DECLARE_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 */
45DEFINE_SPINLOCK(configfs_dirent_lock);
38 46
39static void configfs_d_iput(struct dentry * dentry, 47static 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);