From patchwork Thu Aug 9 21:31:35 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chuck Lever X-Patchwork-Id: 1303051 Return-Path: X-Original-To: patchwork-linux-nfs@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id E2A7B3FD8C for ; Thu, 9 Aug 2012 21:31:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756082Ab2HIVbi (ORCPT ); Thu, 9 Aug 2012 17:31:38 -0400 Received: from mail-yw0-f46.google.com ([209.85.213.46]:44553 "EHLO mail-yw0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755470Ab2HIVbh (ORCPT ); Thu, 9 Aug 2012 17:31:37 -0400 Received: by mail-yw0-f46.google.com with SMTP id m54so1004938yhm.19 for ; Thu, 09 Aug 2012 14:31:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:subject:to:cc:date:message-id:in-reply-to:references :user-agent:mime-version:content-type:content-transfer-encoding; bh=IEw+GX3lIU2tZFsbxFliwvAxUz1ECQWUJbfAywVdXaM=; b=BLT1FmofVfYiBgeWEbEPijVeoE/+SL2OgoZCVOGk6SUK9OoDFNgD3oe5/sHeFZV2ck kNL69qZT9/0kGAkEZWjEXFajdn8y63rqF2FrjjFPxQNkhbksfCiwPXrGQiTSQ9iqKlh7 6FGIPPLzAcgu5k0fectKShiMSEqq3CWj2g4XvJ1Ae5eQCj7WGg+EBCP6ulrPADhV1q8M 5Ek5rCNwR1gg9imrHgXqar+zaT7PQSobMUuh+8WGCY0pwQoll0KoXkHLJnSlkRV5oP1p oEK2OwtchRcG4je865LTlePQC3iLi7OpcXWiE+cOttvCgVLqyzXEg1TUM3c7kt9fGWRE vH5w== Received: by 10.50.188.130 with SMTP id ga2mr2606082igc.32.1344547896921; Thu, 09 Aug 2012 14:31:36 -0700 (PDT) Received: from degas.1015granger.net (adsl-99-26-161-222.dsl.sfldmi.sbcglobal.net. [99.26.161.222]) by mx.google.com with ESMTPS id kn9sm1705110igc.0.2012.08.09.14.31.36 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 09 Aug 2012 14:31:36 -0700 (PDT) From: Chuck Lever Subject: [PATCH 3/3] SUNRPC: Share upcall pipes among an rpc_clnt's rpc_auth objects To: linux-nfs@vger.kernel.org Cc: skinsbursky@parallels.com Date: Thu, 09 Aug 2012 17:31:35 -0400 Message-ID: <20120809213135.12984.30347.stgit@degas.1015granger.net> In-Reply-To: <20120809205748.12984.88991.stgit@degas.1015granger.net> References: <20120809205748.12984.88991.stgit@degas.1015granger.net> User-Agent: StGIT/0.14.3 MIME-Version: 1.0 Sender: linux-nfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-nfs@vger.kernel.org An ULP is supposed to be able to replace a GSS rpc_auth object with another GSS rpc_auth object using rpcauth_create(). However, rpcauth_create() in 3.5 reliably fails with -EEXIST in this case. This is because when gss_create() attempts to create the upcall pipes, sometimes they are already there. For example if a pipe FS mount event occurs, or a previous GSS flavor was in use for this rpc_clnt. The ->pipes_destroy method works only on whatever rpc_auth is associated with the rpc_clnt when it is destroyed. But there is no guarantee that this is the same rpc_auth that was created when the rpc_clnt was created. Thus cached pipe data must be reference counted and destroyed when no more gss_auth's are using it. And, ->pipes_destroy must ensure that both the dentries and the pipe data are gone so the pipe data does not leak. The dentries appear to be created and deleted independently of the rpc_pipe data. We must allow this to continue in order that mounting and unmounting the pipe FS can work. However, pipe dentries must remain in place during gss_free(), since they could be shared. In the meantime, we need to provide a way for gss_create() to find an existing pipe of a particular name so it may be shared. Signed-off-by: Chuck Lever Cc: Stanislav Kinsbursky --- include/linux/sunrpc/clnt.h | 1 net/sunrpc/auth_gss/auth_gss.c | 141 ++++++++++++++++++++++++++++++++++------ net/sunrpc/clnt.c | 1 3 files changed, 123 insertions(+), 20 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index 523547e..31857e0 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -62,6 +62,7 @@ struct rpc_clnt { struct rpc_timeout cl_timeout_default; const struct rpc_program *cl_program; char *cl_principal; /* target to authenticate to */ + struct list_head cl_pipes; /* clnt's upcall pipes */ }; /* diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 2264776..8ff5103 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -84,6 +84,14 @@ struct gss_auth { struct rpc_pipe *pipe[2]; }; +struct gss_cached_pipe { + struct list_head gp_list; + unsigned int gp_ref; + char *gp_name; + struct rpc_pipe *gp_pipe; +}; +DEFINE_MUTEX(gss_pipe_mutex); + /* pipe_version >= 0 if and only if someone has a pipe open. */ static int pipe_version = -1; static atomic_t pipe_users = ATOMIC_INIT(0); @@ -765,6 +773,20 @@ static void gss_pipe_dentry_destroy(struct rpc_pipe *pipe) } } +static void gss_pipe_dentry_destroy_net(struct rpc_clnt *clnt, + struct rpc_pipe *pipe) +{ + struct net *net = rpc_net_ns(clnt); + struct super_block *sb; + + sb = rpc_get_sb_net(net); + if (sb) { + if (clnt->cl_dentry) + gss_pipe_dentry_destroy(pipe); + rpc_put_sb_net(net); + } +} + /* * Release existing pipe dentries, but leave the rpc_pipe data * ready to receive fresh dentries if subsequently needed. @@ -827,20 +849,6 @@ out_err: return err; } -static void gss_pipes_dentries_destroy_net(struct rpc_clnt *clnt, - struct rpc_auth *auth) -{ - struct net *net = rpc_net_ns(clnt); - struct super_block *sb; - - sb = rpc_get_sb_net(net); - if (sb) { - if (clnt->cl_dentry) - gss_pipes_dentries_destroy(auth); - rpc_put_sb_net(net); - } -} - static int gss_pipes_dentries_create_net(struct rpc_clnt *clnt, struct rpc_auth *auth) { @@ -857,16 +865,111 @@ static int gss_pipes_dentries_create_net(struct rpc_clnt *clnt, return err; } +static struct gss_cached_pipe *gss_alloc_cached_pipe(char *name) +{ + struct gss_cached_pipe *cp; + + cp = kmalloc(sizeof(*cp), GFP_KERNEL); + if (cp == NULL) + return NULL; + + cp->gp_name = kstrdup(name, GFP_KERNEL); + if (cp->gp_name == NULL) { + kfree(cp); + return NULL; + } + + INIT_LIST_HEAD(&cp->gp_list); + cp->gp_ref = 1; + return cp; +} + +static void gss_free_cached_pipe(struct gss_cached_pipe *cp) +{ + kfree(cp->gp_name); + kfree(cp); +} + +/* + * Returns a fresh or cached pipe data object. A cached pipe + * data object may already have a dentry attached to it. If an + * object cannot be found or created, an ERR_PTR is returned. + */ static struct rpc_pipe *gss_mkpipe_data(struct rpc_clnt *clnt, const struct rpc_pipe_ops *ops, char *name) { - return rpc_mkpipe_data(ops, RPC_PIPE_WAIT_FOR_OPEN); + struct gss_cached_pipe *cp; + struct rpc_pipe *pipe; + + mutex_lock(&gss_pipe_mutex); + + list_for_each_entry(cp, &clnt->cl_pipes, gp_list) { + if (strcmp(cp->gp_name, name) == 0) { + dprintk("RPC: %s found '%s' for clnt %p: %p\n", + __func__, name, clnt, cp->gp_pipe); + cp->gp_ref++; + pipe = cp->gp_pipe; + goto out; + } + } + + pipe = ERR_PTR(-ENOMEM); + cp = gss_alloc_cached_pipe(name); + if (cp == NULL) + goto out; + + pipe = rpc_mkpipe_data(ops, RPC_PIPE_WAIT_FOR_OPEN); + if (IS_ERR(pipe)) { + gss_free_cached_pipe(cp); + goto out; + } + + dprintk("RPC: %s created '%s' for clnt %p: %p\n", + __func__, name, clnt, pipe); + cp->gp_pipe = pipe; + list_add(&cp->gp_list, &clnt->cl_pipes); + +out: + mutex_unlock(&gss_pipe_mutex); + return pipe; } +/* + * Decrements a cached pipes reference count, and releases it if + * the count goes to zero. Associated dentry is freed if present. + */ static void gss_destroy_pipe_data(struct rpc_clnt *clnt, struct rpc_pipe *pipe) { - rpc_destroy_pipe_data(pipe); + struct gss_cached_pipe *cp; + + mutex_lock(&gss_pipe_mutex); + + list_for_each_entry(cp, &clnt->cl_pipes, gp_list) { + if (cp->gp_pipe == pipe) + goto found; + } + + dprintk("RPC: %s missing cache for pipe %p for clnt %p\n", + __func__, pipe, clnt); + WARN_ON(true); + + mutex_unlock(&gss_pipe_mutex); + return; + +found: + if (--cp->gp_ref == 0) { + dprintk("RPC: %s destroying '%s' (%p) for clnt %p\n", + __func__, cp->gp_name, pipe, clnt); + gss_pipe_dentry_destroy_net(clnt, pipe); + rpc_destroy_pipe_data(pipe); + list_del(&cp->gp_list); + gss_free_cached_pipe(cp); + } else + dprintk("RPC: %s leaving '%s' (%p) for clnt %p\n", + __func__, cp->gp_name, pipe, clnt); + + mutex_unlock(&gss_pipe_mutex); } /* @@ -925,13 +1028,12 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) err = gss_pipes_dentries_create_net(clnt, auth); if (err) goto err_destroy_pipe_0; + err = rpcauth_init_credcache(auth); if (err) - goto err_unlink_pipes; + goto err_destroy_pipe_0; return auth; -err_unlink_pipes: - gss_pipes_dentries_destroy_net(clnt, auth); err_destroy_pipe_0: gss_destroy_pipe_data(clnt, gss_auth->pipe[0]); err_destroy_pipe_1: @@ -948,7 +1050,6 @@ out_dec: static void gss_free(struct gss_auth *gss_auth) { - gss_pipes_dentries_destroy_net(gss_auth->client, &gss_auth->rpc_auth); gss_destroy_pipe_data(gss_auth->client, gss_auth->pipe[0]); gss_destroy_pipe_data(gss_auth->client, gss_auth->pipe[1]); gss_mech_put(gss_auth->mech); diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index f56f045..7bf6ed1 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -349,6 +349,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru if (!clnt->cl_principal) goto out_no_principal; } + INIT_LIST_HEAD(&clnt->cl_pipes); atomic_set(&clnt->cl_count, 1);