aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs
diff options
context:
space:
mode:
authorJeff Layton <jlayton@redhat.com>2010-10-28 11:16:44 -0400
committerSteve French <sfrench@us.ibm.com>2010-11-02 15:20:23 -0400
commitb647c35f77af9c07d336247b23014596e9f0a593 (patch)
tree653949250681fd1c23ad529b631da793f95778a6 /fs/cifs
parent413e661c136c52290de1ee19a1b049a4da9dbf51 (diff)
cifs: convert tlink_tree to a rbtree
Radix trees are ideal when you want to track a bunch of pointers and can't embed a tracking structure within the target of those pointers. The tradeoff is an increase in memory, particularly if the tree is sparse. In CIFS, we use the tlink_tree to track tcon_link structs. A tcon_link can never be in more than one tlink_tree, so there's no impediment to using a rb_tree here instead of a radix tree. Convert the new multiuser mount code to use a rb_tree instead. This should reduce the memory required to manage the tlink_tree. Signed-off-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/cifs_fs_sb.h4
-rw-r--r--fs/cifs/cifsfs.c2
-rw-r--r--fs/cifs/cifsglob.h3
-rw-r--r--fs/cifs/connect.c177
4 files changed, 101 insertions, 85 deletions
diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h
index 79576dac336f..e9a393c9c2ca 100644
--- a/fs/cifs/cifs_fs_sb.h
+++ b/fs/cifs/cifs_fs_sb.h
@@ -15,7 +15,7 @@
15 * the GNU Lesser General Public License for more details. 15 * the GNU Lesser General Public License for more details.
16 * 16 *
17 */ 17 */
18#include <linux/radix-tree.h> 18#include <linux/rbtree.h>
19 19
20#ifndef _CIFS_FS_SB_H 20#ifndef _CIFS_FS_SB_H
21#define _CIFS_FS_SB_H 21#define _CIFS_FS_SB_H
@@ -42,7 +42,7 @@
42#define CIFS_MOUNT_MULTIUSER 0x20000 /* multiuser mount */ 42#define CIFS_MOUNT_MULTIUSER 0x20000 /* multiuser mount */
43 43
44struct cifs_sb_info { 44struct cifs_sb_info {
45 struct radix_tree_root tlink_tree; 45 struct rb_root tlink_tree;
46 spinlock_t tlink_tree_lock; 46 spinlock_t tlink_tree_lock;
47 struct tcon_link *master_tlink; 47 struct tcon_link *master_tlink;
48 struct nls_table *local_nls; 48 struct nls_table *local_nls;
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 75c4eaa79588..38526a6c4acf 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -116,7 +116,7 @@ cifs_read_super(struct super_block *sb, void *data,
116 return -ENOMEM; 116 return -ENOMEM;
117 117
118 spin_lock_init(&cifs_sb->tlink_tree_lock); 118 spin_lock_init(&cifs_sb->tlink_tree_lock);
119 INIT_RADIX_TREE(&cifs_sb->tlink_tree, GFP_KERNEL); 119 cifs_sb->tlink_tree = RB_ROOT;
120 120
121 rc = bdi_setup_and_register(&cifs_sb->bdi, "cifs", BDI_CAP_MAP_COPY); 121 rc = bdi_setup_and_register(&cifs_sb->bdi, "cifs", BDI_CAP_MAP_COPY);
122 if (rc) { 122 if (rc) {
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index f259e4d7612d..b577bf0a1bb3 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -336,7 +336,8 @@ struct cifsTconInfo {
336 * "get" on the container. 336 * "get" on the container.
337 */ 337 */
338struct tcon_link { 338struct tcon_link {
339 unsigned long tl_index; 339 struct rb_node tl_rbnode;
340 uid_t tl_uid;
340 unsigned long tl_flags; 341 unsigned long tl_flags;
341#define TCON_LINK_MASTER 0 342#define TCON_LINK_MASTER 0
342#define TCON_LINK_PENDING 1 343#define TCON_LINK_PENDING 1
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 197ac579a70b..c9699ce767b6 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -116,6 +116,7 @@ struct smb_vol {
116 116
117static int ipv4_connect(struct TCP_Server_Info *server); 117static int ipv4_connect(struct TCP_Server_Info *server);
118static int ipv6_connect(struct TCP_Server_Info *server); 118static int ipv6_connect(struct TCP_Server_Info *server);
119static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink);
119static void cifs_prune_tlinks(struct work_struct *work); 120static void cifs_prune_tlinks(struct work_struct *work);
120 121
121/* 122/*
@@ -2900,24 +2901,16 @@ remote_path_check:
2900 goto mount_fail_check; 2901 goto mount_fail_check;
2901 } 2902 }
2902 2903
2903 tlink->tl_index = pSesInfo->linux_uid; 2904 tlink->tl_uid = pSesInfo->linux_uid;
2904 tlink->tl_tcon = tcon; 2905 tlink->tl_tcon = tcon;
2905 tlink->tl_time = jiffies; 2906 tlink->tl_time = jiffies;
2906 set_bit(TCON_LINK_MASTER, &tlink->tl_flags); 2907 set_bit(TCON_LINK_MASTER, &tlink->tl_flags);
2907 set_bit(TCON_LINK_IN_TREE, &tlink->tl_flags); 2908 set_bit(TCON_LINK_IN_TREE, &tlink->tl_flags);
2908 2909
2909 rc = radix_tree_preload(GFP_KERNEL); 2910 cifs_sb->master_tlink = tlink;
2910 if (rc == -ENOMEM) {
2911 kfree(tlink);
2912 goto mount_fail_check;
2913 }
2914
2915 spin_lock(&cifs_sb->tlink_tree_lock); 2911 spin_lock(&cifs_sb->tlink_tree_lock);
2916 radix_tree_insert(&cifs_sb->tlink_tree, pSesInfo->linux_uid, tlink); 2912 tlink_rb_insert(&cifs_sb->tlink_tree, tlink);
2917 spin_unlock(&cifs_sb->tlink_tree_lock); 2913 spin_unlock(&cifs_sb->tlink_tree_lock);
2918 radix_tree_preload_end();
2919
2920 cifs_sb->master_tlink = tlink;
2921 2914
2922 queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks, 2915 queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks,
2923 TLINK_IDLE_EXPIRE); 2916 TLINK_IDLE_EXPIRE);
@@ -3107,32 +3100,25 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
3107int 3100int
3108cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) 3101cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
3109{ 3102{
3110 int i, ret; 3103 struct rb_root *root = &cifs_sb->tlink_tree;
3104 struct rb_node *node;
3105 struct tcon_link *tlink;
3111 char *tmp; 3106 char *tmp;
3112 struct tcon_link *tlink[8];
3113 unsigned long index = 0;
3114 3107
3115 cancel_delayed_work_sync(&cifs_sb->prune_tlinks); 3108 cancel_delayed_work_sync(&cifs_sb->prune_tlinks);
3116 3109
3117 do { 3110 spin_lock(&cifs_sb->tlink_tree_lock);
3118 spin_lock(&cifs_sb->tlink_tree_lock); 3111 while ((node = rb_first(root))) {
3119 ret = radix_tree_gang_lookup(&cifs_sb->tlink_tree, 3112 tlink = rb_entry(node, struct tcon_link, tl_rbnode);
3120 (void **)tlink, index, 3113 cifs_get_tlink(tlink);
3121 ARRAY_SIZE(tlink)); 3114 clear_bit(TCON_LINK_IN_TREE, &tlink->tl_flags);
3122 /* increment index for next pass */ 3115 rb_erase(node, root);
3123 if (ret > 0)
3124 index = tlink[ret - 1]->tl_index + 1;
3125 for (i = 0; i < ret; i++) {
3126 cifs_get_tlink(tlink[i]);
3127 clear_bit(TCON_LINK_IN_TREE, &tlink[i]->tl_flags);
3128 radix_tree_delete(&cifs_sb->tlink_tree,
3129 tlink[i]->tl_index);
3130 }
3131 spin_unlock(&cifs_sb->tlink_tree_lock);
3132 3116
3133 for (i = 0; i < ret; i++) 3117 spin_unlock(&cifs_sb->tlink_tree_lock);
3134 cifs_put_tlink(tlink[i]); 3118 cifs_put_tlink(tlink);
3135 } while (ret != 0); 3119 spin_lock(&cifs_sb->tlink_tree_lock);
3120 }
3121 spin_unlock(&cifs_sb->tlink_tree_lock);
3136 3122
3137 tmp = cifs_sb->prepath; 3123 tmp = cifs_sb->prepath;
3138 cifs_sb->prepathlen = 0; 3124 cifs_sb->prepathlen = 0;
@@ -3290,6 +3276,47 @@ cifs_sb_tcon_pending_wait(void *unused)
3290 return signal_pending(current) ? -ERESTARTSYS : 0; 3276 return signal_pending(current) ? -ERESTARTSYS : 0;
3291} 3277}
3292 3278
3279/* find and return a tlink with given uid */
3280static struct tcon_link *
3281tlink_rb_search(struct rb_root *root, uid_t uid)
3282{
3283 struct rb_node *node = root->rb_node;
3284 struct tcon_link *tlink;
3285
3286 while (node) {
3287 tlink = rb_entry(node, struct tcon_link, tl_rbnode);
3288
3289 if (tlink->tl_uid > uid)
3290 node = node->rb_left;
3291 else if (tlink->tl_uid < uid)
3292 node = node->rb_right;
3293 else
3294 return tlink;
3295 }
3296 return NULL;
3297}
3298
3299/* insert a tcon_link into the tree */
3300static void
3301tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink)
3302{
3303 struct rb_node **new = &(root->rb_node), *parent = NULL;
3304 struct tcon_link *tlink;
3305
3306 while (*new) {
3307 tlink = rb_entry(*new, struct tcon_link, tl_rbnode);
3308 parent = *new;
3309
3310 if (tlink->tl_uid > new_tlink->tl_uid)
3311 new = &((*new)->rb_left);
3312 else
3313 new = &((*new)->rb_right);
3314 }
3315
3316 rb_link_node(&new_tlink->tl_rbnode, parent, new);
3317 rb_insert_color(&new_tlink->tl_rbnode, root);
3318}
3319
3293/* 3320/*
3294 * Find or construct an appropriate tcon given a cifs_sb and the fsuid of the 3321 * Find or construct an appropriate tcon given a cifs_sb and the fsuid of the
3295 * current task. 3322 * current task.
@@ -3310,14 +3337,14 @@ struct tcon_link *
3310cifs_sb_tlink(struct cifs_sb_info *cifs_sb) 3337cifs_sb_tlink(struct cifs_sb_info *cifs_sb)
3311{ 3338{
3312 int ret; 3339 int ret;
3313 unsigned long fsuid = (unsigned long) current_fsuid(); 3340 uid_t fsuid = current_fsuid();
3314 struct tcon_link *tlink, *newtlink; 3341 struct tcon_link *tlink, *newtlink;
3315 3342
3316 if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) 3343 if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
3317 return cifs_get_tlink(cifs_sb_master_tlink(cifs_sb)); 3344 return cifs_get_tlink(cifs_sb_master_tlink(cifs_sb));
3318 3345
3319 spin_lock(&cifs_sb->tlink_tree_lock); 3346 spin_lock(&cifs_sb->tlink_tree_lock);
3320 tlink = radix_tree_lookup(&cifs_sb->tlink_tree, fsuid); 3347 tlink = tlink_rb_search(&cifs_sb->tlink_tree, fsuid);
3321 if (tlink) 3348 if (tlink)
3322 cifs_get_tlink(tlink); 3349 cifs_get_tlink(tlink);
3323 spin_unlock(&cifs_sb->tlink_tree_lock); 3350 spin_unlock(&cifs_sb->tlink_tree_lock);
@@ -3326,36 +3353,24 @@ cifs_sb_tlink(struct cifs_sb_info *cifs_sb)
3326 newtlink = kzalloc(sizeof(*tlink), GFP_KERNEL); 3353 newtlink = kzalloc(sizeof(*tlink), GFP_KERNEL);
3327 if (newtlink == NULL) 3354 if (newtlink == NULL)
3328 return ERR_PTR(-ENOMEM); 3355 return ERR_PTR(-ENOMEM);
3329 newtlink->tl_index = fsuid; 3356 newtlink->tl_uid = fsuid;
3330 newtlink->tl_tcon = ERR_PTR(-EACCES); 3357 newtlink->tl_tcon = ERR_PTR(-EACCES);
3331 set_bit(TCON_LINK_PENDING, &newtlink->tl_flags); 3358 set_bit(TCON_LINK_PENDING, &newtlink->tl_flags);
3332 set_bit(TCON_LINK_IN_TREE, &newtlink->tl_flags); 3359 set_bit(TCON_LINK_IN_TREE, &newtlink->tl_flags);
3333 cifs_get_tlink(newtlink); 3360 cifs_get_tlink(newtlink);
3334 3361
3335 ret = radix_tree_preload(GFP_KERNEL);
3336 if (ret != 0) {
3337 kfree(newtlink);
3338 return ERR_PTR(ret);
3339 }
3340
3341 spin_lock(&cifs_sb->tlink_tree_lock); 3362 spin_lock(&cifs_sb->tlink_tree_lock);
3342 /* was one inserted after previous search? */ 3363 /* was one inserted after previous search? */
3343 tlink = radix_tree_lookup(&cifs_sb->tlink_tree, fsuid); 3364 tlink = tlink_rb_search(&cifs_sb->tlink_tree, fsuid);
3344 if (tlink) { 3365 if (tlink) {
3345 cifs_get_tlink(tlink); 3366 cifs_get_tlink(tlink);
3346 spin_unlock(&cifs_sb->tlink_tree_lock); 3367 spin_unlock(&cifs_sb->tlink_tree_lock);
3347 radix_tree_preload_end();
3348 kfree(newtlink); 3368 kfree(newtlink);
3349 goto wait_for_construction; 3369 goto wait_for_construction;
3350 } 3370 }
3351 ret = radix_tree_insert(&cifs_sb->tlink_tree, fsuid, newtlink);
3352 spin_unlock(&cifs_sb->tlink_tree_lock);
3353 radix_tree_preload_end();
3354 if (ret) {
3355 kfree(newtlink);
3356 return ERR_PTR(ret);
3357 }
3358 tlink = newtlink; 3371 tlink = newtlink;
3372 tlink_rb_insert(&cifs_sb->tlink_tree, tlink);
3373 spin_unlock(&cifs_sb->tlink_tree_lock);
3359 } else { 3374 } else {
3360wait_for_construction: 3375wait_for_construction:
3361 ret = wait_on_bit(&tlink->tl_flags, TCON_LINK_PENDING, 3376 ret = wait_on_bit(&tlink->tl_flags, TCON_LINK_PENDING,
@@ -3401,39 +3416,39 @@ cifs_prune_tlinks(struct work_struct *work)
3401{ 3416{
3402 struct cifs_sb_info *cifs_sb = container_of(work, struct cifs_sb_info, 3417 struct cifs_sb_info *cifs_sb = container_of(work, struct cifs_sb_info,
3403 prune_tlinks.work); 3418 prune_tlinks.work);
3404 struct tcon_link *tlink[8]; 3419 struct rb_root *root = &cifs_sb->tlink_tree;
3405 unsigned long now = jiffies; 3420 struct rb_node *node = rb_first(root);
3406 unsigned long index = 0; 3421 struct rb_node *tmp;
3407 int i, ret; 3422 struct tcon_link *tlink;
3408 3423
3409 do { 3424 /*
3410 spin_lock(&cifs_sb->tlink_tree_lock); 3425 * Because we drop the spinlock in the loop in order to put the tlink
3411 ret = radix_tree_gang_lookup(&cifs_sb->tlink_tree, 3426 * it's not guarded against removal of links from the tree. The only
3412 (void **)tlink, index, 3427 * places that remove entries from the tree are this function and
3413 ARRAY_SIZE(tlink)); 3428 * umounts. Because this function is non-reentrant and is canceled
3414 /* increment index for next pass */ 3429 * before umount can proceed, this is safe.
3415 if (ret > 0) 3430 */
3416 index = tlink[ret - 1]->tl_index + 1; 3431 spin_lock(&cifs_sb->tlink_tree_lock);
3417 for (i = 0; i < ret; i++) { 3432 node = rb_first(root);
3418 if (test_bit(TCON_LINK_MASTER, &tlink[i]->tl_flags) || 3433 while (node != NULL) {
3419 atomic_read(&tlink[i]->tl_count) != 0 || 3434 tmp = node;
3420 time_after(tlink[i]->tl_time + TLINK_IDLE_EXPIRE, 3435 node = rb_next(tmp);
3421 now)) { 3436 tlink = rb_entry(tmp, struct tcon_link, tl_rbnode);
3422 tlink[i] = NULL; 3437
3423 continue; 3438 if (test_bit(TCON_LINK_MASTER, &tlink->tl_flags) ||
3424 } 3439 atomic_read(&tlink->tl_count) != 0 ||
3425 cifs_get_tlink(tlink[i]); 3440 time_after(tlink->tl_time + TLINK_IDLE_EXPIRE, jiffies))
3426 clear_bit(TCON_LINK_IN_TREE, &tlink[i]->tl_flags); 3441 continue;
3427 radix_tree_delete(&cifs_sb->tlink_tree,
3428 tlink[i]->tl_index);
3429 }
3430 spin_unlock(&cifs_sb->tlink_tree_lock);
3431 3442
3432 for (i = 0; i < ret; i++) { 3443 cifs_get_tlink(tlink);
3433 if (tlink[i] != NULL) 3444 clear_bit(TCON_LINK_IN_TREE, &tlink->tl_flags);
3434 cifs_put_tlink(tlink[i]); 3445 rb_erase(tmp, root);
3435 } 3446
3436 } while (ret != 0); 3447 spin_unlock(&cifs_sb->tlink_tree_lock);
3448 cifs_put_tlink(tlink);
3449 spin_lock(&cifs_sb->tlink_tree_lock);
3450 }
3451 spin_unlock(&cifs_sb->tlink_tree_lock);
3437 3452
3438 queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks, 3453 queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks,
3439 TLINK_IDLE_EXPIRE); 3454 TLINK_IDLE_EXPIRE);