Merge tag 'nfs-for-3.12-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
Pull NFS client updates from Trond Myklebust:
"Highlights include:
- Fix NFSv4 recovery so that it doesn't recover lost locks in cases
such as lease loss due to a network partition, where doing so may
result in data corruption. Add a kernel parameter to control
choice of legacy behaviour or not.
- Performance improvements when 2 processes are writing to the same
file.
- Flush data to disk when an RPCSEC_GSS session timeout is imminent.
- Implement NFSv4.1 SP4_MACH_CRED state protection to prevent other
NFS clients from being able to manipulate our lease and file
locking state.
- Allow sharing of RPCSEC_GSS caches between different rpc clients.
- Fix the broken NFSv4 security auto-negotiation between client and
server.
- Fix rmdir() to wait for outstanding sillyrename unlinks to complete
- Add a tracepoint framework for debugging NFSv4 state recovery
issues.
- Add tracing to the generic NFS layer.
- Add tracing for the SUNRPC socket connection state.
- Clean up the rpc_pipefs mount/umount event management.
- Merge more patches from Chuck in preparation for NFSv4 migration
support"
* tag 'nfs-for-3.12-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (107 commits)
NFSv4: use mach cred for SECINFO_NO_NAME w/ integrity
NFS: nfs_compare_super shouldn't check the auth flavour unless 'sec=' was set
NFSv4: Allow security autonegotiation for submounts
NFSv4: Disallow security negotiation for lookups when 'sec=' is specified
NFSv4: Fix security auto-negotiation
NFS: Clean up nfs_parse_security_flavors()
NFS: Clean up the auth flavour array mess
NFSv4.1 Use MDS auth flavor for data server connection
NFS: Don't check lock owner compatability unless file is locked (part 2)
NFS: Don't check lock owner compatibility in writes unless file is locked
nfs4: Map NFS4ERR_WRONG_CRED to EPERM
nfs4.1: Add SP4_MACH_CRED write and commit support
nfs4.1: Add SP4_MACH_CRED stateid support
nfs4.1: Add SP4_MACH_CRED secinfo support
nfs4.1: Add SP4_MACH_CRED cleanup support
nfs4.1: Add state protection handler
nfs4.1: Minimal SP4_MACH_CRED implementation
SUNRPC: Replace pointer values with task->tk_pid and rpc_clnt->cl_clid
SUNRPC: Add an identifier for struct rpc_clnt
SUNRPC: Ensure rpc_task->tk_pid is available for tracepoints
...
This commit is contained in:
@@ -250,11 +250,11 @@ rpcauth_list_flavors(rpc_authflavor_t *array, int size)
|
||||
EXPORT_SYMBOL_GPL(rpcauth_list_flavors);
|
||||
|
||||
struct rpc_auth *
|
||||
rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt)
|
||||
rpcauth_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
|
||||
{
|
||||
struct rpc_auth *auth;
|
||||
const struct rpc_authops *ops;
|
||||
u32 flavor = pseudoflavor_to_flavor(pseudoflavor);
|
||||
u32 flavor = pseudoflavor_to_flavor(args->pseudoflavor);
|
||||
|
||||
auth = ERR_PTR(-EINVAL);
|
||||
if (flavor >= RPC_AUTH_MAXFLAVOR)
|
||||
@@ -269,7 +269,7 @@ rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt)
|
||||
goto out;
|
||||
}
|
||||
spin_unlock(&rpc_authflavor_lock);
|
||||
auth = ops->create(clnt, pseudoflavor);
|
||||
auth = ops->create(args, clnt);
|
||||
module_put(ops->owner);
|
||||
if (IS_ERR(auth))
|
||||
return auth;
|
||||
@@ -342,6 +342,27 @@ out_nocache:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpcauth_init_credcache);
|
||||
|
||||
/*
|
||||
* Setup a credential key lifetime timeout notification
|
||||
*/
|
||||
int
|
||||
rpcauth_key_timeout_notify(struct rpc_auth *auth, struct rpc_cred *cred)
|
||||
{
|
||||
if (!cred->cr_auth->au_ops->key_timeout)
|
||||
return 0;
|
||||
return cred->cr_auth->au_ops->key_timeout(auth, cred);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpcauth_key_timeout_notify);
|
||||
|
||||
bool
|
||||
rpcauth_cred_key_to_expire(struct rpc_cred *cred)
|
||||
{
|
||||
if (!cred->cr_ops->crkey_to_expire)
|
||||
return false;
|
||||
return cred->cr_ops->crkey_to_expire(cred);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpcauth_cred_key_to_expire);
|
||||
|
||||
/*
|
||||
* Destroy a list of credentials
|
||||
*/
|
||||
|
||||
@@ -89,6 +89,7 @@ generic_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
|
||||
gcred->acred.uid = acred->uid;
|
||||
gcred->acred.gid = acred->gid;
|
||||
gcred->acred.group_info = acred->group_info;
|
||||
gcred->acred.ac_flags = 0;
|
||||
if (gcred->acred.group_info != NULL)
|
||||
get_group_info(gcred->acred.group_info);
|
||||
gcred->acred.machine_cred = acred->machine_cred;
|
||||
@@ -182,11 +183,78 @@ void rpc_destroy_generic_auth(void)
|
||||
rpcauth_destroy_credcache(&generic_auth);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test the the current time (now) against the underlying credential key expiry
|
||||
* minus a timeout and setup notification.
|
||||
*
|
||||
* The normal case:
|
||||
* If 'now' is before the key expiry minus RPC_KEY_EXPIRE_TIMEO, set
|
||||
* the RPC_CRED_NOTIFY_TIMEOUT flag to setup the underlying credential
|
||||
* rpc_credops crmatch routine to notify this generic cred when it's key
|
||||
* expiration is within RPC_KEY_EXPIRE_TIMEO, and return 0.
|
||||
*
|
||||
* The error case:
|
||||
* If the underlying cred lookup fails, return -EACCES.
|
||||
*
|
||||
* The 'almost' error case:
|
||||
* If 'now' is within key expiry minus RPC_KEY_EXPIRE_TIMEO, but not within
|
||||
* key expiry minus RPC_KEY_EXPIRE_FAIL, set the RPC_CRED_EXPIRE_SOON bit
|
||||
* on the acred ac_flags and return 0.
|
||||
*/
|
||||
static int
|
||||
generic_key_timeout(struct rpc_auth *auth, struct rpc_cred *cred)
|
||||
{
|
||||
struct auth_cred *acred = &container_of(cred, struct generic_cred,
|
||||
gc_base)->acred;
|
||||
struct rpc_cred *tcred;
|
||||
int ret = 0;
|
||||
|
||||
|
||||
/* Fast track for non crkey_timeout (no key) underlying credentials */
|
||||
if (test_bit(RPC_CRED_NO_CRKEY_TIMEOUT, &acred->ac_flags))
|
||||
return 0;
|
||||
|
||||
/* Fast track for the normal case */
|
||||
if (test_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags))
|
||||
return 0;
|
||||
|
||||
/* lookup_cred either returns a valid referenced rpc_cred, or PTR_ERR */
|
||||
tcred = auth->au_ops->lookup_cred(auth, acred, 0);
|
||||
if (IS_ERR(tcred))
|
||||
return -EACCES;
|
||||
|
||||
if (!tcred->cr_ops->crkey_timeout) {
|
||||
set_bit(RPC_CRED_NO_CRKEY_TIMEOUT, &acred->ac_flags);
|
||||
ret = 0;
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
/* Test for the almost error case */
|
||||
ret = tcred->cr_ops->crkey_timeout(tcred);
|
||||
if (ret != 0) {
|
||||
set_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags);
|
||||
ret = 0;
|
||||
} else {
|
||||
/* In case underlying cred key has been reset */
|
||||
if (test_and_clear_bit(RPC_CRED_KEY_EXPIRE_SOON,
|
||||
&acred->ac_flags))
|
||||
dprintk("RPC: UID %d Credential key reset\n",
|
||||
tcred->cr_uid);
|
||||
/* set up fasttrack for the normal case */
|
||||
set_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags);
|
||||
}
|
||||
|
||||
out_put:
|
||||
put_rpccred(tcred);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct rpc_authops generic_auth_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.au_name = "Generic",
|
||||
.lookup_cred = generic_lookup_cred,
|
||||
.crcreate = generic_create_cred,
|
||||
.key_timeout = generic_key_timeout,
|
||||
};
|
||||
|
||||
static struct rpc_auth generic_auth = {
|
||||
@@ -194,9 +262,23 @@ static struct rpc_auth generic_auth = {
|
||||
.au_count = ATOMIC_INIT(0),
|
||||
};
|
||||
|
||||
static bool generic_key_to_expire(struct rpc_cred *cred)
|
||||
{
|
||||
struct auth_cred *acred = &container_of(cred, struct generic_cred,
|
||||
gc_base)->acred;
|
||||
bool ret;
|
||||
|
||||
get_rpccred(cred);
|
||||
ret = test_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags);
|
||||
put_rpccred(cred);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct rpc_credops generic_credops = {
|
||||
.cr_name = "Generic cred",
|
||||
.crdestroy = generic_destroy_cred,
|
||||
.crbind = generic_bind_cred,
|
||||
.crmatch = generic_match,
|
||||
.crkey_to_expire = generic_key_to_expire,
|
||||
};
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
#include <linux/sunrpc/rpc_pipe_fs.h>
|
||||
#include <linux/sunrpc/gss_api.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/hashtable.h>
|
||||
|
||||
#include "../netns.h"
|
||||
|
||||
@@ -62,6 +63,9 @@ static const struct rpc_credops gss_nullops;
|
||||
#define GSS_RETRY_EXPIRED 5
|
||||
static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED;
|
||||
|
||||
#define GSS_KEY_EXPIRE_TIMEO 240
|
||||
static unsigned int gss_key_expire_timeo = GSS_KEY_EXPIRE_TIMEO;
|
||||
|
||||
#ifdef RPC_DEBUG
|
||||
# define RPCDBG_FACILITY RPCDBG_AUTH
|
||||
#endif
|
||||
@@ -71,19 +75,33 @@ static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED;
|
||||
* using integrity (two 4-byte integers): */
|
||||
#define GSS_VERF_SLACK 100
|
||||
|
||||
static DEFINE_HASHTABLE(gss_auth_hash_table, 16);
|
||||
static DEFINE_SPINLOCK(gss_auth_hash_lock);
|
||||
|
||||
struct gss_pipe {
|
||||
struct rpc_pipe_dir_object pdo;
|
||||
struct rpc_pipe *pipe;
|
||||
struct rpc_clnt *clnt;
|
||||
const char *name;
|
||||
struct kref kref;
|
||||
};
|
||||
|
||||
struct gss_auth {
|
||||
struct kref kref;
|
||||
struct hlist_node hash;
|
||||
struct rpc_auth rpc_auth;
|
||||
struct gss_api_mech *mech;
|
||||
enum rpc_gss_svc service;
|
||||
struct rpc_clnt *client;
|
||||
struct net *net;
|
||||
/*
|
||||
* There are two upcall pipes; dentry[1], named "gssd", is used
|
||||
* for the new text-based upcall; dentry[0] is named after the
|
||||
* mechanism (for example, "krb5") and exists for
|
||||
* backwards-compatibility with older gssd's.
|
||||
*/
|
||||
struct rpc_pipe *pipe[2];
|
||||
struct gss_pipe *gss_pipe[2];
|
||||
const char *target_name;
|
||||
};
|
||||
|
||||
/* pipe_version >= 0 if and only if someone has a pipe open. */
|
||||
@@ -294,7 +312,7 @@ static void put_pipe_version(struct net *net)
|
||||
static void
|
||||
gss_release_msg(struct gss_upcall_msg *gss_msg)
|
||||
{
|
||||
struct net *net = rpc_net_ns(gss_msg->auth->client);
|
||||
struct net *net = gss_msg->auth->net;
|
||||
if (!atomic_dec_and_test(&gss_msg->count))
|
||||
return;
|
||||
put_pipe_version(net);
|
||||
@@ -406,8 +424,8 @@ static void gss_encode_v0_msg(struct gss_upcall_msg *gss_msg)
|
||||
}
|
||||
|
||||
static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg,
|
||||
struct rpc_clnt *clnt,
|
||||
const char *service_name)
|
||||
const char *service_name,
|
||||
const char *target_name)
|
||||
{
|
||||
struct gss_api_mech *mech = gss_msg->auth->mech;
|
||||
char *p = gss_msg->databuf;
|
||||
@@ -417,8 +435,8 @@ static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg,
|
||||
mech->gm_name,
|
||||
from_kuid(&init_user_ns, gss_msg->uid));
|
||||
p += gss_msg->msg.len;
|
||||
if (clnt->cl_principal) {
|
||||
len = sprintf(p, "target=%s ", clnt->cl_principal);
|
||||
if (target_name) {
|
||||
len = sprintf(p, "target=%s ", target_name);
|
||||
p += len;
|
||||
gss_msg->msg.len += len;
|
||||
}
|
||||
@@ -439,21 +457,8 @@ static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg,
|
||||
BUG_ON(gss_msg->msg.len > UPCALL_BUF_LEN);
|
||||
}
|
||||
|
||||
static void gss_encode_msg(struct gss_upcall_msg *gss_msg,
|
||||
struct rpc_clnt *clnt,
|
||||
const char *service_name)
|
||||
{
|
||||
struct net *net = rpc_net_ns(clnt);
|
||||
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
|
||||
|
||||
if (sn->pipe_version == 0)
|
||||
gss_encode_v0_msg(gss_msg);
|
||||
else /* pipe_version == 1 */
|
||||
gss_encode_v1_msg(gss_msg, clnt, service_name);
|
||||
}
|
||||
|
||||
static struct gss_upcall_msg *
|
||||
gss_alloc_msg(struct gss_auth *gss_auth, struct rpc_clnt *clnt,
|
||||
gss_alloc_msg(struct gss_auth *gss_auth,
|
||||
kuid_t uid, const char *service_name)
|
||||
{
|
||||
struct gss_upcall_msg *gss_msg;
|
||||
@@ -462,31 +467,36 @@ gss_alloc_msg(struct gss_auth *gss_auth, struct rpc_clnt *clnt,
|
||||
gss_msg = kzalloc(sizeof(*gss_msg), GFP_NOFS);
|
||||
if (gss_msg == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
vers = get_pipe_version(rpc_net_ns(clnt));
|
||||
vers = get_pipe_version(gss_auth->net);
|
||||
if (vers < 0) {
|
||||
kfree(gss_msg);
|
||||
return ERR_PTR(vers);
|
||||
}
|
||||
gss_msg->pipe = gss_auth->pipe[vers];
|
||||
gss_msg->pipe = gss_auth->gss_pipe[vers]->pipe;
|
||||
INIT_LIST_HEAD(&gss_msg->list);
|
||||
rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq");
|
||||
init_waitqueue_head(&gss_msg->waitqueue);
|
||||
atomic_set(&gss_msg->count, 1);
|
||||
gss_msg->uid = uid;
|
||||
gss_msg->auth = gss_auth;
|
||||
gss_encode_msg(gss_msg, clnt, service_name);
|
||||
switch (vers) {
|
||||
case 0:
|
||||
gss_encode_v0_msg(gss_msg);
|
||||
default:
|
||||
gss_encode_v1_msg(gss_msg, service_name, gss_auth->target_name);
|
||||
};
|
||||
return gss_msg;
|
||||
}
|
||||
|
||||
static struct gss_upcall_msg *
|
||||
gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cred *cred)
|
||||
gss_setup_upcall(struct gss_auth *gss_auth, struct rpc_cred *cred)
|
||||
{
|
||||
struct gss_cred *gss_cred = container_of(cred,
|
||||
struct gss_cred, gc_base);
|
||||
struct gss_upcall_msg *gss_new, *gss_msg;
|
||||
kuid_t uid = cred->cr_uid;
|
||||
|
||||
gss_new = gss_alloc_msg(gss_auth, clnt, uid, gss_cred->gc_principal);
|
||||
gss_new = gss_alloc_msg(gss_auth, uid, gss_cred->gc_principal);
|
||||
if (IS_ERR(gss_new))
|
||||
return gss_new;
|
||||
gss_msg = gss_add_msg(gss_new);
|
||||
@@ -527,7 +537,7 @@ gss_refresh_upcall(struct rpc_task *task)
|
||||
|
||||
dprintk("RPC: %5u %s for uid %u\n",
|
||||
task->tk_pid, __func__, from_kuid(&init_user_ns, cred->cr_uid));
|
||||
gss_msg = gss_setup_upcall(task->tk_client, gss_auth, cred);
|
||||
gss_msg = gss_setup_upcall(gss_auth, cred);
|
||||
if (PTR_ERR(gss_msg) == -EAGAIN) {
|
||||
/* XXX: warning on the first, under the assumption we
|
||||
* shouldn't normally hit this case on a refresh. */
|
||||
@@ -566,7 +576,7 @@ out:
|
||||
static inline int
|
||||
gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred)
|
||||
{
|
||||
struct net *net = rpc_net_ns(gss_auth->client);
|
||||
struct net *net = gss_auth->net;
|
||||
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
|
||||
struct rpc_pipe *pipe;
|
||||
struct rpc_cred *cred = &gss_cred->gc_base;
|
||||
@@ -583,7 +593,7 @@ retry:
|
||||
timeout = 15 * HZ;
|
||||
if (!sn->gssd_running)
|
||||
timeout = HZ >> 2;
|
||||
gss_msg = gss_setup_upcall(gss_auth->client, gss_auth, cred);
|
||||
gss_msg = gss_setup_upcall(gss_auth, cred);
|
||||
if (PTR_ERR(gss_msg) == -EAGAIN) {
|
||||
err = wait_event_interruptible_timeout(pipe_version_waitqueue,
|
||||
sn->pipe_version >= 0, timeout);
|
||||
@@ -797,83 +807,153 @@ gss_pipe_destroy_msg(struct rpc_pipe_msg *msg)
|
||||
}
|
||||
}
|
||||
|
||||
static void gss_pipes_dentries_destroy(struct rpc_auth *auth)
|
||||
static void gss_pipe_dentry_destroy(struct dentry *dir,
|
||||
struct rpc_pipe_dir_object *pdo)
|
||||
{
|
||||
struct gss_auth *gss_auth;
|
||||
struct gss_pipe *gss_pipe = pdo->pdo_data;
|
||||
struct rpc_pipe *pipe = gss_pipe->pipe;
|
||||
|
||||
gss_auth = container_of(auth, struct gss_auth, rpc_auth);
|
||||
if (gss_auth->pipe[0]->dentry)
|
||||
rpc_unlink(gss_auth->pipe[0]->dentry);
|
||||
if (gss_auth->pipe[1]->dentry)
|
||||
rpc_unlink(gss_auth->pipe[1]->dentry);
|
||||
if (pipe->dentry != NULL) {
|
||||
rpc_unlink(pipe->dentry);
|
||||
pipe->dentry = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int gss_pipes_dentries_create(struct rpc_auth *auth)
|
||||
static int gss_pipe_dentry_create(struct dentry *dir,
|
||||
struct rpc_pipe_dir_object *pdo)
|
||||
{
|
||||
int err;
|
||||
struct gss_auth *gss_auth;
|
||||
struct rpc_clnt *clnt;
|
||||
struct gss_pipe *p = pdo->pdo_data;
|
||||
struct dentry *dentry;
|
||||
|
||||
gss_auth = container_of(auth, struct gss_auth, rpc_auth);
|
||||
clnt = gss_auth->client;
|
||||
|
||||
gss_auth->pipe[1]->dentry = rpc_mkpipe_dentry(clnt->cl_dentry,
|
||||
"gssd",
|
||||
clnt, gss_auth->pipe[1]);
|
||||
if (IS_ERR(gss_auth->pipe[1]->dentry))
|
||||
return PTR_ERR(gss_auth->pipe[1]->dentry);
|
||||
gss_auth->pipe[0]->dentry = rpc_mkpipe_dentry(clnt->cl_dentry,
|
||||
gss_auth->mech->gm_name,
|
||||
clnt, gss_auth->pipe[0]);
|
||||
if (IS_ERR(gss_auth->pipe[0]->dentry)) {
|
||||
err = PTR_ERR(gss_auth->pipe[0]->dentry);
|
||||
goto err_unlink_pipe_1;
|
||||
}
|
||||
dentry = rpc_mkpipe_dentry(dir, p->name, p->clnt, p->pipe);
|
||||
if (IS_ERR(dentry))
|
||||
return PTR_ERR(dentry);
|
||||
p->pipe->dentry = dentry;
|
||||
return 0;
|
||||
|
||||
err_unlink_pipe_1:
|
||||
rpc_unlink(gss_auth->pipe[1]->dentry);
|
||||
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;
|
||||
static const struct rpc_pipe_dir_object_ops gss_pipe_dir_object_ops = {
|
||||
.create = gss_pipe_dentry_create,
|
||||
.destroy = gss_pipe_dentry_destroy,
|
||||
};
|
||||
|
||||
sb = rpc_get_sb_net(net);
|
||||
if (sb) {
|
||||
if (clnt->cl_dentry)
|
||||
gss_pipes_dentries_destroy(auth);
|
||||
rpc_put_sb_net(net);
|
||||
static struct gss_pipe *gss_pipe_alloc(struct rpc_clnt *clnt,
|
||||
const char *name,
|
||||
const struct rpc_pipe_ops *upcall_ops)
|
||||
{
|
||||
struct gss_pipe *p;
|
||||
int err = -ENOMEM;
|
||||
|
||||
p = kmalloc(sizeof(*p), GFP_KERNEL);
|
||||
if (p == NULL)
|
||||
goto err;
|
||||
p->pipe = rpc_mkpipe_data(upcall_ops, RPC_PIPE_WAIT_FOR_OPEN);
|
||||
if (IS_ERR(p->pipe)) {
|
||||
err = PTR_ERR(p->pipe);
|
||||
goto err_free_gss_pipe;
|
||||
}
|
||||
p->name = name;
|
||||
p->clnt = clnt;
|
||||
kref_init(&p->kref);
|
||||
rpc_init_pipe_dir_object(&p->pdo,
|
||||
&gss_pipe_dir_object_ops,
|
||||
p);
|
||||
return p;
|
||||
err_free_gss_pipe:
|
||||
kfree(p);
|
||||
err:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static int gss_pipes_dentries_create_net(struct rpc_clnt *clnt,
|
||||
struct rpc_auth *auth)
|
||||
struct gss_alloc_pdo {
|
||||
struct rpc_clnt *clnt;
|
||||
const char *name;
|
||||
const struct rpc_pipe_ops *upcall_ops;
|
||||
};
|
||||
|
||||
static int gss_pipe_match_pdo(struct rpc_pipe_dir_object *pdo, void *data)
|
||||
{
|
||||
struct gss_pipe *gss_pipe;
|
||||
struct gss_alloc_pdo *args = data;
|
||||
|
||||
if (pdo->pdo_ops != &gss_pipe_dir_object_ops)
|
||||
return 0;
|
||||
gss_pipe = container_of(pdo, struct gss_pipe, pdo);
|
||||
if (strcmp(gss_pipe->name, args->name) != 0)
|
||||
return 0;
|
||||
if (!kref_get_unless_zero(&gss_pipe->kref))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct rpc_pipe_dir_object *gss_pipe_alloc_pdo(void *data)
|
||||
{
|
||||
struct gss_pipe *gss_pipe;
|
||||
struct gss_alloc_pdo *args = data;
|
||||
|
||||
gss_pipe = gss_pipe_alloc(args->clnt, args->name, args->upcall_ops);
|
||||
if (!IS_ERR(gss_pipe))
|
||||
return &gss_pipe->pdo;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct gss_pipe *gss_pipe_get(struct rpc_clnt *clnt,
|
||||
const char *name,
|
||||
const struct rpc_pipe_ops *upcall_ops)
|
||||
{
|
||||
struct net *net = rpc_net_ns(clnt);
|
||||
struct super_block *sb;
|
||||
int err = 0;
|
||||
struct rpc_pipe_dir_object *pdo;
|
||||
struct gss_alloc_pdo args = {
|
||||
.clnt = clnt,
|
||||
.name = name,
|
||||
.upcall_ops = upcall_ops,
|
||||
};
|
||||
|
||||
sb = rpc_get_sb_net(net);
|
||||
if (sb) {
|
||||
if (clnt->cl_dentry)
|
||||
err = gss_pipes_dentries_create(auth);
|
||||
rpc_put_sb_net(net);
|
||||
}
|
||||
return err;
|
||||
pdo = rpc_find_or_alloc_pipe_dir_object(net,
|
||||
&clnt->cl_pipedir_objects,
|
||||
gss_pipe_match_pdo,
|
||||
gss_pipe_alloc_pdo,
|
||||
&args);
|
||||
if (pdo != NULL)
|
||||
return container_of(pdo, struct gss_pipe, pdo);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
static void __gss_pipe_free(struct gss_pipe *p)
|
||||
{
|
||||
struct rpc_clnt *clnt = p->clnt;
|
||||
struct net *net = rpc_net_ns(clnt);
|
||||
|
||||
rpc_remove_pipe_dir_object(net,
|
||||
&clnt->cl_pipedir_objects,
|
||||
&p->pdo);
|
||||
rpc_destroy_pipe_data(p->pipe);
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
static void __gss_pipe_release(struct kref *kref)
|
||||
{
|
||||
struct gss_pipe *p = container_of(kref, struct gss_pipe, kref);
|
||||
|
||||
__gss_pipe_free(p);
|
||||
}
|
||||
|
||||
static void gss_pipe_free(struct gss_pipe *p)
|
||||
{
|
||||
if (p != NULL)
|
||||
kref_put(&p->kref, __gss_pipe_release);
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: we have the opportunity to use different
|
||||
* parameters based on the input flavor (which must be a pseudoflavor)
|
||||
*/
|
||||
static struct rpc_auth *
|
||||
gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
|
||||
static struct gss_auth *
|
||||
gss_create_new(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
|
||||
{
|
||||
rpc_authflavor_t flavor = args->pseudoflavor;
|
||||
struct gss_auth *gss_auth;
|
||||
struct gss_pipe *gss_pipe;
|
||||
struct rpc_auth * auth;
|
||||
int err = -ENOMEM; /* XXX? */
|
||||
|
||||
@@ -883,12 +963,20 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
|
||||
return ERR_PTR(err);
|
||||
if (!(gss_auth = kmalloc(sizeof(*gss_auth), GFP_KERNEL)))
|
||||
goto out_dec;
|
||||
INIT_HLIST_NODE(&gss_auth->hash);
|
||||
gss_auth->target_name = NULL;
|
||||
if (args->target_name) {
|
||||
gss_auth->target_name = kstrdup(args->target_name, GFP_KERNEL);
|
||||
if (gss_auth->target_name == NULL)
|
||||
goto err_free;
|
||||
}
|
||||
gss_auth->client = clnt;
|
||||
gss_auth->net = get_net(rpc_net_ns(clnt));
|
||||
err = -EINVAL;
|
||||
gss_auth->mech = gss_mech_get_by_pseudoflavor(flavor);
|
||||
if (!gss_auth->mech) {
|
||||
dprintk("RPC: Pseudoflavor %d not found!\n", flavor);
|
||||
goto err_free;
|
||||
goto err_put_net;
|
||||
}
|
||||
gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor);
|
||||
if (gss_auth->service == 0)
|
||||
@@ -901,42 +989,41 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
|
||||
atomic_set(&auth->au_count, 1);
|
||||
kref_init(&gss_auth->kref);
|
||||
|
||||
err = rpcauth_init_credcache(auth);
|
||||
if (err)
|
||||
goto err_put_mech;
|
||||
/*
|
||||
* Note: if we created the old pipe first, then someone who
|
||||
* examined the directory at the right moment might conclude
|
||||
* that we supported only the old pipe. So we instead create
|
||||
* the new pipe first.
|
||||
*/
|
||||
gss_auth->pipe[1] = rpc_mkpipe_data(&gss_upcall_ops_v1,
|
||||
RPC_PIPE_WAIT_FOR_OPEN);
|
||||
if (IS_ERR(gss_auth->pipe[1])) {
|
||||
err = PTR_ERR(gss_auth->pipe[1]);
|
||||
goto err_put_mech;
|
||||
gss_pipe = gss_pipe_get(clnt, "gssd", &gss_upcall_ops_v1);
|
||||
if (IS_ERR(gss_pipe)) {
|
||||
err = PTR_ERR(gss_pipe);
|
||||
goto err_destroy_credcache;
|
||||
}
|
||||
gss_auth->gss_pipe[1] = gss_pipe;
|
||||
|
||||
gss_auth->pipe[0] = rpc_mkpipe_data(&gss_upcall_ops_v0,
|
||||
RPC_PIPE_WAIT_FOR_OPEN);
|
||||
if (IS_ERR(gss_auth->pipe[0])) {
|
||||
err = PTR_ERR(gss_auth->pipe[0]);
|
||||
gss_pipe = gss_pipe_get(clnt, gss_auth->mech->gm_name,
|
||||
&gss_upcall_ops_v0);
|
||||
if (IS_ERR(gss_pipe)) {
|
||||
err = PTR_ERR(gss_pipe);
|
||||
goto err_destroy_pipe_1;
|
||||
}
|
||||
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;
|
||||
gss_auth->gss_pipe[0] = gss_pipe;
|
||||
|
||||
return auth;
|
||||
err_unlink_pipes:
|
||||
gss_pipes_dentries_destroy_net(clnt, auth);
|
||||
err_destroy_pipe_0:
|
||||
rpc_destroy_pipe_data(gss_auth->pipe[0]);
|
||||
return gss_auth;
|
||||
err_destroy_pipe_1:
|
||||
rpc_destroy_pipe_data(gss_auth->pipe[1]);
|
||||
gss_pipe_free(gss_auth->gss_pipe[1]);
|
||||
err_destroy_credcache:
|
||||
rpcauth_destroy_credcache(auth);
|
||||
err_put_mech:
|
||||
gss_mech_put(gss_auth->mech);
|
||||
err_put_net:
|
||||
put_net(gss_auth->net);
|
||||
err_free:
|
||||
kfree(gss_auth->target_name);
|
||||
kfree(gss_auth);
|
||||
out_dec:
|
||||
module_put(THIS_MODULE);
|
||||
@@ -946,10 +1033,11 @@ out_dec:
|
||||
static void
|
||||
gss_free(struct gss_auth *gss_auth)
|
||||
{
|
||||
gss_pipes_dentries_destroy_net(gss_auth->client, &gss_auth->rpc_auth);
|
||||
rpc_destroy_pipe_data(gss_auth->pipe[0]);
|
||||
rpc_destroy_pipe_data(gss_auth->pipe[1]);
|
||||
gss_pipe_free(gss_auth->gss_pipe[0]);
|
||||
gss_pipe_free(gss_auth->gss_pipe[1]);
|
||||
gss_mech_put(gss_auth->mech);
|
||||
put_net(gss_auth->net);
|
||||
kfree(gss_auth->target_name);
|
||||
|
||||
kfree(gss_auth);
|
||||
module_put(THIS_MODULE);
|
||||
@@ -966,17 +1054,101 @@ gss_free_callback(struct kref *kref)
|
||||
static void
|
||||
gss_destroy(struct rpc_auth *auth)
|
||||
{
|
||||
struct gss_auth *gss_auth;
|
||||
struct gss_auth *gss_auth = container_of(auth,
|
||||
struct gss_auth, rpc_auth);
|
||||
|
||||
dprintk("RPC: destroying GSS authenticator %p flavor %d\n",
|
||||
auth, auth->au_flavor);
|
||||
|
||||
if (hash_hashed(&gss_auth->hash)) {
|
||||
spin_lock(&gss_auth_hash_lock);
|
||||
hash_del(&gss_auth->hash);
|
||||
spin_unlock(&gss_auth_hash_lock);
|
||||
}
|
||||
|
||||
gss_pipe_free(gss_auth->gss_pipe[0]);
|
||||
gss_auth->gss_pipe[0] = NULL;
|
||||
gss_pipe_free(gss_auth->gss_pipe[1]);
|
||||
gss_auth->gss_pipe[1] = NULL;
|
||||
rpcauth_destroy_credcache(auth);
|
||||
|
||||
gss_auth = container_of(auth, struct gss_auth, rpc_auth);
|
||||
kref_put(&gss_auth->kref, gss_free_callback);
|
||||
}
|
||||
|
||||
static struct gss_auth *
|
||||
gss_auth_find_or_add_hashed(struct rpc_auth_create_args *args,
|
||||
struct rpc_clnt *clnt,
|
||||
struct gss_auth *new)
|
||||
{
|
||||
struct gss_auth *gss_auth;
|
||||
unsigned long hashval = (unsigned long)clnt;
|
||||
|
||||
spin_lock(&gss_auth_hash_lock);
|
||||
hash_for_each_possible(gss_auth_hash_table,
|
||||
gss_auth,
|
||||
hash,
|
||||
hashval) {
|
||||
if (gss_auth->rpc_auth.au_flavor != args->pseudoflavor)
|
||||
continue;
|
||||
if (gss_auth->target_name != args->target_name) {
|
||||
if (gss_auth->target_name == NULL)
|
||||
continue;
|
||||
if (args->target_name == NULL)
|
||||
continue;
|
||||
if (strcmp(gss_auth->target_name, args->target_name))
|
||||
continue;
|
||||
}
|
||||
if (!atomic_inc_not_zero(&gss_auth->rpc_auth.au_count))
|
||||
continue;
|
||||
goto out;
|
||||
}
|
||||
if (new)
|
||||
hash_add(gss_auth_hash_table, &new->hash, hashval);
|
||||
gss_auth = new;
|
||||
out:
|
||||
spin_unlock(&gss_auth_hash_lock);
|
||||
return gss_auth;
|
||||
}
|
||||
|
||||
static struct gss_auth *
|
||||
gss_create_hashed(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
|
||||
{
|
||||
struct gss_auth *gss_auth;
|
||||
struct gss_auth *new;
|
||||
|
||||
gss_auth = gss_auth_find_or_add_hashed(args, clnt, NULL);
|
||||
if (gss_auth != NULL)
|
||||
goto out;
|
||||
new = gss_create_new(args, clnt);
|
||||
if (IS_ERR(new))
|
||||
return new;
|
||||
gss_auth = gss_auth_find_or_add_hashed(args, clnt, new);
|
||||
if (gss_auth != new)
|
||||
gss_destroy(&new->rpc_auth);
|
||||
out:
|
||||
return gss_auth;
|
||||
}
|
||||
|
||||
static struct rpc_auth *
|
||||
gss_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
|
||||
{
|
||||
struct gss_auth *gss_auth;
|
||||
struct rpc_xprt *xprt = rcu_access_pointer(clnt->cl_xprt);
|
||||
|
||||
while (clnt != clnt->cl_parent) {
|
||||
struct rpc_clnt *parent = clnt->cl_parent;
|
||||
/* Find the original parent for this transport */
|
||||
if (rcu_access_pointer(parent->cl_xprt) != xprt)
|
||||
break;
|
||||
clnt = parent;
|
||||
}
|
||||
|
||||
gss_auth = gss_create_hashed(args, clnt);
|
||||
if (IS_ERR(gss_auth))
|
||||
return ERR_CAST(gss_auth);
|
||||
return &gss_auth->rpc_auth;
|
||||
}
|
||||
|
||||
/*
|
||||
* gss_destroying_context will cause the RPCSEC_GSS to send a NULL RPC call
|
||||
* to the server with the GSS control procedure field set to
|
||||
@@ -1126,10 +1298,32 @@ gss_cred_init(struct rpc_auth *auth, struct rpc_cred *cred)
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns -EACCES if GSS context is NULL or will expire within the
|
||||
* timeout (miliseconds)
|
||||
*/
|
||||
static int
|
||||
gss_key_timeout(struct rpc_cred *rc)
|
||||
{
|
||||
struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base);
|
||||
unsigned long now = jiffies;
|
||||
unsigned long expire;
|
||||
|
||||
if (gss_cred->gc_ctx == NULL)
|
||||
return -EACCES;
|
||||
|
||||
expire = gss_cred->gc_ctx->gc_expiry - (gss_key_expire_timeo * HZ);
|
||||
|
||||
if (time_after(now, expire))
|
||||
return -EACCES;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
gss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags)
|
||||
{
|
||||
struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base);
|
||||
int ret;
|
||||
|
||||
if (test_bit(RPCAUTH_CRED_NEW, &rc->cr_flags))
|
||||
goto out;
|
||||
@@ -1142,11 +1336,26 @@ out:
|
||||
if (acred->principal != NULL) {
|
||||
if (gss_cred->gc_principal == NULL)
|
||||
return 0;
|
||||
return strcmp(acred->principal, gss_cred->gc_principal) == 0;
|
||||
ret = strcmp(acred->principal, gss_cred->gc_principal) == 0;
|
||||
goto check_expire;
|
||||
}
|
||||
if (gss_cred->gc_principal != NULL)
|
||||
return 0;
|
||||
return uid_eq(rc->cr_uid, acred->uid);
|
||||
ret = uid_eq(rc->cr_uid, acred->uid);
|
||||
|
||||
check_expire:
|
||||
if (ret == 0)
|
||||
return ret;
|
||||
|
||||
/* Notify acred users of GSS context expiration timeout */
|
||||
if (test_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags) &&
|
||||
(gss_key_timeout(rc) != 0)) {
|
||||
/* test will now be done from generic cred */
|
||||
test_and_clear_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags);
|
||||
/* tell NFS layer that key will expire soon */
|
||||
set_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1292,6 +1501,7 @@ gss_validate(struct rpc_task *task, __be32 *p)
|
||||
struct xdr_netobj mic;
|
||||
u32 flav,len;
|
||||
u32 maj_stat;
|
||||
__be32 *ret = ERR_PTR(-EIO);
|
||||
|
||||
dprintk("RPC: %5u %s\n", task->tk_pid, __func__);
|
||||
|
||||
@@ -1307,6 +1517,7 @@ gss_validate(struct rpc_task *task, __be32 *p)
|
||||
mic.data = (u8 *)p;
|
||||
mic.len = len;
|
||||
|
||||
ret = ERR_PTR(-EACCES);
|
||||
maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic);
|
||||
if (maj_stat == GSS_S_CONTEXT_EXPIRED)
|
||||
clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
|
||||
@@ -1324,8 +1535,9 @@ gss_validate(struct rpc_task *task, __be32 *p)
|
||||
return p + XDR_QUADLEN(len);
|
||||
out_bad:
|
||||
gss_put_ctx(ctx);
|
||||
dprintk("RPC: %5u %s failed.\n", task->tk_pid, __func__);
|
||||
return NULL;
|
||||
dprintk("RPC: %5u %s failed ret %ld.\n", task->tk_pid, __func__,
|
||||
PTR_ERR(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gss_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp,
|
||||
@@ -1657,8 +1869,6 @@ static const struct rpc_authops authgss_ops = {
|
||||
.destroy = gss_destroy,
|
||||
.lookup_cred = gss_lookup_cred,
|
||||
.crcreate = gss_create_cred,
|
||||
.pipes_create = gss_pipes_dentries_create,
|
||||
.pipes_destroy = gss_pipes_dentries_destroy,
|
||||
.list_pseudoflavors = gss_mech_list_pseudoflavors,
|
||||
.info2flavor = gss_mech_info2flavor,
|
||||
.flavor2info = gss_mech_flavor2info,
|
||||
@@ -1675,6 +1885,7 @@ static const struct rpc_credops gss_credops = {
|
||||
.crvalidate = gss_validate,
|
||||
.crwrap_req = gss_wrap_req,
|
||||
.crunwrap_resp = gss_unwrap_resp,
|
||||
.crkey_timeout = gss_key_timeout,
|
||||
};
|
||||
|
||||
static const struct rpc_credops gss_nullops = {
|
||||
@@ -1762,5 +1973,12 @@ module_param_named(expired_cred_retry_delay,
|
||||
MODULE_PARM_DESC(expired_cred_retry_delay, "Timeout (in seconds) until "
|
||||
"the RPC engine retries an expired credential");
|
||||
|
||||
module_param_named(key_expire_timeo,
|
||||
gss_key_expire_timeo,
|
||||
uint, 0644);
|
||||
MODULE_PARM_DESC(key_expire_timeo, "Time (in seconds) at the end of a "
|
||||
"credential keys lifetime where the NFS layer cleans up "
|
||||
"prior to key expiration");
|
||||
|
||||
module_init(init_rpcsec_gss)
|
||||
module_exit(exit_rpcsec_gss)
|
||||
|
||||
@@ -18,7 +18,7 @@ static struct rpc_auth null_auth;
|
||||
static struct rpc_cred null_cred;
|
||||
|
||||
static struct rpc_auth *
|
||||
nul_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
|
||||
nul_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
|
||||
{
|
||||
atomic_inc(&null_auth.au_count);
|
||||
return &null_auth;
|
||||
@@ -88,13 +88,13 @@ nul_validate(struct rpc_task *task, __be32 *p)
|
||||
flavor = ntohl(*p++);
|
||||
if (flavor != RPC_AUTH_NULL) {
|
||||
printk("RPC: bad verf flavor: %u\n", flavor);
|
||||
return NULL;
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
size = ntohl(*p++);
|
||||
if (size != 0) {
|
||||
printk("RPC: bad verf size: %u\n", size);
|
||||
return NULL;
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
return p;
|
||||
|
||||
@@ -33,7 +33,7 @@ static struct rpc_auth unix_auth;
|
||||
static const struct rpc_credops unix_credops;
|
||||
|
||||
static struct rpc_auth *
|
||||
unx_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
|
||||
unx_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
|
||||
{
|
||||
dprintk("RPC: creating UNIX authenticator for client %p\n",
|
||||
clnt);
|
||||
@@ -192,13 +192,13 @@ unx_validate(struct rpc_task *task, __be32 *p)
|
||||
flavor != RPC_AUTH_UNIX &&
|
||||
flavor != RPC_AUTH_SHORT) {
|
||||
printk("RPC: bad verf flavor: %u\n", flavor);
|
||||
return NULL;
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
size = ntohl(*p++);
|
||||
if (size > RPC_MAX_AUTH_SIZE) {
|
||||
printk("RPC: giant verf size: %u\n", size);
|
||||
return NULL;
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
task->tk_rqstp->rq_cred->cr_auth->au_rslack = (size >> 2) + 2;
|
||||
p += (size >> 2);
|
||||
|
||||
@@ -102,12 +102,7 @@ static void rpc_unregister_client(struct rpc_clnt *clnt)
|
||||
|
||||
static void __rpc_clnt_remove_pipedir(struct rpc_clnt *clnt)
|
||||
{
|
||||
if (clnt->cl_dentry) {
|
||||
if (clnt->cl_auth && clnt->cl_auth->au_ops->pipes_destroy)
|
||||
clnt->cl_auth->au_ops->pipes_destroy(clnt->cl_auth);
|
||||
rpc_remove_client_dir(clnt->cl_dentry);
|
||||
}
|
||||
clnt->cl_dentry = NULL;
|
||||
rpc_remove_client_dir(clnt);
|
||||
}
|
||||
|
||||
static void rpc_clnt_remove_pipedir(struct rpc_clnt *clnt)
|
||||
@@ -123,10 +118,10 @@ static void rpc_clnt_remove_pipedir(struct rpc_clnt *clnt)
|
||||
}
|
||||
|
||||
static struct dentry *rpc_setup_pipedir_sb(struct super_block *sb,
|
||||
struct rpc_clnt *clnt,
|
||||
const char *dir_name)
|
||||
struct rpc_clnt *clnt)
|
||||
{
|
||||
static uint32_t clntid;
|
||||
const char *dir_name = clnt->cl_program->pipe_dir_name;
|
||||
char name[15];
|
||||
struct dentry *dir, *dentry;
|
||||
|
||||
@@ -153,28 +148,35 @@ static struct dentry *rpc_setup_pipedir_sb(struct super_block *sb,
|
||||
}
|
||||
|
||||
static int
|
||||
rpc_setup_pipedir(struct rpc_clnt *clnt, const char *dir_name,
|
||||
struct super_block *pipefs_sb)
|
||||
rpc_setup_pipedir(struct super_block *pipefs_sb, struct rpc_clnt *clnt)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
|
||||
clnt->cl_dentry = NULL;
|
||||
if (dir_name == NULL)
|
||||
return 0;
|
||||
dentry = rpc_setup_pipedir_sb(pipefs_sb, clnt, dir_name);
|
||||
if (IS_ERR(dentry))
|
||||
return PTR_ERR(dentry);
|
||||
clnt->cl_dentry = dentry;
|
||||
if (clnt->cl_program->pipe_dir_name != NULL) {
|
||||
dentry = rpc_setup_pipedir_sb(pipefs_sb, clnt);
|
||||
if (IS_ERR(dentry))
|
||||
return PTR_ERR(dentry);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int rpc_clnt_skip_event(struct rpc_clnt *clnt, unsigned long event)
|
||||
static int rpc_clnt_skip_event(struct rpc_clnt *clnt, unsigned long event)
|
||||
{
|
||||
if (((event == RPC_PIPEFS_MOUNT) && clnt->cl_dentry) ||
|
||||
((event == RPC_PIPEFS_UMOUNT) && !clnt->cl_dentry))
|
||||
return 1;
|
||||
if ((event == RPC_PIPEFS_MOUNT) && atomic_read(&clnt->cl_count) == 0)
|
||||
if (clnt->cl_program->pipe_dir_name == NULL)
|
||||
return 1;
|
||||
|
||||
switch (event) {
|
||||
case RPC_PIPEFS_MOUNT:
|
||||
if (clnt->cl_pipedir_objects.pdh_dentry != NULL)
|
||||
return 1;
|
||||
if (atomic_read(&clnt->cl_count) == 0)
|
||||
return 1;
|
||||
break;
|
||||
case RPC_PIPEFS_UMOUNT:
|
||||
if (clnt->cl_pipedir_objects.pdh_dentry == NULL)
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -186,18 +188,11 @@ static int __rpc_clnt_handle_event(struct rpc_clnt *clnt, unsigned long event,
|
||||
|
||||
switch (event) {
|
||||
case RPC_PIPEFS_MOUNT:
|
||||
dentry = rpc_setup_pipedir_sb(sb, clnt,
|
||||
clnt->cl_program->pipe_dir_name);
|
||||
dentry = rpc_setup_pipedir_sb(sb, clnt);
|
||||
if (!dentry)
|
||||
return -ENOENT;
|
||||
if (IS_ERR(dentry))
|
||||
return PTR_ERR(dentry);
|
||||
clnt->cl_dentry = dentry;
|
||||
if (clnt->cl_auth->au_ops->pipes_create) {
|
||||
err = clnt->cl_auth->au_ops->pipes_create(clnt->cl_auth);
|
||||
if (err)
|
||||
__rpc_clnt_remove_pipedir(clnt);
|
||||
}
|
||||
break;
|
||||
case RPC_PIPEFS_UMOUNT:
|
||||
__rpc_clnt_remove_pipedir(clnt);
|
||||
@@ -230,8 +225,6 @@ static struct rpc_clnt *rpc_get_client_for_event(struct net *net, int event)
|
||||
|
||||
spin_lock(&sn->rpc_client_lock);
|
||||
list_for_each_entry(clnt, &sn->all_clients, cl_clients) {
|
||||
if (clnt->cl_program->pipe_dir_name == NULL)
|
||||
continue;
|
||||
if (rpc_clnt_skip_event(clnt, event))
|
||||
continue;
|
||||
spin_unlock(&sn->rpc_client_lock);
|
||||
@@ -282,7 +275,10 @@ static void rpc_clnt_set_nodename(struct rpc_clnt *clnt, const char *nodename)
|
||||
static int rpc_client_register(const struct rpc_create_args *args,
|
||||
struct rpc_clnt *clnt)
|
||||
{
|
||||
const struct rpc_program *program = args->program;
|
||||
struct rpc_auth_create_args auth_args = {
|
||||
.pseudoflavor = args->authflavor,
|
||||
.target_name = args->client_name,
|
||||
};
|
||||
struct rpc_auth *auth;
|
||||
struct net *net = rpc_net_ns(clnt);
|
||||
struct super_block *pipefs_sb;
|
||||
@@ -290,7 +286,7 @@ static int rpc_client_register(const struct rpc_create_args *args,
|
||||
|
||||
pipefs_sb = rpc_get_sb_net(net);
|
||||
if (pipefs_sb) {
|
||||
err = rpc_setup_pipedir(clnt, program->pipe_dir_name, pipefs_sb);
|
||||
err = rpc_setup_pipedir(pipefs_sb, clnt);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
@@ -299,7 +295,7 @@ static int rpc_client_register(const struct rpc_create_args *args,
|
||||
if (pipefs_sb)
|
||||
rpc_put_sb_net(net);
|
||||
|
||||
auth = rpcauth_create(args->authflavor, clnt);
|
||||
auth = rpcauth_create(&auth_args, clnt);
|
||||
if (IS_ERR(auth)) {
|
||||
dprintk("RPC: Couldn't create auth handle (flavor %u)\n",
|
||||
args->authflavor);
|
||||
@@ -317,7 +313,27 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, struct rpc_xprt *xprt)
|
||||
static DEFINE_IDA(rpc_clids);
|
||||
|
||||
static int rpc_alloc_clid(struct rpc_clnt *clnt)
|
||||
{
|
||||
int clid;
|
||||
|
||||
clid = ida_simple_get(&rpc_clids, 0, 0, GFP_KERNEL);
|
||||
if (clid < 0)
|
||||
return clid;
|
||||
clnt->cl_clid = clid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rpc_free_clid(struct rpc_clnt *clnt)
|
||||
{
|
||||
ida_simple_remove(&rpc_clids, clnt->cl_clid);
|
||||
}
|
||||
|
||||
static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args,
|
||||
struct rpc_xprt *xprt,
|
||||
struct rpc_clnt *parent)
|
||||
{
|
||||
const struct rpc_program *program = args->program;
|
||||
const struct rpc_version *version;
|
||||
@@ -343,16 +359,20 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
|
||||
clnt = kzalloc(sizeof(*clnt), GFP_KERNEL);
|
||||
if (!clnt)
|
||||
goto out_err;
|
||||
clnt->cl_parent = clnt;
|
||||
clnt->cl_parent = parent ? : clnt;
|
||||
|
||||
err = rpc_alloc_clid(clnt);
|
||||
if (err)
|
||||
goto out_no_clid;
|
||||
|
||||
rcu_assign_pointer(clnt->cl_xprt, xprt);
|
||||
clnt->cl_procinfo = version->procs;
|
||||
clnt->cl_maxproc = version->nrprocs;
|
||||
clnt->cl_protname = program->name;
|
||||
clnt->cl_prog = args->prognumber ? : program->number;
|
||||
clnt->cl_vers = version->number;
|
||||
clnt->cl_stats = program->stats;
|
||||
clnt->cl_metrics = rpc_alloc_iostats(clnt);
|
||||
rpc_init_pipe_dir_head(&clnt->cl_pipedir_objects);
|
||||
err = -ENOMEM;
|
||||
if (clnt->cl_metrics == NULL)
|
||||
goto out_no_stats;
|
||||
@@ -372,12 +392,6 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
|
||||
|
||||
clnt->cl_rtt = &clnt->cl_rtt_default;
|
||||
rpc_init_rtt(&clnt->cl_rtt_default, clnt->cl_timeout->to_initval);
|
||||
clnt->cl_principal = NULL;
|
||||
if (args->client_name) {
|
||||
clnt->cl_principal = kstrdup(args->client_name, GFP_KERNEL);
|
||||
if (!clnt->cl_principal)
|
||||
goto out_no_principal;
|
||||
}
|
||||
|
||||
atomic_set(&clnt->cl_count, 1);
|
||||
|
||||
@@ -387,13 +401,15 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
|
||||
err = rpc_client_register(args, clnt);
|
||||
if (err)
|
||||
goto out_no_path;
|
||||
if (parent)
|
||||
atomic_inc(&parent->cl_count);
|
||||
return clnt;
|
||||
|
||||
out_no_path:
|
||||
kfree(clnt->cl_principal);
|
||||
out_no_principal:
|
||||
rpc_free_iostats(clnt->cl_metrics);
|
||||
out_no_stats:
|
||||
rpc_free_clid(clnt);
|
||||
out_no_clid:
|
||||
kfree(clnt);
|
||||
out_err:
|
||||
rpciod_down();
|
||||
@@ -479,7 +495,7 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
|
||||
if (args->flags & RPC_CLNT_CREATE_NONPRIVPORT)
|
||||
xprt->resvport = 0;
|
||||
|
||||
clnt = rpc_new_client(args, xprt);
|
||||
clnt = rpc_new_client(args, xprt, NULL);
|
||||
if (IS_ERR(clnt))
|
||||
return clnt;
|
||||
|
||||
@@ -526,15 +542,12 @@ static struct rpc_clnt *__rpc_clone_client(struct rpc_create_args *args,
|
||||
goto out_err;
|
||||
args->servername = xprt->servername;
|
||||
|
||||
new = rpc_new_client(args, xprt);
|
||||
new = rpc_new_client(args, xprt, clnt);
|
||||
if (IS_ERR(new)) {
|
||||
err = PTR_ERR(new);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
atomic_inc(&clnt->cl_count);
|
||||
new->cl_parent = clnt;
|
||||
|
||||
/* Turn off autobind on clones */
|
||||
new->cl_autobind = 0;
|
||||
new->cl_softrtry = clnt->cl_softrtry;
|
||||
@@ -561,7 +574,6 @@ struct rpc_clnt *rpc_clone_client(struct rpc_clnt *clnt)
|
||||
.prognumber = clnt->cl_prog,
|
||||
.version = clnt->cl_vers,
|
||||
.authflavor = clnt->cl_auth->au_flavor,
|
||||
.client_name = clnt->cl_principal,
|
||||
};
|
||||
return __rpc_clone_client(&args, clnt);
|
||||
}
|
||||
@@ -583,7 +595,6 @@ rpc_clone_client_set_auth(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
|
||||
.prognumber = clnt->cl_prog,
|
||||
.version = clnt->cl_vers,
|
||||
.authflavor = flavor,
|
||||
.client_name = clnt->cl_principal,
|
||||
};
|
||||
return __rpc_clone_client(&args, clnt);
|
||||
}
|
||||
@@ -629,7 +640,7 @@ void rpc_shutdown_client(struct rpc_clnt *clnt)
|
||||
might_sleep();
|
||||
|
||||
dprintk_rcu("RPC: shutting down %s client for %s\n",
|
||||
clnt->cl_protname,
|
||||
clnt->cl_program->name,
|
||||
rcu_dereference(clnt->cl_xprt)->servername);
|
||||
|
||||
while (!list_empty(&clnt->cl_tasks)) {
|
||||
@@ -649,17 +660,17 @@ static void
|
||||
rpc_free_client(struct rpc_clnt *clnt)
|
||||
{
|
||||
dprintk_rcu("RPC: destroying %s client for %s\n",
|
||||
clnt->cl_protname,
|
||||
clnt->cl_program->name,
|
||||
rcu_dereference(clnt->cl_xprt)->servername);
|
||||
if (clnt->cl_parent != clnt)
|
||||
rpc_release_client(clnt->cl_parent);
|
||||
rpc_clnt_remove_pipedir(clnt);
|
||||
rpc_unregister_client(clnt);
|
||||
rpc_free_iostats(clnt->cl_metrics);
|
||||
kfree(clnt->cl_principal);
|
||||
clnt->cl_metrics = NULL;
|
||||
xprt_put(rcu_dereference_raw(clnt->cl_xprt));
|
||||
rpciod_down();
|
||||
rpc_free_clid(clnt);
|
||||
kfree(clnt);
|
||||
}
|
||||
|
||||
@@ -720,7 +731,6 @@ struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *old,
|
||||
.prognumber = program->number,
|
||||
.version = vers,
|
||||
.authflavor = old->cl_auth->au_flavor,
|
||||
.client_name = old->cl_principal,
|
||||
};
|
||||
struct rpc_clnt *clnt;
|
||||
int err;
|
||||
@@ -1299,7 +1309,7 @@ call_start(struct rpc_task *task)
|
||||
struct rpc_clnt *clnt = task->tk_client;
|
||||
|
||||
dprintk("RPC: %5u call_start %s%d proc %s (%s)\n", task->tk_pid,
|
||||
clnt->cl_protname, clnt->cl_vers,
|
||||
clnt->cl_program->name, clnt->cl_vers,
|
||||
rpc_proc_name(task),
|
||||
(RPC_IS_ASYNC(task) ? "async" : "sync"));
|
||||
|
||||
@@ -1423,9 +1433,9 @@ call_refreshresult(struct rpc_task *task)
|
||||
return;
|
||||
case -ETIMEDOUT:
|
||||
rpc_delay(task, 3*HZ);
|
||||
case -EKEYEXPIRED:
|
||||
case -EAGAIN:
|
||||
status = -EACCES;
|
||||
case -EKEYEXPIRED:
|
||||
if (!task->tk_cred_retry)
|
||||
break;
|
||||
task->tk_cred_retry--;
|
||||
@@ -1912,7 +1922,7 @@ call_status(struct rpc_task *task)
|
||||
default:
|
||||
if (clnt->cl_chatty)
|
||||
printk("%s: RPC call returned error %d\n",
|
||||
clnt->cl_protname, -status);
|
||||
clnt->cl_program->name, -status);
|
||||
rpc_exit(task, status);
|
||||
}
|
||||
}
|
||||
@@ -1943,7 +1953,7 @@ call_timeout(struct rpc_task *task)
|
||||
if (clnt->cl_chatty) {
|
||||
rcu_read_lock();
|
||||
printk(KERN_NOTICE "%s: server %s not responding, timed out\n",
|
||||
clnt->cl_protname,
|
||||
clnt->cl_program->name,
|
||||
rcu_dereference(clnt->cl_xprt)->servername);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
@@ -1959,7 +1969,7 @@ call_timeout(struct rpc_task *task)
|
||||
if (clnt->cl_chatty) {
|
||||
rcu_read_lock();
|
||||
printk(KERN_NOTICE "%s: server %s not responding, still trying\n",
|
||||
clnt->cl_protname,
|
||||
clnt->cl_program->name,
|
||||
rcu_dereference(clnt->cl_xprt)->servername);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
@@ -1994,7 +2004,7 @@ call_decode(struct rpc_task *task)
|
||||
if (clnt->cl_chatty) {
|
||||
rcu_read_lock();
|
||||
printk(KERN_NOTICE "%s: server %s OK\n",
|
||||
clnt->cl_protname,
|
||||
clnt->cl_program->name,
|
||||
rcu_dereference(clnt->cl_xprt)->servername);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
@@ -2019,7 +2029,7 @@ call_decode(struct rpc_task *task)
|
||||
goto out_retry;
|
||||
}
|
||||
dprintk("RPC: %s: too small RPC reply size (%d bytes)\n",
|
||||
clnt->cl_protname, task->tk_status);
|
||||
clnt->cl_program->name, task->tk_status);
|
||||
task->tk_action = call_timeout;
|
||||
goto out_retry;
|
||||
}
|
||||
@@ -2091,7 +2101,8 @@ rpc_verify_header(struct rpc_task *task)
|
||||
dprintk("RPC: %5u %s: XDR representation not a multiple of"
|
||||
" 4 bytes: 0x%x\n", task->tk_pid, __func__,
|
||||
task->tk_rqstp->rq_rcv_buf.len);
|
||||
goto out_eio;
|
||||
error = -EIO;
|
||||
goto out_err;
|
||||
}
|
||||
if ((len -= 3) < 0)
|
||||
goto out_overflow;
|
||||
@@ -2100,6 +2111,7 @@ rpc_verify_header(struct rpc_task *task)
|
||||
if ((n = ntohl(*p++)) != RPC_REPLY) {
|
||||
dprintk("RPC: %5u %s: not an RPC reply: %x\n",
|
||||
task->tk_pid, __func__, n);
|
||||
error = -EIO;
|
||||
goto out_garbage;
|
||||
}
|
||||
|
||||
@@ -2118,7 +2130,8 @@ rpc_verify_header(struct rpc_task *task)
|
||||
dprintk("RPC: %5u %s: RPC call rejected, "
|
||||
"unknown error: %x\n",
|
||||
task->tk_pid, __func__, n);
|
||||
goto out_eio;
|
||||
error = -EIO;
|
||||
goto out_err;
|
||||
}
|
||||
if (--len < 0)
|
||||
goto out_overflow;
|
||||
@@ -2163,9 +2176,11 @@ rpc_verify_header(struct rpc_task *task)
|
||||
task->tk_pid, __func__, n);
|
||||
goto out_err;
|
||||
}
|
||||
if (!(p = rpcauth_checkverf(task, p))) {
|
||||
dprintk("RPC: %5u %s: auth check failed\n",
|
||||
task->tk_pid, __func__);
|
||||
p = rpcauth_checkverf(task, p);
|
||||
if (IS_ERR(p)) {
|
||||
error = PTR_ERR(p);
|
||||
dprintk("RPC: %5u %s: auth check failed with %d\n",
|
||||
task->tk_pid, __func__, error);
|
||||
goto out_garbage; /* bad verifier, retry */
|
||||
}
|
||||
len = p - (__be32 *)iov->iov_base - 1;
|
||||
@@ -2218,8 +2233,6 @@ out_garbage:
|
||||
out_retry:
|
||||
return ERR_PTR(-EAGAIN);
|
||||
}
|
||||
out_eio:
|
||||
error = -EIO;
|
||||
out_err:
|
||||
rpc_exit(task, error);
|
||||
dprintk("RPC: %5u %s: call failed with error %d\n", task->tk_pid,
|
||||
@@ -2291,7 +2304,7 @@ static void rpc_show_task(const struct rpc_clnt *clnt,
|
||||
printk(KERN_INFO "%5u %04x %6d %8p %8p %8ld %8p %sv%u %s a:%ps q:%s\n",
|
||||
task->tk_pid, task->tk_flags, task->tk_status,
|
||||
clnt, task->tk_rqstp, task->tk_timeout, task->tk_ops,
|
||||
clnt->cl_protname, clnt->cl_vers, rpc_proc_name(task),
|
||||
clnt->cl_program->name, clnt->cl_vers, rpc_proc_name(task),
|
||||
task->tk_action, rpc_waitq);
|
||||
}
|
||||
|
||||
|
||||
@@ -409,7 +409,7 @@ rpc_show_info(struct seq_file *m, void *v)
|
||||
rcu_read_lock();
|
||||
seq_printf(m, "RPC server: %s\n",
|
||||
rcu_dereference(clnt->cl_xprt)->servername);
|
||||
seq_printf(m, "service: %s (%d) version %d\n", clnt->cl_protname,
|
||||
seq_printf(m, "service: %s (%d) version %d\n", clnt->cl_program->name,
|
||||
clnt->cl_prog, clnt->cl_vers);
|
||||
seq_printf(m, "address: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_ADDR));
|
||||
seq_printf(m, "protocol: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_PROTO));
|
||||
@@ -480,23 +480,6 @@ static const struct dentry_operations rpc_dentry_operations = {
|
||||
.d_delete = rpc_delete_dentry,
|
||||
};
|
||||
|
||||
/*
|
||||
* Lookup the data. This is trivial - if the dentry didn't already
|
||||
* exist, we know it is negative.
|
||||
*/
|
||||
static struct dentry *
|
||||
rpc_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
|
||||
{
|
||||
if (dentry->d_name.len > NAME_MAX)
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
d_add(dentry, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct inode_operations rpc_dir_inode_operations = {
|
||||
.lookup = rpc_lookup,
|
||||
};
|
||||
|
||||
static struct inode *
|
||||
rpc_get_inode(struct super_block *sb, umode_t mode)
|
||||
{
|
||||
@@ -509,7 +492,7 @@ rpc_get_inode(struct super_block *sb, umode_t mode)
|
||||
switch (mode & S_IFMT) {
|
||||
case S_IFDIR:
|
||||
inode->i_fop = &simple_dir_operations;
|
||||
inode->i_op = &rpc_dir_inode_operations;
|
||||
inode->i_op = &simple_dir_inode_operations;
|
||||
inc_nlink(inode);
|
||||
default:
|
||||
break;
|
||||
@@ -901,6 +884,159 @@ rpc_unlink(struct dentry *dentry)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpc_unlink);
|
||||
|
||||
/**
|
||||
* rpc_init_pipe_dir_head - initialise a struct rpc_pipe_dir_head
|
||||
* @pdh: pointer to struct rpc_pipe_dir_head
|
||||
*/
|
||||
void rpc_init_pipe_dir_head(struct rpc_pipe_dir_head *pdh)
|
||||
{
|
||||
INIT_LIST_HEAD(&pdh->pdh_entries);
|
||||
pdh->pdh_dentry = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpc_init_pipe_dir_head);
|
||||
|
||||
/**
|
||||
* rpc_init_pipe_dir_object - initialise a struct rpc_pipe_dir_object
|
||||
* @pdo: pointer to struct rpc_pipe_dir_object
|
||||
* @pdo_ops: pointer to const struct rpc_pipe_dir_object_ops
|
||||
* @pdo_data: pointer to caller-defined data
|
||||
*/
|
||||
void rpc_init_pipe_dir_object(struct rpc_pipe_dir_object *pdo,
|
||||
const struct rpc_pipe_dir_object_ops *pdo_ops,
|
||||
void *pdo_data)
|
||||
{
|
||||
INIT_LIST_HEAD(&pdo->pdo_head);
|
||||
pdo->pdo_ops = pdo_ops;
|
||||
pdo->pdo_data = pdo_data;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpc_init_pipe_dir_object);
|
||||
|
||||
static int
|
||||
rpc_add_pipe_dir_object_locked(struct net *net,
|
||||
struct rpc_pipe_dir_head *pdh,
|
||||
struct rpc_pipe_dir_object *pdo)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (pdh->pdh_dentry)
|
||||
ret = pdo->pdo_ops->create(pdh->pdh_dentry, pdo);
|
||||
if (ret == 0)
|
||||
list_add_tail(&pdo->pdo_head, &pdh->pdh_entries);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
rpc_remove_pipe_dir_object_locked(struct net *net,
|
||||
struct rpc_pipe_dir_head *pdh,
|
||||
struct rpc_pipe_dir_object *pdo)
|
||||
{
|
||||
if (pdh->pdh_dentry)
|
||||
pdo->pdo_ops->destroy(pdh->pdh_dentry, pdo);
|
||||
list_del_init(&pdo->pdo_head);
|
||||
}
|
||||
|
||||
/**
|
||||
* rpc_add_pipe_dir_object - associate a rpc_pipe_dir_object to a directory
|
||||
* @net: pointer to struct net
|
||||
* @pdh: pointer to struct rpc_pipe_dir_head
|
||||
* @pdo: pointer to struct rpc_pipe_dir_object
|
||||
*
|
||||
*/
|
||||
int
|
||||
rpc_add_pipe_dir_object(struct net *net,
|
||||
struct rpc_pipe_dir_head *pdh,
|
||||
struct rpc_pipe_dir_object *pdo)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (list_empty(&pdo->pdo_head)) {
|
||||
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
|
||||
|
||||
mutex_lock(&sn->pipefs_sb_lock);
|
||||
ret = rpc_add_pipe_dir_object_locked(net, pdh, pdo);
|
||||
mutex_unlock(&sn->pipefs_sb_lock);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpc_add_pipe_dir_object);
|
||||
|
||||
/**
|
||||
* rpc_remove_pipe_dir_object - remove a rpc_pipe_dir_object from a directory
|
||||
* @net: pointer to struct net
|
||||
* @pdh: pointer to struct rpc_pipe_dir_head
|
||||
* @pdo: pointer to struct rpc_pipe_dir_object
|
||||
*
|
||||
*/
|
||||
void
|
||||
rpc_remove_pipe_dir_object(struct net *net,
|
||||
struct rpc_pipe_dir_head *pdh,
|
||||
struct rpc_pipe_dir_object *pdo)
|
||||
{
|
||||
if (!list_empty(&pdo->pdo_head)) {
|
||||
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
|
||||
|
||||
mutex_lock(&sn->pipefs_sb_lock);
|
||||
rpc_remove_pipe_dir_object_locked(net, pdh, pdo);
|
||||
mutex_unlock(&sn->pipefs_sb_lock);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpc_remove_pipe_dir_object);
|
||||
|
||||
/**
|
||||
* rpc_find_or_alloc_pipe_dir_object
|
||||
* @net: pointer to struct net
|
||||
* @pdh: pointer to struct rpc_pipe_dir_head
|
||||
* @match: match struct rpc_pipe_dir_object to data
|
||||
* @alloc: allocate a new struct rpc_pipe_dir_object
|
||||
* @data: user defined data for match() and alloc()
|
||||
*
|
||||
*/
|
||||
struct rpc_pipe_dir_object *
|
||||
rpc_find_or_alloc_pipe_dir_object(struct net *net,
|
||||
struct rpc_pipe_dir_head *pdh,
|
||||
int (*match)(struct rpc_pipe_dir_object *, void *),
|
||||
struct rpc_pipe_dir_object *(*alloc)(void *),
|
||||
void *data)
|
||||
{
|
||||
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
|
||||
struct rpc_pipe_dir_object *pdo;
|
||||
|
||||
mutex_lock(&sn->pipefs_sb_lock);
|
||||
list_for_each_entry(pdo, &pdh->pdh_entries, pdo_head) {
|
||||
if (!match(pdo, data))
|
||||
continue;
|
||||
goto out;
|
||||
}
|
||||
pdo = alloc(data);
|
||||
if (!pdo)
|
||||
goto out;
|
||||
rpc_add_pipe_dir_object_locked(net, pdh, pdo);
|
||||
out:
|
||||
mutex_unlock(&sn->pipefs_sb_lock);
|
||||
return pdo;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpc_find_or_alloc_pipe_dir_object);
|
||||
|
||||
static void
|
||||
rpc_create_pipe_dir_objects(struct rpc_pipe_dir_head *pdh)
|
||||
{
|
||||
struct rpc_pipe_dir_object *pdo;
|
||||
struct dentry *dir = pdh->pdh_dentry;
|
||||
|
||||
list_for_each_entry(pdo, &pdh->pdh_entries, pdo_head)
|
||||
pdo->pdo_ops->create(dir, pdo);
|
||||
}
|
||||
|
||||
static void
|
||||
rpc_destroy_pipe_dir_objects(struct rpc_pipe_dir_head *pdh)
|
||||
{
|
||||
struct rpc_pipe_dir_object *pdo;
|
||||
struct dentry *dir = pdh->pdh_dentry;
|
||||
|
||||
list_for_each_entry(pdo, &pdh->pdh_entries, pdo_head)
|
||||
pdo->pdo_ops->destroy(dir, pdo);
|
||||
}
|
||||
|
||||
enum {
|
||||
RPCAUTH_info,
|
||||
RPCAUTH_EOF
|
||||
@@ -941,16 +1077,29 @@ struct dentry *rpc_create_client_dir(struct dentry *dentry,
|
||||
const char *name,
|
||||
struct rpc_clnt *rpc_client)
|
||||
{
|
||||
return rpc_mkdir_populate(dentry, name, S_IRUGO | S_IXUGO, NULL,
|
||||
struct dentry *ret;
|
||||
|
||||
ret = rpc_mkdir_populate(dentry, name, S_IRUGO | S_IXUGO, NULL,
|
||||
rpc_clntdir_populate, rpc_client);
|
||||
if (!IS_ERR(ret)) {
|
||||
rpc_client->cl_pipedir_objects.pdh_dentry = ret;
|
||||
rpc_create_pipe_dir_objects(&rpc_client->cl_pipedir_objects);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpc_remove_client_dir - Remove a directory created with rpc_create_client_dir()
|
||||
* @dentry: dentry for the pipe
|
||||
* @rpc_client: rpc_client for the pipe
|
||||
*/
|
||||
int rpc_remove_client_dir(struct dentry *dentry)
|
||||
int rpc_remove_client_dir(struct rpc_clnt *rpc_client)
|
||||
{
|
||||
struct dentry *dentry = rpc_client->cl_pipedir_objects.pdh_dentry;
|
||||
|
||||
if (dentry == NULL)
|
||||
return 0;
|
||||
rpc_destroy_pipe_dir_objects(&rpc_client->cl_pipedir_objects);
|
||||
rpc_client->cl_pipedir_objects.pdh_dentry = NULL;
|
||||
return rpc_rmdir_depopulate(dentry, rpc_clntdir_depopulate);
|
||||
}
|
||||
|
||||
|
||||
@@ -258,7 +258,7 @@ static int rpc_wait_bit_killable(void *word)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef RPC_DEBUG
|
||||
#if defined(RPC_DEBUG) || defined(RPC_TRACEPOINTS)
|
||||
static void rpc_task_set_debuginfo(struct rpc_task *task)
|
||||
{
|
||||
static atomic_t rpc_pid;
|
||||
|
||||
@@ -188,7 +188,7 @@ void rpc_print_iostats(struct seq_file *seq, struct rpc_clnt *clnt)
|
||||
|
||||
seq_printf(seq, "\tRPC iostats version: %s ", RPC_IOSTATS_VERS);
|
||||
seq_printf(seq, "p/v: %u/%u (%s)\n",
|
||||
clnt->cl_prog, clnt->cl_vers, clnt->cl_protname);
|
||||
clnt->cl_prog, clnt->cl_vers, clnt->cl_program->name);
|
||||
|
||||
rcu_read_lock();
|
||||
xprt = rcu_dereference(clnt->cl_xprt);
|
||||
|
||||
@@ -47,6 +47,8 @@
|
||||
#include <net/udp.h>
|
||||
#include <net/tcp.h>
|
||||
|
||||
#include <trace/events/sunrpc.h>
|
||||
|
||||
#include "sunrpc.h"
|
||||
|
||||
static void xs_close(struct rpc_xprt *xprt);
|
||||
@@ -665,8 +667,10 @@ static void xs_tcp_shutdown(struct rpc_xprt *xprt)
|
||||
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
|
||||
struct socket *sock = transport->sock;
|
||||
|
||||
if (sock != NULL)
|
||||
if (sock != NULL) {
|
||||
kernel_sock_shutdown(sock, SHUT_WR);
|
||||
trace_rpc_socket_shutdown(xprt, sock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -811,6 +815,7 @@ static void xs_reset_transport(struct sock_xprt *transport)
|
||||
|
||||
sk->sk_no_check = 0;
|
||||
|
||||
trace_rpc_socket_close(&transport->xprt, sock);
|
||||
sock_release(sock);
|
||||
}
|
||||
|
||||
@@ -1492,6 +1497,7 @@ static void xs_tcp_state_change(struct sock *sk)
|
||||
sock_flag(sk, SOCK_ZAPPED),
|
||||
sk->sk_shutdown);
|
||||
|
||||
trace_rpc_socket_state_change(xprt, sk->sk_socket);
|
||||
switch (sk->sk_state) {
|
||||
case TCP_ESTABLISHED:
|
||||
spin_lock(&xprt->transport_lock);
|
||||
@@ -1896,6 +1902,7 @@ static int xs_local_setup_socket(struct sock_xprt *transport)
|
||||
xprt, xprt->address_strings[RPC_DISPLAY_ADDR]);
|
||||
|
||||
status = xs_local_finish_connecting(xprt, sock);
|
||||
trace_rpc_socket_connect(xprt, sock, status);
|
||||
switch (status) {
|
||||
case 0:
|
||||
dprintk("RPC: xprt %p connected to %s\n",
|
||||
@@ -2039,6 +2046,7 @@ static void xs_udp_setup_socket(struct work_struct *work)
|
||||
xprt->address_strings[RPC_DISPLAY_PORT]);
|
||||
|
||||
xs_udp_finish_connecting(xprt, sock);
|
||||
trace_rpc_socket_connect(xprt, sock, 0);
|
||||
status = 0;
|
||||
out:
|
||||
xprt_clear_connecting(xprt);
|
||||
@@ -2064,6 +2072,8 @@ static void xs_abort_connection(struct sock_xprt *transport)
|
||||
memset(&any, 0, sizeof(any));
|
||||
any.sa_family = AF_UNSPEC;
|
||||
result = kernel_connect(transport->sock, &any, sizeof(any), 0);
|
||||
trace_rpc_socket_reset_connection(&transport->xprt,
|
||||
transport->sock, result);
|
||||
if (!result)
|
||||
xs_sock_reset_connection_flags(&transport->xprt);
|
||||
dprintk("RPC: AF_UNSPEC connect return code %d\n", result);
|
||||
@@ -2194,6 +2204,7 @@ static void xs_tcp_setup_socket(struct work_struct *work)
|
||||
xprt->address_strings[RPC_DISPLAY_PORT]);
|
||||
|
||||
status = xs_tcp_finish_connecting(xprt, sock);
|
||||
trace_rpc_socket_connect(xprt, sock, status);
|
||||
dprintk("RPC: %p connect status %d connected %d sock state %d\n",
|
||||
xprt, -status, xprt_connected(xprt),
|
||||
sock->sk->sk_state);
|
||||
|
||||
Reference in New Issue
Block a user