SUNRPC: Introduce rpc_clone_client_set_auth()

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.

It turns out that's not the only problem here.  While working on a
fix for the above problem, we noticed that replacing an rpc_clnt's
rpc_auth is not safe, since dereferencing the cl_auth field is not
protected in any way.

So we're deprecating the ability of rpcauth_create() to switch an
rpc_clnt's security flavor during normal operation.  Instead, let's
add a fresh API that clones an rpc_clnt and gives the clone a new
flavor before it's used.

This makes immediate use of the new __rpc_clone_client() helper.

This can be used in a similar fashion to rpcauth_create() when a
client is hunting for the correct security flavor.  Instead of
replacing an rpc_clnt's security flavor in a loop, the ULP replaces
the whole rpc_clnt.

To fix the -EEXIST problem, any ULP logic that relies on replacing
an rpc_clnt's rpc_auth with rpcauth_create() must be changed to use
this API instead.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
This commit is contained in:
Chuck Lever 2012-09-14 17:24:02 -04:00 committed by Trond Myklebust
parent 1b63a75180
commit ba9b584c1d
4 changed files with 27 additions and 24 deletions

View File

@ -668,7 +668,8 @@ int nfs_init_server_rpcclient(struct nfs_server *server,
{ {
struct nfs_client *clp = server->nfs_client; struct nfs_client *clp = server->nfs_client;
server->client = rpc_clone_client(clp->cl_rpcclient); server->client = rpc_clone_client_set_auth(clp->cl_rpcclient,
pseudoflavour);
if (IS_ERR(server->client)) { if (IS_ERR(server->client)) {
dprintk("%s: couldn't create rpc_client!\n", __func__); dprintk("%s: couldn't create rpc_client!\n", __func__);
return PTR_ERR(server->client); return PTR_ERR(server->client);
@ -678,16 +679,6 @@ int nfs_init_server_rpcclient(struct nfs_server *server,
timeo, timeo,
sizeof(server->client->cl_timeout_default)); sizeof(server->client->cl_timeout_default));
server->client->cl_timeout = &server->client->cl_timeout_default; server->client->cl_timeout = &server->client->cl_timeout_default;
if (pseudoflavour != clp->cl_rpcclient->cl_auth->au_flavor) {
struct rpc_auth *auth;
auth = rpcauth_create(pseudoflavour, server->client);
if (IS_ERR(auth)) {
dprintk("%s: couldn't create credcache!\n", __func__);
return PTR_ERR(auth);
}
}
server->client->cl_softrtry = 0; server->client->cl_softrtry = 0;
if (server->flags & NFS_MOUNT_SOFT) if (server->flags & NFS_MOUNT_SOFT)
server->client->cl_softrtry = 1; server->client->cl_softrtry = 1;

View File

@ -192,25 +192,13 @@ out:
struct rpc_clnt *nfs4_create_sec_client(struct rpc_clnt *clnt, struct inode *inode, struct rpc_clnt *nfs4_create_sec_client(struct rpc_clnt *clnt, struct inode *inode,
struct qstr *name) struct qstr *name)
{ {
struct rpc_clnt *clone;
struct rpc_auth *auth;
rpc_authflavor_t flavor; rpc_authflavor_t flavor;
flavor = nfs4_negotiate_security(inode, name); flavor = nfs4_negotiate_security(inode, name);
if ((int)flavor < 0) if ((int)flavor < 0)
return ERR_PTR((int)flavor); return ERR_PTR((int)flavor);
clone = rpc_clone_client(clnt); return rpc_clone_client_set_auth(clnt, flavor);
if (IS_ERR(clone))
return clone;
auth = rpcauth_create(flavor, clone);
if (IS_ERR(auth)) {
rpc_shutdown_client(clone);
clone = ERR_PTR(-EIO);
}
return clone;
} }
static struct vfsmount *try_location(struct nfs_clone_mount *mountdata, static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,

View File

@ -130,6 +130,8 @@ struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *,
const struct rpc_program *, u32); const struct rpc_program *, u32);
void rpc_task_reset_client(struct rpc_task *task, struct rpc_clnt *clnt); void rpc_task_reset_client(struct rpc_task *task, struct rpc_clnt *clnt);
struct rpc_clnt *rpc_clone_client(struct rpc_clnt *); struct rpc_clnt *rpc_clone_client(struct rpc_clnt *);
struct rpc_clnt *rpc_clone_client_set_auth(struct rpc_clnt *,
rpc_authflavor_t);
void rpc_shutdown_client(struct rpc_clnt *); void rpc_shutdown_client(struct rpc_clnt *);
void rpc_release_client(struct rpc_clnt *); void rpc_release_client(struct rpc_clnt *);
void rpc_task_release_client(struct rpc_task *); void rpc_task_release_client(struct rpc_task *);

View File

@ -548,6 +548,28 @@ struct rpc_clnt *rpc_clone_client(struct rpc_clnt *clnt)
} }
EXPORT_SYMBOL_GPL(rpc_clone_client); EXPORT_SYMBOL_GPL(rpc_clone_client);
/**
* rpc_clone_client_set_auth - Clone an RPC client structure and set its auth
*
* @clnt: RPC client whose parameters are copied
* @auth: security flavor for new client
*
* Returns a fresh RPC client or an ERR_PTR.
*/
struct rpc_clnt *
rpc_clone_client_set_auth(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
{
struct rpc_create_args args = {
.program = clnt->cl_program,
.prognumber = clnt->cl_prog,
.version = clnt->cl_vers,
.authflavor = flavor,
.client_name = clnt->cl_principal,
};
return __rpc_clone_client(&args, clnt);
}
EXPORT_SYMBOL_GPL(rpc_clone_client_set_auth);
/* /*
* Kill all tasks for the given client. * Kill all tasks for the given client.
* XXX: kill their descendants as well? * XXX: kill their descendants as well?