Merge branch 'for-2.6.37' of git://linux-nfs.org/~bfields/linux

* 'for-2.6.37' of git://linux-nfs.org/~bfields/linux: (99 commits)
  svcrpc: svc_tcp_sendto XPT_DEAD check is redundant
  svcrpc: no need for XPT_DEAD check in svc_xprt_enqueue
  svcrpc: assume svc_delete_xprt() called only once
  svcrpc: never clear XPT_BUSY on dead xprt
  nfsd4: fix connection allocation in sequence()
  nfsd4: only require krb5 principal for NFSv4.0 callbacks
  nfsd4: move minorversion to client
  nfsd4: delay session removal till free_client
  nfsd4: separate callback change and callback probe
  nfsd4: callback program number is per-session
  nfsd4: track backchannel connections
  nfsd4: confirm only on succesful create_session
  nfsd4: make backchannel sequence number per-session
  nfsd4: use client pointer to backchannel session
  nfsd4: move callback setup into session init code
  nfsd4: don't cache seq_misordered replies
  SUNRPC: Properly initialize sock_xprt.srcaddr in all cases
  SUNRPC: Use conventional switch statement when reclassifying sockets
  sunrpc/xprtrdma: clean up workqueue usage
  sunrpc: Turn list_for_each-s into the ..._entry-s
  ...

Fix up trivial conflicts (two different deprecation notices added in
separate branches) in Documentation/feature-removal-schedule.txt
This commit is contained in:
Linus Torvalds 2010-10-26 09:55:25 -07:00
commit 4390110fef
61 changed files with 1549 additions and 1951 deletions

View file

@ -535,3 +535,13 @@ Why: Hareware scan is the prefer method for iwlwifi devices for
Who: Wey-Yi Guy <wey-yi.w.guy@intel.com>
----------------------------
What: access to nfsd auth cache through sys_nfsservctl or '.' files
in the 'nfsd' filesystem.
When: 2.6.40
Why: This is a legacy interface which have been replaced by a more
dynamic cache. Continuing to maintain this interface is an
unnecessary burden.
Who: NeilBrown <neilb@suse.de>
----------------------------

View file

@ -29,10 +29,7 @@ obj-$(CONFIG_EVENTFD) += eventfd.o
obj-$(CONFIG_AIO) += aio.o
obj-$(CONFIG_FILE_LOCKING) += locks.o
obj-$(CONFIG_COMPAT) += compat.o compat_ioctl.o
nfsd-$(CONFIG_NFSD) := nfsctl.o
obj-y += $(nfsd-y) $(nfsd-m)
obj-$(CONFIG_NFSD_DEPRECATED) += nfsctl.o
obj-$(CONFIG_BINFMT_AOUT) += binfmt_aout.o
obj-$(CONFIG_BINFMT_EM86) += binfmt_em86.o
obj-$(CONFIG_BINFMT_MISC) += binfmt_misc.o

View file

