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