diff options
author | Jeff Layton <jlayton@redhat.com> | 2010-10-28 11:16:44 -0400 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2010-11-02 15:20:23 -0400 |
commit | b647c35f77af9c07d336247b23014596e9f0a593 (patch) | |
tree | 653949250681fd1c23ad529b631da793f95778a6 | |
parent | 413e661c136c52290de1ee19a1b049a4da9dbf51 (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>
-rw-r--r-- | fs/cifs/cifs_fs_sb.h | 4 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 2 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 3 | ||||
-rw-r--r-- | fs/cifs/connect.c | 177 |
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 | ||
44 | struct cifs_sb_info { | 44 | struct 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 | */ |
338 | struct tcon_link { | 338 | struct 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 | ||
117 | static int ipv4_connect(struct TCP_Server_Info *server); | 117 | static int ipv4_connect(struct TCP_Server_Info *server); |
118 | static int ipv6_connect(struct TCP_Server_Info *server); | 118 | static int ipv6_connect(struct TCP_Server_Info *server); |
119 | static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink); | ||
119 | static void cifs_prune_tlinks(struct work_struct *work); | 120 | static 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, | |||
3107 | int | 3100 | int |
3108 | cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) | 3101 | cifs_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 */ | ||
3280 | static struct tcon_link * | ||
3281 | tlink_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 */ | ||
3300 | static void | ||
3301 | tlink_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 * | |||
3310 | cifs_sb_tlink(struct cifs_sb_info *cifs_sb) | 3337 | cifs_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 { |
3360 | wait_for_construction: | 3375 | wait_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); |