diff options
| author | Jeff Layton <jlayton@redhat.com> | 2010-10-06 19:51:11 -0400 |
|---|---|---|
| committer | Steve French <sfrench@us.ibm.com> | 2010-10-07 14:18:00 -0400 |
| commit | 9d002df492b14c690425d9785530371b6c1ccbca (patch) | |
| tree | 6ed1a52d0e348e985f7bd194d22ee6e7854fa9e8 /fs | |
| parent | c9928f7040a6e5f39e028bea500e0fde910d4a96 (diff) | |
cifs: add routines to build sessions and tcons on the fly
This patch is rather large, but it's a bit difficult to do piecemeal...
For non-multiuser mounts, everything will basically work as it does
today. A call to cifs_sb_tlink will return the "master" tcon link.
Turn the tcon pointer in the cifs_sb into a radix tree that uses the
fsuid of the process as a key. The value is a new "tcon_link" struct
that contains info about a tcon that's under construction.
When a new process needs a tcon, it'll call cifs_sb_tcon. That will
then look up the tcon_link in the radix tree. If it exists and is
valid, it's returned.
If it doesn't exist, then we stuff a new tcon_link into the tree and
mark it as pending and then go and try to build the session/tcon.
If that works, the tcon pointer in the tcon_link is updated and the
pending flag is cleared.
If the construction fails, then we set the tcon pointer to an ERR_PTR
and clear the pending flag.
If the radix tree is searched and the tcon_link is marked pending
then we go to sleep and wait for the pending flag to be cleared.
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/cifs/cifs_fs_sb.h | 7 | ||||
| -rw-r--r-- | fs/cifs/cifsglob.h | 32 | ||||
| -rw-r--r-- | fs/cifs/connect.c | 268 |
3 files changed, 279 insertions, 28 deletions
diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index e04e6923d354..5ce57bdf1865 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h | |||
| @@ -15,6 +15,8 @@ | |||
| 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> | ||
| 19 | |||
| 18 | #ifndef _CIFS_FS_SB_H | 20 | #ifndef _CIFS_FS_SB_H |
| 19 | #define _CIFS_FS_SB_H | 21 | #define _CIFS_FS_SB_H |
| 20 | 22 | ||
| @@ -40,8 +42,9 @@ | |||
| 40 | #define CIFS_MOUNT_MULTIUSER 0x20000 /* multiuser mount */ | 42 | #define CIFS_MOUNT_MULTIUSER 0x20000 /* multiuser mount */ |
| 41 | 43 | ||
| 42 | struct cifs_sb_info { | 44 | struct cifs_sb_info { |
| 43 | struct cifsTconInfo *ptcon; /* primary mount */ | 45 | struct radix_tree_root tlink_tree; |
| 44 | struct list_head nested_tcon_q; | 46 | #define CIFS_TLINK_MASTER_TAG 0 /* is "master" (mount) tcon */ |
| 47 | spinlock_t tlink_tree_lock; | ||
| 45 | struct nls_table *local_nls; | 48 | struct nls_table *local_nls; |
| 46 | unsigned int rsize; | 49 | unsigned int rsize; |
| 47 | unsigned int wsize; | 50 | unsigned int wsize; |
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index d5324853203b..9a7c472a153f 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
| @@ -317,42 +317,36 @@ struct cifsTconInfo { | |||
| 317 | * "get" on the container. | 317 | * "get" on the container. |
| 318 | */ | 318 | */ |
| 319 | struct tcon_link { | 319 | struct tcon_link { |
| 320 | spinlock_t tl_lock; | 320 | unsigned long tl_index; |
| 321 | u32 tl_count; | 321 | unsigned long tl_flags; |
| 322 | u64 tl_time; | 322 | #define TCON_LINK_MASTER 0 |
| 323 | #define TCON_LINK_PENDING 1 | ||
| 324 | #define TCON_LINK_IN_TREE 2 | ||
| 325 | unsigned long tl_time; | ||
| 326 | atomic_t tl_count; | ||
| 323 | struct cifsTconInfo *tl_tcon; | 327 | struct cifsTconInfo *tl_tcon; |
| 324 | }; | 328 | }; |
| 325 | 329 | ||
| 326 | static inline struct tcon_link * | 330 | extern struct tcon_link *cifs_sb_tlink(struct cifs_sb_info *cifs_sb); |
| 327 | cifs_sb_tlink(struct cifs_sb_info *cifs_sb) | ||
| 328 | { | ||
| 329 | return (struct tcon_link *)cifs_sb->ptcon; | ||
| 330 | } | ||
| 331 | 331 | ||
| 332 | static inline struct cifsTconInfo * | 332 | static inline struct cifsTconInfo * |
| 333 | tlink_tcon(struct tcon_link *tlink) | 333 | tlink_tcon(struct tcon_link *tlink) |
| 334 | { | 334 | { |
| 335 | return (struct cifsTconInfo *)tlink; | 335 | return tlink->tl_tcon; |
| 336 | } | 336 | } |
| 337 | 337 | ||
| 338 | static inline void | 338 | extern void cifs_put_tlink(struct tcon_link *tlink); |
| 339 | cifs_put_tlink(struct tcon_link *tlink) | ||
| 340 | { | ||
| 341 | return; | ||
| 342 | } | ||
| 343 | 339 | ||
| 344 | static inline struct tcon_link * | 340 | static inline struct tcon_link * |
| 345 | cifs_get_tlink(struct tcon_link *tlink) | 341 | cifs_get_tlink(struct tcon_link *tlink) |
| 346 | { | 342 | { |
| 343 | if (tlink && !IS_ERR(tlink)) | ||
| 344 | atomic_inc(&tlink->tl_count); | ||
| 347 | return tlink; | 345 | return tlink; |
| 348 | } | 346 | } |
| 349 | 347 | ||
| 350 | /* This function is always expected to succeed */ | 348 | /* This function is always expected to succeed */ |
| 351 | static inline struct cifsTconInfo * | 349 | extern struct cifsTconInfo *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb); |
| 352 | cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb) | ||
| 353 | { | ||
| 354 | return cifs_sb->ptcon; | ||
| 355 | } | ||
| 356 | 350 | ||
| 357 | /* | 351 | /* |
| 358 | * This info hangs off the cifsFileInfo structure, pointed to by llist. | 352 | * This info hangs off the cifsFileInfo structure, pointed to by llist. |
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index f6a3091c2874..3156a9de947d 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
| @@ -109,6 +109,9 @@ struct smb_vol { | |||
| 109 | struct nls_table *local_nls; | 109 | struct nls_table *local_nls; |
| 110 | }; | 110 | }; |
| 111 | 111 | ||
| 112 | #define TLINK_ERROR_EXPIRE (1 * HZ) | ||
| 113 | |||
| 114 | |||
| 112 | static int ipv4_connect(struct TCP_Server_Info *server); | 115 | static int ipv4_connect(struct TCP_Server_Info *server); |
| 113 | static int ipv6_connect(struct TCP_Server_Info *server); | 116 | static int ipv6_connect(struct TCP_Server_Info *server); |
| 114 | 117 | ||
| @@ -1959,6 +1962,23 @@ out_fail: | |||
| 1959 | return ERR_PTR(rc); | 1962 | return ERR_PTR(rc); |
| 1960 | } | 1963 | } |
| 1961 | 1964 | ||
| 1965 | void | ||
| 1966 | cifs_put_tlink(struct tcon_link *tlink) | ||
| 1967 | { | ||
| 1968 | if (!tlink || IS_ERR(tlink)) | ||
| 1969 | return; | ||
| 1970 | |||
| 1971 | if (!atomic_dec_and_test(&tlink->tl_count) || | ||
| 1972 | test_bit(TCON_LINK_IN_TREE, &tlink->tl_flags)) { | ||
| 1973 | tlink->tl_time = jiffies; | ||
| 1974 | return; | ||
| 1975 | } | ||
| 1976 | |||
| 1977 | if (!IS_ERR(tlink_tcon(tlink))) | ||
| 1978 | cifs_put_tcon(tlink_tcon(tlink)); | ||
| 1979 | kfree(tlink); | ||
| 1980 | return; | ||
| 1981 | } | ||
| 1962 | 1982 | ||
| 1963 | int | 1983 | int |
| 1964 | get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, | 1984 | get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, |
| @@ -2641,6 +2661,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
| 2641 | struct TCP_Server_Info *srvTcp; | 2661 | struct TCP_Server_Info *srvTcp; |
| 2642 | char *full_path; | 2662 | char *full_path; |
| 2643 | char *mount_data = mount_data_global; | 2663 | char *mount_data = mount_data_global; |
| 2664 | struct tcon_link *tlink; | ||
| 2644 | #ifdef CONFIG_CIFS_DFS_UPCALL | 2665 | #ifdef CONFIG_CIFS_DFS_UPCALL |
| 2645 | struct dfs_info3_param *referrals = NULL; | 2666 | struct dfs_info3_param *referrals = NULL; |
| 2646 | unsigned int num_referrals = 0; | 2667 | unsigned int num_referrals = 0; |
| @@ -2652,6 +2673,7 @@ try_mount_again: | |||
| 2652 | pSesInfo = NULL; | 2673 | pSesInfo = NULL; |
| 2653 | srvTcp = NULL; | 2674 | srvTcp = NULL; |
| 2654 | full_path = NULL; | 2675 | full_path = NULL; |
| 2676 | tlink = NULL; | ||
| 2655 | 2677 | ||
| 2656 | xid = GetXid(); | 2678 | xid = GetXid(); |
| 2657 | 2679 | ||
| @@ -2727,8 +2749,6 @@ try_mount_again: | |||
| 2727 | goto remote_path_check; | 2749 | goto remote_path_check; |
| 2728 | } | 2750 | } |
| 2729 | 2751 | ||
| 2730 | cifs_sb->ptcon = tcon; | ||
| 2731 | |||
| 2732 | /* do not care if following two calls succeed - informational */ | 2752 | /* do not care if following two calls succeed - informational */ |
| 2733 | if (!tcon->ipc) { | 2753 | if (!tcon->ipc) { |
| 2734 | CIFSSMBQFSDeviceInfo(xid, tcon); | 2754 | CIFSSMBQFSDeviceInfo(xid, tcon); |
| @@ -2837,6 +2857,35 @@ remote_path_check: | |||
| 2837 | #endif | 2857 | #endif |
| 2838 | } | 2858 | } |
| 2839 | 2859 | ||
| 2860 | if (rc) | ||
| 2861 | goto mount_fail_check; | ||
| 2862 | |||
| 2863 | /* now, hang the tcon off of the superblock */ | ||
| 2864 | tlink = kzalloc(sizeof *tlink, GFP_KERNEL); | ||
| 2865 | if (tlink == NULL) { | ||
| 2866 | rc = -ENOMEM; | ||
| 2867 | goto mount_fail_check; | ||
| 2868 | } | ||
| 2869 | |||
| 2870 | tlink->tl_index = pSesInfo->linux_uid; | ||
| 2871 | tlink->tl_tcon = tcon; | ||
| 2872 | tlink->tl_time = jiffies; | ||
| 2873 | set_bit(TCON_LINK_MASTER, &tlink->tl_flags); | ||
| 2874 | set_bit(TCON_LINK_IN_TREE, &tlink->tl_flags); | ||
| 2875 | |||
| 2876 | rc = radix_tree_preload(GFP_KERNEL); | ||
| 2877 | if (rc == -ENOMEM) { | ||
| 2878 | kfree(tlink); | ||
| 2879 | goto mount_fail_check; | ||
| 2880 | } | ||
| 2881 | |||
| 2882 | spin_lock(&cifs_sb->tlink_tree_lock); | ||
| 2883 | radix_tree_insert(&cifs_sb->tlink_tree, pSesInfo->linux_uid, tlink); | ||
| 2884 | radix_tree_tag_set(&cifs_sb->tlink_tree, pSesInfo->linux_uid, | ||
| 2885 | CIFS_TLINK_MASTER_TAG); | ||
| 2886 | spin_unlock(&cifs_sb->tlink_tree_lock); | ||
| 2887 | radix_tree_preload_end(); | ||
| 2888 | |||
| 2840 | mount_fail_check: | 2889 | mount_fail_check: |
| 2841 | /* on error free sesinfo and tcon struct if needed */ | 2890 | /* on error free sesinfo and tcon struct if needed */ |
| 2842 | if (rc) { | 2891 | if (rc) { |
| @@ -3023,19 +3072,37 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, | |||
| 3023 | int | 3072 | int |
| 3024 | cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) | 3073 | cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) |
| 3025 | { | 3074 | { |
| 3026 | int rc = 0; | 3075 | int i, ret; |
| 3027 | char *tmp; | 3076 | char *tmp; |
| 3028 | struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); | 3077 | struct tcon_link *tlink[8]; |
| 3078 | unsigned long index = 0; | ||
| 3079 | |||
| 3080 | do { | ||
| 3081 | spin_lock(&cifs_sb->tlink_tree_lock); | ||
| 3082 | ret = radix_tree_gang_lookup(&cifs_sb->tlink_tree, | ||
| 3083 | (void **)tlink, index, | ||
| 3084 | ARRAY_SIZE(tlink)); | ||
| 3085 | /* increment index for next pass */ | ||
| 3086 | if (ret > 0) | ||
| 3087 | index = tlink[ret - 1]->tl_index + 1; | ||
| 3088 | for (i = 0; i < ret; i++) { | ||
| 3089 | cifs_get_tlink(tlink[i]); | ||
| 3090 | clear_bit(TCON_LINK_IN_TREE, &tlink[i]->tl_flags); | ||
| 3091 | radix_tree_delete(&cifs_sb->tlink_tree, | ||
| 3092 | tlink[i]->tl_index); | ||
| 3093 | } | ||
| 3094 | spin_unlock(&cifs_sb->tlink_tree_lock); | ||
| 3029 | 3095 | ||
| 3030 | cifs_put_tcon(tcon); | 3096 | for (i = 0; i < ret; i++) |
| 3097 | cifs_put_tlink(tlink[i]); | ||
| 3098 | } while (ret != 0); | ||
| 3031 | 3099 | ||
| 3032 | cifs_sb->ptcon = NULL; | ||
| 3033 | tmp = cifs_sb->prepath; | 3100 | tmp = cifs_sb->prepath; |
| 3034 | cifs_sb->prepathlen = 0; | 3101 | cifs_sb->prepathlen = 0; |
| 3035 | cifs_sb->prepath = NULL; | 3102 | cifs_sb->prepath = NULL; |
| 3036 | kfree(tmp); | 3103 | kfree(tmp); |
| 3037 | 3104 | ||
| 3038 | return rc; | 3105 | return 0; |
| 3039 | } | 3106 | } |
| 3040 | 3107 | ||
| 3041 | int cifs_negotiate_protocol(unsigned int xid, struct cifsSesInfo *ses) | 3108 | int cifs_negotiate_protocol(unsigned int xid, struct cifsSesInfo *ses) |
| @@ -3096,3 +3163,190 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *ses, | |||
| 3096 | return rc; | 3163 | return rc; |
| 3097 | } | 3164 | } |
| 3098 | 3165 | ||
| 3166 | struct cifsTconInfo * | ||
| 3167 | cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) | ||
| 3168 | { | ||
| 3169 | struct cifsTconInfo *master_tcon = cifs_sb_master_tcon(cifs_sb); | ||
| 3170 | struct cifsSesInfo *ses; | ||
| 3171 | struct cifsTconInfo *tcon = NULL; | ||
| 3172 | struct smb_vol *vol_info; | ||
| 3173 | char username[MAX_USERNAME_SIZE + 1]; | ||
| 3174 | |||
| 3175 | vol_info = kzalloc(sizeof(*vol_info), GFP_KERNEL); | ||
| 3176 | if (vol_info == NULL) { | ||
| 3177 | tcon = ERR_PTR(-ENOMEM); | ||
| 3178 | goto out; | ||
| 3179 | } | ||
| 3180 | |||
| 3181 | snprintf(username, MAX_USERNAME_SIZE, "krb50x%x", fsuid); | ||
| 3182 | vol_info->username = username; | ||
| 3183 | vol_info->local_nls = cifs_sb->local_nls; | ||
| 3184 | vol_info->linux_uid = fsuid; | ||
| 3185 | vol_info->cred_uid = fsuid; | ||
| 3186 | vol_info->UNC = master_tcon->treeName; | ||
| 3187 | vol_info->retry = master_tcon->retry; | ||
| 3188 | vol_info->nocase = master_tcon->nocase; | ||
| 3189 | vol_info->local_lease = master_tcon->local_lease; | ||
| 3190 | vol_info->no_linux_ext = !master_tcon->unix_ext; | ||
| 3191 | |||
| 3192 | /* FIXME: allow for other secFlg settings */ | ||
| 3193 | vol_info->secFlg = CIFSSEC_MUST_KRB5; | ||
| 3194 | |||
| 3195 | /* get a reference for the same TCP session */ | ||
| 3196 | write_lock(&cifs_tcp_ses_lock); | ||
| 3197 | ++master_tcon->ses->server->srv_count; | ||
| 3198 | write_unlock(&cifs_tcp_ses_lock); | ||
| 3199 | |||
| 3200 | ses = cifs_get_smb_ses(master_tcon->ses->server, vol_info); | ||
| 3201 | if (IS_ERR(ses)) { | ||
| 3202 | tcon = (struct cifsTconInfo *)ses; | ||
| 3203 | cifs_put_tcp_session(master_tcon->ses->server); | ||
| 3204 | goto out; | ||
| 3205 | } | ||
| 3206 | |||
| 3207 | tcon = cifs_get_tcon(ses, vol_info); | ||
| 3208 | if (IS_ERR(tcon)) { | ||
| 3209 | cifs_put_smb_ses(ses); | ||
| 3210 | goto out; | ||
| 3211 | } | ||
| 3212 | |||
| 3213 | if (ses->capabilities & CAP_UNIX) | ||
| 3214 | reset_cifs_unix_caps(0, tcon, NULL, vol_info); | ||
| 3215 | out: | ||
| 3216 | kfree(vol_info); | ||
| 3217 | |||
| 3218 | return tcon; | ||
| 3219 | } | ||
| 3220 | |||
| 3221 | static struct tcon_link * | ||
| 3222 | cifs_sb_master_tlink(struct cifs_sb_info *cifs_sb) | ||
| 3223 | { | ||
| 3224 | struct tcon_link *tlink; | ||
| 3225 | unsigned int ret; | ||
| 3226 | |||
| 3227 | spin_lock(&cifs_sb->tlink_tree_lock); | ||
| 3228 | ret = radix_tree_gang_lookup_tag(&cifs_sb->tlink_tree, (void **)&tlink, | ||
| 3229 | 0, 1, CIFS_TLINK_MASTER_TAG); | ||
| 3230 | spin_unlock(&cifs_sb->tlink_tree_lock); | ||
| 3231 | |||
| 3232 | /* the master tcon should always be present */ | ||
| 3233 | if (ret == 0) | ||
| 3234 | BUG(); | ||
| 3235 | |||
| 3236 | return tlink; | ||
| 3237 | } | ||
| 3238 | |||
| 3239 | struct cifsTconInfo * | ||
| 3240 | cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb) | ||
| 3241 | { | ||
| 3242 | return tlink_tcon(cifs_sb_master_tlink(cifs_sb)); | ||
| 3243 | } | ||
| 3244 | |||
| 3245 | static int | ||
| 3246 | cifs_sb_tcon_pending_wait(void *unused) | ||
| 3247 | { | ||
| 3248 | schedule(); | ||
| 3249 | return signal_pending(current) ? -ERESTARTSYS : 0; | ||
| 3250 | } | ||
| 3251 | |||
| 3252 | /* | ||
| 3253 | * Find or construct an appropriate tcon given a cifs_sb and the fsuid of the | ||
| 3254 | * current task. | ||
| 3255 | * | ||
| 3256 | * If the superblock doesn't refer to a multiuser mount, then just return | ||
| 3257 | * the master tcon for the mount. | ||
| 3258 | * | ||
| 3259 | * First, search the radix tree for an existing tcon for this fsuid. If one | ||
| 3260 | * exists, then check to see if it's pending construction. If it is then wait | ||
| 3261 | * for construction to complete. Once it's no longer pending, check to see if | ||
| 3262 | * it failed and either return an error or retry construction, depending on | ||
| 3263 | * the timeout. | ||
| 3264 | * | ||
| 3265 | * If one doesn't exist then insert a new tcon_link struct into the tree and | ||
| 3266 | * try to construct a new one. | ||
| 3267 | */ | ||
| 3268 | struct tcon_link * | ||
| 3269 | cifs_sb_tlink(struct cifs_sb_info *cifs_sb) | ||
| 3270 | { | ||
| 3271 | int ret; | ||
| 3272 | unsigned long fsuid = (unsigned long) current_fsuid(); | ||
| 3273 | struct tcon_link *tlink, *newtlink; | ||
| 3274 | |||
| 3275 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) | ||
| 3276 | return cifs_get_tlink(cifs_sb_master_tlink(cifs_sb)); | ||
| 3277 | |||
| 3278 | spin_lock(&cifs_sb->tlink_tree_lock); | ||
| 3279 | tlink = radix_tree_lookup(&cifs_sb->tlink_tree, fsuid); | ||
| 3280 | if (tlink) | ||
| 3281 | cifs_get_tlink(tlink); | ||
| 3282 | spin_unlock(&cifs_sb->tlink_tree_lock); | ||
| 3283 | |||
| 3284 | if (tlink == NULL) { | ||
| 3285 | newtlink = kzalloc(sizeof(*tlink), GFP_KERNEL); | ||
| 3286 | if (newtlink == NULL) | ||
| 3287 | return ERR_PTR(-ENOMEM); | ||
| 3288 | newtlink->tl_index = fsuid; | ||
| 3289 | newtlink->tl_tcon = ERR_PTR(-EACCES); | ||
| 3290 | set_bit(TCON_LINK_PENDING, &newtlink->tl_flags); | ||
| 3291 | set_bit(TCON_LINK_IN_TREE, &newtlink->tl_flags); | ||
| 3292 | cifs_get_tlink(newtlink); | ||
| 3293 | |||
| 3294 | ret = radix_tree_preload(GFP_KERNEL); | ||
| 3295 | if (ret != 0) { | ||
| 3296 | kfree(newtlink); | ||
| 3297 | return ERR_PTR(ret); | ||
| 3298 | } | ||
| 3299 | |||
| 3300 | spin_lock(&cifs_sb->tlink_tree_lock); | ||
| 3301 | /* was one inserted after previous search? */ | ||
| 3302 | tlink = radix_tree_lookup(&cifs_sb->tlink_tree, fsuid); | ||
| 3303 | if (tlink) { | ||
| 3304 | cifs_get_tlink(tlink); | ||
| 3305 | spin_unlock(&cifs_sb->tlink_tree_lock); | ||
| 3306 | radix_tree_preload_end(); | ||
| 3307 | kfree(newtlink); | ||
| 3308 | goto wait_for_construction; | ||
| 3309 | } | ||
| 3310 | ret = radix_tree_insert(&cifs_sb->tlink_tree, fsuid, newtlink); | ||
| 3311 | spin_unlock(&cifs_sb->tlink_tree_lock); | ||
| 3312 | radix_tree_preload_end(); | ||
| 3313 | if (ret) { | ||
| 3314 | kfree(newtlink); | ||
| 3315 | return ERR_PTR(ret); | ||
| 3316 | } | ||
| 3317 | tlink = newtlink; | ||
| 3318 | } else { | ||
| 3319 | wait_for_construction: | ||
| 3320 | ret = wait_on_bit(&tlink->tl_flags, TCON_LINK_PENDING, | ||
| 3321 | cifs_sb_tcon_pending_wait, | ||
| 3322 | TASK_INTERRUPTIBLE); | ||
| 3323 | if (ret) { | ||
| 3324 | cifs_put_tlink(tlink); | ||
| 3325 | return ERR_PTR(ret); | ||
| 3326 | } | ||
| 3327 | |||
| 3328 | /* if it's good, return it */ | ||
| 3329 | if (!IS_ERR(tlink->tl_tcon)) | ||
| 3330 | return tlink; | ||
| 3331 | |||
| 3332 | /* return error if we tried this already recently */ | ||
| 3333 | if (time_before(jiffies, tlink->tl_time + TLINK_ERROR_EXPIRE)) { | ||
| 3334 | cifs_put_tlink(tlink); | ||
| 3335 | return ERR_PTR(-EACCES); | ||
| 3336 | } | ||
| 3337 | |||
| 3338 | if (test_and_set_bit(TCON_LINK_PENDING, &tlink->tl_flags)) | ||
| 3339 | goto wait_for_construction; | ||
| 3340 | } | ||
| 3341 | |||
| 3342 | tlink->tl_tcon = cifs_construct_tcon(cifs_sb, fsuid); | ||
| 3343 | clear_bit(TCON_LINK_PENDING, &tlink->tl_flags); | ||
| 3344 | wake_up_bit(&tlink->tl_flags, TCON_LINK_PENDING); | ||
| 3345 | |||
| 3346 | if (IS_ERR(tlink->tl_tcon)) { | ||
| 3347 | cifs_put_tlink(tlink); | ||
| 3348 | return ERR_PTR(-EACCES); | ||
| 3349 | } | ||
| 3350 | |||
| 3351 | return tlink; | ||
| 3352 | } | ||
