From patchwork Tue Apr 20 20:07:17 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jeff Layton X-Patchwork-Id: 50568 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.samba.org (fn.samba.org [216.83.154.106]) by ozlabs.org (Postfix) with ESMTP id 2C8AEB7D13 for ; Wed, 21 Apr 2010 06:07:50 +1000 (EST) Received: from fn.samba.org (localhost [127.0.0.1]) by lists.samba.org (Postfix) with ESMTP id 82F45AD242; Tue, 20 Apr 2010 14:07:50 -0600 (MDT) X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on fn.samba.org X-Spam-Level: X-Spam-Status: No, score=-3.4 required=3.8 tests=AWL, BAYES_00, NO_MORE_FUNN, SPF_PASS autolearn=no version=3.2.5 X-Original-To: linux-cifs-client@lists.samba.org Delivered-To: linux-cifs-client@lists.samba.org Received: from cdptpa-omtalb.mail.rr.com (cdptpa-omtalb.mail.rr.com [75.180.132.121]) by lists.samba.org (Postfix) with ESMTP id 1C3D246587 for ; Tue, 20 Apr 2010 14:07:24 -0600 (MDT) X-Authority-Analysis: v=1.1 cv=mJ2XtCV6gem0td6545d6o4z3SC3BoM2bxxOQ2/EEFjA= c=1 sm=0 a=mSjt3MlF-YgA:10 a=ld/erqUjW76FpBUqCqkKeA==:17 a=20KFwNOVAAAA:8 a=fdkxm5JXNSvAdKthwyoA:9 a=e5hswIiRUQVpMrwnJQMA:7 a=kIj0ZalstGBPgVO5R3eguWZjwYAA:4 a=jEp0ucaQiEUA:10 a=ld/erqUjW76FpBUqCqkKeA==:117 X-Cloudmark-Score: 0 X-Originating-IP: 71.70.153.3 Received: from [71.70.153.3] ([71.70.153.3:45270] helo=mail.poochiereds.net) by cdptpa-oedge02.mail.rr.com (envelope-from ) (ecelerity 2.2.2.39 r()) with ESMTP id 54/58-22328-A790ECB4; Tue, 20 Apr 2010 20:07:22 +0000 Received: by mail.poochiereds.net (Postfix, from userid 4447) id CBBC85809A; Tue, 20 Apr 2010 16:07:20 -0400 (EDT) From: Jeff Layton To: linux-cifs-client@lists.samba.org, linux-fsdevel@vger.kernel.org Date: Tue, 20 Apr 2010 16:07:17 -0400 Message-Id: <1271794039-22787-10-git-send-email-jlayton@redhat.com> X-Mailer: git-send-email 1.6.6.1 In-Reply-To: <1271794039-22787-1-git-send-email-jlayton@redhat.com> References: <1271794039-22787-1-git-send-email-jlayton@redhat.com> Cc: smfrench@gmail.com Subject: [linux-cifs-client] [PATCH 09/11] cifs: add routines to build sessions and tcons on the fly X-BeenThere: linux-cifs-client@lists.samba.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: The Linux CIFS VFS client List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-cifs-client-bounces@lists.samba.org Errors-To: linux-cifs-client-bounces@lists.samba.org This patch is rather large, but it's a bit difficult to do piecemeal... 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 --- fs/cifs/cifs_fs_sb.h | 7 +- fs/cifs/connect.c | 202 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 200 insertions(+), 9 deletions(-) diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index e658723..14c3b69 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -15,6 +15,8 @@ * the GNU Lesser General Public License for more details. * */ +#include + #ifndef _CIFS_FS_SB_H #define _CIFS_FS_SB_H @@ -36,8 +38,9 @@ #define CIFS_MOUNT_MULTISES 0x8000 /* multisession mount */ struct cifs_sb_info { - struct cifsTconInfo *tcon; /* primary mount */ - struct list_head nested_tcon_q; + struct radix_tree_root tcon_tree; +#define CIFS_TCON_MASTER_TAG 0 /* tcon is "master" (mount) tcon */ + spinlock_t tcon_tree_lock; struct nls_table *local_nls; unsigned int rsize; unsigned int wsize; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 09a1216..bc408ad 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -106,6 +106,15 @@ struct smb_vol { struct nls_table *local_nls; }; +#define TLINK_ERROR_EXPIRE (1 * HZ) + +#define TCON_LINK_PENDING 1 +struct tcon_link { + unsigned long flags; + unsigned long time; + struct cifsTconInfo *tcon; +}; + static int ipv4_connect(struct TCP_Server_Info *server); static int ipv6_connect(struct TCP_Server_Info *server); @@ -2660,6 +2669,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, struct TCP_Server_Info *srvTcp; char *full_path; char *mount_data = mount_data_global; + struct tcon_link *tlink; #ifdef CONFIG_CIFS_DFS_UPCALL struct dfs_info3_param *referrals = NULL; unsigned int num_referrals = 0; @@ -2671,6 +2681,7 @@ try_mount_again: pSesInfo = NULL; srvTcp = NULL; full_path = NULL; + tlink = NULL; xid = GetXid(); @@ -2746,8 +2757,6 @@ try_mount_again: goto remote_path_check; } - cifs_sb->tcon = tcon; - /* do not care if following two calls succeed - informational */ if (!tcon->ipc) { CIFSSMBQFSDeviceInfo(xid, tcon); @@ -2856,6 +2865,29 @@ remote_path_check: #endif } + /* now, hang the tcon off of the superblock */ + tlink = kzalloc(sizeof *tlink, GFP_KERNEL); + if (tlink == NULL) { + rc = -ENOMEM; + goto mount_fail_check; + } + + tlink->tcon = tcon; + tlink->time = jiffies; + + rc = radix_tree_preload(GFP_KERNEL); + if (rc == -ENOMEM) { + kfree(tlink); + goto mount_fail_check; + } + + spin_lock(&cifs_sb->tcon_tree_lock); + radix_tree_insert(&cifs_sb->tcon_tree, pSesInfo->linux_uid, tlink); + radix_tree_tag_set(&cifs_sb->tcon_tree, pSesInfo->linux_uid, + CIFS_TCON_MASTER_TAG); + spin_unlock(&cifs_sb->tcon_tree_lock); + radix_tree_preload_end(); + mount_fail_check: /* on error free sesinfo and tcon struct if needed */ if (rc) { @@ -3041,19 +3073,35 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, int cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) { - int rc = 0; + int i, ret; char *tmp; + struct tcon_link *tlink[8]; + unsigned long index = 0; + + while (1) { + spin_lock(&cifs_sb->tcon_tree_lock); + ret = radix_tree_gang_lookup(&cifs_sb->tcon_tree, + (void **)tlink, index, + ARRAY_SIZE(tlink)); + for (i = 0; i < ret; i++) { + index = (unsigned long)tlink[i]->tcon->ses->linux_uid; + radix_tree_delete(&cifs_sb->tcon_tree, index); + } + spin_unlock(&cifs_sb->tcon_tree_lock); - if (cifs_sb_tcon(cifs_sb)) - cifs_put_tcon(cifs_sb_tcon(cifs_sb)); + for (i = 0; i < ret; i++) + cifs_put_tcon(tlink[i]->tcon); + + if (!ret) + break; + } - cifs_sb->tcon = NULL; tmp = cifs_sb->prepath; cifs_sb->prepathlen = 0; cifs_sb->prepath = NULL; kfree(tmp); - return rc; + return 0; } int cifs_negotiate_protocol(unsigned int xid, struct cifsSesInfo *ses) @@ -3114,3 +3162,143 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *ses, return rc; } +struct cifsTconInfo * +cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) +{ + struct cifsTconInfo *master_tcon = cifs_sb_master_tcon(cifs_sb); + struct cifsSesInfo *ses; + struct cifsTconInfo *tcon = NULL; + struct smb_vol *vol_info; + + vol_info = kzalloc(sizeof(*vol_info), GFP_KERNEL); + if (vol_info == NULL) { + tcon = ERR_PTR(-ENOMEM); + goto out; + } + + vol_info->username = kzalloc(MAX_USERNAME_SIZE + 1, GFP_KERNEL); + if (vol_info->username == NULL) { + tcon = ERR_PTR(-ENOMEM); + goto out; + } + + snprintf(vol_info->username, MAX_USERNAME_SIZE, "krb50x%x", fsuid); + vol_info->local_nls = cifs_sb->local_nls; + vol_info->linux_uid = fsuid; + vol_info->cred_uid = fsuid; + vol_info->UNC = master_tcon->treeName; + vol_info->retry = master_tcon->retry; + vol_info->nocase = master_tcon->nocase; + vol_info->local_lease = master_tcon->local_lease; + vol_info->no_linux_ext = !master_tcon->unix_ext; + vol_info->sectype = master_tcon->ses->secType; + + ses = cifs_get_smb_ses(master_tcon->ses->server, vol_info); + if (IS_ERR(ses)) { + tcon = (struct cifsTconInfo *) ses; + goto out; + } + + tcon = cifs_get_tcon(ses, vol_info); + if (IS_ERR(tcon)) { + cifs_put_smb_ses(ses); + goto out; + } + + if (ses->capabilities & CAP_UNIX) + reset_cifs_unix_caps(0, tcon, NULL, vol_info); +out: + kfree(vol_info->username); + kfree(vol_info); + + return tcon; +} + +struct cifsTconInfo * +cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb) +{ + struct tcon_link *tlink; + unsigned int ret; + + spin_lock(&cifs_sb->tcon_tree_lock); + ret = radix_tree_gang_lookup_tag(&cifs_sb->tcon_tree, (void **) &tlink, + 0, 1, CIFS_TCON_MASTER_TAG); + spin_unlock(&cifs_sb->tcon_tree_lock); + + /* the master tcon should always be present */ + if (ret == 0) + BUG(); + + return tlink->tcon; +} + +static int +cifs_sb_tcon_pending_wait(void *unused) +{ + schedule(); + return signal_pending(current) ? -ERESTARTSYS : 0; +} + +struct cifsTconInfo * +cifs_sb_tcon(struct cifs_sb_info *cifs_sb) +{ + int ret; + unsigned long fsuid = (unsigned long) current_fsuid(); + struct tcon_link *tlink, *newtlink; + + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTISES)) + return cifs_sb_master_tcon(cifs_sb); + + spin_lock(&cifs_sb->tcon_tree_lock); + tlink = radix_tree_lookup(&cifs_sb->tcon_tree, fsuid); + spin_unlock(&cifs_sb->tcon_tree_lock); + + if (tlink == NULL) { + newtlink = kzalloc(sizeof(*tlink), GFP_KERNEL); + if (newtlink == NULL) + return ERR_PTR(-ENOMEM); + newtlink->time = jiffies; + newtlink->tcon = ERR_PTR(-EACCES); + set_bit(TCON_LINK_PENDING, &newtlink->flags); + + ret = radix_tree_preload(GFP_KERNEL); + if (ret != 0) { + kfree(newtlink); + return ERR_PTR(ret); + } + + spin_lock(&cifs_sb->tcon_tree_lock); + tlink = radix_tree_lookup(&cifs_sb->tcon_tree, fsuid); + if (!tlink) { + tlink = newtlink; + newtlink = NULL; + ret = radix_tree_insert(&cifs_sb->tcon_tree, fsuid, + tlink); + } + spin_unlock(&cifs_sb->tcon_tree_lock); + radix_tree_preload_end(); + kfree(newtlink); + if (ret) + return ERR_PTR(ret); + } else { + ret = wait_on_bit(&tlink->flags, TCON_LINK_PENDING, + cifs_sb_tcon_pending_wait, + TASK_INTERRUPTIBLE); + if (ret) + return ERR_PTR(ret); + + spin_lock(&cifs_sb->tcon_tree_lock); + if (!IS_ERR(tlink->tcon) || + time_before(jiffies, tlink->time + TLINK_ERROR_EXPIRE)) { + spin_unlock(&cifs_sb->tcon_tree_lock); + return tlink->tcon; + } + set_bit(TCON_LINK_PENDING, &tlink->flags); + spin_unlock(&cifs_sb->tcon_tree_lock); + } + + tlink->tcon = cifs_construct_tcon(cifs_sb, current_fsuid()); + clear_bit(TCON_LINK_PENDING, &tlink->flags); + wake_up_bit(&tlink->flags, TCON_LINK_PENDING); + return tlink->tcon; +}