aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs/connect.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/cifs/connect.c')
-rw-r--r--fs/cifs/connect.c195
1 files changed, 99 insertions, 96 deletions
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 9eb327defa1d..251a17c03545 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 radix_tree_tag_set(&cifs_sb->tlink_tree, pSesInfo->linux_uid,
2918 CIFS_TLINK_MASTER_TAG);
2919 spin_unlock(&cifs_sb->tlink_tree_lock); 2913 spin_unlock(&cifs_sb->tlink_tree_lock);
2920 radix_tree_preload_end();
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;
@@ -3271,22 +3257,10 @@ out:
3271 return tcon; 3257 return tcon;
3272} 3258}
3273 3259
3274static struct tcon_link * 3260static inline struct tcon_link *
3275cifs_sb_master_tlink(struct cifs_sb_info *cifs_sb) 3261cifs_sb_master_tlink(struct cifs_sb_info *cifs_sb)
3276{ 3262{
3277 struct tcon_link *tlink; 3263 return cifs_sb->master_tlink;
3278 unsigned int ret;
3279
3280 spin_lock(&cifs_sb->tlink_tree_lock);
3281 ret = radix_tree_gang_lookup_tag(&cifs_sb->tlink_tree, (void **)&tlink,
3282 0, 1, CIFS_TLINK_MASTER_TAG);
3283 spin_unlock(&cifs_sb->tlink_tree_lock);
3284
3285 /* the master tcon should always be present */
3286 if (ret == 0)
3287 BUG();
3288
3289 return tlink;
3290} 3264}
3291 3265
3292struct cifsTconInfo * 3266struct cifsTconInfo *
@@ -3302,6 +3276,47 @@ cifs_sb_tcon_pending_wait(void *unused)
3302 return signal_pending(current) ? -ERESTARTSYS : 0; 3276 return signal_pending(current) ? -ERESTARTSYS : 0;
3303} 3277}
3304 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
3305/* 3320/*
3306 * 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
3307 * current task. 3322 * current task.
@@ -3309,7 +3324,7 @@ cifs_sb_tcon_pending_wait(void *unused)
3309 * If the superblock doesn't refer to a multiuser mount, then just return 3324 * If the superblock doesn't refer to a multiuser mount, then just return
3310 * the master tcon for the mount. 3325 * the master tcon for the mount.
3311 * 3326 *
3312 * First, search the radix tree for an existing tcon for this fsuid. If one 3327 * First, search the rbtree for an existing tcon for this fsuid. If one
3313 * exists, then check to see if it's pending construction. If it is then wait 3328 * exists, then check to see if it's pending construction. If it is then wait
3314 * for construction to complete. Once it's no longer pending, check to see if 3329 * for construction to complete. Once it's no longer pending, check to see if
3315 * it failed and either return an error or retry construction, depending on 3330 * it failed and either return an error or retry construction, depending on
@@ -3322,14 +3337,14 @@ struct tcon_link *
3322cifs_sb_tlink(struct cifs_sb_info *cifs_sb) 3337cifs_sb_tlink(struct cifs_sb_info *cifs_sb)
3323{ 3338{
3324 int ret; 3339 int ret;
3325 unsigned long fsuid = (unsigned long) current_fsuid(); 3340 uid_t fsuid = current_fsuid();
3326 struct tcon_link *tlink, *newtlink; 3341 struct tcon_link *tlink, *newtlink;
3327 3342
3328 if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) 3343 if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
3329 return cifs_get_tlink(cifs_sb_master_tlink(cifs_sb)); 3344 return cifs_get_tlink(cifs_sb_master_tlink(cifs_sb));
3330 3345
3331 spin_lock(&cifs_sb->tlink_tree_lock); 3346 spin_lock(&cifs_sb->tlink_tree_lock);
3332 tlink = radix_tree_lookup(&cifs_sb->tlink_tree, fsuid); 3347 tlink = tlink_rb_search(&cifs_sb->tlink_tree, fsuid);
3333 if (tlink) 3348 if (tlink)
3334 cifs_get_tlink(tlink); 3349 cifs_get_tlink(tlink);
3335 spin_unlock(&cifs_sb->tlink_tree_lock); 3350 spin_unlock(&cifs_sb->tlink_tree_lock);
@@ -3338,36 +3353,24 @@ cifs_sb_tlink(struct cifs_sb_info *cifs_sb)
3338 newtlink = kzalloc(sizeof(*tlink), GFP_KERNEL); 3353 newtlink = kzalloc(sizeof(*tlink), GFP_KERNEL);
3339 if (newtlink == NULL) 3354 if (newtlink == NULL)
3340 return ERR_PTR(-ENOMEM); 3355 return ERR_PTR(-ENOMEM);
3341 newtlink->tl_index = fsuid; 3356 newtlink->tl_uid = fsuid;
3342 newtlink->tl_tcon = ERR_PTR(-EACCES); 3357 newtlink->tl_tcon = ERR_PTR(-EACCES);
3343 set_bit(TCON_LINK_PENDING, &newtlink->tl_flags); 3358 set_bit(TCON_LINK_PENDING, &newtlink->tl_flags);
3344 set_bit(TCON_LINK_IN_TREE, &newtlink->tl_flags); 3359 set_bit(TCON_LINK_IN_TREE, &newtlink->tl_flags);
3345 cifs_get_tlink(newtlink); 3360 cifs_get_tlink(newtlink);
3346 3361
3347 ret = radix_tree_preload(GFP_KERNEL);
3348 if (ret != 0) {
3349 kfree(newtlink);
3350 return ERR_PTR(ret);
3351 }
3352
3353 spin_lock(&cifs_sb->tlink_tree_lock); 3362 spin_lock(&cifs_sb->tlink_tree_lock);
3354 /* was one inserted after previous search? */ 3363 /* was one inserted after previous search? */
3355 tlink = radix_tree_lookup(&cifs_sb->tlink_tree, fsuid); 3364 tlink = tlink_rb_search(&cifs_sb->tlink_tree, fsuid);
3356 if (tlink) { 3365 if (tlink) {
3357 cifs_get_tlink(tlink); 3366 cifs_get_tlink(tlink);
3358 spin_unlock(&cifs_sb->tlink_tree_lock); 3367 spin_unlock(&cifs_sb->tlink_tree_lock);
3359 radix_tree_preload_end();
3360 kfree(newtlink); 3368 kfree(newtlink);
3361 goto wait_for_construction; 3369 goto wait_for_construction;
3362 } 3370 }
3363 ret = radix_tree_insert(&cifs_sb->tlink_tree, fsuid, newtlink);
3364 spin_unlock(&cifs_sb->tlink_tree_lock);
3365 radix_tree_preload_end();
3366 if (ret) {
3367 kfree(newtlink);
3368 return ERR_PTR(ret);
3369 }
3370 tlink = newtlink; 3371 tlink = newtlink;
3372 tlink_rb_insert(&cifs_sb->tlink_tree, tlink);
3373 spin_unlock(&cifs_sb->tlink_tree_lock);
3371 } else { 3374 } else {
3372wait_for_construction: 3375wait_for_construction:
3373 ret = wait_on_bit(&tlink->tl_flags, TCON_LINK_PENDING, 3376 ret = wait_on_bit(&tlink->tl_flags, TCON_LINK_PENDING,
@@ -3413,39 +3416,39 @@ cifs_prune_tlinks(struct work_struct *work)
3413{ 3416{
3414 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,
3415 prune_tlinks.work); 3418 prune_tlinks.work);
3416 struct tcon_link *tlink[8]; 3419 struct rb_root *root = &cifs_sb->tlink_tree;
3417 unsigned long now = jiffies; 3420 struct rb_node *node = rb_first(root);
3418 unsigned long index = 0; 3421 struct rb_node *tmp;
3419 int i, ret; 3422 struct tcon_link *tlink;
3420 3423
3421 do { 3424 /*
3422 spin_lock(&cifs_sb->tlink_tree_lock); 3425 * Because we drop the spinlock in the loop in order to put the tlink
3423 ret = radix_tree_gang_lookup(&cifs_sb->tlink_tree, 3426 * it's not guarded against removal of links from the tree. The only
3424 (void **)tlink, index, 3427 * places that remove entries from the tree are this function and
3425 ARRAY_SIZE(tlink)); 3428 * umounts. Because this function is non-reentrant and is canceled
3426 /* increment index for next pass */ 3429 * before umount can proceed, this is safe.
3427 if (ret > 0) 3430 */
3428 index = tlink[ret - 1]->tl_index + 1; 3431 spin_lock(&cifs_sb->tlink_tree_lock);
3429 for (i = 0; i < ret; i++) { 3432 node = rb_first(root);
3430 if (test_bit(TCON_LINK_MASTER, &tlink[i]->tl_flags) || 3433 while (node != NULL) {
3431 atomic_read(&tlink[i]->tl_count) != 0 || 3434 tmp = node;
3432 time_after(tlink[i]->tl_time + TLINK_IDLE_EXPIRE, 3435 node = rb_next(tmp);
3433 now)) { 3436 tlink = rb_entry(tmp, struct tcon_link, tl_rbnode);
3434 tlink[i] = NULL; 3437
3435 continue; 3438 if (test_bit(TCON_LINK_MASTER, &tlink->tl_flags) ||
3436 } 3439 atomic_read(&tlink->tl_count) != 0 ||
3437 cifs_get_tlink(tlink[i]); 3440 time_after(tlink->tl_time + TLINK_IDLE_EXPIRE, jiffies))
3438 clear_bit(TCON_LINK_IN_TREE, &tlink[i]->tl_flags); 3441 continue;
3439 radix_tree_delete(&cifs_sb->tlink_tree,
3440 tlink[i]->tl_index);
3441 }
3442 spin_unlock(&cifs_sb->tlink_tree_lock);
3443 3442
3444 for (i = 0; i < ret; i++) { 3443 cifs_get_tlink(tlink);
3445 if (tlink[i] != NULL) 3444 clear_bit(TCON_LINK_IN_TREE, &tlink->tl_flags);
3446 cifs_put_tlink(tlink[i]); 3445 rb_erase(tmp, root);
3447 } 3446
3448 } 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);
3449 3452
3450 queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks, 3453 queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks,
3451 TLINK_IDLE_EXPIRE); 3454 TLINK_IDLE_EXPIRE);