@ -1963,7 +1963,7 @@ asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds,
}
#endif /* HAVE_SET_RESTORE_SIGMASK */
#if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)
#if (defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)) && !defined(CONFIG_NFSD_DEPRECATED)
/* Stuff for NFS server syscalls... */
struct compat_nfsctl_svc {
u16 svc32_port;

View file

@ -353,6 +353,7 @@ nlm_bind_host(struct nlm_host *host)
.to_retries = 5U,
};
struct rpc_create_args args = {
.net = &init_net,
.protocol = host->h_proto,
.address = nlm_addr(host),
.addrsize = host->h_addrlen,

View file

@ -69,6 +69,7 @@ static struct rpc_clnt *nsm_create(void)
.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
};
struct rpc_create_args args = {
.net = &init_net,
.protocol = XPRT_TRANSPORT_UDP,
.address = (struct sockaddr *)&sin,
.addrsize = sizeof(sin),

View file

@ -206,7 +206,7 @@ static int create_lockd_listener(struct svc_serv *serv, const char *name,
xprt = svc_find_xprt(serv, name, family, 0);
if (xprt == NULL)
return svc_create_xprt(serv, name, family, port,
return svc_create_xprt(serv, name, &init_net, family, port,
SVC_SOCK_DEFAULTS);
svc_xprt_put(xprt);
return 0;

View file

@ -230,9 +230,7 @@ static void nlm4svc_callback_exit(struct rpc_task *task, void *data)
static void nlm4svc_callback_release(void *data)
{
lock_kernel();
nlm_release_call(data);
unlock_kernel();
}
static const struct rpc_call_ops nlm4svc_callback_ops = {

View file

@ -52,12 +52,13 @@ static const struct rpc_call_ops nlmsvc_grant_ops;
* The list of blocked locks to retry
*/
static LIST_HEAD(nlm_blocked);
static DEFINE_SPINLOCK(nlm_blocked_lock);
/*
* Insert a blocked lock into the global list
*/
static void
nlmsvc_insert_block(struct nlm_block *block, unsigned long when)
nlmsvc_insert_block_locked(struct nlm_block *block, unsigned long when)
{
struct nlm_block *b;
struct list_head *pos;
@ -87,6 +88,13 @@ nlmsvc_insert_block(struct nlm_block *block, unsigned long when)
block->b_when = when;
}
static void nlmsvc_insert_block(struct nlm_block *block, unsigned long when)
{
spin_lock(&nlm_blocked_lock);
nlmsvc_insert_block_locked(block, when);
spin_unlock(&nlm_blocked_lock);
}
/*
* Remove a block from the global list
*/
@ -94,7 +102,9 @@ static inline void
nlmsvc_remove_block(struct nlm_block *block)
{
if (!list_empty(&block->b_list)) {
spin_lock(&nlm_blocked_lock);
list_del_init(&block->b_list);
spin_unlock(&nlm_blocked_lock);
nlmsvc_release_block(block);
}
}
@ -651,7 +661,7 @@ static int nlmsvc_grant_deferred(struct file_lock *fl, struct file_lock *conf,
struct nlm_block *block;
int rc = -ENOENT;
lock_kernel();
spin_lock(&nlm_blocked_lock);
list_for_each_entry(block, &nlm_blocked, b_list) {
if (nlm_compare_locks(&block->b_call->a_args.lock.fl, fl)) {
dprintk("lockd: nlmsvc_notify_blocked block %p flags %d\n",
@ -665,13 +675,13 @@ static int nlmsvc_grant_deferred(struct file_lock *fl, struct file_lock *conf,
} else if (result == 0)
block->b_granted = 1;
nlmsvc_insert_block(block, 0);
nlmsvc_insert_block_locked(block, 0);
svc_wake_up(block->b_daemon);
rc = 0;
break;
}
}
unlock_kernel();
spin_unlock(&nlm_blocked_lock);
if (rc == -ENOENT)
printk(KERN_WARNING "lockd: grant for unknown block\n");
return rc;
@ -803,7 +813,7 @@ static void nlmsvc_grant_callback(struct rpc_task *task, void *data)
dprintk("lockd: GRANT_MSG RPC callback\n");
lock_kernel();
spin_lock(&nlm_blocked_lock);
/* if the block is not on a list at this point then it has
* been invalidated. Don't try to requeue it.
*
@ -825,19 +835,20 @@ static void nlmsvc_grant_callback(struct rpc_task *task, void *data)
/* Call was successful, now wait for client callback */
timeout = 60 * HZ;
}
nlmsvc_insert_block(block, timeout);
nlmsvc_insert_block_locked(block, timeout);
svc_wake_up(block->b_daemon);
out:
unlock_kernel();
spin_unlock(&nlm_blocked_lock);
}
/*
* FIXME: nlmsvc_release_block() grabs a mutex. This is not allowed for an
* .rpc_release rpc_call_op
*/
static void nlmsvc_grant_release(void *data)
{
struct nlm_rqst *call = data;
lock_kernel();
nlmsvc_release_block(call->a_block);
unlock_kernel();
}
static const struct rpc_call_ops nlmsvc_grant_ops = {

View file

@ -260,9 +260,7 @@ static void nlmsvc_callback_exit(struct rpc_task *task, void *data)
static void nlmsvc_callback_release(void *data)
{
lock_kernel();
nlm_release_call(data);
unlock_kernel();
}
static const struct rpc_call_ops nlmsvc_callback_ops = {

View file

@ -109,7 +109,7 @@ nfs4_callback_up(struct svc_serv *serv)
{
int ret;
ret = svc_create_xprt(serv, "tcp", PF_INET,
ret = svc_create_xprt(serv, "tcp", &init_net, PF_INET,
nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
if (ret <= 0)
goto out_err;
@ -117,7 +117,7 @@ nfs4_callback_up(struct svc_serv *serv)
dprintk("NFS: Callback listener port = %u (af %u)\n",
nfs_callback_tcpport, PF_INET);
ret = svc_create_xprt(serv, "tcp", PF_INET6,
ret = svc_create_xprt(serv, "tcp", &init_net, PF_INET6,
nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
if (ret > 0) {
nfs_callback_tcpport6 = ret;

View file

@ -605,6 +605,7 @@ static int nfs_create_rpc_client(struct nfs_client *clp,
{
struct rpc_clnt *clnt = NULL;
struct rpc_create_args args = {
.net = &init_net,
.protocol = clp->cl_proto,
.address = (struct sockaddr *)&clp->cl_addr,
.addrsize = clp->cl_addrlen,

View file

@ -167,7 +167,7 @@ static int nfs_dns_show(struct seq_file *m, struct cache_detail *cd,
return 0;
}
item = container_of(h, struct nfs_dns_ent, h);
ttl = (long)item->h.expiry_time - (long)get_seconds();
ttl = item->h.expiry_time - seconds_since_boot();
if (ttl < 0)
ttl = 0;
@ -239,7 +239,7 @@ static int nfs_dns_parse(struct cache_detail *cd, char *buf, int buflen)
ttl = get_expiry(&buf);
if (ttl == 0)
goto out;
key.h.expiry_time = ttl + get_seconds();
key.h.expiry_time = ttl + seconds_since_boot();
ret = -ENOMEM;
item = nfs_dns_lookup(cd, &key);
@ -301,7 +301,7 @@ static int do_cache_lookup_nowait(struct cache_detail *cd,
goto out_err;
ret = -ETIMEDOUT;
if (!test_bit(CACHE_VALID, &(*item)->h.flags)
|| (*item)->h.expiry_time < get_seconds()
|| (*item)->h.expiry_time < seconds_since_boot()
|| cd->flush_time > (*item)->h.last_refresh)
goto out_put;
ret = -ENOENT;

View file

@ -153,6 +153,7 @@ int nfs_mount(struct nfs_mount_request *info)
.rpc_resp = &result,
};
struct rpc_create_args args = {
.net = &init_net,
.protocol = info->protocol,
.address = info->sap,
.addrsize = info->salen,
@ -224,6 +225,7 @@ void nfs_umount(const struct nfs_mount_request *info)
.to_retries = 2,
};
struct rpc_create_args args = {
.net = &init_net,
.protocol = IPPROTO_UDP,
.address = info->sap,
.addrsize = info->salen,

View file

@ -29,6 +29,18 @@ config NFSD
If unsure, say N.
config NFSD_DEPRECATED
bool "Include support for deprecated syscall interface to NFSD"
depends on NFSD
default y
help
The syscall interface to nfsd was obsoleted in 2.6.0 by a new
filesystem based interface. The old interface is due for removal
in 2.6.40. If you wish to remove the interface before then
say N.
In unsure, say Y.
config NFSD_V2_ACL
bool
depends on NFSD

View file

@ -28,9 +28,6 @@
typedef struct auth_domain svc_client;
typedef struct svc_export svc_export;
static void exp_do_unexport(svc_export *unexp);
static int exp_verify_string(char *cp, int max);
/*
* We have two caches.
* One maps client+vfsmnt+dentry to export options - the export map
@ -802,6 +799,7 @@ exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp)
return ek;
}
#ifdef CONFIG_NFSD_DEPRECATED
static int exp_set_key(svc_client *clp, int fsid_type, u32 *fsidv,
struct svc_export *exp)
{
@ -852,6 +850,7 @@ exp_get_fsid_key(svc_client *clp, int fsid)
return exp_find_key(clp, FSID_NUM, fsidv, NULL);
}
#endif
static svc_export *exp_get_by_name(svc_client *clp, const struct path *path,
struct cache_req *reqp)
@ -893,6 +892,7 @@ static struct svc_export *exp_parent(svc_client *clp, struct path *path)
return exp;
}
#ifdef CONFIG_NFSD_DEPRECATED
/*
* Hashtable locking. Write locks are placed only by user processes
* wanting to modify export information.
@ -925,6 +925,19 @@ exp_writeunlock(void)
{
up_write(&hash_sem);
}
#else
/* hash_sem not needed once deprecated interface is removed */
void exp_readlock(void) {}
static inline void exp_writelock(void){}
void exp_readunlock(void) {}
static inline void exp_writeunlock(void){}
#endif
#ifdef CONFIG_NFSD_DEPRECATED
static void exp_do_unexport(svc_export *unexp);
static int exp_verify_string(char *cp, int max);
static void exp_fsid_unhash(struct svc_export *exp)
{
@ -935,10 +948,9 @@ static void exp_fsid_unhash(struct svc_export *exp)
ek = exp_get_fsid_key(exp->ex_client, exp->ex_fsid);
if (!IS_ERR(ek)) {
ek->h.expiry_time = get_seconds()-1;
sunrpc_invalidate(&ek->h, &svc_expkey_cache);
cache_put(&ek->h, &svc_expkey_cache);
}
svc_expkey_cache.nextcheck = get_seconds();
}
static int exp_fsid_hash(svc_client *clp, struct svc_export *exp)
@ -973,10 +985,9 @@ static void exp_unhash(struct svc_export *exp)
ek = exp_get_key(exp->ex_client, inode->i_sb->s_dev, inode->i_ino);
if (!IS_ERR(ek)) {
ek->h.expiry_time = get_seconds()-1;
sunrpc_invalidate(&ek->h, &svc_expkey_cache);
cache_put(&ek->h, &svc_expkey_cache);
}
svc_expkey_cache.nextcheck = get_seconds();
}
/*
@ -1097,8 +1108,7 @@ exp_export(struct nfsctl_export *nxp)
static void
exp_do_unexport(svc_export *unexp)
{
unexp->h.expiry_time = get_seconds()-1;
svc_export_cache.nextcheck = get_seconds();
sunrpc_invalidate(&unexp->h, &svc_export_cache);
exp_unhash(unexp);
exp_fsid_unhash(unexp);
}
@ -1150,6 +1160,7 @@ exp_unexport(struct nfsctl_export *nxp)
exp_writeunlock();
return err;
}
#endif /* CONFIG_NFSD_DEPRECATED */
/*
* Obtain the root fh on behalf of a client.
@ -1459,25 +1470,43 @@ static void show_secinfo_flags(struct seq_file *m, int flags)
show_expflags(m, flags, NFSEXP_SECINFO_FLAGS);
}
static bool secinfo_flags_equal(int f, int g)
{
f &= NFSEXP_SECINFO_FLAGS;
g &= NFSEXP_SECINFO_FLAGS;
return f == g;
}
static int show_secinfo_run(struct seq_file *m, struct exp_flavor_info **fp, struct exp_flavor_info *end)
{
int flags;
flags = (*fp)->flags;
seq_printf(m, ",sec=%d", (*fp)->pseudoflavor);
(*fp)++;
while (*fp != end && secinfo_flags_equal(flags, (*fp)->flags)) {
seq_printf(m, ":%d", (*fp)->pseudoflavor);
(*fp)++;
}
return flags;
}
static void show_secinfo(struct seq_file *m, struct svc_export *exp)
{
struct exp_flavor_info *f;
struct exp_flavor_info *end = exp->ex_flavors + exp->ex_nflavors;
int lastflags = 0, first = 0;
int flags;
if (exp->ex_nflavors == 0)
return;
for (f = exp->ex_flavors; f < end; f++) {
if (first || f->flags != lastflags) {
if (!first)
show_secinfo_flags(m, lastflags);
seq_printf(m, ",sec=%d", f->pseudoflavor);
lastflags = f->flags;
} else {
seq_printf(m, ":%d", f->pseudoflavor);
}
f = exp->ex_flavors;
flags = show_secinfo_run(m, &f, end);
if (!secinfo_flags_equal(flags, exp->ex_flags))
show_secinfo_flags(m, flags);
while (f != end) {
flags = show_secinfo_run(m, &f, end);
show_secinfo_flags(m, flags);
}
show_secinfo_flags(m, lastflags);
}
static void exp_flags(struct seq_file *m, int flag, int fsid,
@ -1532,6 +1561,7 @@ const struct seq_operations nfs_exports_op = {
.show = e_show,
};
#ifdef CONFIG_NFSD_DEPRECATED
/*
* Add or modify a client.
* Change requests may involve the list of host addresses. The list of
@ -1563,7 +1593,7 @@ exp_addclient(struct nfsctl_client *ncp)
/* Insert client into hashtable. */
for (i = 0; i < ncp->cl_naddr; i++) {
ipv6_addr_set_v4mapped(ncp->cl_addrlist[i].s_addr, &addr6);
auth_unix_add_addr(&addr6, dom);
auth_unix_add_addr(&init_net, &addr6, dom);
}
auth_unix_forget_old(dom);
auth_domain_put(dom);
@ -1621,6 +1651,7 @@ exp_verify_string(char *cp, int max)
printk(KERN_NOTICE "nfsd: couldn't validate string %s\n", cp);
return 0;
}
#endif /* CONFIG_NFSD_DEPRECATED */
/*
* Initialize the exports module.

View file

@ -247,10 +247,11 @@ encode_cb_recall(struct xdr_stream *xdr, struct nfs4_delegation *dp,
}
static void
encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_sequence *args,
encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_callback *cb,
struct nfs4_cb_compound_hdr *hdr)
{
__be32 *p;
struct nfsd4_session *ses = cb->cb_clp->cl_cb_session;
if (hdr->minorversion == 0)
return;
@ -258,8 +259,8 @@ encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_sequence *args,
RESERVE_SPACE(1 + NFS4_MAX_SESSIONID_LEN + 20);
WRITE32(OP_CB_SEQUENCE);
WRITEMEM(args->cbs_clp->cl_sessionid.data, NFS4_MAX_SESSIONID_LEN);
WRITE32(args->cbs_clp->cl_cb_seq_nr);
WRITEMEM(ses->se_sessionid.data, NFS4_MAX_SESSIONID_LEN);
WRITE32(ses->se_cb_seq_nr);
WRITE32(0); /* slotid, always 0 */
WRITE32(0); /* highest slotid always 0 */
WRITE32(0); /* cachethis always 0 */
@ -279,18 +280,18 @@ nfs4_xdr_enc_cb_null(struct rpc_rqst *req, __be32 *p)
static int
nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, __be32 *p,
struct nfs4_rpc_args *rpc_args)
struct nfsd4_callback *cb)
{
struct xdr_stream xdr;
struct nfs4_delegation *args = rpc_args->args_op;
struct nfs4_delegation *args = cb->cb_op;
struct nfs4_cb_compound_hdr hdr = {
.ident = args->dl_ident,
.minorversion = rpc_args->args_seq.cbs_minorversion,
.ident = cb->cb_clp->cl_cb_ident,
.minorversion = cb->cb_minorversion,
};
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_cb_compound_hdr(&xdr, &hdr);
encode_cb_sequence(&xdr, &rpc_args->args_seq, &hdr);
encode_cb_sequence(&xdr, cb, &hdr);
encode_cb_recall(&xdr, args, &hdr);
encode_cb_nops(&hdr);
return 0;
@ -338,15 +339,16 @@ decode_cb_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
* with a single slot.
*/
static int
decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_sequence *res,
decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_callback *cb,
struct rpc_rqst *rqstp)
{
struct nfsd4_session *ses = cb->cb_clp->cl_cb_session;
struct nfs4_sessionid id;
int status;
u32 dummy;
__be32 *p;
if (res->cbs_minorversion == 0)
if (cb->cb_minorversion == 0)
return 0;
status = decode_cb_op_hdr(xdr, OP_CB_SEQUENCE);
@ -362,13 +364,12 @@ decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_sequence *res,
READ_BUF(NFS4_MAX_SESSIONID_LEN + 16);
memcpy(id.data, p, NFS4_MAX_SESSIONID_LEN);
p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN);
if (memcmp(id.data, res->cbs_clp->cl_sessionid.data,
NFS4_MAX_SESSIONID_LEN)) {
if (memcmp(id.data, ses->se_sessionid.data, NFS4_MAX_SESSIONID_LEN)) {
dprintk("%s Invalid session id\n", __func__);
goto out;
}
READ32(dummy);
if (dummy != res->cbs_clp->cl_cb_seq_nr) {
if (dummy != ses->se_cb_seq_nr) {
dprintk("%s Invalid sequence number\n", __func__);
goto out;
}
@ -392,7 +393,7 @@ nfs4_xdr_dec_cb_null(struct rpc_rqst *req, __be32 *p)
static int
nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, __be32 *p,
struct nfsd4_cb_sequence *seq)
struct nfsd4_callback *cb)
{
struct xdr_stream xdr;
struct nfs4_cb_compound_hdr hdr;
@ -402,8 +403,8 @@ nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, __be32 *p,
status = decode_cb_compound_hdr(&xdr, &hdr);
if (status)
goto out;
if (seq) {
status = decode_cb_sequence(&xdr, seq, rqstp);
if (cb) {
status = decode_cb_sequence(&xdr, cb, rqstp);
if (status)
goto out;
}
@ -472,30 +473,34 @@ static int max_cb_time(void)
/* Reference counting, callback cleanup, etc., all look racy as heck.
* And why is cl_cb_set an atomic? */
int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *cb)
int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
{
struct rpc_timeout timeparms = {
.to_initval = max_cb_time(),
.to_retries = 0,
};
struct rpc_create_args args = {
.protocol = XPRT_TRANSPORT_TCP,
.address = (struct sockaddr *) &cb->cb_addr,
.addrsize = cb->cb_addrlen,
.net = &init_net,
.address = (struct sockaddr *) &conn->cb_addr,
.addrsize = conn->cb_addrlen,
.timeout = &timeparms,
.program = &cb_program,
.prognumber = cb->cb_prog,
.version = 0,
.authflavor = clp->cl_flavor,
.flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET),
.client_name = clp->cl_principal,
};
struct rpc_clnt *client;
if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5))
return -EINVAL;
if (cb->cb_minorversion) {
args.bc_xprt = cb->cb_xprt;
if (clp->cl_minorversion == 0) {
if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5))
return -EINVAL;
args.client_name = clp->cl_principal;
args.prognumber = conn->cb_prog,
args.protocol = XPRT_TRANSPORT_TCP;
clp->cl_cb_ident = conn->cb_ident;
} else {
args.bc_xprt = conn->cb_xprt;
args.prognumber = clp->cl_cb_session->se_cb_prog;
args.protocol = XPRT_TRANSPORT_BC_TCP;
}
/* Create RPC client */
@ -505,7 +510,7 @@ int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *cb)
PTR_ERR(client));
return PTR_ERR(client);
}
nfsd4_set_callback_client(clp, client);
clp->cl_cb_client = client;
return 0;
}
@ -518,7 +523,7 @@ static void warn_no_callback_path(struct nfs4_client *clp, int reason)
static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata)
{
struct nfs4_client *clp = calldata;
struct nfs4_client *clp = container_of(calldata, struct nfs4_client, cl_cb_null);
if (task->tk_status)
warn_no_callback_path(clp, task->tk_status);
@ -527,6 +532,8 @@ static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata)
}
static const struct rpc_call_ops nfsd4_cb_probe_ops = {
/* XXX: release method to ensure we set the cb channel down if
* necessary on early failure? */
.rpc_call_done = nfsd4_cb_probe_done,
};
@ -542,38 +549,42 @@ int set_callback_cred(void)
return 0;
}
static struct workqueue_struct *callback_wq;
void do_probe_callback(struct nfs4_client *clp)
static void do_probe_callback(struct nfs4_client *clp)
{
struct rpc_message msg = {
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
.rpc_argp = clp,
.rpc_cred = callback_cred
};
int status;
struct nfsd4_callback *cb = &clp->cl_cb_null;
status = rpc_call_async(clp->cl_cb_client, &msg,
RPC_TASK_SOFT | RPC_TASK_SOFTCONN,
&nfsd4_cb_probe_ops, (void *)clp);
if (status)
warn_no_callback_path(clp, status);
cb->cb_op = NULL;
cb->cb_clp = clp;
cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL];
cb->cb_msg.rpc_argp = NULL;
cb->cb_msg.rpc_resp = NULL;
cb->cb_msg.rpc_cred = callback_cred;
cb->cb_ops = &nfsd4_cb_probe_ops;
queue_work(callback_wq, &cb->cb_work);
}
/*
* Set up the callback client and put a NFSPROC4_CB_NULL on the wire...
* Poke the callback thread to process any updates to the callback
* parameters, and send a null probe.
*/
void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *cb)
void nfsd4_probe_callback(struct nfs4_client *clp)
{
int status;
set_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_cb_flags);
do_probe_callback(clp);
}
void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
{
BUG_ON(atomic_read(&clp->cl_cb_set));
status = setup_callback_client(clp, cb);
if (status) {
warn_no_callback_path(clp, status);
return;
}
do_probe_callback(clp);
spin_lock(&clp->cl_lock);
memcpy(&clp->cl_cb_conn, conn, sizeof(struct nfs4_cb_conn));
spin_unlock(&clp->cl_lock);
}
/*
@ -584,8 +595,7 @@ void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *cb)
static int nfsd41_cb_setup_sequence(struct nfs4_client *clp,
struct rpc_task *task)
{
struct nfs4_rpc_args *args = task->tk_msg.rpc_argp;
u32 *ptr = (u32 *)clp->cl_sessionid.data;
u32 *ptr = (u32 *)clp->cl_cb_session->se_sessionid.data;
int status = 0;
dprintk("%s: %u:%u:%u:%u\n", __func__,
@ -597,14 +607,6 @@ static int nfsd41_cb_setup_sequence(struct nfs4_client *clp,
status = -EAGAIN;
goto out;
}
/*
* We'll need the clp during XDR encoding and decoding,
* and the sequence during decoding to verify the reply
*/
args->args_seq.cbs_clp = clp;
task->tk_msg.rpc_resp = &args->args_seq;
out:
dprintk("%s status=%d\n", __func__, status);
return status;
@ -616,13 +618,13 @@ static int nfsd41_cb_setup_sequence(struct nfs4_client *clp,
*/
static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
{
struct nfs4_delegation *dp = calldata;
struct nfsd4_callback *cb = calldata;
struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
struct nfs4_client *clp = dp->dl_client;
struct nfs4_rpc_args *args = task->tk_msg.rpc_argp;
u32 minorversion = clp->cl_cb_conn.cb_minorversion;
u32 minorversion = clp->cl_minorversion;
int status = 0;
args->args_seq.cbs_minorversion = minorversion;
cb->cb_minorversion = minorversion;
if (minorversion) {
status = nfsd41_cb_setup_sequence(clp, task);
if (status) {
@ -639,19 +641,20 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
static void nfsd4_cb_done(struct rpc_task *task, void *calldata)
{
struct nfs4_delegation *dp = calldata;
struct nfsd4_callback *cb = calldata;
struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
struct nfs4_client *clp = dp->dl_client;
dprintk("%s: minorversion=%d\n", __func__,
clp->cl_cb_conn.cb_minorversion);
clp->cl_minorversion);
if (clp->cl_cb_conn.cb_minorversion) {
if (clp->cl_minorversion) {
/* No need for lock, access serialized in nfsd4_cb_prepare */
++clp->cl_cb_seq_nr;
++clp->cl_cb_session->se_cb_seq_nr;
clear_bit(0, &clp->cl_cb_slot_busy);
rpc_wake_up_next(&clp->cl_cb_waitq);
dprintk("%s: freed slot, new seqid=%d\n", __func__,
clp->cl_cb_seq_nr);
clp->cl_cb_session->se_cb_seq_nr);
/* We're done looking into the sequence information */
task->tk_msg.rpc_resp = NULL;
@ -661,7 +664,8 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata)
static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
{
struct nfs4_delegation *dp = calldata;
struct nfsd4_callback *cb = calldata;
struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
struct nfs4_client *clp = dp->dl_client;
struct rpc_clnt *current_rpc_client = clp->cl_cb_client;
@ -706,7 +710,8 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
static void nfsd4_cb_recall_release(void *calldata)
{
struct nfs4_delegation *dp = calldata;
struct nfsd4_callback *cb = calldata;
struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
nfs4_put_delegation(dp);
}
@ -717,8 +722,6 @@ static const struct rpc_call_ops nfsd4_cb_recall_ops = {
.rpc_release = nfsd4_cb_recall_release,
};
static struct workqueue_struct *callback_wq;
int nfsd4_create_callback_queue(void)
{
callback_wq = create_singlethread_workqueue("nfsd4_callbacks");
@ -733,57 +736,88 @@ void nfsd4_destroy_callback_queue(void)
}
/* must be called under the state lock */
void nfsd4_set_callback_client(struct nfs4_client *clp, struct rpc_clnt *new)
void nfsd4_shutdown_callback(struct nfs4_client *clp)
{
struct rpc_clnt *old = clp->cl_cb_client;
clp->cl_cb_client = new;
set_bit(NFSD4_CLIENT_KILL, &clp->cl_cb_flags);
/*
* After this, any work that saw the old value of cl_cb_client will
* be gone:
* Note this won't actually result in a null callback;
* instead, nfsd4_do_callback_rpc() will detect the killed
* client, destroy the rpc client, and stop:
*/
do_probe_callback(clp);
flush_workqueue(callback_wq);
/* So we can safely shut it down: */
if (old)
rpc_shutdown_client(old);
}
/*
* called with dp->dl_count inc'ed.
*/
static void _nfsd4_cb_recall(struct nfs4_delegation *dp)
void nfsd4_release_cb(struct nfsd4_callback *cb)
{
struct nfs4_client *clp = dp->dl_client;
struct rpc_clnt *clnt = clp->cl_cb_client;
struct nfs4_rpc_args *args = &dp->dl_recall.cb_args;
struct rpc_message msg = {
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL],
.rpc_cred = callback_cred
};
if (cb->cb_ops->rpc_release)
cb->cb_ops->rpc_release(cb);
}
if (clnt == NULL) {
nfs4_put_delegation(dp);
return; /* Client is shutting down; give up. */
void nfsd4_process_cb_update(struct nfsd4_callback *cb)
{
struct nfs4_cb_conn conn;
struct nfs4_client *clp = cb->cb_clp;
int err;
/*
* This is either an update, or the client dying; in either case,
* kill the old client:
*/
if (clp->cl_cb_client) {
rpc_shutdown_client(clp->cl_cb_client);
clp->cl_cb_client = NULL;
}
if (test_bit(NFSD4_CLIENT_KILL, &clp->cl_cb_flags))
return;
spin_lock(&clp->cl_lock);
/*
* Only serialized callback code is allowed to clear these
* flags; main nfsd code can only set them:
*/
BUG_ON(!clp->cl_cb_flags);
clear_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_cb_flags);
memcpy(&conn, &cb->cb_clp->cl_cb_conn, sizeof(struct nfs4_cb_conn));
spin_unlock(&clp->cl_lock);
args->args_op = dp;
msg.rpc_argp = args;
dp->dl_retries = 1;
rpc_call_async(clnt, &msg, RPC_TASK_SOFT, &nfsd4_cb_recall_ops, dp);
err = setup_callback_client(clp, &conn);
if (err)
warn_no_callback_path(clp, err);
}
void nfsd4_do_callback_rpc(struct work_struct *w)
{
/* XXX: for now, just send off delegation recall. */
/* In future, generalize to handle any sort of callback. */
struct nfsd4_callback *c = container_of(w, struct nfsd4_callback, cb_work);
struct nfs4_delegation *dp = container_of(c, struct nfs4_delegation, dl_recall);
struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work);
struct nfs4_client *clp = cb->cb_clp;
struct rpc_clnt *clnt;
_nfsd4_cb_recall(dp);
if (clp->cl_cb_flags)
nfsd4_process_cb_update(cb);
clnt = clp->cl_cb_client;
if (!clnt) {
/* Callback channel broken, or client killed; give up: */
nfsd4_release_cb(cb);
return;
}
rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT | RPC_TASK_SOFTCONN,
cb->cb_ops, cb);
}
void nfsd4_cb_recall(struct nfs4_delegation *dp)
{
struct nfsd4_callback *cb = &dp->dl_recall;
dp->dl_retries = 1;
cb->cb_op = dp;
cb->cb_clp = dp->dl_client;
cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL];
cb->cb_msg.rpc_argp = cb;
cb->cb_msg.rpc_resp = cb;
cb->cb_msg.rpc_cred = callback_cred;
cb->cb_ops = &nfsd4_cb_recall_ops;
dp->dl_retries = 1;
queue_work(callback_wq, &dp->dl_recall.cb_work);
}

View file

@ -482,109 +482,26 @@ nfsd_idmap_shutdown(void)
cache_unregister(&nametoid_cache);
}
/*
* Deferred request handling
*/
struct idmap_defer_req {
struct cache_req req;
struct cache_deferred_req deferred_req;
wait_queue_head_t waitq;
atomic_t count;
};
static inline void
put_mdr(struct idmap_defer_req *mdr)
{
if (atomic_dec_and_test(&mdr->count))
kfree(mdr);
}
static inline void
get_mdr(struct idmap_defer_req *mdr)
{
atomic_inc(&mdr->count);
}
static void
idmap_revisit(struct cache_deferred_req *dreq, int toomany)
{
struct idmap_defer_req *mdr =
container_of(dreq, struct idmap_defer_req, deferred_req);
wake_up(&mdr->waitq);
put_mdr(mdr);
}
static struct cache_deferred_req *
idmap_defer(struct cache_req *req)
{
struct idmap_defer_req *mdr =
container_of(req, struct idmap_defer_req, req);
mdr->deferred_req.revisit = idmap_revisit;
get_mdr(mdr);
return (&mdr->deferred_req);
}
static inline int
do_idmap_lookup(struct ent *(*lookup_fn)(struct ent *), struct ent *key,
struct cache_detail *detail, struct ent **item,
struct idmap_defer_req *mdr)
{
*item = lookup_fn(key);
if (!*item)
return -ENOMEM;
return cache_check(detail, &(*item)->h, &mdr->req);
}
static inline int
do_idmap_lookup_nowait(struct ent *(*lookup_fn)(struct ent *),
struct ent *key, struct cache_detail *detail,
struct ent **item)
{
int ret = -ENOMEM;
*item = lookup_fn(key);
if (!*item)
goto out_err;
ret = -ETIMEDOUT;
if (!test_bit(CACHE_VALID, &(*item)->h.flags)
|| (*item)->h.expiry_time < get_seconds()
|| detail->flush_time > (*item)->h.last_refresh)
goto out_put;
ret = -ENOENT;
if (test_bit(CACHE_NEGATIVE, &(*item)->h.flags))
goto out_put;
return 0;
out_put:
cache_put(&(*item)->h, detail);
out_err:
*item = NULL;
return ret;
}
static int
idmap_lookup(struct svc_rqst *rqstp,
struct ent *(*lookup_fn)(struct ent *), struct ent *key,
struct cache_detail *detail, struct ent **item)
{
struct idmap_defer_req *mdr;
int ret;
mdr = kzalloc(sizeof(*mdr), GFP_KERNEL);
if (!mdr)
*item = lookup_fn(key);
if (!*item)
return -ENOMEM;
atomic_set(&mdr->count, 1);
init_waitqueue_head(&mdr->waitq);
mdr->req.defer = idmap_defer;
ret = do_idmap_lookup(lookup_fn, key, detail, item, mdr);
if (ret == -EAGAIN) {
wait_event_interruptible_timeout(mdr->waitq,
test_bit(CACHE_VALID, &(*item)->h.flags), 1 * HZ);
ret = do_idmap_lookup_nowait(lookup_fn, key, detail, item);
retry:
ret = cache_check(detail, &(*item)->h, &rqstp->rq_chandle);
if (ret == -ETIMEDOUT) {
struct ent *prev_item = *item;
*item = lookup_fn(key);
if (*item != prev_item)
goto retry;
cache_put(&(*item)->h, detail);
}
put_mdr(mdr);
return ret;
}

View file

@ -1031,8 +1031,11 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
resp->cstate.session = NULL;
fh_init(&resp->cstate.current_fh, NFS4_FHSIZE);
fh_init(&resp->cstate.save_fh, NFS4_FHSIZE);
/* Use the deferral mechanism only for NFSv4.0 compounds */
rqstp->rq_usedeferral = (args->minorversion == 0);
/*
* Don't use the deferral mechanism for NFSv4; compounds make it
* too hard to avoid non-idempotency problems.
*/
rqstp->rq_usedeferral = 0;
/*
* According to RFC3010, this takes precedence over all other errors.

View file

@ -207,7 +207,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
{
struct nfs4_delegation *dp;
struct nfs4_file *fp = stp->st_file;
struct nfs4_cb_conn *cb = &stp->st_stateowner->so_client->cl_cb_conn;
dprintk("NFSD alloc_init_deleg\n");
/*
@ -234,7 +233,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
nfs4_file_get_access(fp, O_RDONLY);
dp->dl_flock = NULL;
dp->dl_type = type;
dp->dl_ident = cb->cb_ident;
dp->dl_stateid.si_boot = boot_time;
dp->dl_stateid.si_stateownerid = current_delegid++;
dp->dl_stateid.si_fileid = 0;
@ -535,94 +533,6 @@ gen_sessionid(struct nfsd4_session *ses)
*/
#define NFSD_MIN_HDR_SEQ_SZ (24 + 12 + 44)
/*
* Give the client the number of ca_maxresponsesize_cached slots it
* requests, of size bounded by NFSD_SLOT_CACHE_SIZE,
* NFSD_MAX_MEM_PER_SESSION, and nfsd_drc_max_mem. Do not allow more
* than NFSD_MAX_SLOTS_PER_SESSION.
*
* If we run out of reserved DRC memory we should (up to a point)
* re-negotiate active sessions and reduce their slot usage to make
* rooom for new connections. For now we just fail the create session.
*/
static int set_forechannel_drc_size(struct nfsd4_channel_attrs *fchan)
{
int mem, size = fchan->maxresp_cached;
if (fchan->maxreqs < 1)
return nfserr_inval;
if (size < NFSD_MIN_HDR_SEQ_SZ)
size = NFSD_MIN_HDR_SEQ_SZ;
size -= NFSD_MIN_HDR_SEQ_SZ;
if (size > NFSD_SLOT_CACHE_SIZE)
size = NFSD_SLOT_CACHE_SIZE;
/* bound the maxreqs by NFSD_MAX_MEM_PER_SESSION */
mem = fchan->maxreqs * size;
if (mem > NFSD_MAX_MEM_PER_SESSION) {
fchan->maxreqs = NFSD_MAX_MEM_PER_SESSION / size;
if (fchan->maxreqs > NFSD_MAX_SLOTS_PER_SESSION)
fchan->maxreqs = NFSD_MAX_SLOTS_PER_SESSION;
mem = fchan->maxreqs * size;
}
spin_lock(&nfsd_drc_lock);
/* bound the total session drc memory ussage */
if (mem + nfsd_drc_mem_used > nfsd_drc_max_mem) {
fchan->maxreqs = (nfsd_drc_max_mem - nfsd_drc_mem_used) / size;
mem = fchan->maxreqs * size;
}
nfsd_drc_mem_used += mem;
spin_unlock(&nfsd_drc_lock);
if (fchan->maxreqs == 0)
return nfserr_jukebox;
fchan->maxresp_cached = size + NFSD_MIN_HDR_SEQ_SZ;
return 0;
}
/*
* fchan holds the client values on input, and the server values on output
* sv_max_mesg is the maximum payload plus one page for overhead.
*/
static int init_forechannel_attrs(struct svc_rqst *rqstp,
struct nfsd4_channel_attrs *session_fchan,
struct nfsd4_channel_attrs *fchan)
{
int status = 0;
__u32 maxcount = nfsd_serv->sv_max_mesg;
/* headerpadsz set to zero in encode routine */
/* Use the client's max request and max response size if possible */
if (fchan->maxreq_sz > maxcount)
fchan->maxreq_sz = maxcount;
session_fchan->maxreq_sz = fchan->maxreq_sz;
if (fchan->maxresp_sz > maxcount)
fchan->maxresp_sz = maxcount;
session_fchan->maxresp_sz = fchan->maxresp_sz;
/* Use the client's maxops if possible */
if (fchan->maxops > NFSD_MAX_OPS_PER_COMPOUND)
fchan->maxops = NFSD_MAX_OPS_PER_COMPOUND;
session_fchan->maxops = fchan->maxops;
/* FIXME: Error means no more DRC pages so the server should
* recover pages from existing sessions. For now fail session
* creation.
*/
status = set_forechannel_drc_size(fchan);
session_fchan->maxresp_cached = fchan->maxresp_cached;
session_fchan->maxreqs = fchan->maxreqs;
dprintk("%s status %d\n", __func__, status);
return status;
}
static void
free_session_slots(struct nfsd4_session *ses)
{
@ -641,65 +551,240 @@ static inline int slot_bytes(struct nfsd4_channel_attrs *ca)
return ca->maxresp_cached - NFSD_MIN_HDR_SEQ_SZ;
}
static int
alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp,
struct nfsd4_create_session *cses)
static int nfsd4_sanitize_slot_size(u32 size)
{
struct nfsd4_session *new, tmp;
struct nfsd4_slot *sp;
int idx, slotsize, cachesize, i;
int status;
size -= NFSD_MIN_HDR_SEQ_SZ; /* We don't cache the rpc header */
size = min_t(u32, size, NFSD_SLOT_CACHE_SIZE);
memset(&tmp, 0, sizeof(tmp));
return size;
}
/* FIXME: For now, we just accept the client back channel attributes. */
tmp.se_bchannel = cses->back_channel;
status = init_forechannel_attrs(rqstp, &tmp.se_fchannel,
&cses->fore_channel);
if (status)
goto out;
/*
* XXX: If we run out of reserved DRC memory we could (up to a point)
* re-negotiate active sessions and reduce their slot usage to make
* rooom for new connections. For now we just fail the create session.
*/
static int nfsd4_get_drc_mem(int slotsize, u32 num)
{
int avail;
BUILD_BUG_ON(NFSD_MAX_SLOTS_PER_SESSION * sizeof(struct nfsd4_slot)
+ sizeof(struct nfsd4_session) > PAGE_SIZE);
num = min_t(u32, num, NFSD_MAX_SLOTS_PER_SESSION);
status = nfserr_jukebox;
/* allocate struct nfsd4_session and slot table pointers in one piece */
slotsize = tmp.se_fchannel.maxreqs * sizeof(struct nfsd4_slot *);
new = kzalloc(sizeof(*new) + slotsize, GFP_KERNEL);
spin_lock(&nfsd_drc_lock);
avail = min_t(int, NFSD_MAX_MEM_PER_SESSION,
nfsd_drc_max_mem - nfsd_drc_mem_used);
num = min_t(int, num, avail / slotsize);
nfsd_drc_mem_used += num * slotsize;
spin_unlock(&nfsd_drc_lock);
return num;
}
static void nfsd4_put_drc_mem(int slotsize, int num)
{
spin_lock(&nfsd_drc_lock);
nfsd_drc_mem_used -= slotsize * num;
spin_unlock(&nfsd_drc_lock);
}
static struct nfsd4_session *alloc_session(int slotsize, int numslots)
{
struct nfsd4_session *new;
int mem, i;
BUILD_BUG_ON(NFSD_MAX_SLOTS_PER_SESSION * sizeof(struct nfsd4_slot *)
+ sizeof(struct nfsd4_session) > PAGE_SIZE);
mem = numslots * sizeof(struct nfsd4_slot *);
new = kzalloc(sizeof(*new) + mem, GFP_KERNEL);
if (!new)
goto out;
memcpy(new, &tmp, sizeof(*new));
return NULL;
/* allocate each struct nfsd4_slot and data cache in one piece */
cachesize = slot_bytes(&new->se_fchannel);
for (i = 0; i < new->se_fchannel.maxreqs; i++) {
sp = kzalloc(sizeof(*sp) + cachesize, GFP_KERNEL);
if (!sp)
for (i = 0; i < numslots; i++) {
mem = sizeof(struct nfsd4_slot) + slotsize;
new->se_slots[i] = kzalloc(mem, GFP_KERNEL);
if (!new->se_slots[i])
goto out_free;
new->se_slots[i] = sp;
}
return new;
out_free:
while (i--)
kfree(new->se_slots[i]);
kfree(new);
return NULL;
}
static void init_forechannel_attrs(struct nfsd4_channel_attrs *new, struct nfsd4_channel_attrs *req, int numslots, int slotsize)
{
u32 maxrpc = nfsd_serv->sv_max_mesg;
new->maxreqs = numslots;
new->maxresp_cached = slotsize + NFSD_MIN_HDR_SEQ_SZ;
new->maxreq_sz = min_t(u32, req->maxreq_sz, maxrpc);
new->maxresp_sz = min_t(u32, req->maxresp_sz, maxrpc);
new->maxops = min_t(u32, req->maxops, NFSD_MAX_OPS_PER_COMPOUND);
}
static void free_conn(struct nfsd4_conn *c)
{
svc_xprt_put(c->cn_xprt);
kfree(c);
}
static void nfsd4_conn_lost(struct svc_xpt_user *u)
{
struct nfsd4_conn *c = container_of(u, struct nfsd4_conn, cn_xpt_user);
struct nfs4_client *clp = c->cn_session->se_client;
spin_lock(&clp->cl_lock);
if (!list_empty(&c->cn_persession)) {
list_del(&c->cn_persession);
free_conn(c);
}
spin_unlock(&clp->cl_lock);
}
static struct nfsd4_conn *alloc_conn(struct svc_rqst *rqstp, u32 flags)
{
struct nfsd4_conn *conn;
conn = kmalloc(sizeof(struct nfsd4_conn), GFP_KERNEL);
if (!conn)
return NULL;
svc_xprt_get(rqstp->rq_xprt);
conn->cn_xprt = rqstp->rq_xprt;
conn->cn_flags = flags;
INIT_LIST_HEAD(&conn->cn_xpt_user.list);
return conn;
}
static void __nfsd4_hash_conn(struct nfsd4_conn *conn, struct nfsd4_session *ses)
{
conn->cn_session = ses;
list_add(&conn->cn_persession, &ses->se_conns);
}
static void nfsd4_hash_conn(struct nfsd4_conn *conn, struct nfsd4_session *ses)
{
struct nfs4_client *clp = ses->se_client;
spin_lock(&clp->cl_lock);
__nfsd4_hash_conn(conn, ses);
spin_unlock(&clp->cl_lock);
}
static void nfsd4_register_conn(struct nfsd4_conn *conn)
{
conn->cn_xpt_user.callback = nfsd4_conn_lost;
register_xpt_user(conn->cn_xprt, &conn->cn_xpt_user);
}
static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses)
{
struct nfsd4_conn *conn;
u32 flags = NFS4_CDFC4_FORE;
if (ses->se_flags & SESSION4_BACK_CHAN)
flags |= NFS4_CDFC4_BACK;
conn = alloc_conn(rqstp, flags);
if (!conn)
return nfserr_jukebox;
nfsd4_hash_conn(conn, ses);
nfsd4_register_conn(conn);
return nfs_ok;
}
static void nfsd4_del_conns(struct nfsd4_session *s)
{
struct nfs4_client *clp = s->se_client;
struct nfsd4_conn *c;
spin_lock(&clp->cl_lock);
while (!list_empty(&s->se_conns)) {
c = list_first_entry(&s->se_conns, struct nfsd4_conn, cn_persession);
list_del_init(&c->cn_persession);
spin_unlock(&clp->cl_lock);
unregister_xpt_user(c->cn_xprt, &c->cn_xpt_user);
free_conn(c);
spin_lock(&clp->cl_lock);
}
spin_unlock(&clp->cl_lock);
}
void free_session(struct kref *kref)
{
struct nfsd4_session *ses;
int mem;
ses = container_of(kref, struct nfsd4_session, se_ref);
nfsd4_del_conns(ses);
spin_lock(&nfsd_drc_lock);
mem = ses->se_fchannel.maxreqs * slot_bytes(&ses->se_fchannel);
nfsd_drc_mem_used -= mem;
spin_unlock(&nfsd_drc_lock);
free_session_slots(ses);
kfree(ses);
}
static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses)
{
struct nfsd4_session *new;
struct nfsd4_channel_attrs *fchan = &cses->fore_channel;
int numslots, slotsize;
int status;
int idx;
/*
* Note decreasing slot size below client's request may
* make it difficult for client to function correctly, whereas
* decreasing the number of slots will (just?) affect
* performance. When short on memory we therefore prefer to
* decrease number of slots instead of their size.
*/
slotsize = nfsd4_sanitize_slot_size(fchan->maxresp_cached);
numslots = nfsd4_get_drc_mem(slotsize, fchan->maxreqs);
new = alloc_session(slotsize, numslots);
if (!new) {
nfsd4_put_drc_mem(slotsize, fchan->maxreqs);
return NULL;
}
init_forechannel_attrs(&new->se_fchannel, fchan, numslots, slotsize);
new->se_client = clp;
gen_sessionid(new);
idx = hash_sessionid(&new->se_sessionid);
memcpy(clp->cl_sessionid.data, new->se_sessionid.data,
NFS4_MAX_SESSIONID_LEN);
INIT_LIST_HEAD(&new->se_conns);
new->se_cb_seq_nr = 1;
new->se_flags = cses->flags;
new->se_cb_prog = cses->callback_prog;
kref_init(&new->se_ref);
idx = hash_sessionid(&new->se_sessionid);
spin_lock(&client_lock);
list_add(&new->se_hash, &sessionid_hashtbl[idx]);
list_add(&new->se_perclnt, &clp->cl_sessions);
spin_unlock(&client_lock);
status = nfs_ok;
out:
return status;
out_free:
free_session_slots(new);
kfree(new);
goto out;
status = nfsd4_new_conn(rqstp, new);
/* whoops: benny points out, status is ignored! (err, or bogus) */
if (status) {
free_session(&new->se_ref);
return NULL;
}
if (!clp->cl_cb_session && (cses->flags & SESSION4_BACK_CHAN)) {
struct sockaddr *sa = svc_addr(rqstp);
clp->cl_cb_session = new;
clp->cl_cb_conn.cb_xprt = rqstp->rq_xprt;
svc_xprt_get(rqstp->rq_xprt);
rpc_copy_addr((struct sockaddr *)&clp->cl_cb_conn.cb_addr, sa);
clp->cl_cb_conn.cb_addrlen = svc_addr_len(sa);
nfsd4_probe_callback(clp);
}
return new;
}
/* caller must hold client_lock */
@ -731,21 +816,6 @@ unhash_session(struct nfsd4_session *ses)
list_del(&ses->se_perclnt);
}
void
free_session(struct kref *kref)
{
struct nfsd4_session *ses;
int mem;
ses = container_of(kref, struct nfsd4_session, se_ref);
spin_lock(&nfsd_drc_lock);
mem = ses->se_fchannel.maxreqs * slot_bytes(&ses->se_fchannel);
nfsd_drc_mem_used -= mem;
spin_unlock(&nfsd_drc_lock);
free_session_slots(ses);
kfree(ses);
}
/* must be called under the client_lock */
static inline void
renew_client_locked(struct nfs4_client *clp)
@ -812,6 +882,13 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name)
static inline void
free_client(struct nfs4_client *clp)
{
while (!list_empty(&clp->cl_sessions)) {
struct nfsd4_session *ses;
ses = list_entry(clp->cl_sessions.next, struct nfsd4_session,
se_perclnt);
list_del(&ses->se_perclnt);
nfsd4_put_session(ses);
}
if (clp->cl_cred.cr_group_info)
put_group_info(clp->cl_cred.cr_group_info);
kfree(clp->cl_principal);
@ -838,15 +915,12 @@ release_session_client(struct nfsd4_session *session)
static inline void
unhash_client_locked(struct nfs4_client *clp)
{
struct nfsd4_session *ses;
mark_client_expired(clp);
list_del(&clp->cl_lru);
while (!list_empty(&clp->cl_sessions)) {
struct nfsd4_session *ses;
ses = list_entry(clp->cl_sessions.next, struct nfsd4_session,
se_perclnt);
unhash_session(ses);
nfsd4_put_session(ses);
}
list_for_each_entry(ses, &clp->cl_sessions, se_perclnt)
list_del_init(&ses->se_hash);
}
static void
@ -875,7 +949,7 @@ expire_client(struct nfs4_client *clp)
sop = list_entry(clp->cl_openowners.next, struct nfs4_stateowner, so_perclient);
release_openowner(sop);
}
nfsd4_set_callback_client(clp, NULL);
nfsd4_shutdown_callback(clp);
if (clp->cl_cb_conn.cb_xprt)
svc_xprt_put(clp->cl_cb_conn.cb_xprt);
list_del(&clp->cl_idhash);
@ -960,6 +1034,8 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,
if (clp == NULL)
return NULL;
INIT_LIST_HEAD(&clp->cl_sessions);
princ = svc_gss_principal(rqstp);
if (princ) {
clp->cl_principal = kstrdup(princ, GFP_KERNEL);
@ -976,8 +1052,9 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,
INIT_LIST_HEAD(&clp->cl_strhash);
INIT_LIST_HEAD(&clp->cl_openowners);
INIT_LIST_HEAD(&clp->cl_delegations);
INIT_LIST_HEAD(&clp->cl_sessions);
INIT_LIST_HEAD(&clp->cl_lru);
spin_lock_init(&clp->cl_lock);
INIT_WORK(&clp->cl_cb_null.cb_work, nfsd4_do_callback_rpc);
clp->cl_time = get_seconds();
clear_bit(0, &clp->cl_cb_slot_busy);
rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table");
@ -986,7 +1063,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,
clp->cl_flavor = rqstp->rq_flavor;
copy_cred(&clp->cl_cred, &rqstp->rq_cred);
gen_confirm(clp);
clp->cl_cb_session = NULL;
return clp;
}
@ -1098,7 +1175,7 @@ find_unconfirmed_client_by_str(const char *dname, unsigned int hashval,
static void
gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, u32 scopeid)
{
struct nfs4_cb_conn *cb = &clp->cl_cb_conn;
struct nfs4_cb_conn *conn = &clp->cl_cb_conn;
unsigned short expected_family;
/* Currently, we only support tcp and tcp6 for the callback channel */
@ -1111,24 +1188,23 @@ gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, u32 scopeid)
else
goto out_err;
cb->cb_addrlen = rpc_uaddr2sockaddr(se->se_callback_addr_val,
conn->cb_addrlen = rpc_uaddr2sockaddr(se->se_callback_addr_val,
se->se_callback_addr_len,
(struct sockaddr *) &cb->cb_addr,
sizeof(cb->cb_addr));
(struct sockaddr *)&conn->cb_addr,
sizeof(conn->cb_addr));
if (!cb->cb_addrlen || cb->cb_addr.ss_family != expected_family)
if (!conn->cb_addrlen || conn->cb_addr.ss_family != expected_family)
goto out_err;
if (cb->cb_addr.ss_family == AF_INET6)
((struct sockaddr_in6 *) &cb->cb_addr)->sin6_scope_id = scopeid;
if (conn->cb_addr.ss_family == AF_INET6)
((struct sockaddr_in6 *)&conn->cb_addr)->sin6_scope_id = scopeid;
cb->cb_minorversion = 0;
cb->cb_prog = se->se_callback_prog;
cb->cb_ident = se->se_callback_ident;
conn->cb_prog = se->se_callback_prog;
conn->cb_ident = se->se_callback_ident;
return;
out_err:
cb->cb_addr.ss_family = AF_UNSPEC;
cb->cb_addrlen = 0;
conn->cb_addr.ss_family = AF_UNSPEC;
conn->cb_addrlen = 0;
dprintk(KERN_INFO "NFSD: this client (clientid %08x/%08x) "
"will not receive delegations\n",
clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id);
@ -1415,7 +1491,9 @@ nfsd4_create_session(struct svc_rqst *rqstp,
{
struct sockaddr *sa = svc_addr(rqstp);
struct nfs4_client *conf, *unconf;
struct nfsd4_session *new;
struct nfsd4_clid_slot *cs_slot = NULL;
bool confirm_me = false;
int status = 0;
nfs4_lock_state();
@ -1438,7 +1516,6 @@ nfsd4_create_session(struct svc_rqst *rqstp,
cs_slot->sl_seqid, cr_ses->seqid);
goto out;
}
cs_slot->sl_seqid++;
} else if (unconf) {
if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) ||
!rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) {
@ -1451,48 +1528,41 @@ nfsd4_create_session(struct svc_rqst *rqstp,
if (status) {
/* an unconfirmed replay returns misordered */
status = nfserr_seq_misordered;
goto out_cache;
goto out;
}
cs_slot->sl_seqid++; /* from 0 to 1 */
move_to_confirmed(unconf);
if (cr_ses->flags & SESSION4_BACK_CHAN) {
unconf->cl_cb_conn.cb_xprt = rqstp->rq_xprt;
svc_xprt_get(rqstp->rq_xprt);
rpc_copy_addr(
(struct sockaddr *)&unconf->cl_cb_conn.cb_addr,
sa);
unconf->cl_cb_conn.cb_addrlen = svc_addr_len(sa);
unconf->cl_cb_conn.cb_minorversion =
cstate->minorversion;
unconf->cl_cb_conn.cb_prog = cr_ses->callback_prog;
unconf->cl_cb_seq_nr = 1;
nfsd4_probe_callback(unconf, &unconf->cl_cb_conn);
}
confirm_me = true;
conf = unconf;
} else {
status = nfserr_stale_clientid;
goto out;
}
/*
* XXX: we should probably set this at creation time, and check
* for consistent minorversion use throughout:
*/
conf->cl_minorversion = 1;
/*
* We do not support RDMA or persistent sessions
*/
cr_ses->flags &= ~SESSION4_PERSIST;
cr_ses->flags &= ~SESSION4_RDMA;
status = alloc_init_session(rqstp, conf, cr_ses);
if (status)
status = nfserr_jukebox;
new = alloc_init_session(rqstp, conf, cr_ses);
if (!new)
goto out;
memcpy(cr_ses->sessionid.data, conf->cl_sessionid.data,
status = nfs_ok;
memcpy(cr_ses->sessionid.data, new->se_sessionid.data,
NFS4_MAX_SESSIONID_LEN);
cs_slot->sl_seqid++;
cr_ses->seqid = cs_slot->sl_seqid;
out_cache:
/* cache solo and embedded create sessions under the state lock */
nfsd4_cache_create_session(cr_ses, cs_slot, status);
if (confirm_me)
move_to_confirmed(conf);
out:
nfs4_unlock_state();
dprintk("%s returns %d\n", __func__, ntohl(status));
@ -1546,8 +1616,11 @@ nfsd4_destroy_session(struct svc_rqst *r,
nfs4_lock_state();
/* wait for callbacks */
nfsd4_set_callback_client(ses->se_client, NULL);
nfsd4_shutdown_callback(ses->se_client);
nfs4_unlock_state();
nfsd4_del_conns(ses);
nfsd4_put_session(ses);
status = nfs_ok;
out:
@ -1555,6 +1628,36 @@ nfsd4_destroy_session(struct svc_rqst *r,
return status;
}
static struct nfsd4_conn *__nfsd4_find_conn(struct svc_xprt *xpt, struct nfsd4_session *s)
{
struct nfsd4_conn *c;
list_for_each_entry(c, &s->se_conns, cn_persession) {
if (c->cn_xprt == xpt) {
return c;
}
}
return NULL;
}
static void nfsd4_sequence_check_conn(struct nfsd4_conn *new, struct nfsd4_session *ses)
{
struct nfs4_client *clp = ses->se_client;
struct nfsd4_conn *c;
spin_lock(&clp->cl_lock);
c = __nfsd4_find_conn(new->cn_xprt, ses);
if (c) {
spin_unlock(&clp->cl_lock);
free_conn(new);
return;
}
__nfsd4_hash_conn(new, ses);
spin_unlock(&clp->cl_lock);
nfsd4_register_conn(new);
return;
}
__be32
nfsd4_sequence(struct svc_rqst *rqstp,
struct nfsd4_compound_state *cstate,
@ -1563,11 +1666,20 @@ nfsd4_sequence(struct svc_rqst *rqstp,
struct nfsd4_compoundres *resp = rqstp->rq_resp;
struct nfsd4_session *session;
struct nfsd4_slot *slot;
struct nfsd4_conn *conn;
int status;
if (resp->opcnt != 1)
return nfserr_sequence_pos;
/*
* Will be either used or freed by nfsd4_sequence_check_conn
* below.
*/
conn = alloc_conn(rqstp, NFS4_CDFC4_FORE);
if (!conn)
return nfserr_jukebox;
spin_lock(&client_lock);
status = nfserr_badsession;
session = find_in_sessionid_hashtbl(&seq->sessionid);
@ -1599,6 +1711,9 @@ nfsd4_sequence(struct svc_rqst *rqstp,
if (status)
goto out;
nfsd4_sequence_check_conn(conn, session);
conn = NULL;
/* Success! bump slot seqid */
slot->sl_inuse = true;
slot->sl_seqid = seq->seqid;
@ -1613,6 +1728,7 @@ nfsd4_sequence(struct svc_rqst *rqstp,
nfsd4_get_session(cstate->session);
atomic_inc(&session->se_client->cl_refcount);
}
kfree(conn);
spin_unlock(&client_lock);
dprintk("%s: return %d\n", __func__, ntohl(status));
return status;
@ -1747,6 +1863,11 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
goto out;
gen_clid(new);
}
/*
* XXX: we should probably set this at creation time, and check
* for consistent minorversion use throughout:
*/
new->cl_minorversion = 0;
gen_callback(new, setclid, rpc_get_scope_id(sa));
add_to_unconfirmed(new, strhashval);
setclid->se_clientid.cl_boot = new->cl_clientid.cl_boot;
@ -1807,7 +1928,8 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
status = nfserr_clid_inuse;
else {
atomic_set(&conf->cl_cb_set, 0);
nfsd4_probe_callback(conf, &unconf->cl_cb_conn);
nfsd4_change_callback(conf, &unconf->cl_cb_conn);
nfsd4_probe_callback(conf);
expire_client(unconf);
status = nfs_ok;
@ -1841,7 +1963,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
}
move_to_confirmed(unconf);
conf = unconf;
nfsd4_probe_callback(conf, &conf->cl_cb_conn);
nfsd4_probe_callback(conf);
status = nfs_ok;
}
} else if ((!conf || (conf && !same_verf(&conf->cl_confirm, &confirm)))
@ -2944,7 +3066,11 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
if (STALE_STATEID(stateid))
goto out;
status = nfserr_bad_stateid;
/*
* We assume that any stateid that has the current boot time,
* but that we can't find, is expired:
*/
status = nfserr_expired;
if (is_delegation_stateid(stateid)) {
dp = find_delegation_stateid(ino, stateid);
if (!dp)
@ -2964,6 +3090,7 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
stp = find_stateid(stateid, flags);
if (!stp)
goto out;
status = nfserr_bad_stateid;
if (nfs4_check_fh(current_fh, stp))
goto out;
if (!stp->st_stateowner->so_confirmed)
@ -3038,8 +3165,9 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
* a replayed close:
*/
sop = search_close_lru(stateid->si_stateownerid, flags);
/* It's not stale; let's assume it's expired: */
if (sop == NULL)
return nfserr_bad_stateid;
return nfserr_expired;
*sopp = sop;
goto check_replay;
}
@ -3304,6 +3432,7 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfserr_bad_stateid;
if (!is_delegation_stateid(stateid))
goto out;
status = nfserr_expired;
dp = find_delegation_stateid(inode, stateid);
if (!dp)
goto out;

View file

@ -1805,19 +1805,23 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
goto out_nfserr;
}
}
if ((buflen -= 16) < 0)
goto out_resource;
if (unlikely(bmval2)) {
if (bmval2) {
if ((buflen -= 16) < 0)
goto out_resource;
WRITE32(3);
WRITE32(bmval0);
WRITE32(bmval1);
WRITE32(bmval2);
} else if (likely(bmval1)) {
} else if (bmval1) {
if ((buflen -= 12) < 0)
goto out_resource;
WRITE32(2);
WRITE32(bmval0);
WRITE32(bmval1);
} else {
if ((buflen -= 8) < 0)
goto out_resource;
WRITE32(1);
WRITE32(bmval0);
}
@ -1828,15 +1832,17 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
u32 word1 = nfsd_suppattrs1(minorversion);
u32 word2 = nfsd_suppattrs2(minorversion);
if ((buflen -= 12) < 0)
goto out_resource;
if (!aclsupport)
word0 &= ~FATTR4_WORD0_ACL;
if (!word2) {
if ((buflen -= 12) < 0)
goto out_resource;
WRITE32(2);
WRITE32(word0);
WRITE32(word1);
} else {
if ((buflen -= 16) < 0)
goto out_resource;
WRITE32(3);
WRITE32(word0);
WRITE32(word1);

View file

@ -22,6 +22,7 @@
*/
enum {
NFSD_Root = 1,
#ifdef CONFIG_NFSD_DEPRECATED
NFSD_Svc,
NFSD_Add,
NFSD_Del,
@ -29,6 +30,7 @@ enum {
NFSD_Unexport,
NFSD_Getfd,
NFSD_Getfs,
#endif
NFSD_List,
NFSD_Export_features,
NFSD_Fh,
@ -54,6 +56,7 @@ enum {
/*
* write() for these nodes.
*/
#ifdef CONFIG_NFSD_DEPRECATED
static ssize_t write_svc(struct file *file, char *buf, size_t size);
static ssize_t write_add(struct file *file, char *buf, size_t size);
static ssize_t write_del(struct file *file, char *buf, size_t size);
@ -61,6 +64,7 @@ static ssize_t write_export(struct file *file, char *buf, size_t size);
static ssize_t write_unexport(struct file *file, char *buf, size_t size);
static ssize_t write_getfd(struct file *file, char *buf, size_t size);
static ssize_t write_getfs(struct file *file, char *buf, size_t size);
#endif
static ssize_t write_filehandle(struct file *file, char *buf, size_t size);
static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size);
static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size);
@ -76,6 +80,7 @@ static ssize_t write_recoverydir(struct file *file, char *buf, size_t size);
#endif
static ssize_t (*write_op[])(struct file *, char *, size_t) = {
#ifdef CONFIG_NFSD_DEPRECATED
[NFSD_Svc] = write_svc,
[NFSD_Add] = write_add,
[NFSD_Del] = write_del,
@ -83,6 +88,7 @@ static ssize_t (*write_op[])(struct file *, char *, size_t) = {
[NFSD_Unexport] = write_unexport,
[NFSD_Getfd] = write_getfd,
[NFSD_Getfs] = write_getfs,
#endif
[NFSD_Fh] = write_filehandle,
[NFSD_FO_UnlockIP] = write_unlock_ip,
[NFSD_FO_UnlockFS] = write_unlock_fs,
@ -121,6 +127,14 @@ static ssize_t nfsctl_transaction_write(struct file *file, const char __user *bu
static ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos)
{
static int warned;
if (file->f_dentry->d_name.name[0] == '.' && !warned) {
printk(KERN_INFO
"Warning: \"%s\" uses deprecated NFSD interface: %s."
" This will be removed in 2.6.40\n",
current->comm, file->f_dentry->d_name.name);
warned = 1;
}
if (! file->private_data) {
/* An attempt to read a transaction file without writing
* causes a 0-byte write so that the file can return
@ -187,6 +201,7 @@ static const struct file_operations pool_stats_operations = {
* payload - write methods
*/
#ifdef CONFIG_NFSD_DEPRECATED
/**
* write_svc - Start kernel's NFSD server
*
@ -402,7 +417,7 @@ static ssize_t write_getfs(struct file *file, char *buf, size_t size)
ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &in6);
clp = auth_unix_lookup(&in6);
clp = auth_unix_lookup(&init_net, &in6);
if (!clp)
err = -EPERM;
else {
@ -465,7 +480,7 @@ static ssize_t write_getfd(struct file *file, char *buf, size_t size)
ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &in6);
clp = auth_unix_lookup(&in6);
clp = auth_unix_lookup(&init_net, &in6);
if (!clp)
err = -EPERM;
else {
@ -482,6 +497,7 @@ static ssize_t write_getfd(struct file *file, char *buf, size_t size)
out:
return err;
}
#endif /* CONFIG_NFSD_DEPRECATED */
/**
* write_unlock_ip - Release all locks used by a client
@ -1000,12 +1016,12 @@ static ssize_t __write_ports_addxprt(char *buf)
if (err != 0)
return err;
err = svc_create_xprt(nfsd_serv, transport,
err = svc_create_xprt(nfsd_serv, transport, &init_net,
PF_INET, port, SVC_SOCK_ANONYMOUS);
if (err < 0)
goto out_err;
err = svc_create_xprt(nfsd_serv, transport,
err = svc_create_xprt(nfsd_serv, transport, &init_net,
PF_INET6, port, SVC_SOCK_ANONYMOUS);
if (err < 0 && err != -EAFNOSUPPORT)
goto out_close;
@ -1356,6 +1372,7 @@ static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
{
static struct tree_descr nfsd_files[] = {
#ifdef CONFIG_NFSD_DEPRECATED
[NFSD_Svc] = {".svc", &transaction_ops, S_IWUSR},
[NFSD_Add] = {".add", &transaction_ops, S_IWUSR},
[NFSD_Del] = {".del", &transaction_ops, S_IWUSR},
@ -1363,6 +1380,7 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
[NFSD_Unexport] = {".unexport", &transaction_ops, S_IWUSR},
[NFSD_Getfd] = {".getfd", &transaction_ops, S_IWUSR|S_IRUSR},
[NFSD_Getfs] = {".getfs", &transaction_ops, S_IWUSR|S_IRUSR},
#endif
[NFSD_List] = {"exports", &exports_operations, S_IRUGO},
[NFSD_Export_features] = {"export_features",
&export_features_operations, S_IRUGO},

View file

@ -249,7 +249,7 @@ extern time_t nfsd4_grace;
#define COMPOUND_SLACK_SPACE 140 /* OP_GETFH */
#define COMPOUND_ERR_SLACK_SPACE 12 /* OP_SETATTR */
#define NFSD_LAUNDROMAT_MINTIMEOUT 10 /* seconds */
#define NFSD_LAUNDROMAT_MINTIMEOUT 1 /* seconds */
/*
* The following attributes are currently not supported by the NFSv4 server:

View file

@ -16,6 +16,7 @@
#include <linux/lockd/bind.h>
#include <linux/nfsacl.h>
#include <linux/seq_file.h>
#include <net/net_namespace.h>
#include "nfsd.h"
#include "cache.h"
#include "vfs.h"
@ -186,12 +187,12 @@ static int nfsd_init_socks(int port)
if (!list_empty(&nfsd_serv->sv_permsocks))
return 0;
error = svc_create_xprt(nfsd_serv, "udp", PF_INET, port,
error = svc_create_xprt(nfsd_serv, "udp", &init_net, PF_INET, port,
SVC_SOCK_DEFAULTS);
if (error < 0)
return error;
error = svc_create_xprt(nfsd_serv, "tcp", PF_INET, port,
error = svc_create_xprt(nfsd_serv, "tcp", &init_net, PF_INET, port,
SVC_SOCK_DEFAULTS);
if (error < 0)
return error;

View file

@ -35,6 +35,7 @@
#ifndef _NFSD4_STATE_H
#define _NFSD4_STATE_H
#include <linux/sunrpc/svc_xprt.h>
#include <linux/nfsd/nfsfh.h>
#include "nfsfh.h"
@ -64,19 +65,12 @@ typedef struct {
(s)->si_fileid, \
(s)->si_generation
struct nfsd4_cb_sequence {
/* args/res */
u32 cbs_minorversion;
struct nfs4_client *cbs_clp;
};
struct nfs4_rpc_args {
void *args_op;
struct nfsd4_cb_sequence args_seq;
};
struct nfsd4_callback {
struct nfs4_rpc_args cb_args;
void *cb_op;
struct nfs4_client *cb_clp;
u32 cb_minorversion;
struct rpc_message cb_msg;
const struct rpc_call_ops *cb_ops;
struct work_struct cb_work;
};
@ -91,7 +85,6 @@ struct nfs4_delegation {
u32 dl_type;
time_t dl_time;
/* For recall: */
u32 dl_ident;
stateid_t dl_stateid;
struct knfsd_fh dl_fh;
int dl_retries;
@ -103,8 +96,8 @@ struct nfs4_cb_conn {
/* SETCLIENTID info */
struct sockaddr_storage cb_addr;
size_t cb_addrlen;
u32 cb_prog;
u32 cb_minorversion;
u32 cb_prog; /* used only in 4.0 case;
per-session otherwise */
u32 cb_ident; /* minorversion 0 only */
struct svc_xprt *cb_xprt; /* minorversion 1 only */
};
@ -160,6 +153,15 @@ struct nfsd4_clid_slot {
struct nfsd4_create_session sl_cr_ses;
};
struct nfsd4_conn {
struct list_head cn_persession;
struct svc_xprt *cn_xprt;
struct svc_xpt_user cn_xpt_user;
struct nfsd4_session *cn_session;
/* CDFC4_FORE, CDFC4_BACK: */
unsigned char cn_flags;
};
struct nfsd4_session {
struct kref se_ref;
struct list_head se_hash; /* hash by sessionid */
@ -169,6 +171,9 @@ struct nfsd4_session {
struct nfs4_sessionid se_sessionid;
struct nfsd4_channel_attrs se_fchannel;
struct nfsd4_channel_attrs se_bchannel;
struct list_head se_conns;
u32 se_cb_prog;
u32 se_cb_seq_nr;
struct nfsd4_slot *se_slots[]; /* forward channel slots */
};
@ -221,24 +226,32 @@ struct nfs4_client {
clientid_t cl_clientid; /* generated by server */
nfs4_verifier cl_confirm; /* generated by server */
u32 cl_firststate; /* recovery dir creation */
u32 cl_minorversion;
/* for v4.0 and v4.1 callbacks: */
struct nfs4_cb_conn cl_cb_conn;
#define NFSD4_CLIENT_CB_UPDATE 1
#define NFSD4_CLIENT_KILL 2
unsigned long cl_cb_flags;
struct rpc_clnt *cl_cb_client;
u32 cl_cb_ident;
atomic_t cl_cb_set;
struct nfsd4_callback cl_cb_null;
struct nfsd4_session *cl_cb_session;
/* for all client information that callback code might need: */
spinlock_t cl_lock;
/* for nfs41 */
struct list_head cl_sessions;
struct nfsd4_clid_slot cl_cs_slot; /* create_session slot */
u32 cl_exchange_flags;
struct nfs4_sessionid cl_sessionid;
/* number of rpc's in progress over an associated session: */
atomic_t cl_refcount;
/* for nfs41 callbacks */
/* We currently support a single back channel with a single slot */
unsigned long cl_cb_slot_busy;
u32 cl_cb_seq_nr;
struct rpc_wait_queue cl_cb_waitq; /* backchannel callers may */
/* wait here for slots */
};
@ -440,12 +453,13 @@ extern int nfs4_in_grace(void);
extern __be32 nfs4_check_open_reclaim(clientid_t *clid);
extern void nfs4_free_stateowner(struct kref *kref);
extern int set_callback_cred(void);
extern void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *);
extern void nfsd4_probe_callback(struct nfs4_client *clp);
extern void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *);
extern void nfsd4_do_callback_rpc(struct work_struct *);
extern void nfsd4_cb_recall(struct nfs4_delegation *dp);
extern int nfsd4_create_callback_queue(void);
extern void nfsd4_destroy_callback_queue(void);
extern void nfsd4_set_callback_client(struct nfs4_client *, struct rpc_clnt *);
extern void nfsd4_shutdown_callback(struct nfs4_client *);
extern void nfs4_put_delegation(struct nfs4_delegation *dp);
extern __be32 nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname);
extern void nfsd4_init_recdir(char *recdir_name);

View file

@ -229,6 +229,8 @@ enum {
extern int sock_wake_async(struct socket *sk, int how, int band);
extern int sock_register(const struct net_proto_family *fam);
extern void sock_unregister(int family);
extern int __sock_create(struct net *net, int family, int type, int proto,
struct socket **res, int kern);
extern int sock_create(int family, int type, int proto,
struct socket **res);
extern int sock_create_kern(int family, int type, int proto,

View file

@ -63,6 +63,9 @@
#define NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL 0x10000
#define NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED 0x20000
#define NFS4_CDFC4_FORE 0x1
#define NFS4_CDFC4_BACK 0x2
#define NFS4_SET_TO_SERVER_TIME 0
#define NFS4_SET_TO_CLIENT_TIME 1

View file

@ -122,8 +122,8 @@ extern const struct rpc_authops authnull_ops;
int __init rpc_init_authunix(void);
int __init rpc_init_generic_auth(void);
int __init rpcauth_init_module(void);
void __exit rpcauth_remove_module(void);
void __exit rpc_destroy_generic_auth(void);
void rpcauth_remove_module(void);
void rpc_destroy_generic_auth(void);
void rpc_destroy_authunix(void);
struct rpc_cred * rpc_lookup_cred(void);

View file

@ -125,12 +125,15 @@ struct cache_detail {
*/
struct cache_req {
struct cache_deferred_req *(*defer)(struct cache_req *req);
int thread_wait; /* How long (jiffies) we can block the
* current thread to wait for updates.
*/
};
/* this must be embedded in a deferred_request that is being
* delayed awaiting cache-fill
*/
struct cache_deferred_req {
struct list_head hash; /* on hash chain */
struct hlist_node hash; /* on hash chain */
struct list_head recent; /* on fifo */
struct cache_head *item; /* cache item we wait on */
void *owner; /* we might need to discard all defered requests
@ -194,7 +197,9 @@ extern void cache_purge(struct cache_detail *detail);
#define NEVER (0x7FFFFFFF)
extern void __init cache_initialize(void);
extern int cache_register(struct cache_detail *cd);
extern int cache_register_net(struct cache_detail *cd, struct net *net);
extern void cache_unregister(struct cache_detail *cd);
extern void cache_unregister_net(struct cache_detail *cd, struct net *net);
extern int sunrpc_cache_register_pipefs(struct dentry *parent, const char *,
mode_t, struct cache_detail *);
@ -218,14 +223,42 @@ static inline int get_int(char **bpp, int *anint)
return 0;
}
/*
* timestamps kept in the cache are expressed in seconds
* since boot. This is the best for measuring differences in
* real time.
*/
static inline time_t seconds_since_boot(void)
{
struct timespec boot;
getboottime(&boot);
return get_seconds() - boot.tv_sec;
}
static inline time_t convert_to_wallclock(time_t sinceboot)
{
struct timespec boot;
getboottime(&boot);
return boot.tv_sec + sinceboot;
}
static inline time_t get_expiry(char **bpp)
{
int rv;
struct timespec boot;
if (get_int(bpp, &rv))
return 0;
if (rv < 0)
return 0;
return rv;
getboottime(&boot);
return rv - boot.tv_sec;
}
static inline void sunrpc_invalidate(struct cache_head *h,
struct cache_detail *detail)
{
h->expiry_time = seconds_since_boot() - 1;
detail->nextcheck = seconds_since_boot();
}
#endif /* _LINUX_SUNRPC_CACHE_H_ */

View file

@ -102,6 +102,7 @@ struct rpc_procinfo {
#ifdef __KERNEL__
struct rpc_create_args {
struct net *net;
int protocol;
struct sockaddr *address;
size_t addrsize;

View file

@ -1,55 +0,0 @@
/*
* linux/include/linux/sunrpc/gss_spkm3.h
*
* Copyright (c) 2000 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@umich.edu>
*/
#include <linux/sunrpc/auth_gss.h>
#include <linux/sunrpc/gss_err.h>
#include <linux/sunrpc/gss_asn1.h>
struct spkm3_ctx {
struct xdr_netobj ctx_id; /* per message context id */
int endtime; /* endtime of the context */
struct xdr_netobj mech_used;
unsigned int ret_flags ;
struct xdr_netobj conf_alg;
struct xdr_netobj derived_conf_key;
struct xdr_netobj intg_alg;
struct xdr_netobj derived_integ_key;
};
/* OIDs declarations for K-ALG, I-ALG, C-ALG, and OWF-ALG */
extern const struct xdr_netobj hmac_md5_oid;
extern const struct xdr_netobj cast5_cbc_oid;
/* SPKM InnerContext Token types */
#define SPKM_ERROR_TOK 3
#define SPKM_MIC_TOK 4
#define SPKM_WRAP_TOK 5
#define SPKM_DEL_TOK 6
u32 spkm3_make_token(struct spkm3_ctx *ctx, struct xdr_buf * text, struct xdr_netobj * token, int toktype);
u32 spkm3_read_token(struct spkm3_ctx *ctx, struct xdr_netobj *read_token, struct xdr_buf *message_buffer, int toktype);
#define CKSUMTYPE_RSA_MD5 0x0007
#define CKSUMTYPE_HMAC_MD5 0x0008
s32 make_spkm3_checksum(s32 cksumtype, struct xdr_netobj *key, char *header,
unsigned int hdrlen, struct xdr_buf *body,
unsigned int body_offset, struct xdr_netobj *cksum);
void asn1_bitstring_len(struct xdr_netobj *in, int *enclen, int *zerobits);
int decode_asn1_bitstring(struct xdr_netobj *out, char *in, int enclen,
int explen);
void spkm3_mic_header(unsigned char **hdrbuf, unsigned int *hdrlen,
unsigned char *ctxhdr, int elen, int zbit);
void spkm3_make_mic_token(unsigned char **tokp, int toklen,
struct xdr_netobj *mic_hdr,
struct xdr_netobj *md5cksum, int md5elen, int md5zbit);
u32 spkm3_verify_mic_token(unsigned char **tokp, int *mic_hdrlen,
unsigned char **cksum);

View file

@ -38,8 +38,21 @@ struct svc_stat {
rpcbadclnt;
};
void rpc_proc_init(void);
void rpc_proc_exit(void);
struct net;
#ifdef CONFIG_PROC_FS
int rpc_proc_init(struct net *);
void rpc_proc_exit(struct net *);
#else
static inline int rpc_proc_init(struct net *net)
{
return 0;
}
static inline void rpc_proc_exit(struct net *net)
{
}
#endif
#ifdef MODULE
void rpc_modcount(struct inode *, int);
#endif
@ -54,9 +67,6 @@ void svc_proc_unregister(const char *);
void svc_seq_show(struct seq_file *,
const struct svc_stat *);
extern struct proc_dir_entry *proc_net_rpc;
#else
static inline struct proc_dir_entry *rpc_proc_register(struct rpc_stat *s) { return NULL; }
@ -69,9 +79,6 @@ static inline void svc_proc_unregister(const char *p) {}
static inline void svc_seq_show(struct seq_file *seq,
const struct svc_stat *st) {}
#define proc_net_rpc NULL
#endif
#endif /* _LINUX_SUNRPC_STATS_H */

View file

@ -12,6 +12,7 @@
struct svc_xprt_ops {
struct svc_xprt *(*xpo_create)(struct svc_serv *,
struct net *net,
struct sockaddr *, int,
int);
struct svc_xprt *(*xpo_accept)(struct svc_xprt *);
@ -32,6 +33,16 @@ struct svc_xprt_class {
u32 xcl_max_payload;
};
/*
* This is embedded in an object that wants a callback before deleting
* an xprt; intended for use by NFSv4.1, which needs to know when a
* client's tcp connection (and hence possibly a backchannel) goes away.
*/
struct svc_xpt_user {
struct list_head list;
void (*callback)(struct svc_xpt_user *);
};
struct svc_xprt {
struct svc_xprt_class *xpt_class;
struct svc_xprt_ops *xpt_ops;
@ -66,14 +77,31 @@ struct svc_xprt {
struct sockaddr_storage xpt_remote; /* remote peer's address */
size_t xpt_remotelen; /* length of address */
struct rpc_wait_queue xpt_bc_pending; /* backchannel wait queue */
struct list_head xpt_users; /* callbacks on free */
struct net *xpt_net;
};
static inline void register_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u)
{
spin_lock(&xpt->xpt_lock);
list_add(&u->list, &xpt->xpt_users);
spin_unlock(&xpt->xpt_lock);
}
static inline void unregister_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u)
{
spin_lock(&xpt->xpt_lock);
list_del_init(&u->list);
spin_unlock(&xpt->xpt_lock);
}
int svc_reg_xprt_class(struct svc_xprt_class *);
void svc_unreg_xprt_class(struct svc_xprt_class *);
void svc_xprt_init(struct svc_xprt_class *, struct svc_xprt *,
struct svc_serv *);
int svc_create_xprt(struct svc_serv *, const char *, const int,
const unsigned short, int);
int svc_create_xprt(struct svc_serv *, const char *, struct net *,
const int, const unsigned short, int);
void svc_xprt_enqueue(struct svc_xprt *xprt);
void svc_xprt_received(struct svc_xprt *);
void svc_xprt_put(struct svc_xprt *xprt);

View file

@ -108,10 +108,15 @@ struct auth_ops {
#define SVC_NEGATIVE 4
#define SVC_OK 5
#define SVC_DROP 6
#define SVC_DENIED 7
#define SVC_PENDING 8
#define SVC_COMPLETE 9
#define SVC_CLOSE 7 /* Like SVC_DROP, but request is definitely
* lost so if there is a tcp connection, it
* should be closed
*/
#define SVC_DENIED 8
#define SVC_PENDING 9
#define SVC_COMPLETE 10
struct svc_xprt;
extern int svc_authenticate(struct svc_rqst *rqstp, __be32 *authp);
extern int svc_authorise(struct svc_rqst *rqstp);
@ -121,13 +126,13 @@ extern void svc_auth_unregister(rpc_authflavor_t flavor);
extern struct auth_domain *unix_domain_find(char *name);
extern void auth_domain_put(struct auth_domain *item);
extern int auth_unix_add_addr(struct in6_addr *addr, struct auth_domain *dom);
extern int auth_unix_add_addr(struct net *net, struct in6_addr *addr, struct auth_domain *dom);
extern struct auth_domain *auth_domain_lookup(char *name, struct auth_domain *new);
extern struct auth_domain *auth_domain_find(char *name);
extern struct auth_domain *auth_unix_lookup(struct in6_addr *addr);
extern struct auth_domain *auth_unix_lookup(struct net *net, struct in6_addr *addr);
extern int auth_unix_forget_old(struct auth_domain *dom);
extern void svcauth_unix_purge(void);
extern void svcauth_unix_info_release(void *);
extern void svcauth_unix_info_release(struct svc_xprt *xpt);
extern int svcauth_unix_set_client(struct svc_rqst *rqstp);
static inline unsigned long hash_str(char *name, int bits)

View file

@ -224,6 +224,7 @@ struct rpc_xprt {
bklog_u; /* backlog queue utilization */
} stat;
struct net *xprt_net;
const char *address_strings[RPC_DISPLAY_MAX];
};
@ -249,6 +250,7 @@ static inline int bc_prealloc(struct rpc_rqst *req)
struct xprt_create {
int ident; /* XPRT_TRANSPORT identifier */
struct net * net;
struct sockaddr * srcaddr; /* optional local address */
struct sockaddr * dstaddr; /* remote peer address */
size_t addrlen;
@ -280,6 +282,8 @@ void xprt_release_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task);
void xprt_release(struct rpc_task *task);
struct rpc_xprt * xprt_get(struct rpc_xprt *xprt);
void xprt_put(struct rpc_xprt *xprt);
struct rpc_xprt * xprt_alloc(struct net *net, int size, int max_req);
void xprt_free(struct rpc_xprt *);
static inline __be32 *xprt_skip_transport_header(struct rpc_xprt *xprt, __be32 *p)
{

View file

@ -1145,7 +1145,7 @@ int sock_wake_async(struct socket *sock, int how, int band)
}
EXPORT_SYMBOL(sock_wake_async);
static int __sock_create(struct net *net, int family, int type, int protocol,
int __sock_create(struct net *net, int family, int type, int protocol,
struct socket **res, int kern)
{
int err;
@ -1257,6 +1257,7 @@ static int __sock_create(struct net *net, int family, int type, int protocol,
rcu_read_unlock();
goto out_sock_release;
}
EXPORT_SYMBOL(__sock_create);
int sock_create(int family, int type, int protocol, struct socket **res)
{

View file

@ -36,22 +36,3 @@ config RPCSEC_GSS_KRB5
Kerberos support should be installed.
If unsure, say Y.
config RPCSEC_GSS_SPKM3
tristate "Secure RPC: SPKM3 mechanism (EXPERIMENTAL)"
depends on SUNRPC && EXPERIMENTAL
select SUNRPC_GSS
select CRYPTO
select CRYPTO_MD5
select CRYPTO_DES
select CRYPTO_CAST5
select CRYPTO_CBC
help
Choose Y here to enable Secure RPC using the SPKM3 public key
GSS-API mechanism (RFC 2025).
Secure RPC calls with SPKM3 require an auxiliary userspace
daemon which may be found in the Linux nfs-utils package
available from http://linux-nfs.org/.
If unsure, say N.

View file

@ -658,7 +658,7 @@ int __init rpcauth_init_module(void)
return err;
}
void __exit rpcauth_remove_module(void)
void rpcauth_remove_module(void)
{
rpc_destroy_authunix();
rpc_destroy_generic_auth();

View file

@ -158,7 +158,7 @@ int __init rpc_init_generic_auth(void)
return rpcauth_init_credcache(&generic_auth);
}
void __exit rpc_destroy_generic_auth(void)
void rpc_destroy_generic_auth(void)
{
rpcauth_destroy_credcache(&generic_auth);
}

View file

@ -11,8 +11,3 @@ obj-$(CONFIG_RPCSEC_GSS_KRB5) += rpcsec_gss_krb5.o
rpcsec_gss_krb5-objs := gss_krb5_mech.o gss_krb5_seal.o gss_krb5_unseal.o \
gss_krb5_seqnum.o gss_krb5_wrap.o gss_krb5_crypto.o gss_krb5_keys.o
obj-$(CONFIG_RPCSEC_GSS_SPKM3) += rpcsec_gss_spkm3.o
rpcsec_gss_spkm3-objs := gss_spkm3_mech.o gss_spkm3_seal.o gss_spkm3_unseal.o \
gss_spkm3_token.o

View file

@ -1,247 +0,0 @@
/*
* linux/net/sunrpc/gss_spkm3_mech.c
*
* Copyright (c) 2003 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@umich.edu>
* J. Bruce Fields <bfields@umich.edu>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <linux/err.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/sunrpc/auth.h>
#include <linux/in.h>
#include <linux/sunrpc/svcauth_gss.h>
#include <linux/sunrpc/gss_spkm3.h>
#include <linux/sunrpc/xdr.h>
#include <linux/crypto.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
static const void *
simple_get_bytes(const void *p, const void *end, void *res, int len)
{
const void *q = (const void *)((const char *)p + len);
if (unlikely(q > end || q < p))
return ERR_PTR(-EFAULT);
memcpy(res, p, len);
return q;
}
static const void *
simple_get_netobj(const void *p, const void *end, struct xdr_netobj *res)
{
const void *q;
unsigned int len;
p = simple_get_bytes(p, end, &len, sizeof(len));
if (IS_ERR(p))
return p;
res->len = len;
if (len == 0) {
res->data = NULL;
return p;
}
q = (const void *)((const char *)p + len);
if (unlikely(q > end || q < p))
return ERR_PTR(-EFAULT);
res->data = kmemdup(p, len, GFP_NOFS);
if (unlikely(res->data == NULL))
return ERR_PTR(-ENOMEM);
return q;
}
static int
gss_import_sec_context_spkm3(const void *p, size_t len,
struct gss_ctx *ctx_id,
gfp_t gfp_mask)
{
const void *end = (const void *)((const char *)p + len);
struct spkm3_ctx *ctx;
int version;
if (!(ctx = kzalloc(sizeof(*ctx), gfp_mask)))
goto out_err;
p = simple_get_bytes(p, end, &version, sizeof(version));
if (IS_ERR(p))
goto out_err_free_ctx;
if (version != 1) {
dprintk("RPC: unknown spkm3 token format: "
"obsolete nfs-utils?\n");
p = ERR_PTR(-EINVAL);
goto out_err_free_ctx;
}
p = simple_get_netobj(p, end, &ctx->ctx_id);
if (IS_ERR(p))
goto out_err_free_ctx;
p = simple_get_bytes(p, end, &ctx->endtime, sizeof(ctx->endtime));
if (IS_ERR(p))
goto out_err_free_ctx_id;
p = simple_get_netobj(p, end, &ctx->mech_used);
if (IS_ERR(p))
goto out_err_free_ctx_id;
p = simple_get_bytes(p, end, &ctx->ret_flags, sizeof(ctx->ret_flags));
if (IS_ERR(p))
goto out_err_free_mech;
p = simple_get_netobj(p, end, &ctx->conf_alg);
if (IS_ERR(p))
goto out_err_free_mech;
p = simple_get_netobj(p, end, &ctx->derived_conf_key);
if (IS_ERR(p))
goto out_err_free_conf_alg;
p = simple_get_netobj(p, end, &ctx->intg_alg);
if (IS_ERR(p))
goto out_err_free_conf_key;
p = simple_get_netobj(p, end, &ctx->derived_integ_key);
if (IS_ERR(p))
goto out_err_free_intg_alg;
if (p != end) {
p = ERR_PTR(-EFAULT);
goto out_err_free_intg_key;
}
ctx_id->internal_ctx_id = ctx;
dprintk("RPC: Successfully imported new spkm context.\n");
return 0;
out_err_free_intg_key:
kfree(ctx->derived_integ_key.data);
out_err_free_intg_alg:
kfree(ctx->intg_alg.data);
out_err_free_conf_key:
kfree(ctx->derived_conf_key.data);
out_err_free_conf_alg:
kfree(ctx->conf_alg.data);
out_err_free_mech:
kfree(ctx->mech_used.data);
out_err_free_ctx_id:
kfree(ctx->ctx_id.data);
out_err_free_ctx:
kfree(ctx);
out_err:
return PTR_ERR(p);
}
static void
gss_delete_sec_context_spkm3(void *internal_ctx)
{
struct spkm3_ctx *sctx = internal_ctx;
kfree(sctx->derived_integ_key.data);
kfree(sctx->intg_alg.data);
kfree(sctx->derived_conf_key.data);
kfree(sctx->conf_alg.data);
kfree(sctx->mech_used.data);
kfree(sctx->ctx_id.data);
kfree(sctx);
}
static u32
gss_verify_mic_spkm3(struct gss_ctx *ctx,
struct xdr_buf *signbuf,
struct xdr_netobj *checksum)
{
u32 maj_stat = 0;
struct spkm3_ctx *sctx = ctx->internal_ctx_id;
maj_stat = spkm3_read_token(sctx, checksum, signbuf, SPKM_MIC_TOK);
dprintk("RPC: gss_verify_mic_spkm3 returning %d\n", maj_stat);
return maj_stat;
}
static u32
gss_get_mic_spkm3(struct gss_ctx *ctx,
struct xdr_buf *message_buffer,
struct xdr_netobj *message_token)
{
u32 err = 0;
struct spkm3_ctx *sctx = ctx->internal_ctx_id;
err = spkm3_make_token(sctx, message_buffer,
message_token, SPKM_MIC_TOK);
dprintk("RPC: gss_get_mic_spkm3 returning %d\n", err);
return err;
}
static const struct gss_api_ops gss_spkm3_ops = {
.gss_import_sec_context = gss_import_sec_context_spkm3,
.gss_get_mic = gss_get_mic_spkm3,
.gss_verify_mic = gss_verify_mic_spkm3,
.gss_delete_sec_context = gss_delete_sec_context_spkm3,
};
static struct pf_desc gss_spkm3_pfs[] = {
{RPC_AUTH_GSS_SPKM, RPC_GSS_SVC_NONE, "spkm3"},
{RPC_AUTH_GSS_SPKMI, RPC_GSS_SVC_INTEGRITY, "spkm3i"},
};
static struct gss_api_mech gss_spkm3_mech = {
.gm_name = "spkm3",
.gm_owner = THIS_MODULE,
.gm_oid = {7, "\053\006\001\005\005\001\003"},
.gm_ops = &gss_spkm3_ops,
.gm_pf_num = ARRAY_SIZE(gss_spkm3_pfs),
.gm_pfs = gss_spkm3_pfs,
};
static int __init init_spkm3_module(void)
{
int status;
status = gss_mech_register(&gss_spkm3_mech);
if (status)
printk("Failed to register spkm3 gss mechanism!\n");
return status;
}
static void __exit cleanup_spkm3_module(void)
{
gss_mech_unregister(&gss_spkm3_mech);
}
MODULE_LICENSE("GPL");
module_init(init_spkm3_module);
module_exit(cleanup_spkm3_module);

View file

@ -1,186 +0,0 @@
/*
* linux/net/sunrpc/gss_spkm3_seal.c
*
* Copyright (c) 2003 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@umich.edu>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <linux/types.h>
#include <linux/jiffies.h>
#include <linux/sunrpc/gss_spkm3.h>
#include <linux/random.h>
#include <linux/crypto.h>
#include <linux/pagemap.h>
#include <linux/scatterlist.h>
#include <linux/sunrpc/xdr.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
const struct xdr_netobj hmac_md5_oid = { 8, "\x2B\x06\x01\x05\x05\x08\x01\x01"};
const struct xdr_netobj cast5_cbc_oid = {9, "\x2A\x86\x48\x86\xF6\x7D\x07\x42\x0A"};
/*
* spkm3_make_token()
*
* Only SPKM_MIC_TOK with md5 intg-alg is supported
*/
u32
spkm3_make_token(struct spkm3_ctx *ctx,
struct xdr_buf * text, struct xdr_netobj * token,
int toktype)
{
s32 checksum_type;
char tokhdrbuf[25];
char cksumdata[16];
struct xdr_netobj md5cksum = {.len = 0, .data = cksumdata};
struct xdr_netobj mic_hdr = {.len = 0, .data = tokhdrbuf};
int tokenlen = 0;
unsigned char *ptr;
s32 now;
int ctxelen = 0, ctxzbit = 0;
int md5elen = 0, md5zbit = 0;
now = jiffies;
if (ctx->ctx_id.len != 16) {
dprintk("RPC: spkm3_make_token BAD ctx_id.len %d\n",
ctx->ctx_id.len);
goto out_err;
}
if (!g_OID_equal(&ctx->intg_alg, &hmac_md5_oid)) {
dprintk("RPC: gss_spkm3_seal: unsupported I-ALG "
"algorithm. only support hmac-md5 I-ALG.\n");
goto out_err;
} else
checksum_type = CKSUMTYPE_HMAC_MD5;
if (!g_OID_equal(&ctx->conf_alg, &cast5_cbc_oid)) {
dprintk("RPC: gss_spkm3_seal: unsupported C-ALG "
"algorithm\n");
goto out_err;
}
if (toktype == SPKM_MIC_TOK) {
/* Calculate checksum over the mic-header */
asn1_bitstring_len(&ctx->ctx_id, &ctxelen, &ctxzbit);
spkm3_mic_header(&mic_hdr.data, &mic_hdr.len, ctx->ctx_id.data,
ctxelen, ctxzbit);
if (make_spkm3_checksum(checksum_type, &ctx->derived_integ_key,
(char *)mic_hdr.data, mic_hdr.len,
text, 0, &md5cksum))
goto out_err;
asn1_bitstring_len(&md5cksum, &md5elen, &md5zbit);
tokenlen = 10 + ctxelen + 1 + md5elen + 1;
/* Create token header using generic routines */
token->len = g_token_size(&ctx->mech_used, tokenlen + 2);
ptr = token->data;
g_make_token_header(&ctx->mech_used, tokenlen + 2, &ptr);
spkm3_make_mic_token(&ptr, tokenlen, &mic_hdr, &md5cksum, md5elen, md5zbit);
} else if (toktype == SPKM_WRAP_TOK) { /* Not Supported */
dprintk("RPC: gss_spkm3_seal: SPKM_WRAP_TOK "
"not supported\n");
goto out_err;
}
/* XXX need to implement sequence numbers, and ctx->expired */
return GSS_S_COMPLETE;
out_err:
token->data = NULL;
token->len = 0;
return GSS_S_FAILURE;
}
static int
spkm3_checksummer(struct scatterlist *sg, void *data)
{
struct hash_desc *desc = data;
return crypto_hash_update(desc, sg, sg->length);
}
/* checksum the plaintext data and hdrlen bytes of the token header */
s32
make_spkm3_checksum(s32 cksumtype, struct xdr_netobj *key, char *header,
unsigned int hdrlen, struct xdr_buf *body,
unsigned int body_offset, struct xdr_netobj *cksum)
{
char *cksumname;
struct hash_desc desc; /* XXX add to ctx? */
struct scatterlist sg[1];
int err;
switch (cksumtype) {
case CKSUMTYPE_HMAC_MD5:
cksumname = "hmac(md5)";
break;
default:
dprintk("RPC: spkm3_make_checksum:"
" unsupported checksum %d", cksumtype);
return GSS_S_FAILURE;
}
if (key->data == NULL || key->len <= 0) return GSS_S_FAILURE;
desc.tfm = crypto_alloc_hash(cksumname, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(desc.tfm))
return GSS_S_FAILURE;
cksum->len = crypto_hash_digestsize(desc.tfm);
desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
err = crypto_hash_setkey(desc.tfm, key->data, key->len);
if (err)
goto out;
err = crypto_hash_init(&desc);
if (err)
goto out;
sg_init_one(sg, header, hdrlen);
crypto_hash_update(&desc, sg, sg->length);
xdr_process_buf(body, body_offset, body->len - body_offset,
spkm3_checksummer, &desc);
crypto_hash_final(&desc, cksum->data);
out:
crypto_free_hash(desc.tfm);
return err ? GSS_S_FAILURE : 0;
}

View file

@ -1,267 +0,0 @@
/*
* linux/net/sunrpc/gss_spkm3_token.c
*
* Copyright (c) 2003 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@umich.edu>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/sunrpc/gss_spkm3.h>
#include <linux/random.h>
#include <linux/crypto.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
/*
* asn1_bitstring_len()
*
* calculate the asn1 bitstring length of the xdr_netobject
*/
void
asn1_bitstring_len(struct xdr_netobj *in, int *enclen, int *zerobits)
{
int i, zbit = 0,elen = in->len;
char *ptr;
ptr = &in->data[in->len -1];
/* count trailing 0's */
for(i = in->len; i > 0; i--) {
if (*ptr == 0) {
ptr--;
elen--;
} else
break;
}
/* count number of 0 bits in final octet */
ptr = &in->data[elen - 1];
for(i = 0; i < 8; i++) {
short mask = 0x01;
if (!((mask << i) & *ptr))
zbit++;
else
break;
}
*enclen = elen;
*zerobits = zbit;
}
/*
* decode_asn1_bitstring()
*
* decode a bitstring into a buffer of the expected length.
* enclen = bit string length
* explen = expected length (define in rfc)
*/
int
decode_asn1_bitstring(struct xdr_netobj *out, char *in, int enclen, int explen)
{
if (!(out->data = kzalloc(explen,GFP_NOFS)))
return 0;
out->len = explen;
memcpy(out->data, in, enclen);
return 1;
}
/*
* SPKMInnerContextToken choice SPKM_MIC asn1 token layout
*
* contextid is always 16 bytes plain data. max asn1 bitstring len = 17.
*
* tokenlen = pos[0] to end of token (max pos[45] with MD5 cksum)
*
* pos value
* ----------
* [0] a4 SPKM-MIC tag
* [1] ?? innertoken length (max 44)
*
*
* tok_hdr piece of checksum data starts here
*
* the maximum mic-header len = 9 + 17 = 26
* mic-header
* ----------
* [2] 30 SEQUENCE tag
* [3] ?? mic-header length: (max 23) = TokenID + ContextID
*
* TokenID - all fields constant and can be hardcoded
* -------
* [4] 02 Type 2
* [5] 02 Length 2
* [6][7] 01 01 TokenID (SPKM_MIC_TOK)
*
* ContextID - encoded length not constant, calculated
* ---------
* [8] 03 Type 3
* [9] ?? encoded length
* [10] ?? ctxzbit
* [11] contextid
*
* mic_header piece of checksum data ends here.
*
* int-cksum - encoded length not constant, calculated
* ---------
* [??] 03 Type 3
* [??] ?? encoded length
* [??] ?? md5zbit
* [??] int-cksum (NID_md5 = 16)
*
* maximum SPKM-MIC innercontext token length =
* 10 + encoded contextid_size(17 max) + 2 + encoded
* cksum_size (17 maxfor NID_md5) = 46
*/
/*
* spkm3_mic_header()
*
* Prepare the SPKM_MIC_TOK mic-header for check-sum calculation
* elen: 16 byte context id asn1 bitstring encoded length
*/
void
spkm3_mic_header(unsigned char **hdrbuf, unsigned int *hdrlen, unsigned char *ctxdata, int elen, int zbit)
{
char *hptr = *hdrbuf;
char *top = *hdrbuf;
*(u8 *)hptr++ = 0x30;
*(u8 *)hptr++ = elen + 7; /* on the wire header length */
/* tokenid */
*(u8 *)hptr++ = 0x02;
*(u8 *)hptr++ = 0x02;
*(u8 *)hptr++ = 0x01;
*(u8 *)hptr++ = 0x01;
/* coniextid */
*(u8 *)hptr++ = 0x03;
*(u8 *)hptr++ = elen + 1; /* add 1 to include zbit */
*(u8 *)hptr++ = zbit;
memcpy(hptr, ctxdata, elen);
hptr += elen;
*hdrlen = hptr - top;
}
/*
* spkm3_mic_innercontext_token()
*
* *tokp points to the beginning of the SPKM_MIC token described
* in rfc 2025, section 3.2.1:
*
* toklen is the inner token length
*/
void
spkm3_make_mic_token(unsigned char **tokp, int toklen, struct xdr_netobj *mic_hdr, struct xdr_netobj *md5cksum, int md5elen, int md5zbit)
{
unsigned char *ict = *tokp;
*(u8 *)ict++ = 0xa4;
*(u8 *)ict++ = toklen;
memcpy(ict, mic_hdr->data, mic_hdr->len);
ict += mic_hdr->len;
*(u8 *)ict++ = 0x03;
*(u8 *)ict++ = md5elen + 1; /* add 1 to include zbit */
*(u8 *)ict++ = md5zbit;
memcpy(ict, md5cksum->data, md5elen);
}
u32
spkm3_verify_mic_token(unsigned char **tokp, int *mic_hdrlen, unsigned char **cksum)
{
struct xdr_netobj spkm3_ctx_id = {.len =0, .data = NULL};
unsigned char *ptr = *tokp;
int ctxelen;
u32 ret = GSS_S_DEFECTIVE_TOKEN;
/* spkm3 innercontext token preamble */
if ((ptr[0] != 0xa4) || (ptr[2] != 0x30)) {
dprintk("RPC: BAD SPKM ictoken preamble\n");
goto out;
}
*mic_hdrlen = ptr[3];
/* token type */
if ((ptr[4] != 0x02) || (ptr[5] != 0x02)) {
dprintk("RPC: BAD asn1 SPKM3 token type\n");
goto out;
}
/* only support SPKM_MIC_TOK */
if((ptr[6] != 0x01) || (ptr[7] != 0x01)) {
dprintk("RPC: ERROR unsupported SPKM3 token\n");
goto out;
}
/* contextid */
if (ptr[8] != 0x03) {
dprintk("RPC: BAD SPKM3 asn1 context-id type\n");
goto out;
}
ctxelen = ptr[9];
if (ctxelen > 17) { /* length includes asn1 zbit octet */
dprintk("RPC: BAD SPKM3 contextid len %d\n", ctxelen);
goto out;
}
/* ignore ptr[10] */
if(!decode_asn1_bitstring(&spkm3_ctx_id, &ptr[11], ctxelen - 1, 16))
goto out;
/*
* in the current implementation: the optional int-alg is not present
* so the default int-alg (md5) is used the optional snd-seq field is
* also not present
*/
if (*mic_hdrlen != 6 + ctxelen) {
dprintk("RPC: BAD SPKM_ MIC_TOK header len %d: we only "
"support default int-alg (should be absent) "
"and do not support snd-seq\n", *mic_hdrlen);
goto out;
}
/* checksum */
*cksum = (&ptr[10] + ctxelen); /* ctxelen includes ptr[10] */
ret = GSS_S_COMPLETE;
out:
kfree(spkm3_ctx_id.data);
return ret;
}

View file

@ -1,127 +0,0 @@
/*
* linux/net/sunrpc/gss_spkm3_unseal.c
*
* Copyright (c) 2003 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@umich.edu>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/sunrpc/gss_spkm3.h>
#include <linux/crypto.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
/*
* spkm3_read_token()
*
* only SPKM_MIC_TOK with md5 intg-alg is supported
*/
u32
spkm3_read_token(struct spkm3_ctx *ctx,
struct xdr_netobj *read_token, /* checksum */
struct xdr_buf *message_buffer, /* signbuf */
int toktype)
{
s32 checksum_type;
s32 code;
struct xdr_netobj wire_cksum = {.len =0, .data = NULL};
char cksumdata[16];
struct xdr_netobj md5cksum = {.len = 0, .data = cksumdata};
unsigned char *ptr = (unsigned char *)read_token->data;
unsigned char *cksum;
int bodysize, md5elen;
int mic_hdrlen;
u32 ret = GSS_S_DEFECTIVE_TOKEN;
if (g_verify_token_header((struct xdr_netobj *) &ctx->mech_used,
&bodysize, &ptr, read_token->len))
goto out;
/* decode the token */
if (toktype != SPKM_MIC_TOK) {
dprintk("RPC: BAD SPKM3 token type: %d\n", toktype);
goto out;
}
if ((ret = spkm3_verify_mic_token(&ptr, &mic_hdrlen, &cksum)))
goto out;
if (*cksum++ != 0x03) {
dprintk("RPC: spkm3_read_token BAD checksum type\n");
goto out;
}
md5elen = *cksum++;
cksum++; /* move past the zbit */
if (!decode_asn1_bitstring(&wire_cksum, cksum, md5elen - 1, 16))
goto out;
/* HARD CODED FOR MD5 */
/* compute the checksum of the message.
* ptr + 2 = start of header piece of checksum
* mic_hdrlen + 2 = length of header piece of checksum
*/
ret = GSS_S_DEFECTIVE_TOKEN;
if (!g_OID_equal(&ctx->intg_alg, &hmac_md5_oid)) {
dprintk("RPC: gss_spkm3_seal: unsupported I-ALG "
"algorithm\n");
goto out;
}
checksum_type = CKSUMTYPE_HMAC_MD5;
code = make_spkm3_checksum(checksum_type,
&ctx->derived_integ_key, ptr + 2, mic_hdrlen + 2,
message_buffer, 0, &md5cksum);
if (code)
goto out;
ret = GSS_S_BAD_SIG;
code = memcmp(md5cksum.data, wire_cksum.data, wire_cksum.len);
if (code) {
dprintk("RPC: bad MIC checksum\n");
goto out;
}
/* XXX: need to add expiration and sequencing */
ret = GSS_S_COMPLETE;
out:
kfree(wire_cksum.data);
return ret;
}

View file

@ -964,7 +964,7 @@ svcauth_gss_set_client(struct svc_rqst *rqstp)
if (rqstp->rq_gssclient == NULL)
return SVC_DENIED;
stat = svcauth_unix_set_client(rqstp);
if (stat == SVC_DROP)
if (stat == SVC_DROP || stat == SVC_CLOSE)
return stat;
return SVC_OK;
}
@ -1018,7 +1018,7 @@ static int svcauth_gss_handle_init(struct svc_rqst *rqstp,
return SVC_DENIED;
memset(&rsikey, 0, sizeof(rsikey));
if (dup_netobj(&rsikey.in_handle, &gc->gc_ctx))
return SVC_DROP;
return SVC_CLOSE;
*authp = rpc_autherr_badverf;
if (svc_safe_getnetobj(argv, &tmpobj)) {
kfree(rsikey.in_handle.data);
@ -1026,38 +1026,35 @@ static int svcauth_gss_handle_init(struct svc_rqst *rqstp,
}
if (dup_netobj(&rsikey.in_token, &tmpobj)) {
kfree(rsikey.in_handle.data);
return SVC_DROP;
return SVC_CLOSE;
}
/* Perform upcall, or find upcall result: */
rsip = rsi_lookup(&rsikey);
rsi_free(&rsikey);
if (!rsip)
return SVC_DROP;
switch (cache_check(&rsi_cache, &rsip->h, &rqstp->rq_chandle)) {
case -EAGAIN:
case -ETIMEDOUT:
case -ENOENT:
return SVC_CLOSE;
if (cache_check(&rsi_cache, &rsip->h, &rqstp->rq_chandle) < 0)
/* No upcall result: */
return SVC_DROP;
case 0:
ret = SVC_DROP;
/* Got an answer to the upcall; use it: */
if (gss_write_init_verf(rqstp, rsip))
goto out;
if (resv->iov_len + 4 > PAGE_SIZE)
goto out;
svc_putnl(resv, RPC_SUCCESS);
if (svc_safe_putnetobj(resv, &rsip->out_handle))
goto out;
if (resv->iov_len + 3 * 4 > PAGE_SIZE)
goto out;
svc_putnl(resv, rsip->major_status);
svc_putnl(resv, rsip->minor_status);
svc_putnl(resv, GSS_SEQ_WIN);
if (svc_safe_putnetobj(resv, &rsip->out_token))
goto out;
}
return SVC_CLOSE;
ret = SVC_CLOSE;
/* Got an answer to the upcall; use it: */
if (gss_write_init_verf(rqstp, rsip))
goto out;
if (resv->iov_len + 4 > PAGE_SIZE)
goto out;
svc_putnl(resv, RPC_SUCCESS);
if (svc_safe_putnetobj(resv, &rsip->out_handle))
goto out;
if (resv->iov_len + 3 * 4 > PAGE_SIZE)
goto out;
svc_putnl(resv, rsip->major_status);
svc_putnl(resv, rsip->minor_status);
svc_putnl(resv, GSS_SEQ_WIN);
if (svc_safe_putnetobj(resv, &rsip->out_token))
goto out;
ret = SVC_COMPLETE;
out:
cache_put(&rsip->h, &rsi_cache);

View file

@ -33,15 +33,16 @@
#include <linux/sunrpc/cache.h>
#include <linux/sunrpc/stats.h>
#include <linux/sunrpc/rpc_pipe_fs.h>
#include "netns.h"
#define RPCDBG_FACILITY RPCDBG_CACHE
static int cache_defer_req(struct cache_req *req, struct cache_head *item);
static void cache_defer_req(struct cache_req *req, struct cache_head *item);
static void cache_revisit_request(struct cache_head *item);
static void cache_init(struct cache_head *h)
{
time_t now = get_seconds();
time_t now = seconds_since_boot();
h->next = NULL;
h->flags = 0;
kref_init(&h->ref);
@ -51,7 +52,7 @@ static void cache_init(struct cache_head *h)
static inline int cache_is_expired(struct cache_detail *detail, struct cache_head *h)
{
return (h->expiry_time < get_seconds()) ||
return (h->expiry_time < seconds_since_boot()) ||
(detail->flush_time > h->last_refresh);
}
@ -126,7 +127,7 @@ static void cache_dequeue(struct cache_detail *detail, struct cache_head *ch);
static void cache_fresh_locked(struct cache_head *head, time_t expiry)
{
head->expiry_time = expiry;
head->last_refresh = get_seconds();
head->last_refresh = seconds_since_boot();
set_bit(CACHE_VALID, &head->flags);
}
@ -237,7 +238,7 @@ int cache_check(struct cache_detail *detail,
/* now see if we want to start an upcall */
refresh_age = (h->expiry_time - h->last_refresh);
age = get_seconds() - h->last_refresh;
age = seconds_since_boot() - h->last_refresh;
if (rqstp == NULL) {
if (rv == -EAGAIN)
@ -252,7 +253,7 @@ int cache_check(struct cache_detail *detail,
cache_revisit_request(h);
if (rv == -EAGAIN) {
set_bit(CACHE_NEGATIVE, &h->flags);
cache_fresh_locked(h, get_seconds()+CACHE_NEW_EXPIRY);
cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY);
cache_fresh_unlocked(h, detail);
rv = -ENOENT;
}
@ -267,7 +268,8 @@ int cache_check(struct cache_detail *detail,
}
if (rv == -EAGAIN) {
if (cache_defer_req(rqstp, h) < 0) {
cache_defer_req(rqstp, h);
if (!test_bit(CACHE_PENDING, &h->flags)) {
/* Request is not deferred */
rv = cache_is_valid(detail, h);
if (rv == -EAGAIN)
@ -387,11 +389,11 @@ static int cache_clean(void)
return -1;
}
current_detail = list_entry(next, struct cache_detail, others);
if (current_detail->nextcheck > get_seconds())
if (current_detail->nextcheck > seconds_since_boot())
current_index = current_detail->hash_size;
else {
current_index = 0;
current_detail->nextcheck = get_seconds()+30*60;
current_detail->nextcheck = seconds_since_boot()+30*60;
}
}
@ -476,7 +478,7 @@ EXPORT_SYMBOL_GPL(cache_flush);
void cache_purge(struct cache_detail *detail)
{
detail->flush_time = LONG_MAX;
detail->nextcheck = get_seconds();
detail->nextcheck = seconds_since_boot();
cache_flush();
detail->flush_time = 1;
}
@ -505,81 +507,155 @@ EXPORT_SYMBOL_GPL(cache_purge);
static DEFINE_SPINLOCK(cache_defer_lock);
static LIST_HEAD(cache_defer_list);
static struct list_head cache_defer_hash[DFR_HASHSIZE];
static struct hlist_head cache_defer_hash[DFR_HASHSIZE];
static int cache_defer_cnt;
static int cache_defer_req(struct cache_req *req, struct cache_head *item)
static void __unhash_deferred_req(struct cache_deferred_req *dreq)
{
hlist_del_init(&dreq->hash);
if (!list_empty(&dreq->recent)) {
list_del_init(&dreq->recent);
cache_defer_cnt--;
}
}
static void __hash_deferred_req(struct cache_deferred_req *dreq, struct cache_head *item)
{
struct cache_deferred_req *dreq, *discard;
int hash = DFR_HASH(item);
if (cache_defer_cnt >= DFR_MAX) {
/* too much in the cache, randomly drop this one,
* or continue and drop the oldest below
*/
if (net_random()&1)
return -ENOMEM;
}
dreq = req->defer(req);
if (dreq == NULL)
return -ENOMEM;
INIT_LIST_HEAD(&dreq->recent);
hlist_add_head(&dreq->hash, &cache_defer_hash[hash]);
}
static void setup_deferral(struct cache_deferred_req *dreq,
struct cache_head *item,
int count_me)
{
dreq->item = item;
spin_lock(&cache_defer_lock);
list_add(&dreq->recent, &cache_defer_list);
__hash_deferred_req(dreq, item);
if (cache_defer_hash[hash].next == NULL)
INIT_LIST_HEAD(&cache_defer_hash[hash]);
list_add(&dreq->hash, &cache_defer_hash[hash]);
/* it is in, now maybe clean up */
discard = NULL;
if (++cache_defer_cnt > DFR_MAX) {
discard = list_entry(cache_defer_list.prev,
struct cache_deferred_req, recent);
list_del_init(&discard->recent);
list_del_init(&discard->hash);
cache_defer_cnt--;
if (count_me) {
cache_defer_cnt++;
list_add(&dreq->recent, &cache_defer_list);
}
spin_unlock(&cache_defer_lock);
if (discard)
/* there was one too many */
discard->revisit(discard, 1);
}
if (!test_bit(CACHE_PENDING, &item->flags)) {
/* must have just been validated... */
cache_revisit_request(item);
return -EAGAIN;
struct thread_deferred_req {
struct cache_deferred_req handle;
struct completion completion;
};
static void cache_restart_thread(struct cache_deferred_req *dreq, int too_many)
{
struct thread_deferred_req *dr =
container_of(dreq, struct thread_deferred_req, handle);
complete(&dr->completion);
}
static void cache_wait_req(struct cache_req *req, struct cache_head *item)
{
struct thread_deferred_req sleeper;
struct cache_deferred_req *dreq = &sleeper.handle;
sleeper.completion = COMPLETION_INITIALIZER_ONSTACK(sleeper.completion);
dreq->revisit = cache_restart_thread;
setup_deferral(dreq, item, 0);
if (!test_bit(CACHE_PENDING, &item->flags) ||
wait_for_completion_interruptible_timeout(
&sleeper.completion, req->thread_wait) <= 0) {
/* The completion wasn't completed, so we need
* to clean up
*/
spin_lock(&cache_defer_lock);
if (!hlist_unhashed(&sleeper.handle.hash)) {
__unhash_deferred_req(&sleeper.handle);
spin_unlock(&cache_defer_lock);
} else {
/* cache_revisit_request already removed
* this from the hash table, but hasn't
* called ->revisit yet. It will very soon
* and we need to wait for it.
*/
spin_unlock(&cache_defer_lock);
wait_for_completion(&sleeper.completion);
}
}
return 0;
}
static void cache_limit_defers(void)
{
/* Make sure we haven't exceed the limit of allowed deferred
* requests.
*/
struct cache_deferred_req *discard = NULL;
if (cache_defer_cnt <= DFR_MAX)
return;
spin_lock(&cache_defer_lock);
/* Consider removing either the first or the last */
if (cache_defer_cnt > DFR_MAX) {
if (net_random() & 1)
discard = list_entry(cache_defer_list.next,
struct cache_deferred_req, recent);
else
discard = list_entry(cache_defer_list.prev,
struct cache_deferred_req, recent);
__unhash_deferred_req(discard);
}
spin_unlock(&cache_defer_lock);
if (discard)
discard->revisit(discard, 1);
}
static void cache_defer_req(struct cache_req *req, struct cache_head *item)
{
struct cache_deferred_req *dreq;
if (req->thread_wait) {
cache_wait_req(req, item);
if (!test_bit(CACHE_PENDING, &item->flags))
return;
}
dreq = req->defer(req);
if (dreq == NULL)
return;
setup_deferral(dreq, item, 1);
if (!test_bit(CACHE_PENDING, &item->flags))
/* Bit could have been cleared before we managed to
* set up the deferral, so need to revisit just in case
*/
cache_revisit_request(item);
cache_limit_defers();
}
static void cache_revisit_request(struct cache_head *item)
{
struct cache_deferred_req *dreq;
struct list_head pending;
struct list_head *lp;
struct hlist_node *lp, *tmp;
int hash = DFR_HASH(item);
INIT_LIST_HEAD(&pending);
spin_lock(&cache_defer_lock);
lp = cache_defer_hash[hash].next;
if (lp) {
while (lp != &cache_defer_hash[hash]) {
dreq = list_entry(lp, struct cache_deferred_req, hash);
lp = lp->next;
if (dreq->item == item) {
list_del_init(&dreq->hash);
list_move(&dreq->recent, &pending);
cache_defer_cnt--;
}
hlist_for_each_entry_safe(dreq, lp, tmp, &cache_defer_hash[hash], hash)
if (dreq->item == item) {
__unhash_deferred_req(dreq);
list_add(&dreq->recent, &pending);
}
}
spin_unlock(&cache_defer_lock);
while (!list_empty(&pending)) {
@ -600,9 +676,8 @@ void cache_clean_deferred(void *owner)
list_for_each_entry_safe(dreq, tmp, &cache_defer_list, recent) {
if (dreq->owner == owner) {
list_del_init(&dreq->hash);
list_move(&dreq->recent, &pending);
cache_defer_cnt--;
__unhash_deferred_req(dreq);
list_add(&dreq->recent, &pending);
}
}
spin_unlock(&cache_defer_lock);
@ -901,7 +976,7 @@ static int cache_release(struct inode *inode, struct file *filp,
filp->private_data = NULL;
kfree(rp);
cd->last_close = get_seconds();
cd->last_close = seconds_since_boot();
atomic_dec(&cd->readers);
}
module_put(cd->owner);
@ -1014,6 +1089,23 @@ static void warn_no_listener(struct cache_detail *detail)
}
}
static bool cache_listeners_exist(struct cache_detail *detail)
{
if (atomic_read(&detail->readers))
return true;
if (detail->last_close == 0)
/* This cache was never opened */
return false;
if (detail->last_close < seconds_since_boot() - 30)
/*
* We allow for the possibility that someone might
* restart a userspace daemon without restarting the
* server; but after 30 seconds, we give up.
*/
return false;
return true;
}
/*
* register an upcall request to user-space and queue it up for read() by the
* upcall daemon.
@ -1032,10 +1124,9 @@ int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h,
char *bp;
int len;
if (atomic_read(&detail->readers) == 0 &&
detail->last_close < get_seconds() - 30) {
warn_no_listener(detail);
return -EINVAL;
if (!cache_listeners_exist(detail)) {
warn_no_listener(detail);
return -EINVAL;
}
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
@ -1094,13 +1185,19 @@ int qword_get(char **bpp, char *dest, int bufsize)
if (bp[0] == '\\' && bp[1] == 'x') {
/* HEX STRING */
bp += 2;
while (isxdigit(bp[0]) && isxdigit(bp[1]) && len < bufsize) {
int byte = isdigit(*bp) ? *bp-'0' : toupper(*bp)-'A'+10;
bp++;
byte <<= 4;
byte |= isdigit(*bp) ? *bp-'0' : toupper(*bp)-'A'+10;
*dest++ = byte;
bp++;
while (len < bufsize) {
int h, l;
h = hex_to_bin(bp[0]);
if (h < 0)
break;
l = hex_to_bin(bp[1]);
if (l < 0)
break;
*dest++ = (h << 4) | l;
bp += 2;
len++;
}
} else {
@ -1218,7 +1315,8 @@ static int c_show(struct seq_file *m, void *p)
ifdebug(CACHE)
seq_printf(m, "# expiry=%ld refcnt=%d flags=%lx\n",
cp->expiry_time, atomic_read(&cp->ref.refcount), cp->flags);
convert_to_wallclock(cp->expiry_time),
atomic_read(&cp->ref.refcount), cp->flags);
cache_get(cp);
if (cache_check(cd, cp, NULL))
/* cache_check does a cache_put on failure */
@ -1284,7 +1382,7 @@ static ssize_t read_flush(struct file *file, char __user *buf,
unsigned long p = *ppos;
size_t len;
sprintf(tbuf, "%lu\n", cd->flush_time);
sprintf(tbuf, "%lu\n", convert_to_wallclock(cd->flush_time));
len = strlen(tbuf);
if (p >= len)
return 0;
@ -1302,19 +1400,20 @@ static ssize_t write_flush(struct file *file, const char __user *buf,
struct cache_detail *cd)
{
char tbuf[20];
char *ep;
long flushtime;
char *bp, *ep;
if (*ppos || count > sizeof(tbuf)-1)
return -EINVAL;
if (copy_from_user(tbuf, buf, count))
return -EFAULT;
tbuf[count] = 0;
flushtime = simple_strtoul(tbuf, &ep, 0);
simple_strtoul(tbuf, &ep, 0);
if (*ep && *ep != '\n')
return -EINVAL;
cd->flush_time = flushtime;
cd->nextcheck = get_seconds();
bp = tbuf;
cd->flush_time = get_expiry(&bp);
cd->nextcheck = seconds_since_boot();
cache_flush();
*ppos += count;
@ -1438,8 +1537,10 @@ static const struct file_operations cache_flush_operations_procfs = {
.llseek = no_llseek,
};
static void remove_cache_proc_entries(struct cache_detail *cd)
static void remove_cache_proc_entries(struct cache_detail *cd, struct net *net)
{
struct sunrpc_net *sn;
if (cd->u.procfs.proc_ent == NULL)
return;
if (cd->u.procfs.flush_ent)
@ -1449,15 +1550,18 @@ static void remove_cache_proc_entries(struct cache_detail *cd)
if (cd->u.procfs.content_ent)
remove_proc_entry("content", cd->u.procfs.proc_ent);
cd->u.procfs.proc_ent = NULL;
remove_proc_entry(cd->name, proc_net_rpc);
sn = net_generic(net, sunrpc_net_id);
remove_proc_entry(cd->name, sn->proc_net_rpc);
}
#ifdef CONFIG_PROC_FS
static int create_cache_proc_entries(struct cache_detail *cd)
static int create_cache_proc_entries(struct cache_detail *cd, struct net *net)
{
struct proc_dir_entry *p;
struct sunrpc_net *sn;
cd->u.procfs.proc_ent = proc_mkdir(cd->name, proc_net_rpc);
sn = net_generic(net, sunrpc_net_id);
cd->u.procfs.proc_ent = proc_mkdir(cd->name, sn->proc_net_rpc);
if (cd->u.procfs.proc_ent == NULL)
goto out_nomem;
cd->u.procfs.channel_ent = NULL;
@ -1488,11 +1592,11 @@ static int create_cache_proc_entries(struct cache_detail *cd)
}
return 0;
out_nomem:
remove_cache_proc_entries(cd);
remove_cache_proc_entries(cd, net);
return -ENOMEM;
}
#else /* CONFIG_PROC_FS */
static int create_cache_proc_entries(struct cache_detail *cd)
static int create_cache_proc_entries(struct cache_detail *cd, struct net *net)
{
return 0;
}
@ -1503,22 +1607,32 @@ void __init cache_initialize(void)
INIT_DELAYED_WORK_DEFERRABLE(&cache_cleaner, do_cache_clean);
}
int cache_register(struct cache_detail *cd)
int cache_register_net(struct cache_detail *cd, struct net *net)
{
int ret;
sunrpc_init_cache_detail(cd);
ret = create_cache_proc_entries(cd);
ret = create_cache_proc_entries(cd, net);
if (ret)
sunrpc_destroy_cache_detail(cd);
return ret;
}
int cache_register(struct cache_detail *cd)
{
return cache_register_net(cd, &init_net);
}
EXPORT_SYMBOL_GPL(cache_register);
void cache_unregister_net(struct cache_detail *cd, struct net *net)
{
remove_cache_proc_entries(cd, net);
sunrpc_destroy_cache_detail(cd);
}
void cache_unregister(struct cache_detail *cd)
{
remove_cache_proc_entries(cd);
sunrpc_destroy_cache_detail(cd);
cache_unregister_net(cd, &init_net);
}
EXPORT_SYMBOL_GPL(cache_unregister);

View file

@ -284,6 +284,7 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
struct rpc_xprt *xprt;
struct rpc_clnt *clnt;
struct xprt_create xprtargs = {
.net = args->net,
.ident = args->protocol,
.srcaddr = args->saddress,
.dstaddr = args->address,

19
net/sunrpc/netns.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef __SUNRPC_NETNS_H__
#define __SUNRPC_NETNS_H__
#include <net/net_namespace.h>
#include <net/netns/generic.h>
struct cache_detail;
struct sunrpc_net {
struct proc_dir_entry *proc_net_rpc;
struct cache_detail *ip_map_cache;
};
extern int sunrpc_net_id;
int ip_map_cache_create(struct net *);
void ip_map_cache_destroy(struct net *);
#endif

View file

@ -177,6 +177,7 @@ static DEFINE_MUTEX(rpcb_create_local_mutex);
static int rpcb_create_local(void)
{
struct rpc_create_args args = {
.net = &init_net,
.protocol = XPRT_TRANSPORT_TCP,
.address = (struct sockaddr *)&rpcb_inaddr_loopback,
.addrsize = sizeof(rpcb_inaddr_loopback),
@ -229,6 +230,7 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
size_t salen, int proto, u32 version)
{
struct rpc_create_args args = {
.net = &init_net,
.protocol = proto,
.address = srvaddr,
.addrsize = salen,
@ -248,7 +250,7 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
((struct sockaddr_in6 *)srvaddr)->sin6_port = htons(RPCBIND_PORT);
break;
default:
return NULL;
return ERR_PTR(-EAFNOSUPPORT);
}
return rpc_create(&args);

View file

@ -22,12 +22,11 @@
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/sunrpc/metrics.h>
#include <net/net_namespace.h>
#include "netns.h"
#define RPCDBG_FACILITY RPCDBG_MISC
struct proc_dir_entry *proc_net_rpc = NULL;
/*
* Get RPC client stats
*/
@ -218,10 +217,11 @@ EXPORT_SYMBOL_GPL(rpc_print_iostats);
static inline struct proc_dir_entry *
do_register(const char *name, void *data, const struct file_operations *fops)
{
rpc_proc_init();
dprintk("RPC: registering /proc/net/rpc/%s\n", name);
struct sunrpc_net *sn;
return proc_create_data(name, 0, proc_net_rpc, fops, data);
dprintk("RPC: registering /proc/net/rpc/%s\n", name);
sn = net_generic(&init_net, sunrpc_net_id);
return proc_create_data(name, 0, sn->proc_net_rpc, fops, data);
}
struct proc_dir_entry *
@ -234,7 +234,10 @@ EXPORT_SYMBOL_GPL(rpc_proc_register);
void
rpc_proc_unregister(const char *name)
{
remove_proc_entry(name, proc_net_rpc);
struct sunrpc_net *sn;
sn = net_generic(&init_net, sunrpc_net_id);
remove_proc_entry(name, sn->proc_net_rpc);
}
EXPORT_SYMBOL_GPL(rpc_proc_unregister);
@ -248,25 +251,29 @@ EXPORT_SYMBOL_GPL(svc_proc_register);
void
svc_proc_unregister(const char *name)
{
remove_proc_entry(name, proc_net_rpc);
struct sunrpc_net *sn;
sn = net_generic(&init_net, sunrpc_net_id);
remove_proc_entry(name, sn->proc_net_rpc);
}
EXPORT_SYMBOL_GPL(svc_proc_unregister);
void
rpc_proc_init(void)
int rpc_proc_init(struct net *net)
{
struct sunrpc_net *sn;
dprintk("RPC: registering /proc/net/rpc\n");
if (!proc_net_rpc)
proc_net_rpc = proc_mkdir("rpc", init_net.proc_net);
sn = net_generic(net, sunrpc_net_id);
sn->proc_net_rpc = proc_mkdir("rpc", net->proc_net);
if (sn->proc_net_rpc == NULL)
return -ENOMEM;
return 0;
}
void
rpc_proc_exit(void)
void rpc_proc_exit(struct net *net)
{
dprintk("RPC: unregistering /proc/net/rpc\n");
if (proc_net_rpc) {
proc_net_rpc = NULL;
remove_proc_entry("rpc", init_net.proc_net);
}
remove_proc_entry("rpc", net->proc_net);
}

View file

@ -22,7 +22,44 @@
#include <linux/sunrpc/rpc_pipe_fs.h>
#include <linux/sunrpc/xprtsock.h>
extern struct cache_detail ip_map_cache, unix_gid_cache;
#include "netns.h"
int sunrpc_net_id;
static __net_init int sunrpc_init_net(struct net *net)
{
int err;
err = rpc_proc_init(net);
if (err)
goto err_proc;
err = ip_map_cache_create(net);
if (err)
goto err_ipmap;
return 0;
err_ipmap:
rpc_proc_exit(net);
err_proc:
return err;
}
static __net_exit void sunrpc_exit_net(struct net *net)
{
ip_map_cache_destroy(net);
rpc_proc_exit(net);
}
static struct pernet_operations sunrpc_net_ops = {
.init = sunrpc_init_net,
.exit = sunrpc_exit_net,
.id = &sunrpc_net_id,
.size = sizeof(struct sunrpc_net),
};
extern struct cache_detail unix_gid_cache;
extern void cleanup_rpcb_clnt(void);
@ -38,18 +75,22 @@ init_sunrpc(void)
err = rpcauth_init_module();
if (err)
goto out3;
cache_initialize();
err = register_pernet_subsys(&sunrpc_net_ops);
if (err)
goto out4;
#ifdef RPC_DEBUG
rpc_register_sysctl();
#endif
#ifdef CONFIG_PROC_FS
rpc_proc_init();
#endif
cache_initialize();
cache_register(&ip_map_cache);
cache_register(&unix_gid_cache);
svc_init_xprt_sock(); /* svc sock transport */
init_socket_xprt(); /* clnt sock transport */
return 0;
out4:
rpcauth_remove_module();
out3:
rpc_destroy_mempool();
out2:
@ -67,13 +108,10 @@ cleanup_sunrpc(void)
svc_cleanup_xprt_sock();
unregister_rpc_pipefs();
rpc_destroy_mempool();
cache_unregister(&ip_map_cache);
cache_unregister(&unix_gid_cache);
unregister_pernet_subsys(&sunrpc_net_ops);
#ifdef RPC_DEBUG
rpc_unregister_sysctl();
#endif
#ifdef CONFIG_PROC_FS
rpc_proc_exit();
#endif
rcu_barrier(); /* Wait for completion of call_rcu()'s */
}

View file

@ -1055,6 +1055,9 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
goto err_bad;
case SVC_DENIED:
goto err_bad_auth;
case SVC_CLOSE:
if (test_bit(XPT_TEMP, &rqstp->rq_xprt->xpt_flags))
svc_close_xprt(rqstp->rq_xprt);
case SVC_DROP:
goto dropit;
case SVC_COMPLETE:

View file

@ -100,16 +100,14 @@ EXPORT_SYMBOL_GPL(svc_unreg_xprt_class);
*/
int svc_print_xprts(char *buf, int maxlen)
{
struct list_head *le;
struct svc_xprt_class *xcl;
char tmpstr[80];
int len = 0;
buf[0] = '\0';
spin_lock(&svc_xprt_class_lock);
list_for_each(le, &svc_xprt_class_list) {
list_for_each_entry(xcl, &svc_xprt_class_list, xcl_list) {
int slen;
struct svc_xprt_class *xcl =
list_entry(le, struct svc_xprt_class, xcl_list);
sprintf(tmpstr, "%s %d\n", xcl->xcl_name, xcl->xcl_max_payload);
slen = strlen(tmpstr);
@ -128,9 +126,9 @@ static void svc_xprt_free(struct kref *kref)
struct svc_xprt *xprt =
container_of(kref, struct svc_xprt, xpt_ref);
struct module *owner = xprt->xpt_class->xcl_owner;
if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags) &&
xprt->xpt_auth_cache != NULL)
svcauth_unix_info_release(xprt->xpt_auth_cache);
if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags))
svcauth_unix_info_release(xprt);
put_net(xprt->xpt_net);
xprt->xpt_ops->xpo_free(xprt);
module_put(owner);
}
@ -156,15 +154,18 @@ void svc_xprt_init(struct svc_xprt_class *xcl, struct svc_xprt *xprt,
INIT_LIST_HEAD(&xprt->xpt_list);
INIT_LIST_HEAD(&xprt->xpt_ready);
INIT_LIST_HEAD(&xprt->xpt_deferred);
INIT_LIST_HEAD(&xprt->xpt_users);
mutex_init(&xprt->xpt_mutex);
spin_lock_init(&xprt->xpt_lock);
set_bit(XPT_BUSY, &xprt->xpt_flags);
rpc_init_wait_queue(&xprt->xpt_bc_pending, "xpt_bc_pending");
xprt->xpt_net = get_net(&init_net);
}
EXPORT_SYMBOL_GPL(svc_xprt_init);
static struct svc_xprt *__svc_xpo_create(struct svc_xprt_class *xcl,
struct svc_serv *serv,
struct net *net,
const int family,
const unsigned short port,
int flags)
@ -199,12 +200,12 @@ static struct svc_xprt *__svc_xpo_create(struct svc_xprt_class *xcl,
return ERR_PTR(-EAFNOSUPPORT);
}
return xcl->xcl_ops->xpo_create(serv, sap, len, flags);
return xcl->xcl_ops->xpo_create(serv, net, sap, len, flags);
}
int svc_create_xprt(struct svc_serv *serv, const char *xprt_name,
const int family, const unsigned short port,
int flags)
struct net *net, const int family,
const unsigned short port, int flags)
{
struct svc_xprt_class *xcl;
@ -220,7 +221,7 @@ int svc_create_xprt(struct svc_serv *serv, const char *xprt_name,
goto err;
spin_unlock(&svc_xprt_class_lock);
newxprt = __svc_xpo_create(xcl, serv, family, port, flags);
newxprt = __svc_xpo_create(xcl, serv, net, family, port, flags);
if (IS_ERR(newxprt)) {
module_put(xcl->xcl_owner);
return PTR_ERR(newxprt);
@ -329,12 +330,6 @@ void svc_xprt_enqueue(struct svc_xprt *xprt)
"svc_xprt_enqueue: "
"threads and transports both waiting??\n");
if (test_bit(XPT_DEAD, &xprt->xpt_flags)) {
/* Don't enqueue dead transports */
dprintk("svc: transport %p is dead, not enqueued\n", xprt);
goto out_unlock;
}
pool->sp_stats.packets++;
/* Mark transport as busy. It will remain in this state until
@ -651,6 +646,11 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
if (signalled() || kthread_should_stop())
return -EINTR;
/* Normally we will wait up to 5 seconds for any required
* cache information to be provided.
*/
rqstp->rq_chandle.thread_wait = 5*HZ;
spin_lock_bh(&pool->sp_lock);
xprt = svc_xprt_dequeue(pool);
if (xprt) {
@ -658,6 +658,12 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
svc_xprt_get(xprt);
rqstp->rq_reserved = serv->sv_max_mesg;
atomic_add(rqstp->rq_reserved, &xprt->xpt_reserved);
/* As there is a shortage of threads and this request
* had to be queued, don't allow the thread to wait so
* long for cache updates.
*/
rqstp->rq_chandle.thread_wait = 1*HZ;
} else {
/* No data pending. Go to sleep */
svc_thread_enqueue(pool, rqstp);
@ -868,6 +874,19 @@ static void svc_age_temp_xprts(unsigned long closure)
mod_timer(&serv->sv_temptimer, jiffies + svc_conn_age_period * HZ);
}
static void call_xpt_users(struct svc_xprt *xprt)
{
struct svc_xpt_user *u;
spin_lock(&xprt->xpt_lock);
while (!list_empty(&xprt->xpt_users)) {
u = list_first_entry(&xprt->xpt_users, struct svc_xpt_user, list);
list_del(&u->list);
u->callback(u);
}
spin_unlock(&xprt->xpt_lock);
}
/*
* Remove a dead transport
*/
@ -878,7 +897,7 @@ void svc_delete_xprt(struct svc_xprt *xprt)
/* Only do this once */
if (test_and_set_bit(XPT_DEAD, &xprt->xpt_flags))
return;
BUG();
dprintk("svc: svc_delete_xprt(%p)\n", xprt);
xprt->xpt_ops->xpo_detach(xprt);
@ -900,6 +919,7 @@ void svc_delete_xprt(struct svc_xprt *xprt)
while ((dr = svc_deferred_dequeue(xprt)) != NULL)
kfree(dr);
call_xpt_users(xprt);
svc_xprt_put(xprt);
}
@ -910,10 +930,7 @@ void svc_close_xprt(struct svc_xprt *xprt)
/* someone else will have to effect the close */
return;
svc_xprt_get(xprt);
svc_delete_xprt(xprt);
clear_bit(XPT_BUSY, &xprt->xpt_flags);
svc_xprt_put(xprt);
}
EXPORT_SYMBOL_GPL(svc_close_xprt);

View file

@ -18,6 +18,8 @@
#include <linux/sunrpc/clnt.h>
#include "netns.h"
/*
* AUTHUNIX and AUTHNULL credentials are both handled here.
* AUTHNULL is treated just like AUTHUNIX except that the uid/gid
@ -92,7 +94,6 @@ struct ip_map {
struct unix_domain *m_client;
int m_add_change;
};
static struct cache_head *ip_table[IP_HASHMAX];
static void ip_map_put(struct kref *kref)
{
@ -178,8 +179,8 @@ static int ip_map_upcall(struct cache_detail *cd, struct cache_head *h)
return sunrpc_cache_pipe_upcall(cd, h, ip_map_request);
}
static struct ip_map *ip_map_lookup(char *class, struct in6_addr *addr);
static int ip_map_update(struct ip_map *ipm, struct unix_domain *udom, time_t expiry);
static struct ip_map *__ip_map_lookup(struct cache_detail *cd, char *class, struct in6_addr *addr);
static int __ip_map_update(struct cache_detail *cd, struct ip_map *ipm, struct unix_domain *udom, time_t expiry);
static int ip_map_parse(struct cache_detail *cd,
char *mesg, int mlen)
@ -219,10 +220,9 @@ static int ip_map_parse(struct cache_detail *cd,
switch (address.sa.sa_family) {
case AF_INET:
/* Form a mapped IPv4 address in sin6 */
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
sin6.sin6_addr.s6_addr32[3] = address.s4.sin_addr.s_addr;
ipv6_addr_set_v4mapped(address.s4.sin_addr.s_addr,
&sin6.sin6_addr);
break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
@ -249,9 +249,9 @@ static int ip_map_parse(struct cache_detail *cd,
dom = NULL;
/* IPv6 scope IDs are ignored for now */
ipmp = ip_map_lookup(class, &sin6.sin6_addr);
ipmp = __ip_map_lookup(cd, class, &sin6.sin6_addr);
if (ipmp) {
err = ip_map_update(ipmp,
err = __ip_map_update(cd, ipmp,
container_of(dom, struct unix_domain, h),
expiry);
} else
@ -294,29 +294,15 @@ static int ip_map_show(struct seq_file *m,
}
struct cache_detail ip_map_cache = {
.owner = THIS_MODULE,
.hash_size = IP_HASHMAX,
.hash_table = ip_table,
.name = "auth.unix.ip",
.cache_put = ip_map_put,
.cache_upcall = ip_map_upcall,
.cache_parse = ip_map_parse,
.cache_show = ip_map_show,
.match = ip_map_match,
.init = ip_map_init,
.update = update,
.alloc = ip_map_alloc,
};
static struct ip_map *ip_map_lookup(char *class, struct in6_addr *addr)
static struct ip_map *__ip_map_lookup(struct cache_detail *cd, char *class,
struct in6_addr *addr)
{
struct ip_map ip;
struct cache_head *ch;
strcpy(ip.m_class, class);
ipv6_addr_copy(&ip.m_addr, addr);
ch = sunrpc_cache_lookup(&ip_map_cache, &ip.h,
ch = sunrpc_cache_lookup(cd, &ip.h,
hash_str(class, IP_HASHBITS) ^
hash_ip6(*addr));
@ -326,7 +312,17 @@ static struct ip_map *ip_map_lookup(char *class, struct in6_addr *addr)
return NULL;
}
static int ip_map_update(struct ip_map *ipm, struct unix_domain *udom, time_t expiry)
static inline struct ip_map *ip_map_lookup(struct net *net, char *class,
struct in6_addr *addr)
{
struct sunrpc_net *sn;
sn = net_generic(net, sunrpc_net_id);
return __ip_map_lookup(sn->ip_map_cache, class, addr);
}
static int __ip_map_update(struct cache_detail *cd, struct ip_map *ipm,
struct unix_domain *udom, time_t expiry)
{
struct ip_map ip;
struct cache_head *ch;
@ -344,17 +340,25 @@ static int ip_map_update(struct ip_map *ipm, struct unix_domain *udom, time_t ex
ip.m_add_change++;
}
ip.h.expiry_time = expiry;
ch = sunrpc_cache_update(&ip_map_cache,
&ip.h, &ipm->h,
ch = sunrpc_cache_update(cd, &ip.h, &ipm->h,
hash_str(ipm->m_class, IP_HASHBITS) ^
hash_ip6(ipm->m_addr));
if (!ch)
return -ENOMEM;
cache_put(ch, &ip_map_cache);
cache_put(ch, cd);
return 0;
}
int auth_unix_add_addr(struct in6_addr *addr, struct auth_domain *dom)
static inline int ip_map_update(struct net *net, struct ip_map *ipm,
struct unix_domain *udom, time_t expiry)
{
struct sunrpc_net *sn;
sn = net_generic(net, sunrpc_net_id);
return __ip_map_update(sn->ip_map_cache, ipm, udom, expiry);
}
int auth_unix_add_addr(struct net *net, struct in6_addr *addr, struct auth_domain *dom)
{
struct unix_domain *udom;
struct ip_map *ipmp;
@ -362,10 +366,10 @@ int auth_unix_add_addr(struct in6_addr *addr, struct auth_domain *dom)
if (dom->flavour != &svcauth_unix)
return -EINVAL;
udom = container_of(dom, struct unix_domain, h);
ipmp = ip_map_lookup("nfsd", addr);
ipmp = ip_map_lookup(net, "nfsd", addr);
if (ipmp)
return ip_map_update(ipmp, udom, NEVER);
return ip_map_update(net, ipmp, udom, NEVER);
else
return -ENOMEM;
}
@ -383,16 +387,18 @@ int auth_unix_forget_old(struct auth_domain *dom)
}
EXPORT_SYMBOL_GPL(auth_unix_forget_old);
struct auth_domain *auth_unix_lookup(struct in6_addr *addr)
struct auth_domain *auth_unix_lookup(struct net *net, struct in6_addr *addr)
{
struct ip_map *ipm;
struct auth_domain *rv;
struct sunrpc_net *sn;
ipm = ip_map_lookup("nfsd", addr);
sn = net_generic(net, sunrpc_net_id);
ipm = ip_map_lookup(net, "nfsd", addr);
if (!ipm)
return NULL;
if (cache_check(&ip_map_cache, &ipm->h, NULL))
if (cache_check(sn->ip_map_cache, &ipm->h, NULL))
return NULL;
if ((ipm->m_client->addr_changes - ipm->m_add_change) >0) {
@ -403,22 +409,29 @@ struct auth_domain *auth_unix_lookup(struct in6_addr *addr)
rv = &ipm->m_client->h;
kref_get(&rv->ref);
}
cache_put(&ipm->h, &ip_map_cache);
cache_put(&ipm->h, sn->ip_map_cache);
return rv;
}
EXPORT_SYMBOL_GPL(auth_unix_lookup);
void svcauth_unix_purge(void)
{
cache_purge(&ip_map_cache);
struct net *net;
for_each_net(net) {
struct sunrpc_net *sn;
sn = net_generic(net, sunrpc_net_id);
cache_purge(sn->ip_map_cache);
}
}
EXPORT_SYMBOL_GPL(svcauth_unix_purge);
static inline struct ip_map *
ip_map_cached_get(struct svc_rqst *rqstp)
ip_map_cached_get(struct svc_xprt *xprt)
{
struct ip_map *ipm = NULL;
struct svc_xprt *xprt = rqstp->rq_xprt;
struct sunrpc_net *sn;
if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags)) {
spin_lock(&xprt->xpt_lock);
@ -430,9 +443,10 @@ ip_map_cached_get(struct svc_rqst *rqstp)
* remembered, e.g. by a second mount from the
* same IP address.
*/
sn = net_generic(xprt->xpt_net, sunrpc_net_id);
xprt->xpt_auth_cache = NULL;
spin_unlock(&xprt->xpt_lock);
cache_put(&ipm->h, &ip_map_cache);
cache_put(&ipm->h, sn->ip_map_cache);
return NULL;
}
cache_get(&ipm->h);
@ -443,10 +457,8 @@ ip_map_cached_get(struct svc_rqst *rqstp)
}
static inline void
ip_map_cached_put(struct svc_rqst *rqstp, struct ip_map *ipm)
ip_map_cached_put(struct svc_xprt *xprt, struct ip_map *ipm)
{
struct svc_xprt *xprt = rqstp->rq_xprt;
if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags)) {
spin_lock(&xprt->xpt_lock);
if (xprt->xpt_auth_cache == NULL) {
@ -456,15 +468,26 @@ ip_map_cached_put(struct svc_rqst *rqstp, struct ip_map *ipm)
}
spin_unlock(&xprt->xpt_lock);
}
if (ipm)
cache_put(&ipm->h, &ip_map_cache);
if (ipm) {
struct sunrpc_net *sn;
sn = net_generic(xprt->xpt_net, sunrpc_net_id);
cache_put(&ipm->h, sn->ip_map_cache);
}
}
void
svcauth_unix_info_release(void *info)
svcauth_unix_info_release(struct svc_xprt *xpt)
{
struct ip_map *ipm = info;
cache_put(&ipm->h, &ip_map_cache);
struct ip_map *ipm;
ipm = xpt->xpt_auth_cache;
if (ipm != NULL) {
struct sunrpc_net *sn;
sn = net_generic(xpt->xpt_net, sunrpc_net_id);
cache_put(&ipm->h, sn->ip_map_cache);
}
}
/****************************************************************************
@ -674,6 +697,8 @@ static struct group_info *unix_gid_find(uid_t uid, struct svc_rqst *rqstp)
switch (ret) {
case -ENOENT:
return ERR_PTR(-ENOENT);
case -ETIMEDOUT:
return ERR_PTR(-ESHUTDOWN);
case 0:
gi = get_group_info(ug->gi);
cache_put(&ug->h, &unix_gid_cache);
@ -691,6 +716,9 @@ svcauth_unix_set_client(struct svc_rqst *rqstp)
struct ip_map *ipm;
struct group_info *gi;
struct svc_cred *cred = &rqstp->rq_cred;
struct svc_xprt *xprt = rqstp->rq_xprt;
struct net *net = xprt->xpt_net;
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
switch (rqstp->rq_addr.ss_family) {
case AF_INET:
@ -709,26 +737,27 @@ svcauth_unix_set_client(struct svc_rqst *rqstp)
if (rqstp->rq_proc == 0)
return SVC_OK;
ipm = ip_map_cached_get(rqstp);
ipm = ip_map_cached_get(xprt);
if (ipm == NULL)
ipm = ip_map_lookup(rqstp->rq_server->sv_program->pg_class,
ipm = __ip_map_lookup(sn->ip_map_cache, rqstp->rq_server->sv_program->pg_class,
&sin6->sin6_addr);
if (ipm == NULL)
return SVC_DENIED;
switch (cache_check(&ip_map_cache, &ipm->h, &rqstp->rq_chandle)) {
switch (cache_check(sn->ip_map_cache, &ipm->h, &rqstp->rq_chandle)) {
default:
BUG();
case -EAGAIN:
case -ETIMEDOUT:
return SVC_CLOSE;
case -EAGAIN:
return SVC_DROP;
case -ENOENT:
return SVC_DENIED;
case 0:
rqstp->rq_client = &ipm->m_client->h;
kref_get(&rqstp->rq_client->ref);
ip_map_cached_put(rqstp, ipm);
ip_map_cached_put(xprt, ipm);
break;
}
@ -736,6 +765,8 @@ svcauth_unix_set_client(struct svc_rqst *rqstp)
switch (PTR_ERR(gi)) {
case -EAGAIN:
return SVC_DROP;
case -ESHUTDOWN:
return SVC_CLOSE;
case -ENOENT:
break;
default:
@ -776,7 +807,7 @@ svcauth_null_accept(struct svc_rqst *rqstp, __be32 *authp)
cred->cr_gid = (gid_t) -1;
cred->cr_group_info = groups_alloc(0);
if (cred->cr_group_info == NULL)
return SVC_DROP; /* kmalloc failure - client must retry */
return SVC_CLOSE; /* kmalloc failure - client must retry */
/* Put NULL verifier */
svc_putnl(resv, RPC_AUTH_NULL);
@ -840,7 +871,7 @@ svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp)
goto badcred;
cred->cr_group_info = groups_alloc(slen);
if (cred->cr_group_info == NULL)
return SVC_DROP;
return SVC_CLOSE;
for (i = 0; i < slen; i++)
GROUP_AT(cred->cr_group_info, i) = svc_getnl(argv);
if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) {
@ -886,3 +917,56 @@ struct auth_ops svcauth_unix = {
.set_client = svcauth_unix_set_client,
};
int ip_map_cache_create(struct net *net)
{
int err = -ENOMEM;
struct cache_detail *cd;
struct cache_head **tbl;
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
cd = kzalloc(sizeof(struct cache_detail), GFP_KERNEL);
if (cd == NULL)
goto err_cd;
tbl = kzalloc(IP_HASHMAX * sizeof(struct cache_head *), GFP_KERNEL);
if (tbl == NULL)
goto err_tbl;
cd->owner = THIS_MODULE,
cd->hash_size = IP_HASHMAX,
cd->hash_table = tbl,
cd->name = "auth.unix.ip",
cd->cache_put = ip_map_put,
cd->cache_upcall = ip_map_upcall,
cd->cache_parse = ip_map_parse,
cd->cache_show = ip_map_show,
cd->match = ip_map_match,
cd->init = ip_map_init,
cd->update = update,
cd->alloc = ip_map_alloc,
err = cache_register_net(cd, net);
if (err)
goto err_reg;
sn->ip_map_cache = cd;
return 0;
err_reg:
kfree(tbl);
err_tbl:
kfree(cd);
err_cd:
return err;
}
void ip_map_cache_destroy(struct net *net)
{
struct sunrpc_net *sn;
sn = net_generic(net, sunrpc_net_id);
cache_purge(sn->ip_map_cache);
cache_unregister_net(sn->ip_map_cache, net);
kfree(sn->ip_map_cache->hash_table);
kfree(sn->ip_map_cache);
}

View file

@ -64,7 +64,8 @@ static void svc_tcp_sock_detach(struct svc_xprt *);
static void svc_sock_free(struct svc_xprt *);
static struct svc_xprt *svc_create_socket(struct svc_serv *, int,
struct sockaddr *, int, int);
struct net *, struct sockaddr *,
int, int);
#ifdef CONFIG_DEBUG_LOCK_ALLOC
static struct lock_class_key svc_key[2];
static struct lock_class_key svc_slock_key[2];
@ -657,10 +658,11 @@ static struct svc_xprt *svc_udp_accept(struct svc_xprt *xprt)
}
static struct svc_xprt *svc_udp_create(struct svc_serv *serv,
struct net *net,
struct sockaddr *sa, int salen,
int flags)
{
return svc_create_socket(serv, IPPROTO_UDP, sa, salen, flags);
return svc_create_socket(serv, IPPROTO_UDP, net, sa, salen, flags);
}
static struct svc_xprt_ops svc_udp_ops = {
@ -1133,9 +1135,6 @@ static int svc_tcp_sendto(struct svc_rqst *rqstp)
reclen = htonl(0x80000000|((xbufp->len ) - 4));
memcpy(xbufp->head[0].iov_base, &reclen, 4);
if (test_bit(XPT_DEAD, &rqstp->rq_xprt->xpt_flags))
return -ENOTCONN;
sent = svc_sendto(rqstp, &rqstp->rq_res);
if (sent != xbufp->len) {
printk(KERN_NOTICE
@ -1178,10 +1177,11 @@ static int svc_tcp_has_wspace(struct svc_xprt *xprt)
}
static struct svc_xprt *svc_tcp_create(struct svc_serv *serv,
struct net *net,
struct sockaddr *sa, int salen,
int flags)
{
return svc_create_socket(serv, IPPROTO_TCP, sa, salen, flags);
return svc_create_socket(serv, IPPROTO_TCP, net, sa, salen, flags);
}
static struct svc_xprt_ops svc_tcp_ops = {
@ -1258,19 +1258,13 @@ void svc_sock_update_bufs(struct svc_serv *serv)
* The number of server threads has changed. Update
* rcvbuf and sndbuf accordingly on all sockets
*/
struct list_head *le;
struct svc_sock *svsk;
spin_lock_bh(&serv->sv_lock);
list_for_each(le, &serv->sv_permsocks) {
struct svc_sock *svsk =
list_entry(le, struct svc_sock, sk_xprt.xpt_list);
list_for_each_entry(svsk, &serv->sv_permsocks, sk_xprt.xpt_list)
set_bit(XPT_CHNGBUF, &svsk->sk_xprt.xpt_flags);
}
list_for_each(le, &serv->sv_tempsocks) {
struct svc_sock *svsk =
list_entry(le, struct svc_sock, sk_xprt.xpt_list);
list_for_each_entry(svsk, &serv->sv_tempsocks, sk_xprt.xpt_list)
set_bit(XPT_CHNGBUF, &svsk->sk_xprt.xpt_flags);
}
spin_unlock_bh(&serv->sv_lock);
}
EXPORT_SYMBOL_GPL(svc_sock_update_bufs);
@ -1385,6 +1379,7 @@ EXPORT_SYMBOL_GPL(svc_addsock);
*/
static struct svc_xprt *svc_create_socket(struct svc_serv *serv,
int protocol,
struct net *net,
struct sockaddr *sin, int len,
int flags)
{
@ -1421,7 +1416,7 @@ static struct svc_xprt *svc_create_socket(struct svc_serv *serv,
return ERR_PTR(-EINVAL);
}
error = sock_create_kern(family, type, protocol, &sock);
error = __sock_create(net, family, type, protocol, &sock, 1);
if (error < 0)
return ERR_PTR(error);

View file

@ -199,8 +199,6 @@ int xprt_reserve_xprt(struct rpc_task *task)
if (test_and_set_bit(XPRT_LOCKED, &xprt->state)) {
if (task == xprt->snd_task)
return 1;
if (task == NULL)
return 0;
goto out_sleep;
}
xprt->snd_task = task;
@ -757,13 +755,11 @@ static void xprt_connect_status(struct rpc_task *task)
*/
struct rpc_rqst *xprt_lookup_rqst(struct rpc_xprt *xprt, __be32 xid)
{
struct list_head *pos;
struct rpc_rqst *entry;
list_for_each(pos, &xprt->recv) {
struct rpc_rqst *entry = list_entry(pos, struct rpc_rqst, rq_list);
list_for_each_entry(entry, &xprt->recv, rq_list)
if (entry->rq_xid == xid)
return entry;
}
dprintk("RPC: xprt_lookup_rqst did not find xid %08x\n",
ntohl(xid));
@ -962,6 +958,37 @@ static void xprt_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req)
spin_unlock(&xprt->reserve_lock);
}
struct rpc_xprt *xprt_alloc(struct net *net, int size, int max_req)
{
struct rpc_xprt *xprt;
xprt = kzalloc(size, GFP_KERNEL);
if (xprt == NULL)
goto out;
xprt->max_reqs = max_req;
xprt->slot = kcalloc(max_req, sizeof(struct rpc_rqst), GFP_KERNEL);
if (xprt->slot == NULL)
goto out_free;
xprt->xprt_net = get_net(net);
return xprt;
out_free:
kfree(xprt);
out:
return NULL;
}
EXPORT_SYMBOL_GPL(xprt_alloc);
void xprt_free(struct rpc_xprt *xprt)
{
put_net(xprt->xprt_net);
kfree(xprt->slot);
kfree(xprt);
}
EXPORT_SYMBOL_GPL(xprt_free);
/**
* xprt_reserve - allocate an RPC request slot
* @task: RPC task requesting a slot allocation

View file

@ -43,6 +43,7 @@
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/sysctl.h>
#include <linux/workqueue.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/sched.h>
#include <linux/sunrpc/svc_rdma.h>
@ -74,6 +75,8 @@ atomic_t rdma_stat_sq_prod;
struct kmem_cache *svc_rdma_map_cachep;
struct kmem_cache *svc_rdma_ctxt_cachep;
struct workqueue_struct *svc_rdma_wq;
/*
* This function implements reading and resetting an atomic_t stat
* variable through read/write to a proc file. Any write to the file
@ -231,7 +234,7 @@ static ctl_table svcrdma_root_table[] = {
void svc_rdma_cleanup(void)
{
dprintk("SVCRDMA Module Removed, deregister RPC RDMA transport\n");
flush_scheduled_work();
destroy_workqueue(svc_rdma_wq);
if (svcrdma_table_header) {
unregister_sysctl_table(svcrdma_table_header);
svcrdma_table_header = NULL;
@ -249,6 +252,11 @@ int svc_rdma_init(void)
dprintk("\tsq_depth : %d\n",
svcrdma_max_requests * RPCRDMA_SQ_DEPTH_MULT);
dprintk("\tmax_inline : %d\n", svcrdma_max_req_size);
svc_rdma_wq = alloc_workqueue("svc_rdma", 0, 0);
if (!svc_rdma_wq)
return -ENOMEM;
if (!svcrdma_table_header)
svcrdma_table_header =
register_sysctl_table(svcrdma_root_table);
@ -283,6 +291,7 @@ int svc_rdma_init(void)
kmem_cache_destroy(svc_rdma_map_cachep);
err0:
unregister_sysctl_table(svcrdma_table_header);
destroy_workqueue(svc_rdma_wq);
return -ENOMEM;
}
MODULE_AUTHOR("Tom Tucker <tom@opengridcomputing.com>");

View file

@ -263,9 +263,9 @@ static int fast_reg_read_chunks(struct svcxprt_rdma *xprt,
frmr->page_list_len = PAGE_ALIGN(byte_count) >> PAGE_SHIFT;
for (page_no = 0; page_no < frmr->page_list_len; page_no++) {
frmr->page_list->page_list[page_no] =
ib_dma_map_single(xprt->sc_cm_id->device,
page_address(rqstp->rq_arg.pages[page_no]),
PAGE_SIZE, DMA_FROM_DEVICE);
ib_dma_map_page(xprt->sc_cm_id->device,
rqstp->rq_arg.pages[page_no], 0,
PAGE_SIZE, DMA_FROM_DEVICE);
if (ib_dma_mapping_error(xprt->sc_cm_id->device,
frmr->page_list->page_list[page_no]))
goto fatal_err;
@ -309,17 +309,21 @@ static int rdma_set_ctxt_sge(struct svcxprt_rdma *xprt,
int count)
{
int i;
unsigned long off;
ctxt->count = count;
ctxt->direction = DMA_FROM_DEVICE;
for (i = 0; i < count; i++) {
ctxt->sge[i].length = 0; /* in case map fails */
if (!frmr) {
BUG_ON(0 == virt_to_page(vec[i].iov_base));
off = (unsigned long)vec[i].iov_base & ~PAGE_MASK;
ctxt->sge[i].addr =
ib_dma_map_single(xprt->sc_cm_id->device,
vec[i].iov_base,
vec[i].iov_len,
DMA_FROM_DEVICE);
ib_dma_map_page(xprt->sc_cm_id->device,
virt_to_page(vec[i].iov_base),
off,
vec[i].iov_len,
DMA_FROM_DEVICE);
if (ib_dma_mapping_error(xprt->sc_cm_id->device,
ctxt->sge[i].addr))
return -EINVAL;
@ -491,6 +495,7 @@ static int rdma_read_xdr(struct svcxprt_rdma *xprt,
printk(KERN_ERR "svcrdma: Error %d posting RDMA_READ\n",
err);
set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags);
svc_rdma_unmap_dma(ctxt);
svc_rdma_put_context(ctxt, 0);
goto out;
}

View file

@ -70,8 +70,8 @@
* on extra page for the RPCRMDA header.
*/
static int fast_reg_xdr(struct svcxprt_rdma *xprt,
struct xdr_buf *xdr,
struct svc_rdma_req_map *vec)
struct xdr_buf *xdr,
struct svc_rdma_req_map *vec)
{
int sge_no;
u32 sge_bytes;
@ -96,21 +96,25 @@ static int fast_reg_xdr(struct svcxprt_rdma *xprt,
vec->count = 2;
sge_no++;
/* Build the FRMR */
/* Map the XDR head */
frmr->kva = frva;
frmr->direction = DMA_TO_DEVICE;
frmr->access_flags = 0;
frmr->map_len = PAGE_SIZE;
frmr->page_list_len = 1;
page_off = (unsigned long)xdr->head[0].iov_base & ~PAGE_MASK;
frmr->page_list->page_list[page_no] =
ib_dma_map_single(xprt->sc_cm_id->device,
(void *)xdr->head[0].iov_base,
PAGE_SIZE, DMA_TO_DEVICE);
ib_dma_map_page(xprt->sc_cm_id->device,
virt_to_page(xdr->head[0].iov_base),
page_off,
PAGE_SIZE - page_off,
DMA_TO_DEVICE);
if (ib_dma_mapping_error(xprt->sc_cm_id->device,
frmr->page_list->page_list[page_no]))
goto fatal_err;
atomic_inc(&xprt->sc_dma_used);
/* Map the XDR page list */
page_off = xdr->page_base;
page_bytes = xdr->page_len + page_off;
if (!page_bytes)
@ -128,9 +132,9 @@ static int fast_reg_xdr(struct svcxprt_rdma *xprt,
page_bytes -= sge_bytes;
frmr->page_list->page_list[page_no] =
ib_dma_map_single(xprt->sc_cm_id->device,
page_address(page),
PAGE_SIZE, DMA_TO_DEVICE);
ib_dma_map_page(xprt->sc_cm_id->device,
page, page_off,
sge_bytes, DMA_TO_DEVICE);
if (ib_dma_mapping_error(xprt->sc_cm_id->device,
frmr->page_list->page_list[page_no]))
goto fatal_err;
@ -166,8 +170,10 @@ static int fast_reg_xdr(struct svcxprt_rdma *xprt,
vec->sge[sge_no].iov_base = frva + frmr->map_len + page_off;
frmr->page_list->page_list[page_no] =
ib_dma_map_single(xprt->sc_cm_id->device, va, PAGE_SIZE,
DMA_TO_DEVICE);
ib_dma_map_page(xprt->sc_cm_id->device, virt_to_page(va),
page_off,
PAGE_SIZE,
DMA_TO_DEVICE);
if (ib_dma_mapping_error(xprt->sc_cm_id->device,
frmr->page_list->page_list[page_no]))
goto fatal_err;
@ -245,6 +251,35 @@ static int map_xdr(struct svcxprt_rdma *xprt,
return 0;
}
static dma_addr_t dma_map_xdr(struct svcxprt_rdma *xprt,
struct xdr_buf *xdr,
u32 xdr_off, size_t len, int dir)
{
struct page *page;
dma_addr_t dma_addr;
if (xdr_off < xdr->head[0].iov_len) {
/* This offset is in the head */
xdr_off += (unsigned long)xdr->head[0].iov_base & ~PAGE_MASK;
page = virt_to_page(xdr->head[0].iov_base);
} else {
xdr_off -= xdr->head[0].iov_len;
if (xdr_off < xdr->page_len) {
/* This offset is in the page list */
page = xdr->pages[xdr_off >> PAGE_SHIFT];
xdr_off &= ~PAGE_MASK;
} else {
/* This offset is in the tail */
xdr_off -= xdr->page_len;
xdr_off += (unsigned long)
xdr->tail[0].iov_base & ~PAGE_MASK;
page = virt_to_page(xdr->tail[0].iov_base);
}
}
dma_addr = ib_dma_map_page(xprt->sc_cm_id->device, page, xdr_off,
min_t(size_t, PAGE_SIZE, len), dir);
return dma_addr;
}
/* Assumptions:
* - We are using FRMR
* - or -
@ -293,10 +328,9 @@ static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp,
sge[sge_no].length = sge_bytes;
if (!vec->frmr) {
sge[sge_no].addr =
ib_dma_map_single(xprt->sc_cm_id->device,
(void *)
vec->sge[xdr_sge_no].iov_base + sge_off,
sge_bytes, DMA_TO_DEVICE);
dma_map_xdr(xprt, &rqstp->rq_res, xdr_off,
sge_bytes, DMA_TO_DEVICE);
xdr_off += sge_bytes;
if (ib_dma_mapping_error(xprt->sc_cm_id->device,
sge[sge_no].addr))
goto err;
@ -333,6 +367,8 @@ static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp,
goto err;
return 0;
err:
svc_rdma_unmap_dma(ctxt);
svc_rdma_put_frmr(xprt, vec->frmr);
svc_rdma_put_context(ctxt, 0);
/* Fatal error, close transport */
return -EIO;
@ -494,7 +530,8 @@ static int send_reply_chunks(struct svcxprt_rdma *xprt,
* In all three cases, this function prepares the RPCRDMA header in
* sge[0], the 'type' parameter indicates the type to place in the
* RPCRDMA header, and the 'byte_count' field indicates how much of
* the XDR to include in this RDMA_SEND.
* the XDR to include in this RDMA_SEND. NB: The offset of the payload
* to send is zero in the XDR.
*/
static int send_reply(struct svcxprt_rdma *rdma,
struct svc_rqst *rqstp,
@ -536,23 +573,24 @@ static int send_reply(struct svcxprt_rdma *rdma,
ctxt->sge[0].lkey = rdma->sc_dma_lkey;
ctxt->sge[0].length = svc_rdma_xdr_get_reply_hdr_len(rdma_resp);
ctxt->sge[0].addr =
ib_dma_map_single(rdma->sc_cm_id->device, page_address(page),
ctxt->sge[0].length, DMA_TO_DEVICE);
ib_dma_map_page(rdma->sc_cm_id->device, page, 0,
ctxt->sge[0].length, DMA_TO_DEVICE);
if (ib_dma_mapping_error(rdma->sc_cm_id->device, ctxt->sge[0].addr))
goto err;
atomic_inc(&rdma->sc_dma_used);
ctxt->direction = DMA_TO_DEVICE;
/* Determine how many of our SGE are to be transmitted */
/* Map the payload indicated by 'byte_count' */
for (sge_no = 1; byte_count && sge_no < vec->count; sge_no++) {
int xdr_off = 0;
sge_bytes = min_t(size_t, vec->sge[sge_no].iov_len, byte_count);
byte_count -= sge_bytes;
if (!vec->frmr) {
ctxt->sge[sge_no].addr =
ib_dma_map_single(rdma->sc_cm_id->device,
vec->sge[sge_no].iov_base,
sge_bytes, DMA_TO_DEVICE);
dma_map_xdr(rdma, &rqstp->rq_res, xdr_off,
sge_bytes, DMA_TO_DEVICE);
xdr_off += sge_bytes;
if (ib_dma_mapping_error(rdma->sc_cm_id->device,
ctxt->sge[sge_no].addr))
goto err;

View file

@ -45,6 +45,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <rdma/ib_verbs.h>
#include <rdma/rdma_cm.h>
#include <linux/sunrpc/svc_rdma.h>
@ -52,6 +53,7 @@
#define RPCDBG_FACILITY RPCDBG_SVCXPRT
static struct svc_xprt *svc_rdma_create(struct svc_serv *serv,
struct net *net,
struct sockaddr *sa, int salen,
int flags);
static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt);
@ -89,6 +91,9 @@ struct svc_xprt_class svc_rdma_class = {
/* WR context cache. Created in svc_rdma.c */
extern struct kmem_cache *svc_rdma_ctxt_cachep;
/* Workqueue created in svc_rdma.c */
extern struct workqueue_struct *svc_rdma_wq;
struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *xprt)
{
struct svc_rdma_op_ctxt *ctxt;
@ -120,7 +125,7 @@ void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt)
*/
if (ctxt->sge[i].lkey == xprt->sc_dma_lkey) {
atomic_dec(&xprt->sc_dma_used);
ib_dma_unmap_single(xprt->sc_cm_id->device,
ib_dma_unmap_page(xprt->sc_cm_id->device,
ctxt->sge[i].addr,
ctxt->sge[i].length,
ctxt->direction);
@ -502,8 +507,8 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt)
BUG_ON(sge_no >= xprt->sc_max_sge);
page = svc_rdma_get_page();
ctxt->pages[sge_no] = page;
pa = ib_dma_map_single(xprt->sc_cm_id->device,
page_address(page), PAGE_SIZE,
pa = ib_dma_map_page(xprt->sc_cm_id->device,
page, 0, PAGE_SIZE,
DMA_FROM_DEVICE);
if (ib_dma_mapping_error(xprt->sc_cm_id->device, pa))
goto err_put_ctxt;
@ -511,9 +516,9 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt)
ctxt->sge[sge_no].addr = pa;
ctxt->sge[sge_no].length = PAGE_SIZE;
ctxt->sge[sge_no].lkey = xprt->sc_dma_lkey;
ctxt->count = sge_no + 1;
buflen += PAGE_SIZE;
}
ctxt->count = sge_no;
recv_wr.next = NULL;
recv_wr.sg_list = &ctxt->sge[0];
recv_wr.num_sge = ctxt->count;
@ -529,6 +534,7 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt)
return ret;
err_put_ctxt:
svc_rdma_unmap_dma(ctxt);
svc_rdma_put_context(ctxt, 1);
return -ENOMEM;
}
@ -670,6 +676,7 @@ static int rdma_cma_handler(struct rdma_cm_id *cma_id,
* Create a listening RDMA service endpoint.
*/
static struct svc_xprt *svc_rdma_create(struct svc_serv *serv,
struct net *net,
struct sockaddr *sa, int salen,
int flags)
{
@ -798,8 +805,8 @@ static void frmr_unmap_dma(struct svcxprt_rdma *xprt,
if (ib_dma_mapping_error(frmr->mr->device, addr))
continue;
atomic_dec(&xprt->sc_dma_used);
ib_dma_unmap_single(frmr->mr->device, addr, PAGE_SIZE,
frmr->direction);
ib_dma_unmap_page(frmr->mr->device, addr, PAGE_SIZE,
frmr->direction);
}
}
@ -1184,7 +1191,7 @@ static void svc_rdma_free(struct svc_xprt *xprt)
struct svcxprt_rdma *rdma =
container_of(xprt, struct svcxprt_rdma, sc_xprt);
INIT_WORK(&rdma->sc_work, __svc_rdma_free);
schedule_work(&rdma->sc_work);
queue_work(svc_rdma_wq, &rdma->sc_work);
}
static int svc_rdma_has_wspace(struct svc_xprt *xprt)
@ -1274,7 +1281,7 @@ int svc_rdma_send(struct svcxprt_rdma *xprt, struct ib_send_wr *wr)
atomic_read(&xprt->sc_sq_count) <
xprt->sc_sq_depth);
if (test_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags))
return 0;
return -ENOTCONN;
continue;
}
/* Take a transport ref for each WR posted */
@ -1306,7 +1313,6 @@ void svc_rdma_send_error(struct svcxprt_rdma *xprt, struct rpcrdma_msg *rmsgp,
enum rpcrdma_errcode err)
{
struct ib_send_wr err_wr;
struct ib_sge sge;
struct page *p;
struct svc_rdma_op_ctxt *ctxt;
u32 *va;
@ -1319,26 +1325,27 @@ void svc_rdma_send_error(struct svcxprt_rdma *xprt, struct rpcrdma_msg *rmsgp,
/* XDR encode error */
length = svc_rdma_xdr_encode_error(xprt, rmsgp, err, va);
ctxt = svc_rdma_get_context(xprt);
ctxt->direction = DMA_FROM_DEVICE;
ctxt->count = 1;
ctxt->pages[0] = p;
/* Prepare SGE for local address */
sge.addr = ib_dma_map_single(xprt->sc_cm_id->device,
page_address(p), PAGE_SIZE, DMA_FROM_DEVICE);
if (ib_dma_mapping_error(xprt->sc_cm_id->device, sge.addr)) {
ctxt->sge[0].addr = ib_dma_map_page(xprt->sc_cm_id->device,
p, 0, length, DMA_FROM_DEVICE);
if (ib_dma_mapping_error(xprt->sc_cm_id->device, ctxt->sge[0].addr)) {
put_page(p);
return;
}
atomic_inc(&xprt->sc_dma_used);
sge.lkey = xprt->sc_dma_lkey;
sge.length = length;
ctxt = svc_rdma_get_context(xprt);
ctxt->count = 1;
ctxt->pages[0] = p;
ctxt->sge[0].lkey = xprt->sc_dma_lkey;
ctxt->sge[0].length = length;
/* Prepare SEND WR */
memset(&err_wr, 0, sizeof err_wr);
ctxt->wr_op = IB_WR_SEND;
err_wr.wr_id = (unsigned long)ctxt;
err_wr.sg_list = &sge;
err_wr.sg_list = ctxt->sge;
err_wr.num_sge = 1;
err_wr.opcode = IB_WR_SEND;
err_wr.send_flags = IB_SEND_SIGNALED;
@ -1348,9 +1355,7 @@ void svc_rdma_send_error(struct svcxprt_rdma *xprt, struct rpcrdma_msg *rmsgp,
if (ret) {
dprintk("svcrdma: Error %d posting send for protocol error\n",
ret);
ib_dma_unmap_single(xprt->sc_cm_id->device,
sge.addr, PAGE_SIZE,
DMA_FROM_DEVICE);
svc_rdma_unmap_dma(ctxt);
svc_rdma_put_context(ctxt, 1);
}
}

View file

@ -237,8 +237,7 @@ xprt_rdma_destroy(struct rpc_xprt *xprt)
dprintk("RPC: %s: called\n", __func__);
cancel_delayed_work(&r_xprt->rdma_connect);
flush_scheduled_work();
cancel_delayed_work_sync(&r_xprt->rdma_connect);
xprt_clear_connected(xprt);
@ -251,9 +250,7 @@ xprt_rdma_destroy(struct rpc_xprt *xprt)
xprt_rdma_free_addresses(xprt);
kfree(xprt->slot);
xprt->slot = NULL;
kfree(xprt);
xprt_free(xprt);
dprintk("RPC: %s: returning\n", __func__);
@ -285,23 +282,14 @@ xprt_setup_rdma(struct xprt_create *args)
return ERR_PTR(-EBADF);
}
xprt = kzalloc(sizeof(struct rpcrdma_xprt), GFP_KERNEL);
xprt = xprt_alloc(args->net, sizeof(struct rpcrdma_xprt),
xprt_rdma_slot_table_entries);
if (xprt == NULL) {
dprintk("RPC: %s: couldn't allocate rpcrdma_xprt\n",
__func__);
return ERR_PTR(-ENOMEM);
}
xprt->max_reqs = xprt_rdma_slot_table_entries;
xprt->slot = kcalloc(xprt->max_reqs,
sizeof(struct rpc_rqst), GFP_KERNEL);
if (xprt->slot == NULL) {
dprintk("RPC: %s: couldn't allocate %d slots\n",
__func__, xprt->max_reqs);
kfree(xprt);
return ERR_PTR(-ENOMEM);
}
/* 60 second timeout, no retries */
xprt->timeout = &xprt_rdma_default_timeout;
xprt->bind_timeout = (60U * HZ);
@ -410,8 +398,7 @@ xprt_setup_rdma(struct xprt_create *args)
out2:
rpcrdma_ia_close(&new_xprt->rx_ia);
out1:
kfree(xprt->slot);
kfree(xprt);
xprt_free(xprt);
return ERR_PTR(rc);
}
@ -460,7 +447,7 @@ xprt_rdma_connect(struct rpc_task *task)
} else {
schedule_delayed_work(&r_xprt->rdma_connect, 0);
if (!RPC_IS_ASYNC(task))
flush_scheduled_work();
flush_delayed_work(&r_xprt->rdma_connect);
}
}

View file

@ -774,8 +774,7 @@ static void xs_destroy(struct rpc_xprt *xprt)
xs_close(xprt);
xs_free_peer_addresses(xprt);
kfree(xprt->slot);
kfree(xprt);
xprt_free(xprt);
module_put(THIS_MODULE);
}
@ -1516,7 +1515,7 @@ static void xs_set_port(struct rpc_xprt *xprt, unsigned short port)
xs_update_peer_port(xprt);
}
static unsigned short xs_get_srcport(struct sock_xprt *transport, struct socket *sock)
static unsigned short xs_get_srcport(struct sock_xprt *transport)
{
unsigned short port = transport->srcport;
@ -1525,7 +1524,7 @@ static unsigned short xs_get_srcport(struct sock_xprt *transport, struct socket
return port;
}
static unsigned short xs_next_srcport(struct sock_xprt *transport, struct socket *sock, unsigned short port)
static unsigned short xs_next_srcport(struct sock_xprt *transport, unsigned short port)
{
if (transport->srcport != 0)
transport->srcport = 0;
@ -1535,23 +1534,18 @@ static unsigned short xs_next_srcport(struct sock_xprt *transport, struct socket
return xprt_max_resvport;
return --port;
}
static int xs_bind4(struct sock_xprt *transport, struct socket *sock)
static int xs_bind(struct sock_xprt *transport, struct socket *sock)
{
struct sockaddr_in myaddr = {
.sin_family = AF_INET,
};
struct sockaddr_in *sa;
struct sockaddr_storage myaddr;
int err, nloop = 0;
unsigned short port = xs_get_srcport(transport, sock);
unsigned short port = xs_get_srcport(transport);
unsigned short last;
sa = (struct sockaddr_in *)&transport->srcaddr;
myaddr.sin_addr = sa->sin_addr;
memcpy(&myaddr, &transport->srcaddr, transport->xprt.addrlen);
do {
myaddr.sin_port = htons(port);
err = kernel_bind(sock, (struct sockaddr *) &myaddr,
sizeof(myaddr));
rpc_set_port((struct sockaddr *)&myaddr, port);
err = kernel_bind(sock, (struct sockaddr *)&myaddr,
transport->xprt.addrlen);
if (port == 0)
break;
if (err == 0) {
@ -1559,47 +1553,22 @@ static int xs_bind4(struct sock_xprt *transport, struct socket *sock)
break;
}
last = port;
port = xs_next_srcport(transport, sock, port);
port = xs_next_srcport(transport, port);
if (port > last)
nloop++;
} while (err == -EADDRINUSE && nloop != 2);
dprintk("RPC: %s %pI4:%u: %s (%d)\n",
__func__, &myaddr.sin_addr,
port, err ? "failed" : "ok", err);
if (myaddr.ss_family == AF_INET)
dprintk("RPC: %s %pI4:%u: %s (%d)\n", __func__,
&((struct sockaddr_in *)&myaddr)->sin_addr,
port, err ? "failed" : "ok", err);
else
dprintk("RPC: %s %pI6:%u: %s (%d)\n", __func__,
&((struct sockaddr_in6 *)&myaddr)->sin6_addr,
port, err ? "failed" : "ok", err);
return err;
}
static int xs_bind6(struct sock_xprt *transport, struct socket *sock)
{
struct sockaddr_in6 myaddr = {
.sin6_family = AF_INET6,
};
struct sockaddr_in6 *sa;
int err, nloop = 0;
unsigned short port = xs_get_srcport(transport, sock);
unsigned short last;
sa = (struct sockaddr_in6 *)&transport->srcaddr;
myaddr.sin6_addr = sa->sin6_addr;
do {
myaddr.sin6_port = htons(port);
err = kernel_bind(sock, (struct sockaddr *) &myaddr,
sizeof(myaddr));
if (port == 0)
break;
if (err == 0) {
transport->srcport = port;
break;
}
last = port;
port = xs_next_srcport(transport, sock, port);
if (port > last)
nloop++;
} while (err == -EADDRINUSE && nloop != 2);
dprintk("RPC: xs_bind6 %pI6:%u: %s (%d)\n",
&myaddr.sin6_addr, port, err ? "failed" : "ok", err);
return err;
}
#ifdef CONFIG_DEBUG_LOCK_ALLOC
static struct lock_class_key xs_key[2];
@ -1622,6 +1591,18 @@ static inline void xs_reclassify_socket6(struct socket *sock)
sock_lock_init_class_and_name(sk, "slock-AF_INET6-RPC",
&xs_slock_key[1], "sk_lock-AF_INET6-RPC", &xs_key[1]);
}
static inline void xs_reclassify_socket(int family, struct socket *sock)
{
switch (family) {
case AF_INET:
xs_reclassify_socket4(sock);
break;
case AF_INET6:
xs_reclassify_socket6(sock);
break;
}
}
#else
static inline void xs_reclassify_socket4(struct socket *sock)
{
@ -1630,8 +1611,36 @@ static inline void xs_reclassify_socket4(struct socket *sock)
static inline void xs_reclassify_socket6(struct socket *sock)
{
}
static inline void xs_reclassify_socket(int family, struct socket *sock)
{
}
#endif
static struct socket *xs_create_sock(struct rpc_xprt *xprt,
struct sock_xprt *transport, int family, int type, int protocol)
{
struct socket *sock;
int err;
err = __sock_create(xprt->xprt_net, family, type, protocol, &sock, 1);
if (err < 0) {
dprintk("RPC: can't create %d transport socket (%d).\n",
protocol, -err);
goto out;
}
xs_reclassify_socket(family, sock);
if (xs_bind(transport, sock)) {
sock_release(sock);
goto out;
}
return sock;
out:
return ERR_PTR(err);
}
static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
{
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
@ -1661,82 +1670,23 @@ static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
xs_udp_do_set_buffer_size(xprt);
}
/**
* xs_udp_connect_worker4 - set up a UDP socket
* @work: RPC transport to connect
*
* Invoked by a work queue tasklet.
*/
static void xs_udp_connect_worker4(struct work_struct *work)
static void xs_udp_setup_socket(struct work_struct *work)
{
struct sock_xprt *transport =
container_of(work, struct sock_xprt, connect_worker.work);
struct rpc_xprt *xprt = &transport->xprt;
struct socket *sock = transport->sock;
int err, status = -EIO;
int status = -EIO;
if (xprt->shutdown)
goto out;
/* Start by resetting any existing state */
xs_reset_transport(transport);
err = sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock);
if (err < 0) {
dprintk("RPC: can't create UDP transport socket (%d).\n", -err);
sock = xs_create_sock(xprt, transport,
xs_addr(xprt)->sa_family, SOCK_DGRAM, IPPROTO_UDP);
if (IS_ERR(sock))
goto out;
}
xs_reclassify_socket4(sock);
if (xs_bind4(transport, sock)) {
sock_release(sock);
goto out;
}
dprintk("RPC: worker connecting xprt %p via %s to "
"%s (port %s)\n", xprt,
xprt->address_strings[RPC_DISPLAY_PROTO],
xprt->address_strings[RPC_DISPLAY_ADDR],
xprt->address_strings[RPC_DISPLAY_PORT]);
xs_udp_finish_connecting(xprt, sock);
status = 0;
out:
xprt_clear_connecting(xprt);
xprt_wake_pending_tasks(xprt, status);
}
/**
* xs_udp_connect_worker6 - set up a UDP socket
* @work: RPC transport to connect
*
* Invoked by a work queue tasklet.
*/
static void xs_udp_connect_worker6(struct work_struct *work)
{
struct sock_xprt *transport =
container_of(work, struct sock_xprt, connect_worker.work);
struct rpc_xprt *xprt = &transport->xprt;
struct socket *sock = transport->sock;
int err, status = -EIO;
if (xprt->shutdown)
goto out;
/* Start by resetting any existing state */
xs_reset_transport(transport);
err = sock_create_kern(PF_INET6, SOCK_DGRAM, IPPROTO_UDP, &sock);
if (err < 0) {
dprintk("RPC: can't create UDP transport socket (%d).\n", -err);
goto out;
}
xs_reclassify_socket6(sock);
if (xs_bind6(transport, sock) < 0) {
sock_release(sock);
goto out;
}
dprintk("RPC: worker connecting xprt %p via %s to "
"%s (port %s)\n", xprt,
@ -1755,12 +1705,12 @@ static void xs_udp_connect_worker6(struct work_struct *work)
* We need to preserve the port number so the reply cache on the server can
* find our cached RPC replies when we get around to reconnecting.
*/
static void xs_abort_connection(struct rpc_xprt *xprt, struct sock_xprt *transport)
static void xs_abort_connection(struct sock_xprt *transport)
{
int result;
struct sockaddr any;
dprintk("RPC: disconnecting xprt %p to reuse port\n", xprt);
dprintk("RPC: disconnecting xprt %p to reuse port\n", transport);
/*
* Disconnect the transport socket by doing a connect operation
@ -1770,13 +1720,13 @@ static void xs_abort_connection(struct rpc_xprt *xprt, struct sock_xprt *transpo
any.sa_family = AF_UNSPEC;
result = kernel_connect(transport->sock, &any, sizeof(any), 0);
if (!result)
xs_sock_mark_closed(xprt);
xs_sock_mark_closed(&transport->xprt);
else
dprintk("RPC: AF_UNSPEC connect return code %d\n",
result);
}
static void xs_tcp_reuse_connection(struct rpc_xprt *xprt, struct sock_xprt *transport)
static void xs_tcp_reuse_connection(struct sock_xprt *transport)
{
unsigned int state = transport->inet->sk_state;
@ -1799,7 +1749,7 @@ static void xs_tcp_reuse_connection(struct rpc_xprt *xprt, struct sock_xprt *tra
"sk_shutdown set to %d\n",
__func__, transport->inet->sk_shutdown);
}
xs_abort_connection(xprt, transport);
xs_abort_connection(transport);
}
static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
@ -1852,12 +1802,12 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
*
* Invoked by a work queue tasklet.
*/
static void xs_tcp_setup_socket(struct rpc_xprt *xprt,
struct sock_xprt *transport,
struct socket *(*create_sock)(struct rpc_xprt *,
struct sock_xprt *))
static void xs_tcp_setup_socket(struct work_struct *work)
{
struct sock_xprt *transport =
container_of(work, struct sock_xprt, connect_worker.work);
struct socket *sock = transport->sock;
struct rpc_xprt *xprt = &transport->xprt;
int status = -EIO;
if (xprt->shutdown)
@ -1865,7 +1815,8 @@ static void xs_tcp_setup_socket(struct rpc_xprt *xprt,
if (!sock) {
clear_bit(XPRT_CONNECTION_ABORT, &xprt->state);
sock = create_sock(xprt, transport);
sock = xs_create_sock(xprt, transport,
xs_addr(xprt)->sa_family, SOCK_STREAM, IPPROTO_TCP);
if (IS_ERR(sock)) {
status = PTR_ERR(sock);
goto out;
@ -1876,7 +1827,7 @@ static void xs_tcp_setup_socket(struct rpc_xprt *xprt,
abort_and_exit = test_and_clear_bit(XPRT_CONNECTION_ABORT,
&xprt->state);
/* "close" the socket, preserving the local port */
xs_tcp_reuse_connection(xprt, transport);
xs_tcp_reuse_connection(transport);
if (abort_and_exit)
goto out_eagain;
@ -1925,84 +1876,6 @@ static void xs_tcp_setup_socket(struct rpc_xprt *xprt,
xprt_wake_pending_tasks(xprt, status);
}
static struct socket *xs_create_tcp_sock4(struct rpc_xprt *xprt,
struct sock_xprt *transport)
{
struct socket *sock;
int err;
/* start from scratch */
err = sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &sock);
if (err < 0) {
dprintk("RPC: can't create TCP transport socket (%d).\n",
-err);
goto out_err;
}
xs_reclassify_socket4(sock);
if (xs_bind4(transport, sock) < 0) {
sock_release(sock);
goto out_err;
}
return sock;
out_err:
return ERR_PTR(-EIO);
}
/**
* xs_tcp_connect_worker4 - connect a TCP socket to a remote endpoint
* @work: RPC transport to connect
*
* Invoked by a work queue tasklet.
*/
static void xs_tcp_connect_worker4(struct work_struct *work)
{
struct sock_xprt *transport =
container_of(work, struct sock_xprt, connect_worker.work);
struct rpc_xprt *xprt = &transport->xprt;
xs_tcp_setup_socket(xprt, transport, xs_create_tcp_sock4);
}
static struct socket *xs_create_tcp_sock6(struct rpc_xprt *xprt,
struct sock_xprt *transport)
{
struct socket *sock;
int err;
/* start from scratch */
err = sock_create_kern(PF_INET6, SOCK_STREAM, IPPROTO_TCP, &sock);
if (err < 0) {
dprintk("RPC: can't create TCP transport socket (%d).\n",
-err);
goto out_err;
}
xs_reclassify_socket6(sock);
if (xs_bind6(transport, sock) < 0) {
sock_release(sock);
goto out_err;
}
return sock;
out_err:
return ERR_PTR(-EIO);
}
/**
* xs_tcp_connect_worker6 - connect a TCP socket to a remote endpoint
* @work: RPC transport to connect
*
* Invoked by a work queue tasklet.
*/
static void xs_tcp_connect_worker6(struct work_struct *work)
{
struct sock_xprt *transport =
container_of(work, struct sock_xprt, connect_worker.work);
struct rpc_xprt *xprt = &transport->xprt;
xs_tcp_setup_socket(xprt, transport, xs_create_tcp_sock6);
}
/**
* xs_connect - connect a socket to a remote endpoint
* @task: address of RPC task that manages state of connect request
@ -2262,6 +2135,31 @@ static struct rpc_xprt_ops bc_tcp_ops = {
.print_stats = xs_tcp_print_stats,
};
static int xs_init_anyaddr(const int family, struct sockaddr *sap)
{
static const struct sockaddr_in sin = {
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(INADDR_ANY),
};
static const struct sockaddr_in6 sin6 = {
.sin6_family = AF_INET6,
.sin6_addr = IN6ADDR_ANY_INIT,
};
switch (family) {
case AF_INET:
memcpy(sap, &sin, sizeof(sin));
break;
case AF_INET6:
memcpy(sap, &sin6, sizeof(sin6));
break;
default:
dprintk("RPC: %s: Bad address family\n", __func__);
return -EAFNOSUPPORT;
}
return 0;
}
static struct rpc_xprt *xs_setup_xprt(struct xprt_create *args,
unsigned int slot_table_size)
{
@ -2273,27 +2171,25 @@ static struct rpc_xprt *xs_setup_xprt(struct xprt_create *args,
return ERR_PTR(-EBADF);
}
new = kzalloc(sizeof(*new), GFP_KERNEL);
if (new == NULL) {
xprt = xprt_alloc(args->net, sizeof(*new), slot_table_size);
if (xprt == NULL) {
dprintk("RPC: xs_setup_xprt: couldn't allocate "
"rpc_xprt\n");
return ERR_PTR(-ENOMEM);
}
xprt = &new->xprt;
xprt->max_reqs = slot_table_size;
xprt->slot = kcalloc(xprt->max_reqs, sizeof(struct rpc_rqst), GFP_KERNEL);
if (xprt->slot == NULL) {
kfree(xprt);
dprintk("RPC: xs_setup_xprt: couldn't allocate slot "
"table\n");
return ERR_PTR(-ENOMEM);
}
new = container_of(xprt, struct sock_xprt, xprt);
memcpy(&xprt->addr, args->dstaddr, args->addrlen);
xprt->addrlen = args->addrlen;
if (args->srcaddr)
memcpy(&new->srcaddr, args->srcaddr, args->addrlen);
else {
int err;
err = xs_init_anyaddr(args->dstaddr->sa_family,
(struct sockaddr *)&new->srcaddr);
if (err != 0)
return ERR_PTR(err);
}
return xprt;
}
@ -2341,7 +2237,7 @@ static struct rpc_xprt *xs_setup_udp(struct xprt_create *args)
xprt_set_bound(xprt);
INIT_DELAYED_WORK(&transport->connect_worker,
xs_udp_connect_worker4);
xs_udp_setup_socket);
xs_format_peer_addresses(xprt, "udp", RPCBIND_NETID_UDP);
break;
case AF_INET6:
@ -2349,7 +2245,7 @@ static struct rpc_xprt *xs_setup_udp(struct xprt_create *args)
xprt_set_bound(xprt);
INIT_DELAYED_WORK(&transport->connect_worker,
xs_udp_connect_worker6);
xs_udp_setup_socket);
xs_format_peer_addresses(xprt, "udp", RPCBIND_NETID_UDP6);
break;
default:
@ -2371,8 +2267,7 @@ static struct rpc_xprt *xs_setup_udp(struct xprt_create *args)
return xprt;
ret = ERR_PTR(-EINVAL);
out_err:
kfree(xprt->slot);
kfree(xprt);
xprt_free(xprt);
return ret;
}
@ -2416,7 +2311,7 @@ static struct rpc_xprt *xs_setup_tcp(struct xprt_create *args)
xprt_set_bound(xprt);
INIT_DELAYED_WORK(&transport->connect_worker,
xs_tcp_connect_worker4);
xs_tcp_setup_socket);
xs_format_peer_addresses(xprt, "tcp", RPCBIND_NETID_TCP);
break;
case AF_INET6:
@ -2424,7 +2319,7 @@ static struct rpc_xprt *xs_setup_tcp(struct xprt_create *args)
xprt_set_bound(xprt);
INIT_DELAYED_WORK(&transport->connect_worker,
xs_tcp_connect_worker6);
xs_tcp_setup_socket);
xs_format_peer_addresses(xprt, "tcp", RPCBIND_NETID_TCP6);
break;
default:
@ -2447,8 +2342,7 @@ static struct rpc_xprt *xs_setup_tcp(struct xprt_create *args)
return xprt;
ret = ERR_PTR(-EINVAL);
out_err:
kfree(xprt->slot);
kfree(xprt);
xprt_free(xprt);
return ret;
}
@ -2507,15 +2401,10 @@ static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args)
goto out_err;
}
if (xprt_bound(xprt))
dprintk("RPC: set up xprt to %s (port %s) via %s\n",
xprt->address_strings[RPC_DISPLAY_ADDR],
xprt->address_strings[RPC_DISPLAY_PORT],
xprt->address_strings[RPC_DISPLAY_PROTO]);
else
dprintk("RPC: set up xprt to %s (autobind) via %s\n",
xprt->address_strings[RPC_DISPLAY_ADDR],
xprt->address_strings[RPC_DISPLAY_PROTO]);
dprintk("RPC: set up xprt to %s (port %s) via %s\n",
xprt->address_strings[RPC_DISPLAY_ADDR],
xprt->address_strings[RPC_DISPLAY_PORT],
xprt->address_strings[RPC_DISPLAY_PROTO]);
/*
* Since we don't want connections for the backchannel, we set
@ -2528,8 +2417,7 @@ static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args)
return xprt;
ret = ERR_PTR(-EINVAL);
out_err:
kfree(xprt->slot);
kfree(xprt);
xprt_free(xprt);
return ret;
}