Commit f57fa1d6 authored by Linus Torvalds's avatar Linus Torvalds

Merge git://git.linux-nfs.org/projects/trondmy/nfs-2.6

* git://git.linux-nfs.org/projects/trondmy/nfs-2.6: (70 commits)
  fs/nfs/nfs4proc.c: make nfs4_map_errors() static
  rpc: add service field to new upcall
  rpc: add target field to new upcall
  nfsd: support callbacks with gss flavors
  rpc: allow gss callbacks to client
  rpc: pass target name down to rpc level on callbacks
  nfsd: pass client principal name in rsc downcall
  rpc: implement new upcall
  rpc: store pointer to pipe inode in gss upcall message
  rpc: use count of pipe openers to wait for first open
  rpc: track number of users of the gss upcall pipe
  rpc: call release_pipe only on last close
  rpc: add an rpc_pipe_open method
  rpc: minor gss_alloc_msg cleanup
  rpc: factor out warning code from gss_pipe_destroy_msg
  rpc: remove unnecessary assignment
  NFS: remove unused status from encode routines
  NFS: increment number of operations in each encode routine
  NFS: fix comment placement in nfs4xdr.c
  NFS: fix tabs in nfs4xdr.c
  ...
parents 6094c85a 08cc36cb
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/sunrpc/svc.h> #include <linux/sunrpc/svc.h>
#include <linux/lockd/lockd.h> #include <linux/lockd/lockd.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <linux/kthread.h>
#define NLMDBG_FACILITY NLMDBG_CLIENT #define NLMDBG_FACILITY NLMDBG_CLIENT
...@@ -60,7 +61,7 @@ struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init) ...@@ -60,7 +61,7 @@ struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init)
host = nlmclnt_lookup_host(nlm_init->address, nlm_init->addrlen, host = nlmclnt_lookup_host(nlm_init->address, nlm_init->addrlen,
nlm_init->protocol, nlm_version, nlm_init->protocol, nlm_version,
nlm_init->hostname); nlm_init->hostname, nlm_init->noresvport);
if (host == NULL) { if (host == NULL) {
lockd_down(); lockd_down();
return ERR_PTR(-ENOLCK); return ERR_PTR(-ENOLCK);
...@@ -191,11 +192,15 @@ __be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock) ...@@ -191,11 +192,15 @@ __be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock)
void void
nlmclnt_recovery(struct nlm_host *host) nlmclnt_recovery(struct nlm_host *host)
{ {
struct task_struct *task;
if (!host->h_reclaiming++) { if (!host->h_reclaiming++) {
nlm_get_host(host); nlm_get_host(host);
__module_get(THIS_MODULE); task = kthread_run(reclaimer, host, "%s-reclaim", host->h_name);
if (kernel_thread(reclaimer, host, CLONE_FS | CLONE_FILES) < 0) if (IS_ERR(task))
module_put(THIS_MODULE); printk(KERN_ERR "lockd: unable to spawn reclaimer "
"thread. Locks for %s won't be reclaimed! "
"(%ld)\n", host->h_name, PTR_ERR(task));
} }
} }
...@@ -207,7 +212,6 @@ reclaimer(void *ptr) ...@@ -207,7 +212,6 @@ reclaimer(void *ptr)
struct file_lock *fl, *next; struct file_lock *fl, *next;
u32 nsmstate; u32 nsmstate;
daemonize("%s-reclaim", host->h_name);
allow_signal(SIGKILL); allow_signal(SIGKILL);
down_write(&host->h_rwsem); down_write(&host->h_rwsem);
...@@ -233,7 +237,12 @@ reclaimer(void *ptr) ...@@ -233,7 +237,12 @@ reclaimer(void *ptr)
list_for_each_entry_safe(fl, next, &host->h_reclaim, fl_u.nfs_fl.list) { list_for_each_entry_safe(fl, next, &host->h_reclaim, fl_u.nfs_fl.list) {
list_del_init(&fl->fl_u.nfs_fl.list); list_del_init(&fl->fl_u.nfs_fl.list);
/* Why are we leaking memory here? --okir */ /*
* sending this thread a SIGKILL will result in any unreclaimed
* locks being removed from the h_granted list. This means that
* the kernel will not attempt to reclaim them again if a new
* reclaimer thread is spawned for this host.
*/
if (signalled()) if (signalled())
continue; continue;
if (nlmclnt_reclaim(host, fl) != 0) if (nlmclnt_reclaim(host, fl) != 0)
...@@ -261,5 +270,5 @@ reclaimer(void *ptr) ...@@ -261,5 +270,5 @@ reclaimer(void *ptr)
nlm_release_host(host); nlm_release_host(host);
lockd_down(); lockd_down();
unlock_kernel(); unlock_kernel();
module_put_and_exit(0); return 0;
} }
...@@ -48,6 +48,7 @@ struct nlm_lookup_host_info { ...@@ -48,6 +48,7 @@ struct nlm_lookup_host_info {
const size_t hostname_len; /* it's length */ const size_t hostname_len; /* it's length */
const struct sockaddr *src_sap; /* our address (optional) */ const struct sockaddr *src_sap; /* our address (optional) */
const size_t src_len; /* it's length */ const size_t src_len; /* it's length */
const int noresvport; /* use non-priv port */
}; };
/* /*
...@@ -222,6 +223,7 @@ static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni) ...@@ -222,6 +223,7 @@ static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni)
host->h_nsmstate = 0; /* real NSM state */ host->h_nsmstate = 0; /* real NSM state */
host->h_nsmhandle = nsm; host->h_nsmhandle = nsm;
host->h_server = ni->server; host->h_server = ni->server;
host->h_noresvport = ni->noresvport;
hlist_add_head(&host->h_hash, chain); hlist_add_head(&host->h_hash, chain);
INIT_LIST_HEAD(&host->h_lockowners); INIT_LIST_HEAD(&host->h_lockowners);
spin_lock_init(&host->h_lock); spin_lock_init(&host->h_lock);
...@@ -272,6 +274,7 @@ nlm_destroy_host(struct nlm_host *host) ...@@ -272,6 +274,7 @@ nlm_destroy_host(struct nlm_host *host)
* @protocol: transport protocol to use * @protocol: transport protocol to use
* @version: NLM protocol version * @version: NLM protocol version
* @hostname: '\0'-terminated hostname of server * @hostname: '\0'-terminated hostname of server
* @noresvport: 1 if non-privileged port should be used
* *
* Returns an nlm_host structure that matches the passed-in * Returns an nlm_host structure that matches the passed-in
* [server address, transport protocol, NLM version, server hostname]. * [server address, transport protocol, NLM version, server hostname].
...@@ -281,7 +284,9 @@ nlm_destroy_host(struct nlm_host *host) ...@@ -281,7 +284,9 @@ nlm_destroy_host(struct nlm_host *host)
struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap, struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
const size_t salen, const size_t salen,
const unsigned short protocol, const unsigned short protocol,
const u32 version, const char *hostname) const u32 version,
const char *hostname,
int noresvport)
{ {
const struct sockaddr source = { const struct sockaddr source = {
.sa_family = AF_UNSPEC, .sa_family = AF_UNSPEC,
...@@ -296,6 +301,7 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap, ...@@ -296,6 +301,7 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
.hostname_len = strlen(hostname), .hostname_len = strlen(hostname),
.src_sap = &source, .src_sap = &source,
.src_len = sizeof(source), .src_len = sizeof(source),
.noresvport = noresvport,
}; };
dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__, dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__,
...@@ -417,6 +423,8 @@ nlm_bind_host(struct nlm_host *host) ...@@ -417,6 +423,8 @@ nlm_bind_host(struct nlm_host *host)
*/ */
if (!host->h_server) if (!host->h_server)
args.flags |= RPC_CLNT_CREATE_HARDRTRY; args.flags |= RPC_CLNT_CREATE_HARDRTRY;
if (host->h_noresvport)
args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
clnt = rpc_create(&args); clnt = rpc_create(&args);
if (!IS_ERR(clnt)) if (!IS_ERR(clnt))
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
static struct svc_program nlmsvc_program; static struct svc_program nlmsvc_program;
struct nlmsvc_binding * nlmsvc_ops; struct nlmsvc_binding * nlmsvc_ops;
EXPORT_SYMBOL(nlmsvc_ops); EXPORT_SYMBOL_GPL(nlmsvc_ops);
static DEFINE_MUTEX(nlmsvc_mutex); static DEFINE_MUTEX(nlmsvc_mutex);
static unsigned int nlmsvc_users; static unsigned int nlmsvc_users;
...@@ -300,7 +300,7 @@ int lockd_up(void) ...@@ -300,7 +300,7 @@ int lockd_up(void)
mutex_unlock(&nlmsvc_mutex); mutex_unlock(&nlmsvc_mutex);
return error; return error;
} }
EXPORT_SYMBOL(lockd_up); EXPORT_SYMBOL_GPL(lockd_up);
/* /*
* Decrement the user count and bring down lockd if we're the last. * Decrement the user count and bring down lockd if we're the last.
...@@ -329,7 +329,7 @@ lockd_down(void) ...@@ -329,7 +329,7 @@ lockd_down(void)
out: out:
mutex_unlock(&nlmsvc_mutex); mutex_unlock(&nlmsvc_mutex);
} }
EXPORT_SYMBOL(lockd_down); EXPORT_SYMBOL_GPL(lockd_down);
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/sunrpc/svcauth_gss.h>
#include <net/inet_sock.h> #include <net/inet_sock.h>
...@@ -182,10 +183,34 @@ void nfs_callback_down(void) ...@@ -182,10 +183,34 @@ void nfs_callback_down(void)
mutex_unlock(&nfs_callback_mutex); mutex_unlock(&nfs_callback_mutex);
} }
static int check_gss_callback_principal(struct nfs_client *clp,
struct svc_rqst *rqstp)
{
struct rpc_clnt *r = clp->cl_rpcclient;
char *p = svc_gss_principal(rqstp);
/*
* It might just be a normal user principal, in which case
* userspace won't bother to tell us the name at all.
*/
if (p == NULL)
return SVC_DENIED;
/* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */
if (memcmp(p, "nfs@", 4) != 0)
return SVC_DENIED;
p += 4;
if (strcmp(p, r->cl_server) != 0)
return SVC_DENIED;
return SVC_OK;
}
static int nfs_callback_authenticate(struct svc_rqst *rqstp) static int nfs_callback_authenticate(struct svc_rqst *rqstp)
{ {
struct nfs_client *clp; struct nfs_client *clp;
RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]); RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
int ret = SVC_OK;
/* Don't talk to strangers */ /* Don't talk to strangers */
clp = nfs_find_client(svc_addr(rqstp), 4); clp = nfs_find_client(svc_addr(rqstp), 4);
...@@ -194,21 +219,22 @@ static int nfs_callback_authenticate(struct svc_rqst *rqstp) ...@@ -194,21 +219,22 @@ static int nfs_callback_authenticate(struct svc_rqst *rqstp)
dprintk("%s: %s NFSv4 callback!\n", __func__, dprintk("%s: %s NFSv4 callback!\n", __func__,
svc_print_addr(rqstp, buf, sizeof(buf))); svc_print_addr(rqstp, buf, sizeof(buf)));
nfs_put_client(clp);
switch (rqstp->rq_authop->flavour) { switch (rqstp->rq_authop->flavour) {
case RPC_AUTH_NULL: case RPC_AUTH_NULL:
if (rqstp->rq_proc != CB_NULL) if (rqstp->rq_proc != CB_NULL)
return SVC_DENIED; ret = SVC_DENIED;
break; break;
case RPC_AUTH_UNIX: case RPC_AUTH_UNIX:
break; break;
case RPC_AUTH_GSS: case RPC_AUTH_GSS:
/* FIXME: RPCSEC_GSS handling? */ ret = check_gss_callback_principal(clp, rqstp);
break;
default: default:
return SVC_DENIED; ret = SVC_DENIED;
} }
return SVC_OK; nfs_put_client(clp);
return ret;
} }
/* /*
......
...@@ -143,7 +143,6 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_ ...@@ -143,7 +143,6 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
clp->cl_proto = cl_init->proto; clp->cl_proto = cl_init->proto;
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
init_rwsem(&clp->cl_sem);
INIT_LIST_HEAD(&clp->cl_delegations); INIT_LIST_HEAD(&clp->cl_delegations);
spin_lock_init(&clp->cl_lock); spin_lock_init(&clp->cl_lock);
INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state);
...@@ -224,31 +223,54 @@ void nfs_put_client(struct nfs_client *clp) ...@@ -224,31 +223,54 @@ void nfs_put_client(struct nfs_client *clp)
} }
} }
static int nfs_sockaddr_match_ipaddr4(const struct sockaddr_in *sa1, #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
const struct sockaddr_in *sa2) static const struct in6_addr *nfs_map_ipv4_addr(const struct sockaddr *sa, struct in6_addr *addr_mapped)
{ {
return sa1->sin_addr.s_addr == sa2->sin_addr.s_addr; switch (sa->sa_family) {
default:
return NULL;
case AF_INET6:
return &((const struct sockaddr_in6 *)sa)->sin6_addr;
break;
case AF_INET:
ipv6_addr_set_v4mapped(((const struct sockaddr_in *)sa)->sin_addr.s_addr,
addr_mapped);
return addr_mapped;
}
} }
static int nfs_sockaddr_match_ipaddr6(const struct sockaddr_in6 *sa1, static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1,
const struct sockaddr_in6 *sa2) const struct sockaddr *sa2)
{
const struct in6_addr *addr1;
const struct in6_addr *addr2;
struct in6_addr addr1_mapped;
struct in6_addr addr2_mapped;
addr1 = nfs_map_ipv4_addr(sa1, &addr1_mapped);
if (likely(addr1 != NULL)) {
addr2 = nfs_map_ipv4_addr(sa2, &addr2_mapped);
if (likely(addr2 != NULL))
return ipv6_addr_equal(addr1, addr2);
}
return 0;
}
#else
static int nfs_sockaddr_match_ipaddr4(const struct sockaddr_in *sa1,
const struct sockaddr_in *sa2)
{ {
return ipv6_addr_equal(&sa1->sin6_addr, &sa2->sin6_addr); return sa1->sin_addr.s_addr == sa2->sin_addr.s_addr;
} }
static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1, static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1,
const struct sockaddr *sa2) const struct sockaddr *sa2)
{ {
switch (sa1->sa_family) { if (unlikely(sa1->sa_family != AF_INET || sa2->sa_family != AF_INET))
case AF_INET: return 0;
return nfs_sockaddr_match_ipaddr4((const struct sockaddr_in *)sa1, return nfs_sockaddr_match_ipaddr4((const struct sockaddr_in *)sa1,
(const struct sockaddr_in *)sa2); (const struct sockaddr_in *)sa2);
case AF_INET6:
return nfs_sockaddr_match_ipaddr6((const struct sockaddr_in6 *)sa1,
(const struct sockaddr_in6 *)sa2);
}
BUG();
} }
#endif
/* /*
* Find a client by IP address and protocol version * Find a client by IP address and protocol version
...@@ -270,8 +292,6 @@ struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion) ...@@ -270,8 +292,6 @@ struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion)
if (clp->rpc_ops->version != nfsversion) if (clp->rpc_ops->version != nfsversion)
continue; continue;
if (addr->sa_family != clap->sa_family)
continue;
/* Match only the IP address, not the port number */ /* Match only the IP address, not the port number */
if (!nfs_sockaddr_match_ipaddr(addr, clap)) if (!nfs_sockaddr_match_ipaddr(addr, clap))
continue; continue;
...@@ -305,8 +325,6 @@ struct nfs_client *nfs_find_client_next(struct nfs_client *clp) ...@@ -305,8 +325,6 @@ struct nfs_client *nfs_find_client_next(struct nfs_client *clp)
if (clp->rpc_ops->version != nfsvers) if (clp->rpc_ops->version != nfsvers)
continue; continue;
if (sap->sa_family != clap->sa_family)
continue;
/* Match only the IP address, not the port number */ /* Match only the IP address, not the port number */
if (!nfs_sockaddr_match_ipaddr(sap, clap)) if (!nfs_sockaddr_match_ipaddr(sap, clap))
continue; continue;
...@@ -470,7 +488,7 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, ...@@ -470,7 +488,7 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
static int nfs_create_rpc_client(struct nfs_client *clp, static int nfs_create_rpc_client(struct nfs_client *clp,
const struct rpc_timeout *timeparms, const struct rpc_timeout *timeparms,
rpc_authflavor_t flavor, rpc_authflavor_t flavor,
int flags) int discrtry, int noresvport)
{ {
struct rpc_clnt *clnt = NULL; struct rpc_clnt *clnt = NULL;
struct rpc_create_args args = { struct rpc_create_args args = {
...@@ -482,9 +500,13 @@ static int nfs_create_rpc_client(struct nfs_client *clp, ...@@ -482,9 +500,13 @@ static int nfs_create_rpc_client(struct nfs_client *clp,
.program = &nfs_program, .program = &nfs_program,
.version = clp->rpc_ops->version, .version = clp->rpc_ops->version,
.authflavor = flavor, .authflavor = flavor,
.flags = flags,
}; };
if (discrtry)
args.flags |= RPC_CLNT_CREATE_DISCRTRY;
if (noresvport)
args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
if (!IS_ERR(clp->cl_rpcclient)) if (!IS_ERR(clp->cl_rpcclient))
return 0; return 0;
...@@ -522,6 +544,8 @@ static int nfs_start_lockd(struct nfs_server *server) ...@@ -522,6 +544,8 @@ static int nfs_start_lockd(struct nfs_server *server)
.protocol = server->flags & NFS_MOUNT_TCP ? .protocol = server->flags & NFS_MOUNT_TCP ?
IPPROTO_TCP : IPPROTO_UDP, IPPROTO_TCP : IPPROTO_UDP,
.nfs_version = clp->rpc_ops->version, .nfs_version = clp->rpc_ops->version,
.noresvport = server->flags & NFS_MOUNT_NORESVPORT ?
1 : 0,
}; };
if (nlm_init.nfs_version > 3) if (nlm_init.nfs_version > 3)
...@@ -623,7 +647,8 @@ static int nfs_init_client(struct nfs_client *clp, ...@@ -623,7 +647,8 @@ static int nfs_init_client(struct nfs_client *clp,
* Create a client RPC handle for doing FSSTAT with UNIX auth only * Create a client RPC handle for doing FSSTAT with UNIX auth only
* - RFC 2623, sec 2.3.2 * - RFC 2623, sec 2.3.2
*/ */
error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX, 0); error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX,
0, data->flags & NFS_MOUNT_NORESVPORT);
if (error < 0) if (error < 0)
goto error; goto error;
nfs_mark_client_ready(clp, NFS_CS_READY); nfs_mark_client_ready(clp, NFS_CS_READY);
...@@ -965,7 +990,8 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, ...@@ -965,7 +990,8 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,
static int nfs4_init_client(struct nfs_client *clp, static int nfs4_init_client(struct nfs_client *clp,
const struct rpc_timeout *timeparms, const struct rpc_timeout *timeparms,
const char *ip_addr, const char *ip_addr,
rpc_authflavor_t authflavour) rpc_authflavor_t authflavour,
int flags)
{ {
int error; int error;
...@@ -979,7 +1005,7 @@ static int nfs4_init_client(struct nfs_client *clp, ...@@ -979,7 +1005,7 @@ static int nfs4_init_client(struct nfs_client *clp,
clp->rpc_ops = &nfs_v4_clientops; clp->rpc_ops = &nfs_v4_clientops;
error = nfs_create_rpc_client(clp, timeparms, authflavour, error = nfs_create_rpc_client(clp, timeparms, authflavour,
RPC_CLNT_CREATE_DISCRTRY); 1, flags & NFS_MOUNT_NORESVPORT);
if (error < 0) if (error < 0)
goto error; goto error;
memcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr)); memcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr));
...@@ -1030,7 +1056,8 @@ static int nfs4_set_client(struct nfs_server *server, ...@@ -1030,7 +1056,8 @@ static int nfs4_set_client(struct nfs_server *server,
error = PTR_ERR(clp); error = PTR_ERR(clp);
goto error; goto error;
} }
error = nfs4_init_client(clp, timeparms, ip_addr, authflavour); error = nfs4_init_client(clp, timeparms, ip_addr, authflavour,
server->flags);
if (error < 0) if (error < 0)
goto error_put; goto error_put;
...@@ -1059,6 +1086,10 @@ static int nfs4_init_server(struct nfs_server *server, ...@@ -1059,6 +1086,10 @@ static int nfs4_init_server(struct nfs_server *server,
nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
data->timeo, data->retrans); data->timeo, data->retrans);
/* Initialise the client representation from the mount data */
server->flags = data->flags;
server->caps |= NFS_CAP_ATOMIC_OPEN;
/* Get a client record */ /* Get a client record */
error = nfs4_set_client(server, error = nfs4_set_client(server,
data->nfs_server.hostname, data->nfs_server.hostname,
...@@ -1071,10 +1102,6 @@ static int nfs4_init_server(struct nfs_server *server, ...@@ -1071,10 +1102,6 @@ static int nfs4_init_server(struct nfs_server *server,
if (error < 0) if (error < 0)
goto error; goto error;
/* Initialise the client representation from the mount data */
server->flags = data->flags;
server->caps |= NFS_CAP_ATOMIC_OPEN;
if (data->rsize) if (data->rsize)
server->rsize = nfs_block_size(data->rsize, NULL); server->rsize = nfs_block_size(data->rsize, NULL);
if (data->wsize) if (data->wsize)
...@@ -1177,6 +1204,10 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, ...@@ -1177,6 +1204,10 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
parent_server = NFS_SB(data->sb); parent_server = NFS_SB(data->sb);
parent_client = parent_server->nfs_client; parent_client = parent_server->nfs_client;
/* Initialise the client representation from the parent server */
nfs_server_copy_userdata(server, parent_server);
server->caps |= NFS_CAP_ATOMIC_OPEN;
/* Get a client representation. /* Get a client representation.
* Note: NFSv4 always uses TCP, */ * Note: NFSv4 always uses TCP, */
error = nfs4_set_client(server, data->hostname, error = nfs4_set_client(server, data->hostname,
...@@ -1189,10 +1220,6 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, ...@@ -1189,10 +1220,6 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
if (error < 0) if (error < 0)
goto error; goto error;
/* Initialise the client representation from the parent server */
nfs_server_copy_userdata(server, parent_server);
server->caps |= NFS_CAP_ATOMIC_OPEN;
error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout, data->authflavor); error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout, data->authflavor);
if (error < 0) if (error < 0)
goto error; goto error;
......
...@@ -43,6 +43,27 @@ static void nfs_free_delegation(struct nfs_delegation *delegation) ...@@ -43,6 +43,27 @@ static void nfs_free_delegation(struct nfs_delegation *delegation)
put_rpccred(cred); put_rpccred(cred);
} }
void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
{
set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags);
}
int nfs_have_delegation(struct inode *inode, fmode_t flags)
{
struct nfs_delegation *delegation;
int ret = 0;
flags &= FMODE_READ|FMODE_WRITE;
rcu_read_lock();
delegation = rcu_dereference(NFS_I(inode)->delegation);
if (delegation != NULL && (delegation->type & flags) == flags) {
nfs_mark_delegation_referenced(delegation);
ret = 1;
}
rcu_read_unlock();
return ret;
}
static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state) static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state)
{ {
struct inode *inode = state->inode; struct inode *inode = state->inode;
...@@ -119,7 +140,7 @@ void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, st ...@@ -119,7 +140,7 @@ void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, st
delegation->maxsize = res->maxsize; delegation->maxsize = res->maxsize;
oldcred = delegation->cred; oldcred = delegation->cred;
delegation->cred = get_rpccred(cred); delegation->cred = get_rpccred(cred);
delegation->flags &= ~NFS_DELEGATION_NEED_RECLAIM; clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
NFS_I(inode)->delegation_state = delegation->type; NFS_I(inode)->delegation_state = delegation->type;
smp_wmb(); smp_wmb();
put_rpccred(oldcred); put_rpccred(oldcred);
...@@ -134,19 +155,35 @@ static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation * ...@@ -134,19 +155,35 @@ static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *
return res; return res;
} }
static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation)
{
struct inode *inode = NULL;
spin_lock(&delegation->lock);
if (delegation->inode != NULL)
inode = igrab(delegation->inode);
spin_unlock(&delegation->lock);
return inode;
}
static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid) static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid)
{ {
struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation);
if (delegation == NULL) if (delegation == NULL)
goto nomatch; goto nomatch;
spin_lock(&delegation->lock);
if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data, if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data,
sizeof(delegation->stateid.data)) != 0) sizeof(delegation->stateid.data)) != 0)
goto nomatch; goto nomatch_unlock;
list_del_rcu(&delegation->super_list); list_del_rcu(&delegation->super_list);
delegation->inode = NULL;
nfsi->delegation_state = 0; nfsi->delegation_state = 0;
rcu_assign_pointer(nfsi->delegation, NULL); rcu_assign_pointer(nfsi->delegation, NULL);
spin_unlock(&delegation->lock);
return delegation; return delegation;
nomatch_unlock:
spin_unlock(&delegation->lock);
nomatch: nomatch:
return NULL; return NULL;
} }
...@@ -172,6 +209,8 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct ...@@ -172,6 +209,8 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
delegation->change_attr = nfsi->change_attr; delegation->change_attr = nfsi->change_attr;
delegation->cred = get_rpccred(cred); delegation->cred = get_rpccred(cred);
delegation->inode = inode; delegation->inode = inode;
delegation->flags = 1<<NFS_DELEGATION_REFERENCED;
spin_lock_init(&delegation->lock);
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
if (rcu_dereference(nfsi->delegation) != NULL) { if (rcu_dereference(nfsi->delegation) != NULL) {
...@@ -226,21 +265,46 @@ static void nfs_msync_inode(struct inode *inode) ...@@ -226,21 +265,46 @@ static void nfs_msync_inode(struct inode *inode)
*/ */
static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation) static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation)
{ {
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
nfs_msync_inode(inode); nfs_msync_inode(inode);
down_read(&clp->cl_sem);
/* Guard against new delegated open calls */ /* Guard against new delegated open calls */
down_write(&nfsi->rwsem); down_write(&nfsi->rwsem);
nfs_delegation_claim_opens(inode, &delegation->stateid); nfs_delegation_claim_opens(inode, &delegation->stateid);
up_write(&nfsi->rwsem); up_write(&nfsi->rwsem);
up_read(&clp->cl_sem);
nfs_msync_inode(inode); nfs_msync_inode(inode);
return nfs_do_return_delegation(inode, delegation, 1); return nfs_do_return_delegation(inode, delegation, 1);
} }
/*
* Return all delegations that have been marked for return
*/
void nfs_client_return_marked_delegations(struct nfs_client *clp)
{
struct nfs_delegation *delegation;
struct inode *inode;
restart:
rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
if (!test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags))
continue;
inode = nfs_delegation_grab_inode(delegation);
if (inode == NULL)
continue;
spin_lock(&clp->cl_lock);
delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
spin_unlock(&clp->cl_lock);
rcu_read_unlock();
if (delegation != NULL)
__nfs_inode_return_delegation(inode, delegation);
iput(inode);
goto restart;
}
rcu_read_unlock();
}
/* /*
* This function returns the delegation without reclaiming opens * This function returns the delegation without reclaiming opens
* or protecting against delegation reclaims. * or protecting against delegation reclaims.
...@@ -279,83 +343,55 @@ int nfs_inode_return_delegation(struct inode *inode) ...@@ -279,83 +343,55 @@ int nfs_inode_return_delegation(struct inode *inode)
return err; return err;
} }
static void nfs_mark_return_delegation(struct nfs_client *clp, struct nfs_delegation *delegation)
{
set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
}
/* /*
* Return all delegations associated to a super block * Return all delegations associated to a super block
*/ */
void nfs_return_all_delegations(struct super_block *sb) void nfs_super_return_all_delegations(struct super_block *sb)
{ {
struct nfs_client *clp = NFS_SB(sb)->nfs_client; struct nfs_client *clp = NFS_SB(sb)->nfs_client;
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
struct inode *inode;
if (clp == NULL) if (clp == NULL)
return; return;
restart:
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
if (delegation->inode->i_sb != sb) spin_lock(&delegation->lock);
continue; if (delegation->inode != NULL && delegation->inode->i_sb == sb)
inode = igrab(delegation->inode); set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
if (inode == NULL) spin_unlock(&delegation->lock);
continue;
spin_lock(&clp->cl_lock);
delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
spin_unlock(&clp->cl_lock);
rcu_read_unlock();
if (delegation != NULL)
__nfs_inode_return_delegation(inode, delegation);
iput(inode);
goto restart;
} }
rcu_read_unlock(); rcu_read_unlock();
nfs_client_return_marked_delegations(clp);
} }
static int nfs_do_expire_all_delegations(void *ptr) static void nfs_client_mark_return_all_delegations(struct nfs_client *clp)
{ {
struct nfs_client *clp = ptr;
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
struct inode *inode;
allow_signal(SIGKILL);
restart:
if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) != 0)
goto out;
if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0)
goto out;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
inode = igrab(delegation->inode); set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
if (inode == NULL) set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
continue;
spin_lock(&clp->cl_lock);
delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
spin_unlock(&clp->cl_lock);
rcu_read_unlock();
if (delegation)
__nfs_inode_return_delegation(inode, delegation);
iput(inode);
goto restart;
} }
rcu_read_unlock(); rcu_read_unlock();
out: }
nfs_put_client(clp);
module_put_and_exit(0); static void nfs_delegation_run_state_manager(struct nfs_client *clp)
{
if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state))
nfs4_schedule_state_manager(clp);
} }
void nfs_expire_all_delegations(struct nfs_client *clp) void nfs_expire_all_delegations(struct nfs_client *clp)
{ {
struct task_struct *task; nfs_client_mark_return_all_delegations(clp);
nfs_delegation_run_state_manager(clp);
__module_get(THIS_MODULE);
atomic_inc(&clp->cl_count);
task = kthread_run(nfs_do_expire_all_delegations, clp,
"%s-delegreturn",
rpc_peeraddr2str(clp->cl_rpcclient,
RPC_DISPLAY_ADDR));
if (!IS_ERR(task))
return;
nfs_put_client(clp);
module_put(THIS_MODULE);
} }
/* /*
...@@ -363,68 +399,29 @@ void nfs_expire_all_delegations(struct nfs_client *clp) ...@@ -363,68 +399,29 @@ void nfs_expire_all_delegations(struct nfs_client *clp)
*/ */
void nfs_handle_cb_pathdown(struct nfs_client *clp) void nfs_handle_cb_pathdown(struct nfs_client *clp)
{ {
struct nfs_delegation *delegation;
struct inode *inode;
if (clp == NULL) if (clp == NULL)
return; return;
restart: nfs_client_mark_return_all_delegations(clp);
}
static void nfs_client_mark_return_unreferenced_delegations(struct nfs_client *clp)
{
struct nfs_delegation *delegation;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
inode = igrab(delegation->inode); if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
if (inode == NULL)
continue; continue;
spin_lock(&clp->cl_lock); set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
spin_unlock(&clp->cl_lock);
rcu_read_unlock();
if (delegation != NULL)
__nfs_inode_return_delegation(inode, delegation);
iput(inode);
goto restart;
} }
rcu_read_unlock(); rcu_read_unlock();
} }
struct recall_threadargs { void nfs_expire_unreferenced_delegations(struct nfs_client *clp)
struct inode *inode;
struct nfs_client *clp;
const nfs4_stateid *stateid;
struct completion started;
int result;
};
static int recall_thread(void *data)
{ {
struct recall_threadargs *args = (struct recall_threadargs *)data; nfs_client_mark_return_unreferenced_delegations(clp);
struct inode *inode = igrab(args->inode); nfs_delegation_run_state_manager(clp);
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation;
daemonize("nfsv4-delegreturn");
nfs_msync_inode(inode);
down_read(&clp->cl_sem);
down_write(&nfsi->rwsem);
spin_lock(&clp->cl_lock);
delegation = nfs_detach_delegation_locked(nfsi, args->stateid);
if (delegation != NULL)
args->result = 0;
else
args->result = -ENOENT;
spin_unlock(&clp->cl_lock);
complete(&args->started);
nfs_delegation_claim_opens(inode, args->stateid);
up_write(&nfsi->rwsem);
up_read(&clp->cl_sem);
nfs_msync_inode(inode);
if (delegation != NULL)
nfs_do_return_delegation(inode, delegation, 1);
iput(inode);
module_put_and_exit(0);
} }
/* /*
...@@ -432,22 +429,20 @@ static int recall_thread(void *data) ...@@ -432,22 +429,20 @@ static int recall_thread(void *data)
*/ */
int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid) int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid)
{ {
struct recall_threadargs data = { struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
.inode = inode, struct nfs_delegation *delegation;
.stateid = stateid,
};
int status;
init_completion(&data.started); rcu_read_lock();
__module_get(THIS_MODULE); delegation = rcu_dereference(NFS_I(inode)->delegation);
status = kernel_thread(recall_thread, &data, CLONE_KERNEL); if (delegation == NULL || memcmp(delegation->stateid.data, stateid->data,
if (status < 0) sizeof(delegation->stateid.data)) != 0) {
goto out_module_put; rcu_read_unlock();
wait_for_completion(&data.started); return -ENOENT;
return data.result; }
out_module_put: nfs_mark_return_delegation(clp, delegation);
module_put(THIS_MODULE); rcu_read_unlock();
return status; nfs_delegation_run_state_manager(clp);
return 0;
} }
/* /*
...@@ -459,10 +454,14 @@ struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs ...@@ -459,10 +454,14 @@ struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs
struct inode *res = NULL; struct inode *res = NULL;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
if (nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { spin_lock(&delegation->lock);
if (delegation->inode != NULL &&
nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
res = igrab(delegation->inode); res = igrab(delegation->inode);
break;
} }
spin_unlock(&delegation->lock);
if (res != NULL)
break;
} }
rcu_read_unlock(); rcu_read_unlock();
return res; return res;
...@@ -476,7 +475,7 @@ void nfs_delegation_mark_reclaim(struct nfs_client *clp) ...@@ -476,7 +475,7 @@ void nfs_delegation_mark_reclaim(struct nfs_client *clp)
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list)
delegation->flags |= NFS_DELEGATION_NEED_RECLAIM; set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
rcu_read_unlock(); rcu_read_unlock();
} }
...@@ -486,17 +485,22 @@ void nfs_delegation_mark_reclaim(struct nfs_client *clp) ...@@ -486,17 +485,22 @@ void nfs_delegation_mark_reclaim(struct nfs_client *clp)
void nfs_delegation_reap_unclaimed(struct nfs_client *clp) void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
{ {
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
struct inode *inode;
restart: restart:
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
if ((delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0) if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) == 0)
continue;
inode = nfs_delegation_grab_inode(delegation);
if (inode == NULL)
continue; continue;
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
delegation = nfs_detach_delegation_locked(NFS_I(delegation->inode), NULL); delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
rcu_read_unlock(); rcu_read_unlock();
if (delegation != NULL) if (delegation != NULL)
nfs_free_delegation(delegation); nfs_free_delegation(delegation);
iput(inode);
goto restart; goto restart;
} }
rcu_read_unlock(); rcu_read_unlock();
......
...@@ -17,14 +17,20 @@ struct nfs_delegation { ...@@ -17,14 +17,20 @@ struct nfs_delegation {
struct rpc_cred *cred; struct rpc_cred *cred;
struct inode *inode; struct inode *inode;
nfs4_stateid stateid; nfs4_stateid stateid;
int type; fmode_t type;
#define NFS_DELEGATION_NEED_RECLAIM 1
long flags;
loff_t maxsize; loff_t maxsize;
__u64 change_attr; __u64 change_attr;
unsigned long flags;
spinlock_t lock;
struct rcu_head rcu; struct rcu_head rcu;
}; };
enum {
NFS_DELEGATION_NEED_RECLAIM = 0,
NFS_DELEGATION_RETURN,
NFS_DELEGATION_REFERENCED,
};
int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
int nfs_inode_return_delegation(struct inode *inode); int nfs_inode_return_delegation(struct inode *inode);
...@@ -32,9 +38,11 @@ int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *s ...@@ -32,9 +38,11 @@ int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *s
void nfs_inode_return_delegation_noreclaim(struct inode *inode); void nfs_inode_return_delegation_noreclaim(struct inode *inode);
struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle); struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle);
void nfs_return_all_delegations(struct super_block *sb); void nfs_super_return_all_delegations(struct super_block *sb);
void nfs_expire_all_delegations(struct nfs_client *clp); void nfs_expire_all_delegations(struct nfs_client *clp);
void nfs_expire_unreferenced_delegations(struct nfs_client *clp);
void nfs_handle_cb_pathdown(struct nfs_client *clp); void nfs_handle_cb_pathdown(struct nfs_client *clp);
void nfs_client_return_marked_delegations(struct nfs_client *clp);
void nfs_delegation_mark_reclaim(struct nfs_client *clp); void nfs_delegation_mark_reclaim(struct nfs_client *clp);
void nfs_delegation_reap_unclaimed(struct nfs_client *clp); void nfs_delegation_reap_unclaimed(struct nfs_client *clp);
...@@ -45,22 +53,11 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state ...@@ -45,22 +53,11 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state
int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl); int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl);
int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode); int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode);
static inline int nfs_have_delegation(struct inode *inode, int flags) void nfs_mark_delegation_referenced(struct nfs_delegation *delegation);
{ int nfs_have_delegation(struct inode *inode, fmode_t flags);
struct nfs_delegation *delegation;
int ret = 0;
flags &= FMODE_READ|FMODE_WRITE;
rcu_read_lock();
delegation = rcu_dereference(NFS_I(inode)->delegation);
if (delegation != NULL && (delegation->type & flags) == flags)
ret = 1;
rcu_read_unlock();
return ret;
}
#else #else
static inline int nfs_have_delegation(struct inode *inode, int flags) static inline int nfs_have_delegation(struct inode *inode, fmode_t flags)
{ {
return 0; return 0;
} }
......
...@@ -799,6 +799,9 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) ...@@ -799,6 +799,9 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
goto out_bad; goto out_bad;
} }
if (nfs_have_delegation(inode, FMODE_READ))
goto out_set_verifier;
/* Force a full look up iff the parent directory has changed */ /* Force a full look up iff the parent directory has changed */
if (!nfs_is_exclusive_create(dir, nd) && nfs_check_verifier(dir, dentry)) { if (!nfs_is_exclusive_create(dir, nd) && nfs_check_verifier(dir, dentry)) {
if (nfs_lookup_verify_inode(inode, nd)) if (nfs_lookup_verify_inode(inode, nd))
...@@ -817,6 +820,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) ...@@ -817,6 +820,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
if ((error = nfs_refresh_inode(inode, &fattr)) != 0) if ((error = nfs_refresh_inode(inode, &fattr)) != 0)
goto out_bad; goto out_bad;
out_set_verifier:
nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
out_valid: out_valid:
dput(parent); dput(parent);
...@@ -973,7 +977,7 @@ struct dentry_operations nfs4_dentry_operations = { ...@@ -973,7 +977,7 @@ struct dentry_operations nfs4_dentry_operations = {
* Use intent information to determine whether we need to substitute * Use intent information to determine whether we need to substitute
* the NFSv4-style stateful OPEN for the LOOKUP call * the NFSv4-style stateful OPEN for the LOOKUP call
*/ */
static int is_atomic_open(struct inode *dir, struct nameidata *nd) static int is_atomic_open(struct nameidata *nd)
{ {
if (nd == NULL || nfs_lookup_check_intent(nd, LOOKUP_OPEN) == 0) if (nd == NULL || nfs_lookup_check_intent(nd, LOOKUP_OPEN) == 0)
return 0; return 0;
...@@ -996,7 +1000,7 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry ...@@ -996,7 +1000,7 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
/* Check that we are indeed trying to open this file */ /* Check that we are indeed trying to open this file */
if (!is_atomic_open(dir, nd)) if (!is_atomic_open(nd))
goto no_open; goto no_open;
if (dentry->d_name.len > NFS_SERVER(dir)->namelen) { if (dentry->d_name.len > NFS_SERVER(dir)->namelen) {
...@@ -1047,10 +1051,10 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) ...@@ -1047,10 +1051,10 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
struct inode *dir; struct inode *dir;
int openflags, ret = 0; int openflags, ret = 0;
if (!is_atomic_open(nd))
goto no_open;
parent = dget_parent(dentry); parent = dget_parent(dentry);
dir = parent->d_inode; dir = parent->d_inode;
if (!is_atomic_open(dir, nd))
goto no_open;
/* We can't create new files in nfs_open_revalidate(), so we /* We can't create new files in nfs_open_revalidate(), so we
* optimize away revalidation of negative dentries. * optimize away revalidation of negative dentries.
*/ */
...@@ -1062,11 +1066,11 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) ...@@ -1062,11 +1066,11 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
/* NFS only supports OPEN on regular files */ /* NFS only supports OPEN on regular files */
if (!S_ISREG(inode->i_mode)) if (!S_ISREG(inode->i_mode))
goto no_open; goto no_open_dput;
openflags = nd->intent.open.flags; openflags = nd->intent.open.flags;
/* We cannot do exclusive creation on a positive dentry */ /* We cannot do exclusive creation on a positive dentry */
if ((openflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL)) if ((openflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
goto no_open; goto no_open_dput;
/* We can't create new files, or truncate existing ones here */ /* We can't create new files, or truncate existing ones here */
openflags &= ~(O_CREAT|O_TRUNC); openflags &= ~(O_CREAT|O_TRUNC);
...@@ -1081,10 +1085,9 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) ...@@ -1081,10 +1085,9 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
if (!ret) if (!ret)
d_drop(dentry); d_drop(dentry);
return ret; return ret;
no_open: no_open_dput:
dput(parent); dput(parent);
if (inode != NULL && nfs_have_delegation(inode, FMODE_READ)) no_open:
return 1;
return nfs_lookup_revalidate(dentry, nd); return nfs_lookup_revalidate(dentry, nd);
} }
#endif /* CONFIG_NFSV4 */ #endif /* CONFIG_NFSV4 */
...@@ -1794,7 +1797,8 @@ static int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, str ...@@ -1794,7 +1797,8 @@ static int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, str
cache = nfs_access_search_rbtree(inode, cred); cache = nfs_access_search_rbtree(inode, cred);
if (cache == NULL) if (cache == NULL)
goto out; goto out;
if (!time_in_range(jiffies, cache->jiffies, cache->jiffies + nfsi->attrtimeo)) if (!nfs_have_delegation(inode, FMODE_READ) &&
!time_in_range_open(jiffies, cache->jiffies, cache->jiffies + nfsi->attrtimeo))
goto out_stale; goto out_stale;
res->jiffies = cache->jiffies; res->jiffies = cache->jiffies;
res->cred = cache->cred; res->cred = cache->cred;
......
...@@ -592,7 +592,7 @@ static void nfs_file_set_open_context(struct file *filp, struct nfs_open_context ...@@ -592,7 +592,7 @@ static void nfs_file_set_open_context(struct file *filp, struct nfs_open_context
/* /*
* Given an inode, search for an open context with the desired characteristics * Given an inode, search for an open context with the desired characteristics
*/ */
struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, int mode) struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, fmode_t mode)
{ {
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_open_context *pos, *ctx = NULL; struct nfs_open_context *pos, *ctx = NULL;
...@@ -712,14 +712,7 @@ int nfs_attribute_timeout(struct inode *inode) ...@@ -712,14 +712,7 @@ int nfs_attribute_timeout(struct inode *inode)
if (nfs_have_delegation(inode, FMODE_READ)) if (nfs_have_delegation(inode, FMODE_READ))
return 0; return 0;
/* return !time_in_range_open(jiffies, nfsi->read_cache_jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo);
* Special case: if the attribute timeout is set to 0, then always
* treat the cache as having expired (unless holding
* a delegation).
*/
if (nfsi->attrtimeo == 0)
return 1;
return !time_in_range(jiffies, nfsi->read_cache_jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo);
} }
/** /**
...@@ -1182,7 +1175,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) ...@@ -1182,7 +1175,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
nfsi->attrtimeo_timestamp = now; nfsi->attrtimeo_timestamp = now;
nfsi->attr_gencount = nfs_inc_attr_generation_counter(); nfsi->attr_gencount = nfs_inc_attr_generation_counter();
} else { } else {
if (!time_in_range(now, nfsi->attrtimeo_timestamp, nfsi->attrtimeo_timestamp + nfsi->attrtimeo)) { if (!time_in_range_open(now, nfsi->attrtimeo_timestamp, nfsi->attrtimeo_timestamp + nfsi->attrtimeo)) {
if ((nfsi->attrtimeo <<= 1) > NFS_MAXATTRTIMEO(inode)) if ((nfsi->attrtimeo <<= 1) > NFS_MAXATTRTIMEO(inode))
nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode); nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode);
nfsi->attrtimeo_timestamp = now; nfsi->attrtimeo_timestamp = now;
......
...@@ -63,6 +63,20 @@ struct nfs_parsed_mount_data { ...@@ -63,6 +63,20 @@ struct nfs_parsed_mount_data {
struct security_mnt_opts lsm_opts; struct security_mnt_opts lsm_opts;
}; };
/* mount_clnt.c */
struct nfs_mount_request {
struct sockaddr *sap;
size_t salen;
char *hostname;
char *dirpath;
u32 version;
unsigned short protocol;
struct nfs_fh *fh;
int noresvport;
};
extern int nfs_mount(struct nfs_mount_request *info);
/* client.c */ /* client.c */
extern struct rpc_program nfs_program; extern struct rpc_program nfs_program;
......
...@@ -29,47 +29,43 @@ struct mnt_fhstatus { ...@@ -29,47 +29,43 @@ struct mnt_fhstatus {
/** /**
* nfs_mount - Obtain an NFS file handle for the given host and path * nfs_mount - Obtain an NFS file handle for the given host and path
* @addr: pointer to server's address * @info: pointer to mount request arguments
* @len: size of server's address
* @hostname: name of server host, or NULL
* @path: pointer to string containing export path to mount
* @version: mount version to use for this request
* @protocol: transport protocol to use for thie request
* @fh: pointer to location to place returned file handle
* *
* Uses default timeout parameters specified by underlying transport. * Uses default timeout parameters specified by underlying transport.
*/ */
int nfs_mount(struct sockaddr *addr, size_t len, char *hostname, char *path, int nfs_mount(struct nfs_mount_request *info)
int version, int protocol, struct nfs_fh *fh)
{ {
struct mnt_fhstatus result = { struct mnt_fhstatus result = {
.fh = fh .fh = info->fh
}; };
struct rpc_message msg = { struct rpc_message msg = {
.rpc_argp = path, .rpc_argp = info->dirpath,
.rpc_resp = &result, .rpc_resp = &result,
}; };
struct rpc_create_args args = { struct rpc_create_args args = {
.protocol = protocol, .protocol = info->protocol,
.address = addr, .address = info->sap,
.addrsize = len, .addrsize = info->salen,
.servername = hostname, .servername = info->hostname,
.program = &mnt_program, .program = &mnt_program,
.version = version, .version = info->version,
.authflavor = RPC_AUTH_UNIX, .authflavor = RPC_AUTH_UNIX,
.flags = 0,
}; };
struct rpc_clnt *mnt_clnt; struct rpc_clnt *mnt_clnt;
int status; int status;
dprintk("NFS: sending MNT request for %s:%s\n", dprintk("NFS: sending MNT request for %s:%s\n",
(hostname ? hostname : "server"), path); (info->hostname ? info->hostname : "server"),
info->dirpath);
if (info->noresvport)
args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
mnt_clnt = rpc_create(&args); mnt_clnt = rpc_create(&args);
if (IS_ERR(mnt_clnt)) if (IS_ERR(mnt_clnt))
goto out_clnt_err; goto out_clnt_err;
if (version == NFS_MNT3_VERSION) if (info->version == NFS_MNT3_VERSION)
msg.rpc_proc = &mnt_clnt->cl_procinfo[MOUNTPROC3_MNT]; msg.rpc_proc = &mnt_clnt->cl_procinfo[MOUNTPROC3_MNT];
else else
msg.rpc_proc = &mnt_clnt->cl_procinfo[MNTPROC_MNT]; msg.rpc_proc = &mnt_clnt->cl_procinfo[MNTPROC_MNT];
......
...@@ -38,8 +38,12 @@ struct idmap; ...@@ -38,8 +38,12 @@ struct idmap;
((err) != NFSERR_NOFILEHANDLE)) ((err) != NFSERR_NOFILEHANDLE))
enum nfs4_client_state { enum nfs4_client_state {
NFS4CLNT_STATE_RECOVER = 0, NFS4CLNT_MANAGER_RUNNING = 0,
NFS4CLNT_CHECK_LEASE,
NFS4CLNT_LEASE_EXPIRED, NFS4CLNT_LEASE_EXPIRED,
NFS4CLNT_RECLAIM_REBOOT,
NFS4CLNT_RECLAIM_NOGRACE,
NFS4CLNT_DELEGRETURN,
}; };
/* /*
...@@ -90,12 +94,18 @@ struct nfs4_state_owner { ...@@ -90,12 +94,18 @@ struct nfs4_state_owner {
spinlock_t so_lock; spinlock_t so_lock;
atomic_t so_count; atomic_t so_count;
unsigned long so_flags;
struct list_head so_states; struct list_head so_states;
struct list_head so_delegations; struct list_head so_delegations;
struct nfs_seqid_counter so_seqid; struct nfs_seqid_counter so_seqid;
struct rpc_sequence so_sequence; struct rpc_sequence so_sequence;
}; };
enum {
NFS_OWNER_RECLAIM_REBOOT,
NFS_OWNER_RECLAIM_NOGRACE
};
/* /*
* struct nfs4_state maintains the client-side state for a given * struct nfs4_state maintains the client-side state for a given
* (state_owner,inode) tuple (OPEN) or state_owner (LOCK). * (state_owner,inode) tuple (OPEN) or state_owner (LOCK).
...@@ -128,6 +138,8 @@ enum { ...@@ -128,6 +138,8 @@ enum {
NFS_O_RDONLY_STATE, /* OPEN stateid has read-only state */ NFS_O_RDONLY_STATE, /* OPEN stateid has read-only state */
NFS_O_WRONLY_STATE, /* OPEN stateid has write-only state */ NFS_O_WRONLY_STATE, /* OPEN stateid has write-only state */
NFS_O_RDWR_STATE, /* OPEN stateid has read/write state */ NFS_O_RDWR_STATE, /* OPEN stateid has read/write state */
NFS_STATE_RECLAIM_REBOOT, /* OPEN stateid server rebooted */
NFS_STATE_RECLAIM_NOGRACE, /* OPEN stateid needs to recover state */
}; };
struct nfs4_state { struct nfs4_state {
...@@ -149,7 +161,7 @@ struct nfs4_state { ...@@ -149,7 +161,7 @@ struct nfs4_state {
unsigned int n_rdonly; /* Number of read-only references */ unsigned int n_rdonly; /* Number of read-only references */
unsigned int n_wronly; /* Number of write-only references */ unsigned int n_wronly; /* Number of write-only references */
unsigned int n_rdwr; /* Number of read/write references */ unsigned int n_rdwr; /* Number of read/write references */
int state; /* State on the server (R,W, or RW) */ fmode_t state; /* State on the server (R,W, or RW) */
atomic_t count; atomic_t count;
}; };
...@@ -157,9 +169,12 @@ struct nfs4_state { ...@@ -157,9 +169,12 @@ struct nfs4_state {
struct nfs4_exception { struct nfs4_exception {
long timeout; long timeout;
int retry; int retry;
struct nfs4_state *state;
}; };
struct nfs4_state_recovery_ops { struct nfs4_state_recovery_ops {
int owner_flag_bit;
int state_flag_bit;
int (*recover_open)(struct nfs4_state_owner *, struct nfs4_state *); int (*recover_open)(struct nfs4_state_owner *, struct nfs4_state *);
int (*recover_lock)(struct nfs4_state *, struct file_lock *); int (*recover_lock)(struct nfs4_state *, struct file_lock *);
}; };
...@@ -174,7 +189,6 @@ extern ssize_t nfs4_listxattr(struct dentry *, char *, size_t); ...@@ -174,7 +189,6 @@ extern ssize_t nfs4_listxattr(struct dentry *, char *, size_t);
/* nfs4proc.c */ /* nfs4proc.c */
extern int nfs4_map_errors(int err);
extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struct rpc_cred *); extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struct rpc_cred *);
extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct rpc_cred *); extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct rpc_cred *);
extern int nfs4_proc_async_renew(struct nfs_client *, struct rpc_cred *); extern int nfs4_proc_async_renew(struct nfs_client *, struct rpc_cred *);
...@@ -187,7 +201,7 @@ extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, ...@@ -187,7 +201,7 @@ extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
struct nfs4_fs_locations *fs_locations, struct page *page); struct nfs4_fs_locations *fs_locations, struct page *page);
extern struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops; extern struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops;
extern struct nfs4_state_recovery_ops nfs4_network_partition_recovery_ops; extern struct nfs4_state_recovery_ops nfs4_nograce_recovery_ops;
extern const u32 nfs4_fattr_bitmap[2]; extern const u32 nfs4_fattr_bitmap[2];
extern const u32 nfs4_statfs_bitmap[2]; extern const u32 nfs4_statfs_bitmap[2];
...@@ -202,16 +216,18 @@ extern void nfs4_kill_renewd(struct nfs_client *); ...@@ -202,16 +216,18 @@ extern void nfs4_kill_renewd(struct nfs_client *);
extern void nfs4_renew_state(struct work_struct *); extern void nfs4_renew_state(struct work_struct *);
/* nfs4state.c */ /* nfs4state.c */
struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp); struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp);
extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *); extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *);
extern void nfs4_put_state_owner(struct nfs4_state_owner *); extern void nfs4_put_state_owner(struct nfs4_state_owner *);
extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *);
extern void nfs4_put_open_state(struct nfs4_state *); extern void nfs4_put_open_state(struct nfs4_state *);
extern void nfs4_close_state(struct path *, struct nfs4_state *, mode_t); extern void nfs4_close_state(struct path *, struct nfs4_state *, fmode_t);
extern void nfs4_close_sync(struct path *, struct nfs4_state *, mode_t); extern void nfs4_close_sync(struct path *, struct nfs4_state *, fmode_t);
extern void nfs4_state_set_mode_locked(struct nfs4_state *, mode_t); extern void nfs4_state_set_mode_locked(struct nfs4_state *, fmode_t);
extern void nfs4_schedule_state_recovery(struct nfs_client *); extern void nfs4_schedule_state_recovery(struct nfs_client *);
extern void nfs4_schedule_state_manager(struct nfs_client *);
extern int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_state *state);
extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp); extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp);
extern int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl); extern int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl);
extern void nfs4_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t); extern void nfs4_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t);
......
...@@ -62,14 +62,12 @@ ...@@ -62,14 +62,12 @@
struct nfs4_opendata; struct nfs4_opendata;
static int _nfs4_proc_open(struct nfs4_opendata *data); static int _nfs4_proc_open(struct nfs4_opendata *data);
static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *); static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *); static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *);
static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception);
static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs_client *clp);
static int _nfs4_proc_lookup(struct inode *dir, const struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr); static int _nfs4_proc_lookup(struct inode *dir, const struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr); static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
/* Prevent leaks of NFSv4 errors into userland */ /* Prevent leaks of NFSv4 errors into userland */
int nfs4_map_errors(int err) static int nfs4_map_errors(int err)
{ {
if (err < -1000) { if (err < -1000) {
dprintk("%s could not handle NFSv4 error %d\n", dprintk("%s could not handle NFSv4 error %d\n",
...@@ -195,6 +193,83 @@ static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dent ...@@ -195,6 +193,83 @@ static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dent
kunmap_atomic(start, KM_USER0); kunmap_atomic(start, KM_USER0);
} }
static int nfs4_wait_bit_killable(void *word)
{
if (fatal_signal_pending(current))
return -ERESTARTSYS;
schedule();
return 0;
}
static int nfs4_wait_clnt_recover(struct nfs_client *clp)
{
int res;
might_sleep();
res = wait_on_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING,
nfs4_wait_bit_killable, TASK_KILLABLE);
return res;
}
static int nfs4_delay(struct rpc_clnt *clnt, long *timeout)
{
int res = 0;
might_sleep();
if (*timeout <= 0)
*timeout = NFS4_POLL_RETRY_MIN;
if (*timeout > NFS4_POLL_RETRY_MAX)
*timeout = NFS4_POLL_RETRY_MAX;
schedule_timeout_killable(*timeout);
if (fatal_signal_pending(current))
res = -ERESTARTSYS;
*timeout <<= 1;
return res;
}
/* This is the error handling routine for processes that are allowed
* to sleep.
*/
static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception)
{
struct nfs_client *clp = server->nfs_client;
struct nfs4_state *state = exception->state;
int ret = errorcode;
exception->retry = 0;
switch(errorcode) {
case 0:
return 0;
case -NFS4ERR_ADMIN_REVOKED:
case -NFS4ERR_BAD_STATEID:
case -NFS4ERR_OPENMODE:
if (state == NULL)
break;
nfs4_state_mark_reclaim_nograce(clp, state);
case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
nfs4_schedule_state_recovery(clp);
ret = nfs4_wait_clnt_recover(clp);
if (ret == 0)
exception->retry = 1;
break;
case -NFS4ERR_FILE_OPEN:
case -NFS4ERR_GRACE:
case -NFS4ERR_DELAY:
ret = nfs4_delay(server->client, &exception->timeout);
if (ret != 0)
break;
case -NFS4ERR_OLD_STATEID:
exception->retry = 1;
}
/* We failed to handle the error */
return nfs4_map_errors(ret);
}
static void renew_lease(const struct nfs_server *server, unsigned long timestamp) static void renew_lease(const struct nfs_server *server, unsigned long timestamp)
{ {
struct nfs_client *clp = server->nfs_client; struct nfs_client *clp = server->nfs_client;
...@@ -248,7 +323,7 @@ static void nfs4_init_opendata_res(struct nfs4_opendata *p) ...@@ -248,7 +323,7 @@ static void nfs4_init_opendata_res(struct nfs4_opendata *p)
} }
static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path, static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path,
struct nfs4_state_owner *sp, int flags, struct nfs4_state_owner *sp, fmode_t fmode, int flags,
const struct iattr *attrs) const struct iattr *attrs)
{ {
struct dentry *parent = dget_parent(path->dentry); struct dentry *parent = dget_parent(path->dentry);
...@@ -268,7 +343,8 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path, ...@@ -268,7 +343,8 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path,
p->owner = sp; p->owner = sp;
atomic_inc(&sp->so_count); atomic_inc(&sp->so_count);
p->o_arg.fh = NFS_FH(dir); p->o_arg.fh = NFS_FH(dir);
p->o_arg.open_flags = flags, p->o_arg.open_flags = flags;
p->o_arg.fmode = fmode & (FMODE_READ|FMODE_WRITE);
p->o_arg.clientid = server->nfs_client->cl_clientid; p->o_arg.clientid = server->nfs_client->cl_clientid;
p->o_arg.id = sp->so_owner_id.id; p->o_arg.id = sp->so_owner_id.id;
p->o_arg.name = &p->path.dentry->d_name; p->o_arg.name = &p->path.dentry->d_name;
...@@ -324,10 +400,13 @@ static int nfs4_wait_for_completion_rpc_task(struct rpc_task *task) ...@@ -324,10 +400,13 @@ static int nfs4_wait_for_completion_rpc_task(struct rpc_task *task)
return ret; return ret;
} }
static int can_open_cached(struct nfs4_state *state, int mode) static int can_open_cached(struct nfs4_state *state, fmode_t mode, int open_mode)
{ {
int ret = 0; int ret = 0;
switch (mode & (FMODE_READ|FMODE_WRITE|O_EXCL)) {
if (open_mode & O_EXCL)
goto out;
switch (mode & (FMODE_READ|FMODE_WRITE)) {
case FMODE_READ: case FMODE_READ:
ret |= test_bit(NFS_O_RDONLY_STATE, &state->flags) != 0; ret |= test_bit(NFS_O_RDONLY_STATE, &state->flags) != 0;
break; break;
...@@ -337,21 +416,23 @@ static int can_open_cached(struct nfs4_state *state, int mode) ...@@ -337,21 +416,23 @@ static int can_open_cached(struct nfs4_state *state, int mode)
case FMODE_READ|FMODE_WRITE: case FMODE_READ|FMODE_WRITE:
ret |= test_bit(NFS_O_RDWR_STATE, &state->flags) != 0; ret |= test_bit(NFS_O_RDWR_STATE, &state->flags) != 0;
} }
out:
return ret; return ret;
} }
static int can_open_delegated(struct nfs_delegation *delegation, mode_t open_flags) static int can_open_delegated(struct nfs_delegation *delegation, fmode_t fmode)
{ {
if ((delegation->type & open_flags) != open_flags) if ((delegation->type & fmode) != fmode)
return 0; return 0;
if (delegation->flags & NFS_DELEGATION_NEED_RECLAIM) if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags))
return 0; return 0;
nfs_mark_delegation_referenced(delegation);
return 1; return 1;
} }
static void update_open_stateflags(struct nfs4_state *state, mode_t open_flags) static void update_open_stateflags(struct nfs4_state *state, fmode_t fmode)
{ {
switch (open_flags) { switch (fmode) {
case FMODE_WRITE: case FMODE_WRITE:
state->n_wronly++; state->n_wronly++;
break; break;
...@@ -361,15 +442,15 @@ static void update_open_stateflags(struct nfs4_state *state, mode_t open_flags) ...@@ -361,15 +442,15 @@ static void update_open_stateflags(struct nfs4_state *state, mode_t open_flags)
case FMODE_READ|FMODE_WRITE: case FMODE_READ|FMODE_WRITE:
state->n_rdwr++; state->n_rdwr++;
} }
nfs4_state_set_mode_locked(state, state->state | open_flags); nfs4_state_set_mode_locked(state, state->state | fmode);
} }
static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *stateid, int open_flags) static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode)
{ {
if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0) if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
memcpy(state->stateid.data, stateid->data, sizeof(state->stateid.data)); memcpy(state->stateid.data, stateid->data, sizeof(state->stateid.data));
memcpy(state->open_stateid.data, stateid->data, sizeof(state->open_stateid.data)); memcpy(state->open_stateid.data, stateid->data, sizeof(state->open_stateid.data));
switch (open_flags) { switch (fmode) {
case FMODE_READ: case FMODE_READ:
set_bit(NFS_O_RDONLY_STATE, &state->flags); set_bit(NFS_O_RDONLY_STATE, &state->flags);
break; break;
...@@ -381,16 +462,15 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid * ...@@ -381,16 +462,15 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *
} }
} }
static void nfs_set_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, int open_flags) static void nfs_set_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode)
{ {
write_seqlock(&state->seqlock); write_seqlock(&state->seqlock);
nfs_set_open_stateid_locked(state, stateid, open_flags); nfs_set_open_stateid_locked(state, stateid, fmode);
write_sequnlock(&state->seqlock); write_sequnlock(&state->seqlock);
} }
static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, nfs4_stateid *deleg_stateid, int open_flags) static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, const nfs4_stateid *deleg_stateid, fmode_t fmode)
{ {
open_flags &= (FMODE_READ|FMODE_WRITE);
/* /*
* Protect the call to nfs4_state_set_mode_locked and * Protect the call to nfs4_state_set_mode_locked and
* serialise the stateid update * serialise the stateid update
...@@ -401,20 +481,60 @@ static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_sta ...@@ -401,20 +481,60 @@ static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_sta
set_bit(NFS_DELEGATED_STATE, &state->flags); set_bit(NFS_DELEGATED_STATE, &state->flags);
} }
if (open_stateid != NULL) if (open_stateid != NULL)
nfs_set_open_stateid_locked(state, open_stateid, open_flags); nfs_set_open_stateid_locked(state, open_stateid, fmode);
write_sequnlock(&state->seqlock); write_sequnlock(&state->seqlock);
spin_lock(&state->owner->so_lock); spin_lock(&state->owner->so_lock);
update_open_stateflags(state, open_flags); update_open_stateflags(state, fmode);
spin_unlock(&state->owner->so_lock); spin_unlock(&state->owner->so_lock);
} }
static void nfs4_return_incompatible_delegation(struct inode *inode, mode_t open_flags) static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, nfs4_stateid *delegation, fmode_t fmode)
{
struct nfs_inode *nfsi = NFS_I(state->inode);
struct nfs_delegation *deleg_cur;
int ret = 0;
fmode &= (FMODE_READ|FMODE_WRITE);
rcu_read_lock();
deleg_cur = rcu_dereference(nfsi->delegation);
if (deleg_cur == NULL)
goto no_delegation;
spin_lock(&deleg_cur->lock);
if (nfsi->delegation != deleg_cur ||
(deleg_cur->type & fmode) != fmode)
goto no_delegation_unlock;
if (delegation == NULL)
delegation = &deleg_cur->stateid;
else if (memcmp(deleg_cur->stateid.data, delegation->data, NFS4_STATEID_SIZE) != 0)
goto no_delegation_unlock;
nfs_mark_delegation_referenced(deleg_cur);
__update_open_stateid(state, open_stateid, &deleg_cur->stateid, fmode);
ret = 1;
no_delegation_unlock:
spin_unlock(&deleg_cur->lock);
no_delegation:
rcu_read_unlock();
if (!ret && open_stateid != NULL) {
__update_open_stateid(state, open_stateid, NULL, fmode);
ret = 1;
}
return ret;
}
static void nfs4_return_incompatible_delegation(struct inode *inode, fmode_t fmode)
{ {
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
rcu_read_lock(); rcu_read_lock();
delegation = rcu_dereference(NFS_I(inode)->delegation); delegation = rcu_dereference(NFS_I(inode)->delegation);
if (delegation == NULL || (delegation->type & open_flags) == open_flags) { if (delegation == NULL || (delegation->type & fmode) == fmode) {
rcu_read_unlock(); rcu_read_unlock();
return; return;
} }
...@@ -427,27 +547,28 @@ static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata) ...@@ -427,27 +547,28 @@ static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
struct nfs4_state *state = opendata->state; struct nfs4_state *state = opendata->state;
struct nfs_inode *nfsi = NFS_I(state->inode); struct nfs_inode *nfsi = NFS_I(state->inode);
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
int open_mode = opendata->o_arg.open_flags & (FMODE_READ|FMODE_WRITE|O_EXCL); int open_mode = opendata->o_arg.open_flags & O_EXCL;
fmode_t fmode = opendata->o_arg.fmode;
nfs4_stateid stateid; nfs4_stateid stateid;
int ret = -EAGAIN; int ret = -EAGAIN;
rcu_read_lock();
delegation = rcu_dereference(nfsi->delegation);
for (;;) { for (;;) {
if (can_open_cached(state, open_mode)) { if (can_open_cached(state, fmode, open_mode)) {
spin_lock(&state->owner->so_lock); spin_lock(&state->owner->so_lock);
if (can_open_cached(state, open_mode)) { if (can_open_cached(state, fmode, open_mode)) {
update_open_stateflags(state, open_mode); update_open_stateflags(state, fmode);
spin_unlock(&state->owner->so_lock); spin_unlock(&state->owner->so_lock);
rcu_read_unlock();
goto out_return_state; goto out_return_state;
} }
spin_unlock(&state->owner->so_lock); spin_unlock(&state->owner->so_lock);
} }
if (delegation == NULL) rcu_read_lock();
break; delegation = rcu_dereference(nfsi->delegation);
if (!can_open_delegated(delegation, open_mode)) if (delegation == NULL ||
!can_open_delegated(delegation, fmode)) {
rcu_read_unlock();
break; break;
}
/* Save the delegation */ /* Save the delegation */
memcpy(stateid.data, delegation->stateid.data, sizeof(stateid.data)); memcpy(stateid.data, delegation->stateid.data, sizeof(stateid.data));
rcu_read_unlock(); rcu_read_unlock();
...@@ -455,19 +576,11 @@ static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata) ...@@ -455,19 +576,11 @@ static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
if (ret != 0) if (ret != 0)
goto out; goto out;
ret = -EAGAIN; ret = -EAGAIN;
rcu_read_lock();
delegation = rcu_dereference(nfsi->delegation); /* Try to update the stateid using the delegation */
/* If no delegation, try a cached open */ if (update_open_stateid(state, NULL, &stateid, fmode))
if (delegation == NULL)
continue;
/* Is the delegation still valid? */
if (memcmp(stateid.data, delegation->stateid.data, sizeof(stateid.data)) != 0)
continue;
rcu_read_unlock();
update_open_stateid(state, NULL, &stateid, open_mode);
goto out_return_state; goto out_return_state;
} }
rcu_read_unlock();
out: out:
return ERR_PTR(ret); return ERR_PTR(ret);
out_return_state: out_return_state:
...@@ -480,7 +593,6 @@ static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data ...@@ -480,7 +593,6 @@ static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data
struct inode *inode; struct inode *inode;
struct nfs4_state *state = NULL; struct nfs4_state *state = NULL;
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
nfs4_stateid *deleg_stateid = NULL;
int ret; int ret;
if (!data->rpc_done) { if (!data->rpc_done) {
...@@ -507,7 +619,7 @@ static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data ...@@ -507,7 +619,7 @@ static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data
if (delegation) if (delegation)
delegation_flags = delegation->flags; delegation_flags = delegation->flags;
rcu_read_unlock(); rcu_read_unlock();
if (!(delegation_flags & NFS_DELEGATION_NEED_RECLAIM)) if ((delegation_flags & 1UL<<NFS_DELEGATION_NEED_RECLAIM) == 0)
nfs_inode_set_delegation(state->inode, nfs_inode_set_delegation(state->inode,
data->owner->so_cred, data->owner->so_cred,
&data->o_res); &data->o_res);
...@@ -516,12 +628,9 @@ static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data ...@@ -516,12 +628,9 @@ static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data
data->owner->so_cred, data->owner->so_cred,
&data->o_res); &data->o_res);
} }
rcu_read_lock();
delegation = rcu_dereference(NFS_I(inode)->delegation); update_open_stateid(state, &data->o_res.stateid, NULL,
if (delegation != NULL) data->o_arg.fmode);
deleg_stateid = &delegation->stateid;
update_open_stateid(state, &data->o_res.stateid, deleg_stateid, data->o_arg.open_flags);
rcu_read_unlock();
iput(inode); iput(inode);
out: out:
return state; return state;
...@@ -552,7 +661,7 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context ...@@ -552,7 +661,7 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context
{ {
struct nfs4_opendata *opendata; struct nfs4_opendata *opendata;
opendata = nfs4_opendata_alloc(&ctx->path, state->owner, 0, NULL); opendata = nfs4_opendata_alloc(&ctx->path, state->owner, 0, 0, NULL);
if (opendata == NULL) if (opendata == NULL)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
opendata->state = state; opendata->state = state;
...@@ -560,12 +669,13 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context ...@@ -560,12 +669,13 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context
return opendata; return opendata;
} }
static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, mode_t openflags, struct nfs4_state **res) static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, fmode_t fmode, struct nfs4_state **res)
{ {
struct nfs4_state *newstate; struct nfs4_state *newstate;
int ret; int ret;
opendata->o_arg.open_flags = openflags; opendata->o_arg.open_flags = 0;
opendata->o_arg.fmode = fmode;
memset(&opendata->o_res, 0, sizeof(opendata->o_res)); memset(&opendata->o_res, 0, sizeof(opendata->o_res));
memset(&opendata->c_res, 0, sizeof(opendata->c_res)); memset(&opendata->c_res, 0, sizeof(opendata->c_res));
nfs4_init_opendata_res(opendata); nfs4_init_opendata_res(opendata);
...@@ -575,7 +685,7 @@ static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, mode_t openf ...@@ -575,7 +685,7 @@ static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, mode_t openf
newstate = nfs4_opendata_to_nfs4_state(opendata); newstate = nfs4_opendata_to_nfs4_state(opendata);
if (IS_ERR(newstate)) if (IS_ERR(newstate))
return PTR_ERR(newstate); return PTR_ERR(newstate);
nfs4_close_state(&opendata->path, newstate, openflags); nfs4_close_state(&opendata->path, newstate, fmode);
*res = newstate; *res = newstate;
return 0; return 0;
} }
...@@ -631,7 +741,7 @@ static int _nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state ...@@ -631,7 +741,7 @@ static int _nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state
{ {
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
struct nfs4_opendata *opendata; struct nfs4_opendata *opendata;
int delegation_type = 0; fmode_t delegation_type = 0;
int status; int status;
opendata = nfs4_open_recoverdata_alloc(ctx, state); opendata = nfs4_open_recoverdata_alloc(ctx, state);
...@@ -641,7 +751,7 @@ static int _nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state ...@@ -641,7 +751,7 @@ static int _nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state
opendata->o_arg.fh = NFS_FH(state->inode); opendata->o_arg.fh = NFS_FH(state->inode);
rcu_read_lock(); rcu_read_lock();
delegation = rcu_dereference(NFS_I(state->inode)->delegation); delegation = rcu_dereference(NFS_I(state->inode)->delegation);
if (delegation != NULL && (delegation->flags & NFS_DELEGATION_NEED_RECLAIM) != 0) if (delegation != NULL && test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) != 0)
delegation_type = delegation->type; delegation_type = delegation->type;
rcu_read_unlock(); rcu_read_unlock();
opendata->o_arg.u.delegation_type = delegation_type; opendata->o_arg.u.delegation_type = delegation_type;
...@@ -744,7 +854,7 @@ static void nfs4_open_confirm_release(void *calldata) ...@@ -744,7 +854,7 @@ static void nfs4_open_confirm_release(void *calldata)
goto out_free; goto out_free;
state = nfs4_opendata_to_nfs4_state(data); state = nfs4_opendata_to_nfs4_state(data);
if (!IS_ERR(state)) if (!IS_ERR(state))
nfs4_close_state(&data->path, state, data->o_arg.open_flags); nfs4_close_state(&data->path, state, data->o_arg.fmode);
out_free: out_free:
nfs4_opendata_put(data); nfs4_opendata_put(data);
} }
...@@ -808,12 +918,12 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata) ...@@ -808,12 +918,12 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
if (data->state != NULL) { if (data->state != NULL) {
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
if (can_open_cached(data->state, data->o_arg.open_flags & (FMODE_READ|FMODE_WRITE|O_EXCL))) if (can_open_cached(data->state, data->o_arg.fmode, data->o_arg.open_flags))
goto out_no_action; goto out_no_action;
rcu_read_lock(); rcu_read_lock();
delegation = rcu_dereference(NFS_I(data->state->inode)->delegation); delegation = rcu_dereference(NFS_I(data->state->inode)->delegation);
if (delegation != NULL && if (delegation != NULL &&
(delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0) { test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) == 0) {
rcu_read_unlock(); rcu_read_unlock();
goto out_no_action; goto out_no_action;
} }
...@@ -877,7 +987,7 @@ static void nfs4_open_release(void *calldata) ...@@ -877,7 +987,7 @@ static void nfs4_open_release(void *calldata)
goto out_free; goto out_free;
state = nfs4_opendata_to_nfs4_state(data); state = nfs4_opendata_to_nfs4_state(data);
if (!IS_ERR(state)) if (!IS_ERR(state))
nfs4_close_state(&data->path, state, data->o_arg.open_flags); nfs4_close_state(&data->path, state, data->o_arg.fmode);
out_free: out_free:
nfs4_opendata_put(data); nfs4_opendata_put(data);
} }
...@@ -955,10 +1065,11 @@ static int nfs4_recover_expired_lease(struct nfs_server *server) ...@@ -955,10 +1065,11 @@ static int nfs4_recover_expired_lease(struct nfs_server *server)
int ret; int ret;
for (;;) { for (;;) {
ret = nfs4_wait_clnt_recover(server->client, clp); ret = nfs4_wait_clnt_recover(clp);
if (ret != 0) if (ret != 0)
return ret; return ret;
if (!test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) &&
!test_bit(NFS4CLNT_CHECK_LEASE,&clp->cl_state))
break; break;
nfs4_schedule_state_recovery(clp); nfs4_schedule_state_recovery(clp);
} }
...@@ -993,7 +1104,8 @@ static inline int nfs4_do_open_expired(struct nfs_open_context *ctx, struct nfs4 ...@@ -993,7 +1104,8 @@ static inline int nfs4_do_open_expired(struct nfs_open_context *ctx, struct nfs4
do { do {
err = _nfs4_open_expired(ctx, state); err = _nfs4_open_expired(ctx, state);
if (err == -NFS4ERR_DELAY) if (err != -NFS4ERR_DELAY)
break;
nfs4_handle_exception(server, err, &exception); nfs4_handle_exception(server, err, &exception);
} while (exception.retry); } while (exception.retry);
return err; return err;
...@@ -1031,12 +1143,11 @@ static inline void nfs4_exclusive_attrset(struct nfs4_opendata *opendata, struct ...@@ -1031,12 +1143,11 @@ static inline void nfs4_exclusive_attrset(struct nfs4_opendata *opendata, struct
/* /*
* Returns a referenced nfs4_state * Returns a referenced nfs4_state
*/ */
static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res) static int _nfs4_do_open(struct inode *dir, struct path *path, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
{ {
struct nfs4_state_owner *sp; struct nfs4_state_owner *sp;
struct nfs4_state *state = NULL; struct nfs4_state *state = NULL;
struct nfs_server *server = NFS_SERVER(dir); struct nfs_server *server = NFS_SERVER(dir);
struct nfs_client *clp = server->nfs_client;
struct nfs4_opendata *opendata; struct nfs4_opendata *opendata;
int status; int status;
...@@ -1050,12 +1161,11 @@ static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct ...@@ -1050,12 +1161,11 @@ static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct
if (status != 0) if (status != 0)
goto err_put_state_owner; goto err_put_state_owner;
if (path->dentry->d_inode != NULL) if (path->dentry->d_inode != NULL)
nfs4_return_incompatible_delegation(path->dentry->d_inode, flags & (FMODE_READ|FMODE_WRITE)); nfs4_return_incompatible_delegation(path->dentry->d_inode, fmode);
down_read(&clp->cl_sem);
status = -ENOMEM; status = -ENOMEM;
opendata = nfs4_opendata_alloc(path, sp, flags, sattr); opendata = nfs4_opendata_alloc(path, sp, fmode, flags, sattr);
if (opendata == NULL) if (opendata == NULL)
goto err_release_rwsem; goto err_put_state_owner;
if (path->dentry->d_inode != NULL) if (path->dentry->d_inode != NULL)
opendata->state = nfs4_get_open_state(path->dentry->d_inode, sp); opendata->state = nfs4_get_open_state(path->dentry->d_inode, sp);
...@@ -1073,13 +1183,10 @@ static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct ...@@ -1073,13 +1183,10 @@ static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct
goto err_opendata_put; goto err_opendata_put;
nfs4_opendata_put(opendata); nfs4_opendata_put(opendata);
nfs4_put_state_owner(sp); nfs4_put_state_owner(sp);
up_read(&clp->cl_sem);
*res = state; *res = state;
return 0; return 0;
err_opendata_put: err_opendata_put:
nfs4_opendata_put(opendata); nfs4_opendata_put(opendata);
err_release_rwsem:
up_read(&clp->cl_sem);
err_put_state_owner: err_put_state_owner:
nfs4_put_state_owner(sp); nfs4_put_state_owner(sp);
out_err: out_err:
...@@ -1088,14 +1195,14 @@ static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct ...@@ -1088,14 +1195,14 @@ static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct
} }
static struct nfs4_state *nfs4_do_open(struct inode *dir, struct path *path, int flags, struct iattr *sattr, struct rpc_cred *cred) static struct nfs4_state *nfs4_do_open(struct inode *dir, struct path *path, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred)
{ {
struct nfs4_exception exception = { }; struct nfs4_exception exception = { };
struct nfs4_state *res; struct nfs4_state *res;
int status; int status;
do { do {
status = _nfs4_do_open(dir, path, flags, sattr, cred, &res); status = _nfs4_do_open(dir, path, fmode, flags, sattr, cred, &res);
if (status == 0) if (status == 0)
break; break;
/* NOTE: BAD_SEQID means the server and client disagree about the /* NOTE: BAD_SEQID means the server and client disagree about the
...@@ -1230,10 +1337,13 @@ static void nfs4_close_done(struct rpc_task *task, void *data) ...@@ -1230,10 +1337,13 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
renew_lease(server, calldata->timestamp); renew_lease(server, calldata->timestamp);
break; break;
case -NFS4ERR_STALE_STATEID: case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_OLD_STATEID:
case -NFS4ERR_BAD_STATEID:
case -NFS4ERR_EXPIRED: case -NFS4ERR_EXPIRED:
if (calldata->arg.fmode == 0)
break; break;
default: default:
if (nfs4_async_handle_error(task, server) == -EAGAIN) { if (nfs4_async_handle_error(task, server, state) == -EAGAIN) {
rpc_restart_call(task); rpc_restart_call(task);
return; return;
} }
...@@ -1272,10 +1382,10 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data) ...@@ -1272,10 +1382,10 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
nfs_fattr_init(calldata->res.fattr); nfs_fattr_init(calldata->res.fattr);
if (test_bit(NFS_O_RDONLY_STATE, &state->flags) != 0) { if (test_bit(NFS_O_RDONLY_STATE, &state->flags) != 0) {
task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE]; task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
calldata->arg.open_flags = FMODE_READ; calldata->arg.fmode = FMODE_READ;
} else if (test_bit(NFS_O_WRONLY_STATE, &state->flags) != 0) { } else if (test_bit(NFS_O_WRONLY_STATE, &state->flags) != 0) {
task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE]; task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
calldata->arg.open_flags = FMODE_WRITE; calldata->arg.fmode = FMODE_WRITE;
} }
calldata->timestamp = jiffies; calldata->timestamp = jiffies;
rpc_call_start(task); rpc_call_start(task);
...@@ -1328,6 +1438,7 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait) ...@@ -1328,6 +1438,7 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait)
calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid); calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid);
if (calldata->arg.seqid == NULL) if (calldata->arg.seqid == NULL)
goto out_free_calldata; goto out_free_calldata;
calldata->arg.fmode = 0;
calldata->arg.bitmask = server->attr_bitmask; calldata->arg.bitmask = server->attr_bitmask;
calldata->res.fattr = &calldata->fattr; calldata->res.fattr = &calldata->fattr;
calldata->res.seqid = calldata->arg.seqid; calldata->res.seqid = calldata->arg.seqid;
...@@ -1354,13 +1465,13 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait) ...@@ -1354,13 +1465,13 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait)
return status; return status;
} }
static int nfs4_intent_set_file(struct nameidata *nd, struct path *path, struct nfs4_state *state) static int nfs4_intent_set_file(struct nameidata *nd, struct path *path, struct nfs4_state *state, fmode_t fmode)
{ {
struct file *filp; struct file *filp;
int ret; int ret;
/* If the open_intent is for execute, we have an extra check to make */ /* If the open_intent is for execute, we have an extra check to make */
if (nd->intent.open.flags & FMODE_EXEC) { if (fmode & FMODE_EXEC) {
ret = nfs_may_open(state->inode, ret = nfs_may_open(state->inode,
state->owner->so_cred, state->owner->so_cred,
nd->intent.open.flags); nd->intent.open.flags);
...@@ -1376,7 +1487,7 @@ static int nfs4_intent_set_file(struct nameidata *nd, struct path *path, struct ...@@ -1376,7 +1487,7 @@ static int nfs4_intent_set_file(struct nameidata *nd, struct path *path, struct
} }
ret = PTR_ERR(filp); ret = PTR_ERR(filp);
out_close: out_close:
nfs4_close_sync(path, state, nd->intent.open.flags); nfs4_close_sync(path, state, fmode & (FMODE_READ|FMODE_WRITE));
return ret; return ret;
} }
...@@ -1392,6 +1503,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd) ...@@ -1392,6 +1503,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
struct rpc_cred *cred; struct rpc_cred *cred;
struct nfs4_state *state; struct nfs4_state *state;
struct dentry *res; struct dentry *res;
fmode_t fmode = nd->intent.open.flags & (FMODE_READ | FMODE_WRITE | FMODE_EXEC);
if (nd->flags & LOOKUP_CREATE) { if (nd->flags & LOOKUP_CREATE) {
attr.ia_mode = nd->intent.open.create_mode; attr.ia_mode = nd->intent.open.create_mode;
...@@ -1409,7 +1521,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd) ...@@ -1409,7 +1521,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
parent = dentry->d_parent; parent = dentry->d_parent;
/* Protect against concurrent sillydeletes */ /* Protect against concurrent sillydeletes */
nfs_block_sillyrename(parent); nfs_block_sillyrename(parent);
state = nfs4_do_open(dir, &path, nd->intent.open.flags, &attr, cred); state = nfs4_do_open(dir, &path, fmode, nd->intent.open.flags, &attr, cred);
put_rpccred(cred); put_rpccred(cred);
if (IS_ERR(state)) { if (IS_ERR(state)) {
if (PTR_ERR(state) == -ENOENT) { if (PTR_ERR(state) == -ENOENT) {
...@@ -1424,7 +1536,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd) ...@@ -1424,7 +1536,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
path.dentry = res; path.dentry = res;
nfs_set_verifier(path.dentry, nfs_save_change_attribute(dir)); nfs_set_verifier(path.dentry, nfs_save_change_attribute(dir));
nfs_unblock_sillyrename(parent); nfs_unblock_sillyrename(parent);
nfs4_intent_set_file(nd, &path, state); nfs4_intent_set_file(nd, &path, state, fmode);
return res; return res;
} }
...@@ -1437,11 +1549,12 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, st ...@@ -1437,11 +1549,12 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, st
}; };
struct rpc_cred *cred; struct rpc_cred *cred;
struct nfs4_state *state; struct nfs4_state *state;
fmode_t fmode = openflags & (FMODE_READ | FMODE_WRITE);
cred = rpc_lookup_cred(); cred = rpc_lookup_cred();
if (IS_ERR(cred)) if (IS_ERR(cred))
return PTR_ERR(cred); return PTR_ERR(cred);
state = nfs4_do_open(dir, &path, openflags, NULL, cred); state = nfs4_do_open(dir, &path, fmode, openflags, NULL, cred);
put_rpccred(cred); put_rpccred(cred);
if (IS_ERR(state)) { if (IS_ERR(state)) {
switch (PTR_ERR(state)) { switch (PTR_ERR(state)) {
...@@ -1458,10 +1571,10 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, st ...@@ -1458,10 +1571,10 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, st
} }
if (state->inode == dentry->d_inode) { if (state->inode == dentry->d_inode) {
nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
nfs4_intent_set_file(nd, &path, state); nfs4_intent_set_file(nd, &path, state, fmode);
return 1; return 1;
} }
nfs4_close_sync(&path, state, openflags); nfs4_close_sync(&path, state, fmode);
out_drop: out_drop:
d_drop(dentry); d_drop(dentry);
return 0; return 0;
...@@ -1887,6 +2000,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, ...@@ -1887,6 +2000,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
}; };
struct nfs4_state *state; struct nfs4_state *state;
struct rpc_cred *cred; struct rpc_cred *cred;
fmode_t fmode = flags & (FMODE_READ | FMODE_WRITE);
int status = 0; int status = 0;
cred = rpc_lookup_cred(); cred = rpc_lookup_cred();
...@@ -1894,7 +2008,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, ...@@ -1894,7 +2008,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
status = PTR_ERR(cred); status = PTR_ERR(cred);
goto out; goto out;
} }
state = nfs4_do_open(dir, &path, flags, sattr, cred); state = nfs4_do_open(dir, &path, fmode, flags, sattr, cred);
d_drop(dentry); d_drop(dentry);
if (IS_ERR(state)) { if (IS_ERR(state)) {
status = PTR_ERR(state); status = PTR_ERR(state);
...@@ -1910,9 +2024,9 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, ...@@ -1910,9 +2024,9 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
nfs_post_op_update_inode(state->inode, &fattr); nfs_post_op_update_inode(state->inode, &fattr);
} }
if (status == 0 && (nd->flags & LOOKUP_OPEN) != 0) if (status == 0 && (nd->flags & LOOKUP_OPEN) != 0)
status = nfs4_intent_set_file(nd, &path, state); status = nfs4_intent_set_file(nd, &path, state, fmode);
else else
nfs4_close_sync(&path, state, flags); nfs4_close_sync(&path, state, fmode);
out_putcred: out_putcred:
put_rpccred(cred); put_rpccred(cred);
out: out:
...@@ -1974,7 +2088,7 @@ static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir) ...@@ -1974,7 +2088,7 @@ static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir)
{ {
struct nfs_removeres *res = task->tk_msg.rpc_resp; struct nfs_removeres *res = task->tk_msg.rpc_resp;
if (nfs4_async_handle_error(task, res->server) == -EAGAIN) if (nfs4_async_handle_error(task, res->server, NULL) == -EAGAIN)
return 0; return 0;
update_changeattr(dir, &res->cinfo); update_changeattr(dir, &res->cinfo);
nfs_post_op_update_inode(dir, &res->dir_attr); nfs_post_op_update_inode(dir, &res->dir_attr);
...@@ -2402,7 +2516,7 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data) ...@@ -2402,7 +2516,7 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data)
{ {
struct nfs_server *server = NFS_SERVER(data->inode); struct nfs_server *server = NFS_SERVER(data->inode);
if (nfs4_async_handle_error(task, server) == -EAGAIN) { if (nfs4_async_handle_error(task, server, data->args.context->state) == -EAGAIN) {
rpc_restart_call(task); rpc_restart_call(task);
return -EAGAIN; return -EAGAIN;
} }
...@@ -2423,7 +2537,7 @@ static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data) ...@@ -2423,7 +2537,7 @@ static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data)
{ {
struct inode *inode = data->inode; struct inode *inode = data->inode;
if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) { if (nfs4_async_handle_error(task, NFS_SERVER(inode), data->args.context->state) == -EAGAIN) {
rpc_restart_call(task); rpc_restart_call(task);
return -EAGAIN; return -EAGAIN;
} }
...@@ -2449,7 +2563,7 @@ static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data) ...@@ -2449,7 +2563,7 @@ static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data)
{ {
struct inode *inode = data->inode; struct inode *inode = data->inode;
if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) { if (nfs4_async_handle_error(task, NFS_SERVER(inode), NULL) == -EAGAIN) {
rpc_restart_call(task); rpc_restart_call(task);
return -EAGAIN; return -EAGAIN;
} }
...@@ -2742,19 +2856,25 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen ...@@ -2742,19 +2856,25 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen
} }
static int static int
nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server) nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs4_state *state)
{ {
struct nfs_client *clp = server->nfs_client; struct nfs_client *clp = server->nfs_client;
if (!clp || task->tk_status >= 0) if (!clp || task->tk_status >= 0)
return 0; return 0;
switch(task->tk_status) { switch(task->tk_status) {
case -NFS4ERR_ADMIN_REVOKED:
case -NFS4ERR_BAD_STATEID:
case -NFS4ERR_OPENMODE:
if (state == NULL)
break;
nfs4_state_mark_reclaim_nograce(clp, state);
case -NFS4ERR_STALE_CLIENTID: case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_STALE_STATEID: case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED: case -NFS4ERR_EXPIRED:
rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL); rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);
nfs4_schedule_state_recovery(clp); nfs4_schedule_state_recovery(clp);
if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) == 0) if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0)
rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task); rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
task->tk_status = 0; task->tk_status = 0;
return -EAGAIN; return -EAGAIN;
...@@ -2772,79 +2892,6 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server) ...@@ -2772,79 +2892,6 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server)
return 0; return 0;
} }
static int nfs4_wait_bit_killable(void *word)
{
if (fatal_signal_pending(current))
return -ERESTARTSYS;
schedule();
return 0;
}
static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs_client *clp)
{
int res;
might_sleep();
rwsem_acquire(&clp->cl_sem.dep_map, 0, 0, _RET_IP_);
res = wait_on_bit(&clp->cl_state, NFS4CLNT_STATE_RECOVER,
nfs4_wait_bit_killable, TASK_KILLABLE);
rwsem_release(&clp->cl_sem.dep_map, 1, _RET_IP_);
return res;
}
static int nfs4_delay(struct rpc_clnt *clnt, long *timeout)
{
int res = 0;
might_sleep();
if (*timeout <= 0)
*timeout = NFS4_POLL_RETRY_MIN;
if (*timeout > NFS4_POLL_RETRY_MAX)
*timeout = NFS4_POLL_RETRY_MAX;
schedule_timeout_killable(*timeout);
if (fatal_signal_pending(current))
res = -ERESTARTSYS;
*timeout <<= 1;
return res;
}
/* This is the error handling routine for processes that are allowed
* to sleep.
*/
static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception)
{
struct nfs_client *clp = server->nfs_client;
int ret = errorcode;
exception->retry = 0;
switch(errorcode) {
case 0:
return 0;
case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
nfs4_schedule_state_recovery(clp);
ret = nfs4_wait_clnt_recover(server->client, clp);
if (ret == 0)
exception->retry = 1;
break;
case -NFS4ERR_FILE_OPEN:
case -NFS4ERR_GRACE:
case -NFS4ERR_DELAY:
ret = nfs4_delay(server->client, &exception->timeout);
if (ret != 0)
break;
case -NFS4ERR_OLD_STATEID:
exception->retry = 1;
}
/* We failed to handle the error */
return nfs4_map_errors(ret);
}
int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, unsigned short port, struct rpc_cred *cred) int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, unsigned short port, struct rpc_cred *cred)
{ {
nfs4_verifier sc_verifier; nfs4_verifier sc_verifier;
...@@ -2916,7 +2963,6 @@ static int _nfs4_proc_setclientid_confirm(struct nfs_client *clp, struct rpc_cre ...@@ -2916,7 +2963,6 @@ static int _nfs4_proc_setclientid_confirm(struct nfs_client *clp, struct rpc_cre
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
clp->cl_lease_time = fsinfo.lease_time * HZ; clp->cl_lease_time = fsinfo.lease_time * HZ;
clp->cl_last_renewal = now; clp->cl_last_renewal = now;
clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
} }
return status; return status;
...@@ -3074,7 +3120,6 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock ...@@ -3074,7 +3120,6 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock
struct nfs4_lock_state *lsp; struct nfs4_lock_state *lsp;
int status; int status;
down_read(&clp->cl_sem);
arg.lock_owner.clientid = clp->cl_clientid; arg.lock_owner.clientid = clp->cl_clientid;
status = nfs4_set_lock_state(state, request); status = nfs4_set_lock_state(state, request);
if (status != 0) if (status != 0)
...@@ -3091,7 +3136,6 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock ...@@ -3091,7 +3136,6 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock
} }
request->fl_ops->fl_release_private(request); request->fl_ops->fl_release_private(request);
out: out:
up_read(&clp->cl_sem);
return status; return status;
} }
...@@ -3181,11 +3225,13 @@ static void nfs4_locku_done(struct rpc_task *task, void *data) ...@@ -3181,11 +3225,13 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)
sizeof(calldata->lsp->ls_stateid.data)); sizeof(calldata->lsp->ls_stateid.data));
renew_lease(calldata->server, calldata->timestamp); renew_lease(calldata->server, calldata->timestamp);
break; break;
case -NFS4ERR_BAD_STATEID:
case -NFS4ERR_OLD_STATEID:
case -NFS4ERR_STALE_STATEID: case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED: case -NFS4ERR_EXPIRED:
break; break;
default: default:
if (nfs4_async_handle_error(task, calldata->server) == -EAGAIN) if (nfs4_async_handle_error(task, calldata->server, NULL) == -EAGAIN)
rpc_restart_call(task); rpc_restart_call(task);
} }
} }
...@@ -3248,6 +3294,7 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl, ...@@ -3248,6 +3294,7 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl,
static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request) static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
{ {
struct nfs_inode *nfsi = NFS_I(state->inode);
struct nfs_seqid *seqid; struct nfs_seqid *seqid;
struct nfs4_lock_state *lsp; struct nfs4_lock_state *lsp;
struct rpc_task *task; struct rpc_task *task;
...@@ -3257,8 +3304,12 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock * ...@@ -3257,8 +3304,12 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *
status = nfs4_set_lock_state(state, request); status = nfs4_set_lock_state(state, request);
/* Unlock _before_ we do the RPC call */ /* Unlock _before_ we do the RPC call */
request->fl_flags |= FL_EXISTS; request->fl_flags |= FL_EXISTS;
if (do_vfs_lock(request->fl_file, request) == -ENOENT) down_read(&nfsi->rwsem);
if (do_vfs_lock(request->fl_file, request) == -ENOENT) {
up_read(&nfsi->rwsem);
goto out; goto out;
}
up_read(&nfsi->rwsem);
if (status != 0) if (status != 0)
goto out; goto out;
/* Is this a delegated lock? */ /* Is this a delegated lock? */
...@@ -3484,7 +3535,7 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request ...@@ -3484,7 +3535,7 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request
static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request) static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
{ {
struct nfs_client *clp = state->owner->so_client; struct nfs_inode *nfsi = NFS_I(state->inode);
unsigned char fl_flags = request->fl_flags; unsigned char fl_flags = request->fl_flags;
int status; int status;
...@@ -3496,20 +3547,14 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock ...@@ -3496,20 +3547,14 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock
status = do_vfs_lock(request->fl_file, request); status = do_vfs_lock(request->fl_file, request);
if (status < 0) if (status < 0)
goto out; goto out;
down_read(&clp->cl_sem); down_read(&nfsi->rwsem);
if (test_bit(NFS_DELEGATED_STATE, &state->flags)) { if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
struct nfs_inode *nfsi = NFS_I(state->inode);
/* Yes: cache locks! */ /* Yes: cache locks! */
down_read(&nfsi->rwsem);
/* ...but avoid races with delegation recall... */ /* ...but avoid races with delegation recall... */
if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
request->fl_flags = fl_flags & ~FL_SLEEP; request->fl_flags = fl_flags & ~FL_SLEEP;
status = do_vfs_lock(request->fl_file, request); status = do_vfs_lock(request->fl_file, request);
up_read(&nfsi->rwsem);
goto out_unlock; goto out_unlock;
} }
up_read(&nfsi->rwsem);
}
status = _nfs4_do_setlk(state, cmd, request, 0); status = _nfs4_do_setlk(state, cmd, request, 0);
if (status != 0) if (status != 0)
goto out_unlock; goto out_unlock;
...@@ -3518,7 +3563,7 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock ...@@ -3518,7 +3563,7 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock
if (do_vfs_lock(request->fl_file, request) < 0) if (do_vfs_lock(request->fl_file, request) < 0)
printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __func__); printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __func__);
out_unlock: out_unlock:
up_read(&clp->cl_sem); up_read(&nfsi->rwsem);
out: out:
request->fl_flags = fl_flags; request->fl_flags = fl_flags;
return status; return status;
...@@ -3664,11 +3709,15 @@ int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, ...@@ -3664,11 +3709,15 @@ int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
} }
struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops = { struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops = {
.owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT,
.state_flag_bit = NFS_STATE_RECLAIM_REBOOT,
.recover_open = nfs4_open_reclaim, .recover_open = nfs4_open_reclaim,
.recover_lock = nfs4_lock_reclaim, .recover_lock = nfs4_lock_reclaim,
}; };
struct nfs4_state_recovery_ops nfs4_network_partition_recovery_ops = { struct nfs4_state_recovery_ops nfs4_nograce_recovery_ops = {
.owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE,
.state_flag_bit = NFS_STATE_RECLAIM_NOGRACE,
.recover_open = nfs4_open_expired, .recover_open = nfs4_open_expired,
.recover_lock = nfs4_lock_expired, .recover_lock = nfs4_lock_expired,
}; };
......
...@@ -65,7 +65,6 @@ nfs4_renew_state(struct work_struct *work) ...@@ -65,7 +65,6 @@ nfs4_renew_state(struct work_struct *work)
long lease, timeout; long lease, timeout;
unsigned long last, now; unsigned long last, now;
down_read(&clp->cl_sem);
dprintk("%s: start\n", __func__); dprintk("%s: start\n", __func__);
/* Are there any active superblocks? */ /* Are there any active superblocks? */
if (list_empty(&clp->cl_superblocks)) if (list_empty(&clp->cl_superblocks))
...@@ -77,17 +76,19 @@ nfs4_renew_state(struct work_struct *work) ...@@ -77,17 +76,19 @@ nfs4_renew_state(struct work_struct *work)
timeout = (2 * lease) / 3 + (long)last - (long)now; timeout = (2 * lease) / 3 + (long)last - (long)now;
/* Are we close to a lease timeout? */ /* Are we close to a lease timeout? */
if (time_after(now, last + lease/3)) { if (time_after(now, last + lease/3)) {
cred = nfs4_get_renew_cred(clp); cred = nfs4_get_renew_cred_locked(clp);
spin_unlock(&clp->cl_lock);
if (cred == NULL) { if (cred == NULL) {
if (list_empty(&clp->cl_delegations)) {
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
spin_unlock(&clp->cl_lock);
nfs_expire_all_delegations(clp);
goto out; goto out;
} }
spin_unlock(&clp->cl_lock); nfs_expire_all_delegations(clp);
} else {
/* Queue an asynchronous RENEW. */ /* Queue an asynchronous RENEW. */
nfs4_proc_async_renew(clp, cred); nfs4_proc_async_renew(clp, cred);
put_rpccred(cred); put_rpccred(cred);
}
timeout = (2 * lease) / 3; timeout = (2 * lease) / 3;
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
} else } else
...@@ -100,12 +101,11 @@ nfs4_renew_state(struct work_struct *work) ...@@ -100,12 +101,11 @@ nfs4_renew_state(struct work_struct *work)
cancel_delayed_work(&clp->cl_renewd); cancel_delayed_work(&clp->cl_renewd);
schedule_delayed_work(&clp->cl_renewd, timeout); schedule_delayed_work(&clp->cl_renewd, timeout);
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
nfs_expire_unreferenced_delegations(clp);
out: out:
up_read(&clp->cl_sem);
dprintk("%s: done\n", __func__); dprintk("%s: done\n", __func__);
} }
/* Must be called with clp->cl_sem locked for writes */
void void
nfs4_schedule_state_renewal(struct nfs_client *clp) nfs4_schedule_state_renewal(struct nfs_client *clp)
{ {
......
...@@ -71,14 +71,12 @@ static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred) ...@@ -71,14 +71,12 @@ static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred)
return status; return status;
} }
static struct rpc_cred *nfs4_get_machine_cred(struct nfs_client *clp) static struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp)
{ {
struct rpc_cred *cred = NULL; struct rpc_cred *cred = NULL;
spin_lock(&clp->cl_lock);
if (clp->cl_machine_cred != NULL) if (clp->cl_machine_cred != NULL)
cred = get_rpccred(clp->cl_machine_cred); cred = get_rpccred(clp->cl_machine_cred);
spin_unlock(&clp->cl_lock);
return cred; return cred;
} }
...@@ -94,7 +92,7 @@ static void nfs4_clear_machine_cred(struct nfs_client *clp) ...@@ -94,7 +92,7 @@ static void nfs4_clear_machine_cred(struct nfs_client *clp)
put_rpccred(cred); put_rpccred(cred);
} }
struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp) struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp)
{ {
struct nfs4_state_owner *sp; struct nfs4_state_owner *sp;
struct rb_node *pos; struct rb_node *pos;
...@@ -110,13 +108,24 @@ struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp) ...@@ -110,13 +108,24 @@ struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp)
return cred; return cred;
} }
static struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp)
{
struct rpc_cred *cred;
spin_lock(&clp->cl_lock);
cred = nfs4_get_renew_cred_locked(clp);
spin_unlock(&clp->cl_lock);
return cred;
}
static struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp) static struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp)
{ {
struct nfs4_state_owner *sp; struct nfs4_state_owner *sp;
struct rb_node *pos; struct rb_node *pos;
struct rpc_cred *cred; struct rpc_cred *cred;
cred = nfs4_get_machine_cred(clp); spin_lock(&clp->cl_lock);
cred = nfs4_get_machine_cred_locked(clp);
if (cred != NULL) if (cred != NULL)
goto out; goto out;
pos = rb_first(&clp->cl_state_owners); pos = rb_first(&clp->cl_state_owners);
...@@ -125,6 +134,7 @@ static struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp) ...@@ -125,6 +134,7 @@ static struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp)
cred = get_rpccred(sp->so_cred); cred = get_rpccred(sp->so_cred);
} }
out: out:
spin_unlock(&clp->cl_lock);
return cred; return cred;
} }
...@@ -295,10 +305,6 @@ nfs4_drop_state_owner(struct nfs4_state_owner *sp) ...@@ -295,10 +305,6 @@ nfs4_drop_state_owner(struct nfs4_state_owner *sp)
} }
} }
/*
* Note: must be called with clp->cl_sem held in order to prevent races
* with reboot recovery!
*/
struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred) struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred)
{ {
struct nfs_client *clp = server->nfs_client; struct nfs_client *clp = server->nfs_client;
...@@ -327,10 +333,6 @@ struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct ...@@ -327,10 +333,6 @@ struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct
return sp; return sp;
} }
/*
* Must be called with clp->cl_sem held in order to avoid races
* with state recovery...
*/
void nfs4_put_state_owner(struct nfs4_state_owner *sp) void nfs4_put_state_owner(struct nfs4_state_owner *sp)
{ {
struct nfs_client *clp = sp->so_client; struct nfs_client *clp = sp->so_client;
...@@ -361,18 +363,18 @@ nfs4_alloc_open_state(void) ...@@ -361,18 +363,18 @@ nfs4_alloc_open_state(void)
} }
void void
nfs4_state_set_mode_locked(struct nfs4_state *state, mode_t mode) nfs4_state_set_mode_locked(struct nfs4_state *state, fmode_t fmode)
{ {
if (state->state == mode) if (state->state == fmode)
return; return;
/* NB! List reordering - see the reclaim code for why. */ /* NB! List reordering - see the reclaim code for why. */
if ((mode & FMODE_WRITE) != (state->state & FMODE_WRITE)) { if ((fmode & FMODE_WRITE) != (state->state & FMODE_WRITE)) {
if (mode & FMODE_WRITE) if (fmode & FMODE_WRITE)
list_move(&state->open_states, &state->owner->so_states); list_move(&state->open_states, &state->owner->so_states);
else else
list_move_tail(&state->open_states, &state->owner->so_states); list_move_tail(&state->open_states, &state->owner->so_states);
} }
state->state = mode; state->state = fmode;
} }
static struct nfs4_state * static struct nfs4_state *
...@@ -432,10 +434,6 @@ nfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner) ...@@ -432,10 +434,6 @@ nfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner)
return state; return state;
} }
/*
* Beware! Caller must be holding exactly one
* reference to clp->cl_sem!
*/
void nfs4_put_open_state(struct nfs4_state *state) void nfs4_put_open_state(struct nfs4_state *state)
{ {
struct inode *inode = state->inode; struct inode *inode = state->inode;
...@@ -456,16 +454,16 @@ void nfs4_put_open_state(struct nfs4_state *state) ...@@ -456,16 +454,16 @@ void nfs4_put_open_state(struct nfs4_state *state)
/* /*
* Close the current file. * Close the current file.
*/ */
static void __nfs4_close(struct path *path, struct nfs4_state *state, mode_t mode, int wait) static void __nfs4_close(struct path *path, struct nfs4_state *state, fmode_t fmode, int wait)
{ {
struct nfs4_state_owner *owner = state->owner; struct nfs4_state_owner *owner = state->owner;
int call_close = 0; int call_close = 0;
int newstate; fmode_t newstate;
atomic_inc(&owner->so_count); atomic_inc(&owner->so_count);
/* Protect against nfs4_find_state() */ /* Protect against nfs4_find_state() */
spin_lock(&owner->so_lock); spin_lock(&owner->so_lock);
switch (mode & (FMODE_READ | FMODE_WRITE)) { switch (fmode & (FMODE_READ | FMODE_WRITE)) {
case FMODE_READ: case FMODE_READ:
state->n_rdonly--; state->n_rdonly--;
break; break;
...@@ -500,14 +498,14 @@ static void __nfs4_close(struct path *path, struct nfs4_state *state, mode_t mod ...@@ -500,14 +498,14 @@ static void __nfs4_close(struct path *path, struct nfs4_state *state, mode_t mod
nfs4_do_close(path, state, wait); nfs4_do_close(path, state, wait);
} }
void nfs4_close_state(struct path *path, struct nfs4_state *state, mode_t mode) void nfs4_close_state(struct path *path, struct nfs4_state *state, fmode_t fmode)
{ {
__nfs4_close(path, state, mode, 0); __nfs4_close(path, state, fmode, 0);
} }
void nfs4_close_sync(struct path *path, struct nfs4_state *state, mode_t mode) void nfs4_close_sync(struct path *path, struct nfs4_state *state, fmode_t fmode)
{ {
__nfs4_close(path, state, mode, 1); __nfs4_close(path, state, fmode, 1);
} }
/* /*
...@@ -568,7 +566,6 @@ static void nfs4_free_lock_state(struct nfs4_lock_state *lsp) ...@@ -568,7 +566,6 @@ static void nfs4_free_lock_state(struct nfs4_lock_state *lsp)
* Return a compatible lock_state. If no initialized lock_state structure * Return a compatible lock_state. If no initialized lock_state structure
* exists, return an uninitialized one. * exists, return an uninitialized one.
* *
* The caller must be holding clp->cl_sem
*/ */
static struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner) static struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner)
{ {
...@@ -770,32 +767,34 @@ int nfs_wait_on_sequence(struct nfs_seqid *seqid, struct rpc_task *task) ...@@ -770,32 +767,34 @@ int nfs_wait_on_sequence(struct nfs_seqid *seqid, struct rpc_task *task)
return status; return status;
} }
static int reclaimer(void *); static int nfs4_run_state_manager(void *);
static inline void nfs4_clear_recover_bit(struct nfs_client *clp) static void nfs4_clear_state_manager_bit(struct nfs_client *clp)
{ {
smp_mb__before_clear_bit(); smp_mb__before_clear_bit();
clear_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state); clear_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state);
smp_mb__after_clear_bit(); smp_mb__after_clear_bit();
wake_up_bit(&clp->cl_state, NFS4CLNT_STATE_RECOVER); wake_up_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING);
rpc_wake_up(&clp->cl_rpcwaitq); rpc_wake_up(&clp->cl_rpcwaitq);
} }
/* /*
* State recovery routine * Schedule the nfs_client asynchronous state management routine
*/ */
static void nfs4_recover_state(struct nfs_client *clp) void nfs4_schedule_state_manager(struct nfs_client *clp)
{ {
struct task_struct *task; struct task_struct *task;
if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
return;
__module_get(THIS_MODULE); __module_get(THIS_MODULE);
atomic_inc(&clp->cl_count); atomic_inc(&clp->cl_count);
task = kthread_run(reclaimer, clp, "%s-reclaim", task = kthread_run(nfs4_run_state_manager, clp, "%s-manager",
rpc_peeraddr2str(clp->cl_rpcclient, rpc_peeraddr2str(clp->cl_rpcclient,
RPC_DISPLAY_ADDR)); RPC_DISPLAY_ADDR));
if (!IS_ERR(task)) if (!IS_ERR(task))
return; return;
nfs4_clear_recover_bit(clp); nfs4_clear_state_manager_bit(clp);
nfs_put_client(clp); nfs_put_client(clp);
module_put(THIS_MODULE); module_put(THIS_MODULE);
} }
...@@ -807,16 +806,42 @@ void nfs4_schedule_state_recovery(struct nfs_client *clp) ...@@ -807,16 +806,42 @@ void nfs4_schedule_state_recovery(struct nfs_client *clp)
{ {
if (!clp) if (!clp)
return; return;
if (test_and_set_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) == 0) if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
nfs4_recover_state(clp); set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
nfs4_schedule_state_manager(clp);
} }
static int nfs4_reclaim_locks(struct nfs4_state_recovery_ops *ops, struct nfs4_state *state) static int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_state *state)
{
set_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
/* Don't recover state that expired before the reboot */
if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags)) {
clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
return 0;
}
set_bit(NFS_OWNER_RECLAIM_REBOOT, &state->owner->so_flags);
set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state);
return 1;
}
int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_state *state)
{
set_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags);
clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
set_bit(NFS_OWNER_RECLAIM_NOGRACE, &state->owner->so_flags);
set_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state);
return 1;
}
static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_recovery_ops *ops)
{ {
struct inode *inode = state->inode; struct inode *inode = state->inode;
struct nfs_inode *nfsi = NFS_I(inode);
struct file_lock *fl; struct file_lock *fl;
int status = 0; int status = 0;
down_write(&nfsi->rwsem);
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK))) if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
continue; continue;
...@@ -839,12 +864,14 @@ static int nfs4_reclaim_locks(struct nfs4_state_recovery_ops *ops, struct nfs4_s ...@@ -839,12 +864,14 @@ static int nfs4_reclaim_locks(struct nfs4_state_recovery_ops *ops, struct nfs4_s
goto out_err; goto out_err;
} }
} }
up_write(&nfsi->rwsem);
return 0; return 0;
out_err: out_err:
up_write(&nfsi->rwsem);
return status; return status;
} }
static int nfs4_reclaim_open_state(struct nfs4_state_recovery_ops *ops, struct nfs4_state_owner *sp) static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs4_state_recovery_ops *ops)
{ {
struct nfs4_state *state; struct nfs4_state *state;
struct nfs4_lock_state *lock; struct nfs4_lock_state *lock;
...@@ -858,28 +885,34 @@ static int nfs4_reclaim_open_state(struct nfs4_state_recovery_ops *ops, struct n ...@@ -858,28 +885,34 @@ static int nfs4_reclaim_open_state(struct nfs4_state_recovery_ops *ops, struct n
* recovering after a network partition or a reboot from a * recovering after a network partition or a reboot from a
* server that doesn't support a grace period. * server that doesn't support a grace period.
*/ */
restart:
spin_lock(&sp->so_lock);
list_for_each_entry(state, &sp->so_states, open_states) { list_for_each_entry(state, &sp->so_states, open_states) {
if (!test_and_clear_bit(ops->state_flag_bit, &state->flags))
continue;
if (state->state == 0) if (state->state == 0)
continue; continue;
atomic_inc(&state->count);
spin_unlock(&sp->so_lock);
status = ops->recover_open(sp, state); status = ops->recover_open(sp, state);
if (status >= 0) { if (status >= 0) {
status = nfs4_reclaim_locks(ops, state); status = nfs4_reclaim_locks(state, ops);
if (status < 0) if (status >= 0) {
goto out_err;
list_for_each_entry(lock, &state->lock_states, ls_locks) { list_for_each_entry(lock, &state->lock_states, ls_locks) {
if (!(lock->ls_flags & NFS_LOCK_INITIALIZED)) if (!(lock->ls_flags & NFS_LOCK_INITIALIZED))
printk("%s: Lock reclaim failed!\n", printk("%s: Lock reclaim failed!\n",
__func__); __func__);
} }
continue; nfs4_put_open_state(state);
goto restart;
}
} }
switch (status) { switch (status) {
default: default:
printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n", printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n",
__func__, status); __func__, status);
case -ENOENT: case -ENOENT:
case -NFS4ERR_RECLAIM_BAD: case -ESTALE:
case -NFS4ERR_RECLAIM_CONFLICT:
/* /*
* Open state on this file cannot be recovered * Open state on this file cannot be recovered
* All we can do is revert to using the zero stateid. * All we can do is revert to using the zero stateid.
...@@ -889,84 +922,176 @@ static int nfs4_reclaim_open_state(struct nfs4_state_recovery_ops *ops, struct n ...@@ -889,84 +922,176 @@ static int nfs4_reclaim_open_state(struct nfs4_state_recovery_ops *ops, struct n
/* Mark the file as being 'closed' */ /* Mark the file as being 'closed' */
state->state = 0; state->state = 0;
break; break;
case -NFS4ERR_RECLAIM_BAD:
case -NFS4ERR_RECLAIM_CONFLICT:
nfs4_state_mark_reclaim_nograce(sp->so_client, state);
break;
case -NFS4ERR_EXPIRED: case -NFS4ERR_EXPIRED:
case -NFS4ERR_NO_GRACE: case -NFS4ERR_NO_GRACE:
nfs4_state_mark_reclaim_nograce(sp->so_client, state);
case -NFS4ERR_STALE_CLIENTID: case -NFS4ERR_STALE_CLIENTID:
goto out_err; goto out_err;
} }
nfs4_put_open_state(state);
goto restart;
} }
spin_unlock(&sp->so_lock);
return 0; return 0;
out_err: out_err:
nfs4_put_open_state(state);
return status; return status;
} }
static void nfs4_state_mark_reclaim(struct nfs_client *clp) static void nfs4_clear_open_state(struct nfs4_state *state)
{ {
struct nfs4_state_owner *sp;
struct rb_node *pos;
struct nfs4_state *state;
struct nfs4_lock_state *lock; struct nfs4_lock_state *lock;
/* Reset all sequence ids to zero */
for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
sp->so_seqid.counter = 0;
sp->so_seqid.flags = 0;
spin_lock(&sp->so_lock);
list_for_each_entry(state, &sp->so_states, open_states) {
clear_bit(NFS_DELEGATED_STATE, &state->flags); clear_bit(NFS_DELEGATED_STATE, &state->flags);
clear_bit(NFS_O_RDONLY_STATE, &state->flags); clear_bit(NFS_O_RDONLY_STATE, &state->flags);
clear_bit(NFS_O_WRONLY_STATE, &state->flags); clear_bit(NFS_O_WRONLY_STATE, &state->flags);
clear_bit(NFS_O_RDWR_STATE, &state->flags); clear_bit(NFS_O_RDWR_STATE, &state->flags);
list_for_each_entry(lock, &state->lock_states, ls_locks) { list_for_each_entry(lock, &state->lock_states, ls_locks) {
lock->ls_seqid.counter = 0;
lock->ls_seqid.flags = 0; lock->ls_seqid.flags = 0;
lock->ls_flags &= ~NFS_LOCK_INITIALIZED; lock->ls_flags &= ~NFS_LOCK_INITIALIZED;
} }
}
static void nfs4_state_mark_reclaim_helper(struct nfs_client *clp, int (*mark_reclaim)(struct nfs_client *clp, struct nfs4_state *state))
{
struct nfs4_state_owner *sp;
struct rb_node *pos;
struct nfs4_state *state;
/* Reset all sequence ids to zero */
for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
sp->so_seqid.flags = 0;
spin_lock(&sp->so_lock);
list_for_each_entry(state, &sp->so_states, open_states) {
if (mark_reclaim(clp, state))
nfs4_clear_open_state(state);
} }
spin_unlock(&sp->so_lock); spin_unlock(&sp->so_lock);
} }
} }
static int reclaimer(void *ptr) static void nfs4_state_start_reclaim_reboot(struct nfs_client *clp)
{
/* Mark all delegations for reclaim */
nfs_delegation_mark_reclaim(clp);
nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_reboot);
}
static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp)
{ {
struct nfs_client *clp = ptr;
struct nfs4_state_owner *sp; struct nfs4_state_owner *sp;
struct rb_node *pos; struct rb_node *pos;
struct nfs4_state_recovery_ops *ops; struct nfs4_state *state;
struct rpc_cred *cred;
int status = 0;
allow_signal(SIGKILL); if (!test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state))
return;
/* Ensure exclusive access to NFSv4 state */ for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
down_write(&clp->cl_sem); sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
/* Are there any NFS mounts out there? */ spin_lock(&sp->so_lock);
if (list_empty(&clp->cl_superblocks)) list_for_each_entry(state, &sp->so_states, open_states) {
goto out; if (!test_and_clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags))
restart_loop: continue;
ops = &nfs4_network_partition_recovery_ops; nfs4_state_mark_reclaim_nograce(clp, state);
/* Are there any open files on this volume? */ }
cred = nfs4_get_renew_cred(clp); spin_unlock(&sp->so_lock);
if (cred != NULL) { }
/* Yes there are: try to renew the old lease */
status = nfs4_proc_renew(clp, cred); nfs_delegation_reap_unclaimed(clp);
put_rpccred(cred); }
switch (status) {
case 0: static void nfs_delegation_clear_all(struct nfs_client *clp)
{
nfs_delegation_mark_reclaim(clp);
nfs_delegation_reap_unclaimed(clp);
}
static void nfs4_state_start_reclaim_nograce(struct nfs_client *clp)
{
nfs_delegation_clear_all(clp);
nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce);
}
static void nfs4_state_end_reclaim_nograce(struct nfs_client *clp)
{
clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state);
}
static void nfs4_recovery_handle_error(struct nfs_client *clp, int error)
{
switch (error) {
case -NFS4ERR_CB_PATH_DOWN: case -NFS4ERR_CB_PATH_DOWN:
goto out; nfs_handle_cb_pathdown(clp);
break;
case -NFS4ERR_STALE_CLIENTID: case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_LEASE_MOVED: case -NFS4ERR_LEASE_MOVED:
ops = &nfs4_reboot_recovery_ops; set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
nfs4_state_start_reclaim_reboot(clp);
break;
case -NFS4ERR_EXPIRED:
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
nfs4_state_start_reclaim_nograce(clp);
} }
} else { }
/* "reboot" to ensure we clear all state on the server */
clp->cl_boot_time = CURRENT_TIME; static int nfs4_do_reclaim(struct nfs_client *clp, const struct nfs4_state_recovery_ops *ops)
{
struct rb_node *pos;
int status = 0;
restart:
spin_lock(&clp->cl_lock);
for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
struct nfs4_state_owner *sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
if (!test_and_clear_bit(ops->owner_flag_bit, &sp->so_flags))
continue;
atomic_inc(&sp->so_count);
spin_unlock(&clp->cl_lock);
status = nfs4_reclaim_open_state(sp, ops);
if (status < 0) {
set_bit(ops->owner_flag_bit, &sp->so_flags);
nfs4_put_state_owner(sp);
nfs4_recovery_handle_error(clp, status);
return status;
} }
/* We're going to have to re-establish a clientid */ nfs4_put_state_owner(sp);
nfs4_state_mark_reclaim(clp); goto restart;
status = -ENOENT; }
spin_unlock(&clp->cl_lock);
return status;
}
static int nfs4_check_lease(struct nfs_client *clp)
{
struct rpc_cred *cred;
int status = -NFS4ERR_EXPIRED;
/* Is the client already known to have an expired lease? */
if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
return 0;
cred = nfs4_get_renew_cred(clp);
if (cred == NULL) {
cred = nfs4_get_setclientid_cred(clp);
if (cred == NULL)
goto out;
}
status = nfs4_proc_renew(clp, cred);
put_rpccred(cred);
out:
nfs4_recovery_handle_error(clp, status);
return status;
}
static int nfs4_reclaim_lease(struct nfs_client *clp)
{
struct rpc_cred *cred;
int status = -ENOENT;
cred = nfs4_get_setclientid_cred(clp); cred = nfs4_get_setclientid_cred(clp);
if (cred != NULL) { if (cred != NULL) {
status = nfs4_init_client(clp, cred); status = nfs4_init_client(clp, cred);
...@@ -974,42 +1099,90 @@ static int reclaimer(void *ptr) ...@@ -974,42 +1099,90 @@ static int reclaimer(void *ptr)
/* Handle case where the user hasn't set up machine creds */ /* Handle case where the user hasn't set up machine creds */
if (status == -EACCES && cred == clp->cl_machine_cred) { if (status == -EACCES && cred == clp->cl_machine_cred) {
nfs4_clear_machine_cred(clp); nfs4_clear_machine_cred(clp);
goto restart_loop; status = -EAGAIN;
} }
} }
if (status) return status;
}
static void nfs4_state_manager(struct nfs_client *clp)
{
int status = 0;
/* Ensure exclusive access to NFSv4 state */
for(;;) {
if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) {
/* We're going to have to re-establish a clientid */
status = nfs4_reclaim_lease(clp);
if (status) {
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
if (status == -EAGAIN)
continue;
goto out_error; goto out_error;
/* Mark all delegations for reclaim */
nfs_delegation_mark_reclaim(clp);
/* Note: list is protected by exclusive lock on cl->cl_sem */
for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
status = nfs4_reclaim_open_state(ops, sp);
if (status < 0) {
if (status == -NFS4ERR_NO_GRACE) {
ops = &nfs4_network_partition_recovery_ops;
status = nfs4_reclaim_open_state(ops, sp);
} }
clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
}
if (test_and_clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state)) {
status = nfs4_check_lease(clp);
if (status != 0)
continue;
}
/* First recover reboot state... */
if (test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) {
status = nfs4_do_reclaim(clp, &nfs4_reboot_recovery_ops);
if (status == -NFS4ERR_STALE_CLIENTID) if (status == -NFS4ERR_STALE_CLIENTID)
goto restart_loop; continue;
nfs4_state_end_reclaim_reboot(clp);
continue;
}
/* Now recover expired state... */
if (test_and_clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) {
status = nfs4_do_reclaim(clp, &nfs4_nograce_recovery_ops);
if (status < 0) {
set_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state);
if (status == -NFS4ERR_STALE_CLIENTID)
continue;
if (status == -NFS4ERR_EXPIRED) if (status == -NFS4ERR_EXPIRED)
goto restart_loop; continue;
goto out_error;
} else
nfs4_state_end_reclaim_nograce(clp);
continue;
} }
if (test_and_clear_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) {
nfs_client_return_marked_delegations(clp);
continue;
} }
nfs_delegation_reap_unclaimed(clp);
out: nfs4_clear_state_manager_bit(clp);
up_write(&clp->cl_sem); /* Did we race with an attempt to give us more work? */
if (status == -NFS4ERR_CB_PATH_DOWN) if (clp->cl_state == 0)
nfs_handle_cb_pathdown(clp); break;
nfs4_clear_recover_bit(clp); if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
break;
}
return;
out_error:
printk(KERN_WARNING "Error: state manager failed on NFSv4 server %s"
" with error %d\n", clp->cl_hostname, -status);
if (test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state))
nfs4_state_end_reclaim_reboot(clp);
nfs4_clear_state_manager_bit(clp);
}
static int nfs4_run_state_manager(void *ptr)
{
struct nfs_client *clp = ptr;
allow_signal(SIGKILL);
nfs4_state_manager(clp);
nfs_put_client(clp); nfs_put_client(clp);
module_put_and_exit(0); module_put_and_exit(0);
return 0; return 0;
out_error:
printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %s"
" with error %d\n", clp->cl_hostname, -status);
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
goto out;
} }
/* /*
......
...@@ -541,6 +541,7 @@ static struct { ...@@ -541,6 +541,7 @@ static struct {
struct compound_hdr { struct compound_hdr {
int32_t status; int32_t status;
uint32_t nops; uint32_t nops;
__be32 * nops_p;
uint32_t taglen; uint32_t taglen;
char * tag; char * tag;
}; };
...@@ -578,7 +579,7 @@ static void encode_string(struct xdr_stream *xdr, unsigned int len, const char * ...@@ -578,7 +579,7 @@ static void encode_string(struct xdr_stream *xdr, unsigned int len, const char *
xdr_encode_opaque(p, str, len); xdr_encode_opaque(p, str, len);
} }
static int encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr) static void encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
...@@ -588,8 +589,13 @@ static int encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr) ...@@ -588,8 +589,13 @@ static int encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
WRITE32(hdr->taglen); WRITE32(hdr->taglen);
WRITEMEM(hdr->tag, hdr->taglen); WRITEMEM(hdr->tag, hdr->taglen);
WRITE32(NFS4_MINOR_VERSION); WRITE32(NFS4_MINOR_VERSION);
hdr->nops_p = p;
WRITE32(hdr->nops); WRITE32(hdr->nops);
return 0; }
static void encode_nops(struct compound_hdr *hdr)
{
*hdr->nops_p = htonl(hdr->nops);
} }
static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *verf) static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *verf)
...@@ -601,7 +607,7 @@ static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *ve ...@@ -601,7 +607,7 @@ static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *ve
xdr_encode_opaque_fixed(p, verf->data, NFS4_VERIFIER_SIZE); xdr_encode_opaque_fixed(p, verf->data, NFS4_VERIFIER_SIZE);
} }
static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const struct nfs_server *server) static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const struct nfs_server *server)
{ {
char owner_name[IDMAP_NAMESZ]; char owner_name[IDMAP_NAMESZ];
char owner_group[IDMAP_NAMESZ]; char owner_group[IDMAP_NAMESZ];
...@@ -612,7 +618,6 @@ static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const s ...@@ -612,7 +618,6 @@ static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const s
int len; int len;
uint32_t bmval0 = 0; uint32_t bmval0 = 0;
uint32_t bmval1 = 0; uint32_t bmval1 = 0;
int status;
/* /*
* We reserve enough space to write the entire attribute buffer at once. * We reserve enough space to write the entire attribute buffer at once.
...@@ -723,23 +728,20 @@ static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const s ...@@ -723,23 +728,20 @@ static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const s
*q++ = htonl(bmval1); *q++ = htonl(bmval1);
*q++ = htonl(len); *q++ = htonl(len);
status = 0;
/* out: */ /* out: */
return status;
} }
static int encode_access(struct xdr_stream *xdr, u32 access) static void encode_access(struct xdr_stream *xdr, u32 access, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
RESERVE_SPACE(8); RESERVE_SPACE(8);
WRITE32(OP_ACCESS); WRITE32(OP_ACCESS);
WRITE32(access); WRITE32(access);
hdr->nops++;
return 0;
} }
static int encode_close(struct xdr_stream *xdr, const struct nfs_closeargs *arg) static void encode_close(struct xdr_stream *xdr, const struct nfs_closeargs *arg, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
...@@ -747,11 +749,10 @@ static int encode_close(struct xdr_stream *xdr, const struct nfs_closeargs *arg) ...@@ -747,11 +749,10 @@ static int encode_close(struct xdr_stream *xdr, const struct nfs_closeargs *arg)
WRITE32(OP_CLOSE); WRITE32(OP_CLOSE);
WRITE32(arg->seqid->sequence->counter); WRITE32(arg->seqid->sequence->counter);
WRITEMEM(arg->stateid->data, NFS4_STATEID_SIZE); WRITEMEM(arg->stateid->data, NFS4_STATEID_SIZE);
hdr->nops++;
return 0;
} }
static int encode_commit(struct xdr_stream *xdr, const struct nfs_writeargs *args) static void encode_commit(struct xdr_stream *xdr, const struct nfs_writeargs *args, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
...@@ -759,11 +760,10 @@ static int encode_commit(struct xdr_stream *xdr, const struct nfs_writeargs *arg ...@@ -759,11 +760,10 @@ static int encode_commit(struct xdr_stream *xdr, const struct nfs_writeargs *arg
WRITE32(OP_COMMIT); WRITE32(OP_COMMIT);
WRITE64(args->offset); WRITE64(args->offset);
WRITE32(args->count); WRITE32(args->count);
hdr->nops++;
return 0;
} }
static int encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *create) static void encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *create, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
...@@ -791,11 +791,12 @@ static int encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *c ...@@ -791,11 +791,12 @@ static int encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *c
RESERVE_SPACE(4 + create->name->len); RESERVE_SPACE(4 + create->name->len);
WRITE32(create->name->len); WRITE32(create->name->len);
WRITEMEM(create->name->name, create->name->len); WRITEMEM(create->name->name, create->name->len);
hdr->nops++;
return encode_attrs(xdr, create->attrs, create->server); encode_attrs(xdr, create->attrs, create->server);
} }
static int encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap) static void encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
...@@ -803,10 +804,10 @@ static int encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap) ...@@ -803,10 +804,10 @@ static int encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap)
WRITE32(OP_GETATTR); WRITE32(OP_GETATTR);
WRITE32(1); WRITE32(1);
WRITE32(bitmap); WRITE32(bitmap);
return 0; hdr->nops++;
} }
static int encode_getattr_two(struct xdr_stream *xdr, uint32_t bm0, uint32_t bm1) static void encode_getattr_two(struct xdr_stream *xdr, uint32_t bm0, uint32_t bm1, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
...@@ -815,40 +816,37 @@ static int encode_getattr_two(struct xdr_stream *xdr, uint32_t bm0, uint32_t bm1 ...@@ -815,40 +816,37 @@ static int encode_getattr_two(struct xdr_stream *xdr, uint32_t bm0, uint32_t bm1
WRITE32(2); WRITE32(2);
WRITE32(bm0); WRITE32(bm0);
WRITE32(bm1); WRITE32(bm1);
return 0; hdr->nops++;
} }
static int encode_getfattr(struct xdr_stream *xdr, const u32* bitmask) static void encode_getfattr(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr)
{ {
return encode_getattr_two(xdr, encode_getattr_two(xdr, bitmask[0] & nfs4_fattr_bitmap[0],
bitmask[0] & nfs4_fattr_bitmap[0], bitmask[1] & nfs4_fattr_bitmap[1], hdr);
bitmask[1] & nfs4_fattr_bitmap[1]);
} }
static int encode_fsinfo(struct xdr_stream *xdr, const u32* bitmask) static void encode_fsinfo(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr)
{ {
return encode_getattr_two(xdr, bitmask[0] & nfs4_fsinfo_bitmap[0], encode_getattr_two(xdr, bitmask[0] & nfs4_fsinfo_bitmap[0],
bitmask[1] & nfs4_fsinfo_bitmap[1]); bitmask[1] & nfs4_fsinfo_bitmap[1], hdr);
} }
static int encode_fs_locations(struct xdr_stream *xdr, const u32* bitmask) static void encode_fs_locations(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr)
{ {
return encode_getattr_two(xdr, encode_getattr_two(xdr, bitmask[0] & nfs4_fs_locations_bitmap[0],
bitmask[0] & nfs4_fs_locations_bitmap[0], bitmask[1] & nfs4_fs_locations_bitmap[1], hdr);
bitmask[1] & nfs4_fs_locations_bitmap[1]);
} }
static int encode_getfh(struct xdr_stream *xdr) static void encode_getfh(struct xdr_stream *xdr, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
RESERVE_SPACE(4); RESERVE_SPACE(4);
WRITE32(OP_GETFH); WRITE32(OP_GETFH);
hdr->nops++;
return 0;
} }
static int encode_link(struct xdr_stream *xdr, const struct qstr *name) static void encode_link(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
...@@ -856,8 +854,7 @@ static int encode_link(struct xdr_stream *xdr, const struct qstr *name) ...@@ -856,8 +854,7 @@ static int encode_link(struct xdr_stream *xdr, const struct qstr *name)
WRITE32(OP_LINK); WRITE32(OP_LINK);
WRITE32(name->len); WRITE32(name->len);
WRITEMEM(name->name, name->len); WRITEMEM(name->name, name->len);
hdr->nops++;
return 0;
} }
static inline int nfs4_lock_type(struct file_lock *fl, int block) static inline int nfs4_lock_type(struct file_lock *fl, int block)
...@@ -878,7 +875,7 @@ static inline uint64_t nfs4_lock_length(struct file_lock *fl) ...@@ -878,7 +875,7 @@ static inline uint64_t nfs4_lock_length(struct file_lock *fl)
* opcode,type,reclaim,offset,length,new_lock_owner = 32 * opcode,type,reclaim,offset,length,new_lock_owner = 32
* open_seqid,open_stateid,lock_seqid,lock_owner.clientid, lock_owner.id = 40 * open_seqid,open_stateid,lock_seqid,lock_owner.clientid, lock_owner.id = 40
*/ */
static int encode_lock(struct xdr_stream *xdr, const struct nfs_lock_args *args) static void encode_lock(struct xdr_stream *xdr, const struct nfs_lock_args *args, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
...@@ -904,11 +901,10 @@ static int encode_lock(struct xdr_stream *xdr, const struct nfs_lock_args *args) ...@@ -904,11 +901,10 @@ static int encode_lock(struct xdr_stream *xdr, const struct nfs_lock_args *args)
WRITEMEM(args->lock_stateid->data, NFS4_STATEID_SIZE); WRITEMEM(args->lock_stateid->data, NFS4_STATEID_SIZE);
WRITE32(args->lock_seqid->sequence->counter); WRITE32(args->lock_seqid->sequence->counter);
} }
hdr->nops++;
return 0;
} }
static int encode_lockt(struct xdr_stream *xdr, const struct nfs_lockt_args *args) static void encode_lockt(struct xdr_stream *xdr, const struct nfs_lockt_args *args, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
...@@ -921,11 +917,10 @@ static int encode_lockt(struct xdr_stream *xdr, const struct nfs_lockt_args *arg ...@@ -921,11 +917,10 @@ static int encode_lockt(struct xdr_stream *xdr, const struct nfs_lockt_args *arg
WRITE32(16); WRITE32(16);
WRITEMEM("lock id:", 8); WRITEMEM("lock id:", 8);
WRITE64(args->lock_owner.id); WRITE64(args->lock_owner.id);
hdr->nops++;
return 0;
} }
static int encode_locku(struct xdr_stream *xdr, const struct nfs_locku_args *args) static void encode_locku(struct xdr_stream *xdr, const struct nfs_locku_args *args, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
...@@ -936,11 +931,10 @@ static int encode_locku(struct xdr_stream *xdr, const struct nfs_locku_args *arg ...@@ -936,11 +931,10 @@ static int encode_locku(struct xdr_stream *xdr, const struct nfs_locku_args *arg
WRITEMEM(args->stateid->data, NFS4_STATEID_SIZE); WRITEMEM(args->stateid->data, NFS4_STATEID_SIZE);
WRITE64(args->fl->fl_start); WRITE64(args->fl->fl_start);
WRITE64(nfs4_lock_length(args->fl)); WRITE64(nfs4_lock_length(args->fl));
hdr->nops++;
return 0;
} }
static int encode_lookup(struct xdr_stream *xdr, const struct qstr *name) static void encode_lookup(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr)
{ {
int len = name->len; int len = name->len;
__be32 *p; __be32 *p;
...@@ -949,16 +943,15 @@ static int encode_lookup(struct xdr_stream *xdr, const struct qstr *name) ...@@ -949,16 +943,15 @@ static int encode_lookup(struct xdr_stream *xdr, const struct qstr *name)
WRITE32(OP_LOOKUP); WRITE32(OP_LOOKUP);
WRITE32(len); WRITE32(len);
WRITEMEM(name->name, len); WRITEMEM(name->name, len);
hdr->nops++;
return 0;
} }
static void encode_share_access(struct xdr_stream *xdr, int open_flags) static void encode_share_access(struct xdr_stream *xdr, fmode_t fmode)
{ {
__be32 *p; __be32 *p;
RESERVE_SPACE(8); RESERVE_SPACE(8);
switch (open_flags & (FMODE_READ|FMODE_WRITE)) { switch (fmode & (FMODE_READ|FMODE_WRITE)) {
case FMODE_READ: case FMODE_READ:
WRITE32(NFS4_SHARE_ACCESS_READ); WRITE32(NFS4_SHARE_ACCESS_READ);
break; break;
...@@ -969,7 +962,7 @@ static void encode_share_access(struct xdr_stream *xdr, int open_flags) ...@@ -969,7 +962,7 @@ static void encode_share_access(struct xdr_stream *xdr, int open_flags)
WRITE32(NFS4_SHARE_ACCESS_BOTH); WRITE32(NFS4_SHARE_ACCESS_BOTH);
break; break;
default: default:
BUG(); WRITE32(0);
} }
WRITE32(0); /* for linux, share_deny = 0 always */ WRITE32(0); /* for linux, share_deny = 0 always */
} }
...@@ -984,7 +977,7 @@ static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_opena ...@@ -984,7 +977,7 @@ static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_opena
RESERVE_SPACE(8); RESERVE_SPACE(8);
WRITE32(OP_OPEN); WRITE32(OP_OPEN);
WRITE32(arg->seqid->sequence->counter); WRITE32(arg->seqid->sequence->counter);
encode_share_access(xdr, arg->open_flags); encode_share_access(xdr, arg->fmode);
RESERVE_SPACE(28); RESERVE_SPACE(28);
WRITE64(arg->clientid); WRITE64(arg->clientid);
WRITE32(16); WRITE32(16);
...@@ -1024,7 +1017,7 @@ static void encode_opentype(struct xdr_stream *xdr, const struct nfs_openargs *a ...@@ -1024,7 +1017,7 @@ static void encode_opentype(struct xdr_stream *xdr, const struct nfs_openargs *a
} }
} }
static inline void encode_delegation_type(struct xdr_stream *xdr, int delegation_type) static inline void encode_delegation_type(struct xdr_stream *xdr, fmode_t delegation_type)
{ {
__be32 *p; __be32 *p;
...@@ -1053,7 +1046,7 @@ static inline void encode_claim_null(struct xdr_stream *xdr, const struct qstr * ...@@ -1053,7 +1046,7 @@ static inline void encode_claim_null(struct xdr_stream *xdr, const struct qstr *
encode_string(xdr, name->len, name->name); encode_string(xdr, name->len, name->name);
} }
static inline void encode_claim_previous(struct xdr_stream *xdr, int type) static inline void encode_claim_previous(struct xdr_stream *xdr, fmode_t type)
{ {
__be32 *p; __be32 *p;
...@@ -1072,7 +1065,7 @@ static inline void encode_claim_delegate_cur(struct xdr_stream *xdr, const struc ...@@ -1072,7 +1065,7 @@ static inline void encode_claim_delegate_cur(struct xdr_stream *xdr, const struc
encode_string(xdr, name->len, name->name); encode_string(xdr, name->len, name->name);
} }
static int encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg) static void encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg, struct compound_hdr *hdr)
{ {
encode_openhdr(xdr, arg); encode_openhdr(xdr, arg);
encode_opentype(xdr, arg); encode_opentype(xdr, arg);
...@@ -1089,10 +1082,10 @@ static int encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg) ...@@ -1089,10 +1082,10 @@ static int encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg)
default: default:
BUG(); BUG();
} }
return 0; hdr->nops++;
} }
static int encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_confirmargs *arg) static void encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_confirmargs *arg, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
...@@ -1100,11 +1093,10 @@ static int encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_con ...@@ -1100,11 +1093,10 @@ static int encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_con
WRITE32(OP_OPEN_CONFIRM); WRITE32(OP_OPEN_CONFIRM);
WRITEMEM(arg->stateid->data, NFS4_STATEID_SIZE); WRITEMEM(arg->stateid->data, NFS4_STATEID_SIZE);
WRITE32(arg->seqid->sequence->counter); WRITE32(arg->seqid->sequence->counter);
hdr->nops++;
return 0;
} }
static int encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_closeargs *arg) static void encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_closeargs *arg, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
...@@ -1112,12 +1104,12 @@ static int encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_closea ...@@ -1112,12 +1104,12 @@ static int encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_closea
WRITE32(OP_OPEN_DOWNGRADE); WRITE32(OP_OPEN_DOWNGRADE);
WRITEMEM(arg->stateid->data, NFS4_STATEID_SIZE); WRITEMEM(arg->stateid->data, NFS4_STATEID_SIZE);
WRITE32(arg->seqid->sequence->counter); WRITE32(arg->seqid->sequence->counter);
encode_share_access(xdr, arg->open_flags); encode_share_access(xdr, arg->fmode);
return 0; hdr->nops++;
} }
static int static void
encode_putfh(struct xdr_stream *xdr, const struct nfs_fh *fh) encode_putfh(struct xdr_stream *xdr, const struct nfs_fh *fh, struct compound_hdr *hdr)
{ {
int len = fh->size; int len = fh->size;
__be32 *p; __be32 *p;
...@@ -1126,18 +1118,16 @@ encode_putfh(struct xdr_stream *xdr, const struct nfs_fh *fh) ...@@ -1126,18 +1118,16 @@ encode_putfh(struct xdr_stream *xdr, const struct nfs_fh *fh)
WRITE32(OP_PUTFH); WRITE32(OP_PUTFH);
WRITE32(len); WRITE32(len);
WRITEMEM(fh->data, len); WRITEMEM(fh->data, len);
hdr->nops++;
return 0;
} }
static int encode_putrootfh(struct xdr_stream *xdr) static void encode_putrootfh(struct xdr_stream *xdr, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
RESERVE_SPACE(4); RESERVE_SPACE(4);
WRITE32(OP_PUTROOTFH); WRITE32(OP_PUTROOTFH);
hdr->nops++;
return 0;
} }
static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context *ctx) static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context *ctx)
...@@ -1153,7 +1143,7 @@ static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context ...@@ -1153,7 +1143,7 @@ static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context
WRITEMEM(zero_stateid.data, NFS4_STATEID_SIZE); WRITEMEM(zero_stateid.data, NFS4_STATEID_SIZE);
} }
static int encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args) static void encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
...@@ -1165,11 +1155,10 @@ static int encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args) ...@@ -1165,11 +1155,10 @@ static int encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args)
RESERVE_SPACE(12); RESERVE_SPACE(12);
WRITE64(args->offset); WRITE64(args->offset);
WRITE32(args->count); WRITE32(args->count);
hdr->nops++;
return 0;
} }
static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req) static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req, struct compound_hdr *hdr)
{ {
uint32_t attrs[2] = { uint32_t attrs[2] = {
FATTR4_WORD0_RDATTR_ERROR|FATTR4_WORD0_FILEID, FATTR4_WORD0_RDATTR_ERROR|FATTR4_WORD0_FILEID,
...@@ -1191,6 +1180,7 @@ static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg ...@@ -1191,6 +1180,7 @@ static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg
attrs[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID; attrs[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID;
WRITE32(attrs[0] & readdir->bitmask[0]); WRITE32(attrs[0] & readdir->bitmask[0]);
WRITE32(attrs[1] & readdir->bitmask[1]); WRITE32(attrs[1] & readdir->bitmask[1]);
hdr->nops++;
dprintk("%s: cookie = %Lu, verifier = %08x:%08x, bitmap = %08x:%08x\n", dprintk("%s: cookie = %Lu, verifier = %08x:%08x, bitmap = %08x:%08x\n",
__func__, __func__,
(unsigned long long)readdir->cookie, (unsigned long long)readdir->cookie,
...@@ -1198,21 +1188,18 @@ static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg ...@@ -1198,21 +1188,18 @@ static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg
((u32 *)readdir->verifier.data)[1], ((u32 *)readdir->verifier.data)[1],
attrs[0] & readdir->bitmask[0], attrs[0] & readdir->bitmask[0],
attrs[1] & readdir->bitmask[1]); attrs[1] & readdir->bitmask[1]);
return 0;
} }
static int encode_readlink(struct xdr_stream *xdr, const struct nfs4_readlink *readlink, struct rpc_rqst *req) static void encode_readlink(struct xdr_stream *xdr, const struct nfs4_readlink *readlink, struct rpc_rqst *req, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
RESERVE_SPACE(4); RESERVE_SPACE(4);
WRITE32(OP_READLINK); WRITE32(OP_READLINK);
hdr->nops++;
return 0;
} }
static int encode_remove(struct xdr_stream *xdr, const struct qstr *name) static void encode_remove(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
...@@ -1220,11 +1207,10 @@ static int encode_remove(struct xdr_stream *xdr, const struct qstr *name) ...@@ -1220,11 +1207,10 @@ static int encode_remove(struct xdr_stream *xdr, const struct qstr *name)
WRITE32(OP_REMOVE); WRITE32(OP_REMOVE);
WRITE32(name->len); WRITE32(name->len);
WRITEMEM(name->name, name->len); WRITEMEM(name->name, name->len);
hdr->nops++;
return 0;
} }
static int encode_rename(struct xdr_stream *xdr, const struct qstr *oldname, const struct qstr *newname) static void encode_rename(struct xdr_stream *xdr, const struct qstr *oldname, const struct qstr *newname, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
...@@ -1236,34 +1222,31 @@ static int encode_rename(struct xdr_stream *xdr, const struct qstr *oldname, con ...@@ -1236,34 +1222,31 @@ static int encode_rename(struct xdr_stream *xdr, const struct qstr *oldname, con
RESERVE_SPACE(4 + newname->len); RESERVE_SPACE(4 + newname->len);
WRITE32(newname->len); WRITE32(newname->len);
WRITEMEM(newname->name, newname->len); WRITEMEM(newname->name, newname->len);
hdr->nops++;
return 0;
} }
static int encode_renew(struct xdr_stream *xdr, const struct nfs_client *client_stateid) static void encode_renew(struct xdr_stream *xdr, const struct nfs_client *client_stateid, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
RESERVE_SPACE(12); RESERVE_SPACE(12);
WRITE32(OP_RENEW); WRITE32(OP_RENEW);
WRITE64(client_stateid->cl_clientid); WRITE64(client_stateid->cl_clientid);
hdr->nops++;
return 0;
} }
static int static void
encode_restorefh(struct xdr_stream *xdr) encode_restorefh(struct xdr_stream *xdr, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
RESERVE_SPACE(4); RESERVE_SPACE(4);
WRITE32(OP_RESTOREFH); WRITE32(OP_RESTOREFH);
hdr->nops++;
return 0;
} }
static int static int
encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg) encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
...@@ -1278,36 +1261,32 @@ encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg) ...@@ -1278,36 +1261,32 @@ encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg)
RESERVE_SPACE(4); RESERVE_SPACE(4);
WRITE32(arg->acl_len); WRITE32(arg->acl_len);
xdr_write_pages(xdr, arg->acl_pages, arg->acl_pgbase, arg->acl_len); xdr_write_pages(xdr, arg->acl_pages, arg->acl_pgbase, arg->acl_len);
hdr->nops++;
return 0; return 0;
} }
static int static void
encode_savefh(struct xdr_stream *xdr) encode_savefh(struct xdr_stream *xdr, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
RESERVE_SPACE(4); RESERVE_SPACE(4);
WRITE32(OP_SAVEFH); WRITE32(OP_SAVEFH);
hdr->nops++;
return 0;
} }
static int encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs *arg, const struct nfs_server *server) static void encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs *arg, const struct nfs_server *server, struct compound_hdr *hdr)
{ {
int status;
__be32 *p; __be32 *p;
RESERVE_SPACE(4+NFS4_STATEID_SIZE); RESERVE_SPACE(4+NFS4_STATEID_SIZE);
WRITE32(OP_SETATTR); WRITE32(OP_SETATTR);
WRITEMEM(arg->stateid.data, NFS4_STATEID_SIZE); WRITEMEM(arg->stateid.data, NFS4_STATEID_SIZE);
hdr->nops++;
if ((status = encode_attrs(xdr, arg->iap, server))) encode_attrs(xdr, arg->iap, server);
return status;
return 0;
} }
static int encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclientid *setclientid) static void encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclientid *setclientid, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
...@@ -1322,11 +1301,10 @@ static int encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclien ...@@ -1322,11 +1301,10 @@ static int encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclien
encode_string(xdr, setclientid->sc_uaddr_len, setclientid->sc_uaddr); encode_string(xdr, setclientid->sc_uaddr_len, setclientid->sc_uaddr);
RESERVE_SPACE(4); RESERVE_SPACE(4);
WRITE32(setclientid->sc_cb_ident); WRITE32(setclientid->sc_cb_ident);
hdr->nops++;
return 0;
} }
static int encode_setclientid_confirm(struct xdr_stream *xdr, const struct nfs_client *client_state) static void encode_setclientid_confirm(struct xdr_stream *xdr, const struct nfs_client *client_state, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
...@@ -1334,11 +1312,10 @@ static int encode_setclientid_confirm(struct xdr_stream *xdr, const struct nfs_c ...@@ -1334,11 +1312,10 @@ static int encode_setclientid_confirm(struct xdr_stream *xdr, const struct nfs_c
WRITE32(OP_SETCLIENTID_CONFIRM); WRITE32(OP_SETCLIENTID_CONFIRM);
WRITE64(client_state->cl_clientid); WRITE64(client_state->cl_clientid);
WRITEMEM(client_state->cl_confirm.data, NFS4_VERIFIER_SIZE); WRITEMEM(client_state->cl_confirm.data, NFS4_VERIFIER_SIZE);
hdr->nops++;
return 0;
} }
static int encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *args) static void encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *args, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
...@@ -1353,11 +1330,10 @@ static int encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *args ...@@ -1353,11 +1330,10 @@ static int encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *args
WRITE32(args->count); WRITE32(args->count);
xdr_write_pages(xdr, args->pages, args->pgbase, args->count); xdr_write_pages(xdr, args->pages, args->pgbase, args->count);
hdr->nops++;
return 0;
} }
static int encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *stateid) static void encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *stateid, struct compound_hdr *hdr)
{ {
__be32 *p; __be32 *p;
...@@ -1365,8 +1341,7 @@ static int encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *statei ...@@ -1365,8 +1341,7 @@ static int encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *statei
WRITE32(OP_DELEGRETURN); WRITE32(OP_DELEGRETURN);
WRITEMEM(stateid->data, NFS4_STATEID_SIZE); WRITEMEM(stateid->data, NFS4_STATEID_SIZE);
return 0; hdr->nops++;
} }
/* /*
* END OF "GENERIC" ENCODE ROUTINES. * END OF "GENERIC" ENCODE ROUTINES.
...@@ -1379,21 +1354,16 @@ static int nfs4_xdr_enc_access(struct rpc_rqst *req, __be32 *p, const struct nfs ...@@ -1379,21 +1354,16 @@ static int nfs4_xdr_enc_access(struct rpc_rqst *req, __be32 *p, const struct nfs
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 3, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh); encode_putfh(&xdr, args->fh, &hdr);
if (status != 0) encode_access(&xdr, args->access, &hdr);
goto out; encode_getfattr(&xdr, args->bitmask, &hdr);
status = encode_access(&xdr, args->access); encode_nops(&hdr);
if (status != 0) return 0;
goto out;
status = encode_getfattr(&xdr, args->bitmask);
out:
return status;
} }
/* /*
...@@ -1403,21 +1373,17 @@ static int nfs4_xdr_enc_lookup(struct rpc_rqst *req, __be32 *p, const struct nfs ...@@ -1403,21 +1373,17 @@ static int nfs4_xdr_enc_lookup(struct rpc_rqst *req, __be32 *p, const struct nfs
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 4, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
if ((status = encode_putfh(&xdr, args->dir_fh)) != 0) encode_putfh(&xdr, args->dir_fh, &hdr);
goto out; encode_lookup(&xdr, args->name, &hdr);
if ((status = encode_lookup(&xdr, args->name)) != 0) encode_getfh(&xdr, &hdr);
goto out; encode_getfattr(&xdr, args->bitmask, &hdr);
if ((status = encode_getfh(&xdr)) != 0) encode_nops(&hdr);
goto out; return 0;
status = encode_getfattr(&xdr, args->bitmask);
out:
return status;
} }
/* /*
...@@ -1427,18 +1393,16 @@ static int nfs4_xdr_enc_lookup_root(struct rpc_rqst *req, __be32 *p, const struc ...@@ -1427,18 +1393,16 @@ static int nfs4_xdr_enc_lookup_root(struct rpc_rqst *req, __be32 *p, const struc
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 3, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
if ((status = encode_putrootfh(&xdr)) != 0) encode_putrootfh(&xdr, &hdr);
goto out; encode_getfh(&xdr, &hdr);
if ((status = encode_getfh(&xdr)) == 0) encode_getfattr(&xdr, args->bitmask, &hdr);
status = encode_getfattr(&xdr, args->bitmask); encode_nops(&hdr);
out: return 0;
return status;
} }
/* /*
...@@ -1448,19 +1412,16 @@ static int nfs4_xdr_enc_remove(struct rpc_rqst *req, __be32 *p, const struct nfs ...@@ -1448,19 +1412,16 @@ static int nfs4_xdr_enc_remove(struct rpc_rqst *req, __be32 *p, const struct nfs
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 3, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
if ((status = encode_putfh(&xdr, args->fh)) != 0) encode_putfh(&xdr, args->fh, &hdr);
goto out; encode_remove(&xdr, &args->name, &hdr);
if ((status = encode_remove(&xdr, &args->name)) != 0) encode_getfattr(&xdr, args->bitmask, &hdr);
goto out; encode_nops(&hdr);
status = encode_getfattr(&xdr, args->bitmask); return 0;
out:
return status;
} }
/* /*
...@@ -1470,27 +1431,20 @@ static int nfs4_xdr_enc_rename(struct rpc_rqst *req, __be32 *p, const struct nfs ...@@ -1470,27 +1431,20 @@ static int nfs4_xdr_enc_rename(struct rpc_rqst *req, __be32 *p, const struct nfs
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 7, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
if ((status = encode_putfh(&xdr, args->old_dir)) != 0) encode_putfh(&xdr, args->old_dir, &hdr);
goto out; encode_savefh(&xdr, &hdr);
if ((status = encode_savefh(&xdr)) != 0) encode_putfh(&xdr, args->new_dir, &hdr);
goto out; encode_rename(&xdr, args->old_name, args->new_name, &hdr);
if ((status = encode_putfh(&xdr, args->new_dir)) != 0) encode_getfattr(&xdr, args->bitmask, &hdr);
goto out; encode_restorefh(&xdr, &hdr);
if ((status = encode_rename(&xdr, args->old_name, args->new_name)) != 0) encode_getfattr(&xdr, args->bitmask, &hdr);
goto out; encode_nops(&hdr);
if ((status = encode_getfattr(&xdr, args->bitmask)) != 0) return 0;
goto out;
if ((status = encode_restorefh(&xdr)) != 0)
goto out;
status = encode_getfattr(&xdr, args->bitmask);
out:
return status;
} }
/* /*
...@@ -1500,27 +1454,20 @@ static int nfs4_xdr_enc_link(struct rpc_rqst *req, __be32 *p, const struct nfs4_ ...@@ -1500,27 +1454,20 @@ static int nfs4_xdr_enc_link(struct rpc_rqst *req, __be32 *p, const struct nfs4_
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 7, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
if ((status = encode_putfh(&xdr, args->fh)) != 0) encode_putfh(&xdr, args->fh, &hdr);
goto out; encode_savefh(&xdr, &hdr);
if ((status = encode_savefh(&xdr)) != 0) encode_putfh(&xdr, args->dir_fh, &hdr);
goto out; encode_link(&xdr, args->name, &hdr);
if ((status = encode_putfh(&xdr, args->dir_fh)) != 0) encode_getfattr(&xdr, args->bitmask, &hdr);
goto out; encode_restorefh(&xdr, &hdr);
if ((status = encode_link(&xdr, args->name)) != 0) encode_getfattr(&xdr, args->bitmask, &hdr);
goto out; encode_nops(&hdr);
if ((status = encode_getfattr(&xdr, args->bitmask)) != 0) return 0;
goto out;
if ((status = encode_restorefh(&xdr)) != 0)
goto out;
status = encode_getfattr(&xdr, args->bitmask);
out:
return status;
} }
/* /*
...@@ -1530,27 +1477,20 @@ static int nfs4_xdr_enc_create(struct rpc_rqst *req, __be32 *p, const struct nfs ...@@ -1530,27 +1477,20 @@ static int nfs4_xdr_enc_create(struct rpc_rqst *req, __be32 *p, const struct nfs
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 7, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
if ((status = encode_putfh(&xdr, args->dir_fh)) != 0) encode_putfh(&xdr, args->dir_fh, &hdr);
goto out; encode_savefh(&xdr, &hdr);
if ((status = encode_savefh(&xdr)) != 0) encode_create(&xdr, args, &hdr);
goto out; encode_getfh(&xdr, &hdr);
if ((status = encode_create(&xdr, args)) != 0) encode_getfattr(&xdr, args->bitmask, &hdr);
goto out; encode_restorefh(&xdr, &hdr);
if ((status = encode_getfh(&xdr)) != 0) encode_getfattr(&xdr, args->bitmask, &hdr);
goto out; encode_nops(&hdr);
if ((status = encode_getfattr(&xdr, args->bitmask)) != 0) return 0;
goto out;
if ((status = encode_restorefh(&xdr)) != 0)
goto out;
status = encode_getfattr(&xdr, args->bitmask);
out:
return status;
} }
/* /*
...@@ -1568,15 +1508,15 @@ static int nfs4_xdr_enc_getattr(struct rpc_rqst *req, __be32 *p, const struct nf ...@@ -1568,15 +1508,15 @@ static int nfs4_xdr_enc_getattr(struct rpc_rqst *req, __be32 *p, const struct nf
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 2, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
if ((status = encode_putfh(&xdr, args->fh)) == 0) encode_putfh(&xdr, args->fh, &hdr);
status = encode_getfattr(&xdr, args->bitmask); encode_getfattr(&xdr, args->bitmask, &hdr);
return status; encode_nops(&hdr);
return 0;
} }
/* /*
...@@ -1586,21 +1526,16 @@ static int nfs4_xdr_enc_close(struct rpc_rqst *req, __be32 *p, struct nfs_closea ...@@ -1586,21 +1526,16 @@ static int nfs4_xdr_enc_close(struct rpc_rqst *req, __be32 *p, struct nfs_closea
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 3, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh); encode_putfh(&xdr, args->fh, &hdr);
if(status) encode_close(&xdr, args, &hdr);
goto out; encode_getfattr(&xdr, args->bitmask, &hdr);
status = encode_close(&xdr, args); encode_nops(&hdr);
if (status != 0) return 0;
goto out;
status = encode_getfattr(&xdr, args->bitmask);
out:
return status;
} }
/* /*
...@@ -1610,33 +1545,20 @@ static int nfs4_xdr_enc_open(struct rpc_rqst *req, __be32 *p, struct nfs_openarg ...@@ -1610,33 +1545,20 @@ static int nfs4_xdr_enc_open(struct rpc_rqst *req, __be32 *p, struct nfs_openarg
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 7, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh); encode_putfh(&xdr, args->fh, &hdr);
if (status) encode_savefh(&xdr, &hdr);
goto out; encode_open(&xdr, args, &hdr);
status = encode_savefh(&xdr); encode_getfh(&xdr, &hdr);
if (status) encode_getfattr(&xdr, args->bitmask, &hdr);
goto out; encode_restorefh(&xdr, &hdr);
status = encode_open(&xdr, args); encode_getfattr(&xdr, args->bitmask, &hdr);
if (status) encode_nops(&hdr);
goto out; return 0;
status = encode_getfh(&xdr);
if (status)
goto out;
status = encode_getfattr(&xdr, args->bitmask);
if (status)
goto out;
status = encode_restorefh(&xdr);
if (status)
goto out;
status = encode_getfattr(&xdr, args->bitmask);
out:
return status;
} }
/* /*
...@@ -1646,18 +1568,15 @@ static int nfs4_xdr_enc_open_confirm(struct rpc_rqst *req, __be32 *p, struct nfs ...@@ -1646,18 +1568,15 @@ static int nfs4_xdr_enc_open_confirm(struct rpc_rqst *req, __be32 *p, struct nfs
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 2, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh); encode_putfh(&xdr, args->fh, &hdr);
if(status) encode_open_confirm(&xdr, args, &hdr);
goto out; encode_nops(&hdr);
status = encode_open_confirm(&xdr, args); return 0;
out:
return status;
} }
/* /*
...@@ -1667,21 +1586,16 @@ static int nfs4_xdr_enc_open_noattr(struct rpc_rqst *req, __be32 *p, struct nfs_ ...@@ -1667,21 +1586,16 @@ static int nfs4_xdr_enc_open_noattr(struct rpc_rqst *req, __be32 *p, struct nfs_
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 3, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh); encode_putfh(&xdr, args->fh, &hdr);
if (status) encode_open(&xdr, args, &hdr);
goto out; encode_getfattr(&xdr, args->bitmask, &hdr);
status = encode_open(&xdr, args); encode_nops(&hdr);
if (status) return 0;
goto out;
status = encode_getfattr(&xdr, args->bitmask);
out:
return status;
} }
/* /*
...@@ -1691,21 +1605,16 @@ static int nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req, __be32 *p, struct n ...@@ -1691,21 +1605,16 @@ static int nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req, __be32 *p, struct n
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 3, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh); encode_putfh(&xdr, args->fh, &hdr);
if (status) encode_open_downgrade(&xdr, args, &hdr);
goto out; encode_getfattr(&xdr, args->bitmask, &hdr);
status = encode_open_downgrade(&xdr, args); encode_nops(&hdr);
if (status != 0) return 0;
goto out;
status = encode_getfattr(&xdr, args->bitmask);
out:
return status;
} }
/* /*
...@@ -1715,18 +1624,15 @@ static int nfs4_xdr_enc_lock(struct rpc_rqst *req, __be32 *p, struct nfs_lock_ar ...@@ -1715,18 +1624,15 @@ static int nfs4_xdr_enc_lock(struct rpc_rqst *req, __be32 *p, struct nfs_lock_ar
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 2, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh); encode_putfh(&xdr, args->fh, &hdr);
if(status) encode_lock(&xdr, args, &hdr);
goto out; encode_nops(&hdr);
status = encode_lock(&xdr, args); return 0;
out:
return status;
} }
/* /*
...@@ -1736,18 +1642,15 @@ static int nfs4_xdr_enc_lockt(struct rpc_rqst *req, __be32 *p, struct nfs_lockt_ ...@@ -1736,18 +1642,15 @@ static int nfs4_xdr_enc_lockt(struct rpc_rqst *req, __be32 *p, struct nfs_lockt_
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 2, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh); encode_putfh(&xdr, args->fh, &hdr);
if(status) encode_lockt(&xdr, args, &hdr);
goto out; encode_nops(&hdr);
status = encode_lockt(&xdr, args); return 0;
out:
return status;
} }
/* /*
...@@ -1757,18 +1660,15 @@ static int nfs4_xdr_enc_locku(struct rpc_rqst *req, __be32 *p, struct nfs_locku_ ...@@ -1757,18 +1660,15 @@ static int nfs4_xdr_enc_locku(struct rpc_rqst *req, __be32 *p, struct nfs_locku_
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 2, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh); encode_putfh(&xdr, args->fh, &hdr);
if(status) encode_locku(&xdr, args, &hdr);
goto out; encode_nops(&hdr);
status = encode_locku(&xdr, args); return 0;
out:
return status;
} }
/* /*
...@@ -1778,18 +1678,15 @@ static int nfs4_xdr_enc_readlink(struct rpc_rqst *req, __be32 *p, const struct n ...@@ -1778,18 +1678,15 @@ static int nfs4_xdr_enc_readlink(struct rpc_rqst *req, __be32 *p, const struct n
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 2, .nops = 0,
}; };
struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
unsigned int replen; unsigned int replen;
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh); encode_putfh(&xdr, args->fh, &hdr);
if(status) encode_readlink(&xdr, args, req, &hdr);
goto out;
status = encode_readlink(&xdr, args, req);
/* set up reply kvec /* set up reply kvec
* toplevel_status + taglen + rescount + OP_PUTFH + status * toplevel_status + taglen + rescount + OP_PUTFH + status
...@@ -1798,9 +1695,8 @@ static int nfs4_xdr_enc_readlink(struct rpc_rqst *req, __be32 *p, const struct n ...@@ -1798,9 +1695,8 @@ static int nfs4_xdr_enc_readlink(struct rpc_rqst *req, __be32 *p, const struct n
replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS4_dec_readlink_sz) << 2; replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS4_dec_readlink_sz) << 2;
xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages,
args->pgbase, args->pglen); args->pgbase, args->pglen);
encode_nops(&hdr);
out: return 0;
return status;
} }
/* /*
...@@ -1810,18 +1706,15 @@ static int nfs4_xdr_enc_readdir(struct rpc_rqst *req, __be32 *p, const struct nf ...@@ -1810,18 +1706,15 @@ static int nfs4_xdr_enc_readdir(struct rpc_rqst *req, __be32 *p, const struct nf
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 2, .nops = 0,
}; };
struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
int replen; int replen;
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh); encode_putfh(&xdr, args->fh, &hdr);
if(status) encode_readdir(&xdr, args, req, &hdr);
goto out;
status = encode_readdir(&xdr, args, req);
/* set up reply kvec /* set up reply kvec
* toplevel_status + taglen + rescount + OP_PUTFH + status * toplevel_status + taglen + rescount + OP_PUTFH + status
...@@ -1833,9 +1726,8 @@ static int nfs4_xdr_enc_readdir(struct rpc_rqst *req, __be32 *p, const struct nf ...@@ -1833,9 +1726,8 @@ static int nfs4_xdr_enc_readdir(struct rpc_rqst *req, __be32 *p, const struct nf
dprintk("%s: inlined page args = (%u, %p, %u, %u)\n", dprintk("%s: inlined page args = (%u, %p, %u, %u)\n",
__func__, replen, args->pages, __func__, replen, args->pages,
args->pgbase, args->count); args->pgbase, args->count);
encode_nops(&hdr);
out: return 0;
return status;
} }
/* /*
...@@ -1846,18 +1738,14 @@ static int nfs4_xdr_enc_read(struct rpc_rqst *req, __be32 *p, struct nfs_readarg ...@@ -1846,18 +1738,14 @@ static int nfs4_xdr_enc_read(struct rpc_rqst *req, __be32 *p, struct nfs_readarg
struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 2, .nops = 0,
}; };
int replen, status; int replen;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh); encode_putfh(&xdr, args->fh, &hdr);
if (status) encode_read(&xdr, args, &hdr);
goto out;
status = encode_read(&xdr, args);
if (status)
goto out;
/* set up reply kvec /* set up reply kvec
* toplevel status + taglen=0 + rescount + OP_PUTFH + status * toplevel status + taglen=0 + rescount + OP_PUTFH + status
...@@ -1867,33 +1755,27 @@ static int nfs4_xdr_enc_read(struct rpc_rqst *req, __be32 *p, struct nfs_readarg ...@@ -1867,33 +1755,27 @@ static int nfs4_xdr_enc_read(struct rpc_rqst *req, __be32 *p, struct nfs_readarg
xdr_inline_pages(&req->rq_rcv_buf, replen, xdr_inline_pages(&req->rq_rcv_buf, replen,
args->pages, args->pgbase, args->count); args->pages, args->pgbase, args->count);
req->rq_rcv_buf.flags |= XDRBUF_READ; req->rq_rcv_buf.flags |= XDRBUF_READ;
out: encode_nops(&hdr);
return status; return 0;
} }
/* /*
* Encode an SETATTR request * Encode an SETATTR request
*/ */
static int nfs4_xdr_enc_setattr(struct rpc_rqst *req, __be32 *p, struct nfs_setattrargs *args) static int nfs4_xdr_enc_setattr(struct rpc_rqst *req, __be32 *p, struct nfs_setattrargs *args)
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 3, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh); encode_putfh(&xdr, args->fh, &hdr);
if(status) encode_setattr(&xdr, args, args->server, &hdr);
goto out; encode_getfattr(&xdr, args->bitmask, &hdr);
status = encode_setattr(&xdr, args, args->server); encode_nops(&hdr);
if(status) return 0;
goto out;
status = encode_getfattr(&xdr, args->bitmask);
out:
return status;
} }
/* /*
...@@ -1906,22 +1788,21 @@ nfs4_xdr_enc_getacl(struct rpc_rqst *req, __be32 *p, ...@@ -1906,22 +1788,21 @@ nfs4_xdr_enc_getacl(struct rpc_rqst *req, __be32 *p,
struct xdr_stream xdr; struct xdr_stream xdr;
struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 2, .nops = 0,
}; };
int replen, status; int replen;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh); encode_putfh(&xdr, args->fh, &hdr);
if (status) encode_getattr_two(&xdr, FATTR4_WORD0_ACL, 0, &hdr);
goto out;
status = encode_getattr_two(&xdr, FATTR4_WORD0_ACL, 0);
/* set up reply buffer: */ /* set up reply buffer: */
replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS4_dec_getacl_sz) << 2; replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS4_dec_getacl_sz) << 2;
xdr_inline_pages(&req->rq_rcv_buf, replen, xdr_inline_pages(&req->rq_rcv_buf, replen,
args->acl_pages, args->acl_pgbase, args->acl_len); args->acl_pages, args->acl_pgbase, args->acl_len);
out: encode_nops(&hdr);
return status; return 0;
} }
/* /*
...@@ -1931,22 +1812,17 @@ static int nfs4_xdr_enc_write(struct rpc_rqst *req, __be32 *p, struct nfs_writea ...@@ -1931,22 +1812,17 @@ static int nfs4_xdr_enc_write(struct rpc_rqst *req, __be32 *p, struct nfs_writea
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 3, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh); encode_putfh(&xdr, args->fh, &hdr);
if (status) encode_write(&xdr, args, &hdr);
goto out;
status = encode_write(&xdr, args);
if (status)
goto out;
req->rq_snd_buf.flags |= XDRBUF_WRITE; req->rq_snd_buf.flags |= XDRBUF_WRITE;
status = encode_getfattr(&xdr, args->bitmask); encode_getfattr(&xdr, args->bitmask, &hdr);
out: encode_nops(&hdr);
return status; return 0;
} }
/* /*
...@@ -1956,21 +1832,16 @@ static int nfs4_xdr_enc_commit(struct rpc_rqst *req, __be32 *p, struct nfs_write ...@@ -1956,21 +1832,16 @@ static int nfs4_xdr_enc_commit(struct rpc_rqst *req, __be32 *p, struct nfs_write
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 3, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh); encode_putfh(&xdr, args->fh, &hdr);
if (status) encode_commit(&xdr, args, &hdr);
goto out; encode_getfattr(&xdr, args->bitmask, &hdr);
status = encode_commit(&xdr, args); encode_nops(&hdr);
if (status) return 0;
goto out;
status = encode_getfattr(&xdr, args->bitmask);
out:
return status;
} }
/* /*
...@@ -1980,16 +1851,15 @@ static int nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, __be32 *p, struct nfs4_fsin ...@@ -1980,16 +1851,15 @@ static int nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, __be32 *p, struct nfs4_fsin
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 2, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh); encode_putfh(&xdr, args->fh, &hdr);
if (!status) encode_fsinfo(&xdr, args->bitmask, &hdr);
status = encode_fsinfo(&xdr, args->bitmask); encode_nops(&hdr);
return status; return 0;
} }
/* /*
...@@ -1999,17 +1869,16 @@ static int nfs4_xdr_enc_pathconf(struct rpc_rqst *req, __be32 *p, const struct n ...@@ -1999,17 +1869,16 @@ static int nfs4_xdr_enc_pathconf(struct rpc_rqst *req, __be32 *p, const struct n
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 2, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh); encode_putfh(&xdr, args->fh, &hdr);
if (!status) encode_getattr_one(&xdr, args->bitmask[0] & nfs4_pathconf_bitmap[0],
status = encode_getattr_one(&xdr, &hdr);
args->bitmask[0] & nfs4_pathconf_bitmap[0]); encode_nops(&hdr);
return status; return 0;
} }
/* /*
...@@ -2019,18 +1888,16 @@ static int nfs4_xdr_enc_statfs(struct rpc_rqst *req, __be32 *p, const struct nfs ...@@ -2019,18 +1888,16 @@ static int nfs4_xdr_enc_statfs(struct rpc_rqst *req, __be32 *p, const struct nfs
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 2, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh); encode_putfh(&xdr, args->fh, &hdr);
if (status == 0) encode_getattr_two(&xdr, args->bitmask[0] & nfs4_statfs_bitmap[0],
status = encode_getattr_two(&xdr, args->bitmask[1] & nfs4_statfs_bitmap[1], &hdr);
args->bitmask[0] & nfs4_statfs_bitmap[0], encode_nops(&hdr);
args->bitmask[1] & nfs4_statfs_bitmap[1]); return 0;
return status;
} }
/* /*
...@@ -2040,19 +1907,18 @@ static int nfs4_xdr_enc_server_caps(struct rpc_rqst *req, __be32 *p, const struc ...@@ -2040,19 +1907,18 @@ static int nfs4_xdr_enc_server_caps(struct rpc_rqst *req, __be32 *p, const struc
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 2, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, fhandle); encode_putfh(&xdr, fhandle, &hdr);
if (status == 0) encode_getattr_one(&xdr, FATTR4_WORD0_SUPPORTED_ATTRS|
status = encode_getattr_one(&xdr, FATTR4_WORD0_SUPPORTED_ATTRS|
FATTR4_WORD0_LINK_SUPPORT| FATTR4_WORD0_LINK_SUPPORT|
FATTR4_WORD0_SYMLINK_SUPPORT| FATTR4_WORD0_SYMLINK_SUPPORT|
FATTR4_WORD0_ACLSUPPORT); FATTR4_WORD0_ACLSUPPORT, &hdr);
return status; encode_nops(&hdr);
return 0;
} }
/* /*
...@@ -2062,12 +1928,14 @@ static int nfs4_xdr_enc_renew(struct rpc_rqst *req, __be32 *p, struct nfs_client ...@@ -2062,12 +1928,14 @@ static int nfs4_xdr_enc_renew(struct rpc_rqst *req, __be32 *p, struct nfs_client
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 1, .nops = 0,
}; };
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
return encode_renew(&xdr, clp); encode_renew(&xdr, clp, &hdr);
encode_nops(&hdr);
return 0;
} }
/* /*
...@@ -2077,12 +1945,14 @@ static int nfs4_xdr_enc_setclientid(struct rpc_rqst *req, __be32 *p, struct nfs4 ...@@ -2077,12 +1945,14 @@ static int nfs4_xdr_enc_setclientid(struct rpc_rqst *req, __be32 *p, struct nfs4
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 1, .nops = 0,
}; };
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
return encode_setclientid(&xdr, sc); encode_setclientid(&xdr, sc, &hdr);
encode_nops(&hdr);
return 0;
} }
/* /*
...@@ -2092,19 +1962,17 @@ static int nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req, __be32 *p, str ...@@ -2092,19 +1962,17 @@ static int nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req, __be32 *p, str
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 3, .nops = 0,
}; };
const u32 lease_bitmap[2] = { FATTR4_WORD0_LEASE_TIME, 0 }; const u32 lease_bitmap[2] = { FATTR4_WORD0_LEASE_TIME, 0 };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_setclientid_confirm(&xdr, clp); encode_setclientid_confirm(&xdr, clp, &hdr);
if (!status) encode_putrootfh(&xdr, &hdr);
status = encode_putrootfh(&xdr); encode_fsinfo(&xdr, lease_bitmap, &hdr);
if (!status) encode_nops(&hdr);
status = encode_fsinfo(&xdr, lease_bitmap); return 0;
return status;
} }
/* /*
...@@ -2114,21 +1982,16 @@ static int nfs4_xdr_enc_delegreturn(struct rpc_rqst *req, __be32 *p, const struc ...@@ -2114,21 +1982,16 @@ static int nfs4_xdr_enc_delegreturn(struct rpc_rqst *req, __be32 *p, const struc
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 3, .nops = 0,
}; };
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fhandle); encode_putfh(&xdr, args->fhandle, &hdr);
if (status != 0) encode_delegreturn(&xdr, args->stateid, &hdr);
goto out; encode_getfattr(&xdr, args->bitmask, &hdr);
status = encode_delegreturn(&xdr, args->stateid); encode_nops(&hdr);
if (status != 0) return 0;
goto out;
status = encode_getfattr(&xdr, args->bitmask);
out:
return status;
} }
/* /*
...@@ -2138,20 +2001,17 @@ static int nfs4_xdr_enc_fs_locations(struct rpc_rqst *req, __be32 *p, struct nfs ...@@ -2138,20 +2001,17 @@ static int nfs4_xdr_enc_fs_locations(struct rpc_rqst *req, __be32 *p, struct nfs
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 3, .nops = 0,
}; };
struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
int replen; int replen;
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
if ((status = encode_putfh(&xdr, args->dir_fh)) != 0) encode_putfh(&xdr, args->dir_fh, &hdr);
goto out; encode_lookup(&xdr, args->name, &hdr);
if ((status = encode_lookup(&xdr, args->name)) != 0) encode_fs_locations(&xdr, args->bitmask, &hdr);
goto out;
if ((status = encode_fs_locations(&xdr, args->bitmask)) != 0)
goto out;
/* set up reply /* set up reply
* toplevel_status + OP_PUTFH + status * toplevel_status + OP_PUTFH + status
* + OP_LOOKUP + status + OP_GETATTR + status = 7 * + OP_LOOKUP + status + OP_GETATTR + status = 7
...@@ -2159,8 +2019,8 @@ static int nfs4_xdr_enc_fs_locations(struct rpc_rqst *req, __be32 *p, struct nfs ...@@ -2159,8 +2019,8 @@ static int nfs4_xdr_enc_fs_locations(struct rpc_rqst *req, __be32 *p, struct nfs
replen = (RPC_REPHDRSIZE + auth->au_rslack + 7) << 2; replen = (RPC_REPHDRSIZE + auth->au_rslack + 7) << 2;
xdr_inline_pages(&req->rq_rcv_buf, replen, &args->page, xdr_inline_pages(&req->rq_rcv_buf, replen, &args->page,
0, PAGE_SIZE); 0, PAGE_SIZE);
out: encode_nops(&hdr);
return status; return 0;
} }
/* /*
...@@ -2222,6 +2082,8 @@ static int decode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr) ...@@ -2222,6 +2082,8 @@ static int decode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
hdr->tag = (char *)p; hdr->tag = (char *)p;
p += XDR_QUADLEN(hdr->taglen); p += XDR_QUADLEN(hdr->taglen);
READ32(hdr->nops); READ32(hdr->nops);
if (unlikely(hdr->nops < 1))
return nfs4_stat_to_errno(hdr->status);
return 0; return 0;
} }
...@@ -3047,8 +2909,7 @@ static int decode_create(struct xdr_stream *xdr, struct nfs4_change_info *cinfo) ...@@ -3047,8 +2909,7 @@ static int decode_create(struct xdr_stream *xdr, struct nfs4_change_info *cinfo)
static int decode_server_caps(struct xdr_stream *xdr, struct nfs4_server_caps_res *res) static int decode_server_caps(struct xdr_stream *xdr, struct nfs4_server_caps_res *res)
{ {
__be32 *savep; __be32 *savep;
uint32_t attrlen, uint32_t attrlen, bitmap[2] = {0};
bitmap[2] = {0};
int status; int status;
if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
...@@ -3074,8 +2935,7 @@ static int decode_server_caps(struct xdr_stream *xdr, struct nfs4_server_caps_re ...@@ -3074,8 +2935,7 @@ static int decode_server_caps(struct xdr_stream *xdr, struct nfs4_server_caps_re
static int decode_statfs(struct xdr_stream *xdr, struct nfs_fsstat *fsstat) static int decode_statfs(struct xdr_stream *xdr, struct nfs_fsstat *fsstat)
{ {
__be32 *savep; __be32 *savep;
uint32_t attrlen, uint32_t attrlen, bitmap[2] = {0};
bitmap[2] = {0};
int status; int status;
if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
...@@ -3107,8 +2967,7 @@ static int decode_statfs(struct xdr_stream *xdr, struct nfs_fsstat *fsstat) ...@@ -3107,8 +2967,7 @@ static int decode_statfs(struct xdr_stream *xdr, struct nfs_fsstat *fsstat)
static int decode_pathconf(struct xdr_stream *xdr, struct nfs_pathconf *pathconf) static int decode_pathconf(struct xdr_stream *xdr, struct nfs_pathconf *pathconf)
{ {
__be32 *savep; __be32 *savep;
uint32_t attrlen, uint32_t attrlen, bitmap[2] = {0};
bitmap[2] = {0};
int status; int status;
if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
...@@ -3375,6 +3234,7 @@ static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res) ...@@ -3375,6 +3234,7 @@ static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
READ_BUF(NFS4_STATEID_SIZE+4); READ_BUF(NFS4_STATEID_SIZE+4);
COPYMEM(res->delegation.data, NFS4_STATEID_SIZE); COPYMEM(res->delegation.data, NFS4_STATEID_SIZE);
READ32(res->do_recall); READ32(res->do_recall);
switch (delegation_type) { switch (delegation_type) {
case NFS4_OPEN_DELEGATE_READ: case NFS4_OPEN_DELEGATE_READ:
res->delegation_type = FMODE_READ; res->delegation_type = FMODE_READ;
...@@ -3718,7 +3578,6 @@ static int decode_setattr(struct xdr_stream *xdr, struct nfs_setattrres *res) ...@@ -3718,7 +3578,6 @@ static int decode_setattr(struct xdr_stream *xdr, struct nfs_setattrres *res)
uint32_t bmlen; uint32_t bmlen;
int status; int status;
status = decode_op_hdr(xdr, OP_SETATTR); status = decode_op_hdr(xdr, OP_SETATTR);
if (status) if (status)
return status; return status;
...@@ -3791,6 +3650,10 @@ static int decode_delegreturn(struct xdr_stream *xdr) ...@@ -3791,6 +3650,10 @@ static int decode_delegreturn(struct xdr_stream *xdr)
return decode_op_hdr(xdr, OP_DELEGRETURN); return decode_op_hdr(xdr, OP_DELEGRETURN);
} }
/*
* END OF "GENERIC" DECODE ROUTINES.
*/
/* /*
* Decode OPEN_DOWNGRADE response * Decode OPEN_DOWNGRADE response
*/ */
...@@ -3815,10 +3678,6 @@ static int nfs4_xdr_dec_open_downgrade(struct rpc_rqst *rqstp, __be32 *p, struct ...@@ -3815,10 +3678,6 @@ static int nfs4_xdr_dec_open_downgrade(struct rpc_rqst *rqstp, __be32 *p, struct
return status; return status;
} }
/*
* END OF "GENERIC" DECODE ROUTINES.
*/
/* /*
* Decode ACCESS response * Decode ACCESS response
*/ */
...@@ -4025,7 +3884,6 @@ static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_g ...@@ -4025,7 +3884,6 @@ static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_g
status = decode_getfattr(&xdr, res->fattr, res->server); status = decode_getfattr(&xdr, res->fattr, res->server);
out: out:
return status; return status;
} }
/* /*
...@@ -4036,19 +3894,18 @@ nfs4_xdr_enc_setacl(struct rpc_rqst *req, __be32 *p, struct nfs_setaclargs *args ...@@ -4036,19 +3894,18 @@ nfs4_xdr_enc_setacl(struct rpc_rqst *req, __be32 *p, struct nfs_setaclargs *args
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 2, .nops = 0,
}; };
int status; int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr); encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh); encode_putfh(&xdr, args->fh, &hdr);
if (status) status = encode_setacl(&xdr, args, &hdr);
goto out; encode_nops(&hdr);
status = encode_setacl(&xdr, args);
out:
return status; return status;
} }
/* /*
* Decode SETACL response * Decode SETACL response
*/ */
...@@ -4421,8 +4278,6 @@ static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, __be32 *p, struct nfs_fsinf ...@@ -4421,8 +4278,6 @@ static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, __be32 *p, struct nfs_fsinf
status = decode_putfh(&xdr); status = decode_putfh(&xdr);
if (!status) if (!status)
status = decode_fsinfo(&xdr, fsinfo); status = decode_fsinfo(&xdr, fsinfo);
if (!status)
status = nfs4_stat_to_errno(hdr.status);
return status; return status;
} }
...@@ -4511,8 +4366,6 @@ static int nfs4_xdr_dec_setclientid(struct rpc_rqst *req, __be32 *p, ...@@ -4511,8 +4366,6 @@ static int nfs4_xdr_dec_setclientid(struct rpc_rqst *req, __be32 *p,
status = decode_compound_hdr(&xdr, &hdr); status = decode_compound_hdr(&xdr, &hdr);
if (!status) if (!status)
status = decode_setclientid(&xdr, clp); status = decode_setclientid(&xdr, clp);
if (!status)
status = nfs4_stat_to_errno(hdr.status);
return status; return status;
} }
...@@ -4533,8 +4386,6 @@ static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req, __be32 *p, str ...@@ -4533,8 +4386,6 @@ static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req, __be32 *p, str
status = decode_putrootfh(&xdr); status = decode_putrootfh(&xdr);
if (!status) if (!status)
status = decode_fsinfo(&xdr, fsinfo); status = decode_fsinfo(&xdr, fsinfo);
if (!status)
status = nfs4_stat_to_errno(hdr.status);
return status; return status;
} }
...@@ -4715,7 +4566,7 @@ nfs4_stat_to_errno(int stat) ...@@ -4715,7 +4566,7 @@ nfs4_stat_to_errno(int stat)
.p_replen = NFS4_##restype##_sz, \ .p_replen = NFS4_##restype##_sz, \
.p_statidx = NFSPROC4_CLNT_##proc, \ .p_statidx = NFSPROC4_CLNT_##proc, \
.p_name = #proc, \ .p_name = #proc, \
} }
struct rpc_procinfo nfs4_procedures[] = { struct rpc_procinfo nfs4_procedures[] = {
PROC(READ, enc_read, dec_read), PROC(READ, enc_read, dec_read),
......
...@@ -86,6 +86,8 @@ ...@@ -86,6 +86,8 @@
#include <net/ipconfig.h> #include <net/ipconfig.h>
#include <linux/parser.h> #include <linux/parser.h>
#include "internal.h"
/* Define this to allow debugging output */ /* Define this to allow debugging output */
#undef NFSROOT_DEBUG #undef NFSROOT_DEBUG
#define NFSDBG_FACILITY NFSDBG_ROOT #define NFSDBG_FACILITY NFSDBG_ROOT
...@@ -100,7 +102,7 @@ static char nfs_root_name[256] __initdata = ""; ...@@ -100,7 +102,7 @@ static char nfs_root_name[256] __initdata = "";
static __be32 servaddr __initdata = 0; static __be32 servaddr __initdata = 0;
/* Name of directory to mount */ /* Name of directory to mount */
static char nfs_path[NFS_MAXPATHLEN] __initdata = { 0, }; static char nfs_export_path[NFS_MAXPATHLEN] __initdata = { 0, };
/* NFS-related data */ /* NFS-related data */
static struct nfs_mount_data nfs_data __initdata = { 0, };/* NFS mount info */ static struct nfs_mount_data nfs_data __initdata = { 0, };/* NFS mount info */
...@@ -312,7 +314,7 @@ static int __init root_nfs_name(char *name) ...@@ -312,7 +314,7 @@ static int __init root_nfs_name(char *name)
printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n"); printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n");
return -1; return -1;
} }
sprintf(nfs_path, buf, cp); sprintf(nfs_export_path, buf, cp);
return 1; return 1;
} }
...@@ -340,7 +342,7 @@ static int __init root_nfs_addr(void) ...@@ -340,7 +342,7 @@ static int __init root_nfs_addr(void)
static void __init root_nfs_print(void) static void __init root_nfs_print(void)
{ {
printk(KERN_NOTICE "Root-NFS: Mounting %s on server %s as root\n", printk(KERN_NOTICE "Root-NFS: Mounting %s on server %s as root\n",
nfs_path, nfs_data.hostname); nfs_export_path, nfs_data.hostname);
printk(KERN_NOTICE "Root-NFS: rsize = %d, wsize = %d, timeo = %d, retrans = %d\n", printk(KERN_NOTICE "Root-NFS: rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
nfs_data.rsize, nfs_data.wsize, nfs_data.timeo, nfs_data.retrans); nfs_data.rsize, nfs_data.wsize, nfs_data.timeo, nfs_data.retrans);
printk(KERN_NOTICE "Root-NFS: acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n", printk(KERN_NOTICE "Root-NFS: acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n",
...@@ -485,18 +487,23 @@ static int __init root_nfs_get_handle(void) ...@@ -485,18 +487,23 @@ static int __init root_nfs_get_handle(void)
{ {
struct nfs_fh fh; struct nfs_fh fh;
struct sockaddr_in sin; struct sockaddr_in sin;
struct nfs_mount_request request = {
.sap = (struct sockaddr *)&sin,
.salen = sizeof(sin),
.dirpath = nfs_export_path,
.version = (nfs_data.flags & NFS_MOUNT_VER3) ?
NFS_MNT3_VERSION : NFS_MNT_VERSION,
.protocol = (nfs_data.flags & NFS_MOUNT_TCP) ?
XPRT_TRANSPORT_TCP : XPRT_TRANSPORT_UDP,
.fh = &fh,
};
int status; int status;
int protocol = (nfs_data.flags & NFS_MOUNT_TCP) ?
XPRT_TRANSPORT_TCP : XPRT_TRANSPORT_UDP;
int version = (nfs_data.flags & NFS_MOUNT_VER3) ?
NFS_MNT3_VERSION : NFS_MNT_VERSION;
set_sockaddr(&sin, servaddr, htons(mount_port)); set_sockaddr(&sin, servaddr, htons(mount_port));
status = nfs_mount((struct sockaddr *) &sin, sizeof(sin), NULL, status = nfs_mount(&request);
nfs_path, version, protocol, &fh);
if (status < 0) if (status < 0)
printk(KERN_ERR "Root-NFS: Server returned error %d " printk(KERN_ERR "Root-NFS: Server returned error %d "
"while mounting %s\n", status, nfs_path); "while mounting %s\n", status, nfs_export_path);
else { else {
nfs_data.root.size = fh.size; nfs_data.root.size = fh.size;
memcpy(nfs_data.root.data, fh.data, fh.size); memcpy(nfs_data.root.data, fh.data, fh.size);
......
...@@ -533,12 +533,6 @@ readpage_async_filler(void *data, struct page *page) ...@@ -533,12 +533,6 @@ readpage_async_filler(void *data, struct page *page)
unsigned int len; unsigned int len;
int error; int error;
error = nfs_wb_page(inode, page);
if (error)
goto out_unlock;
if (PageUptodate(page))
goto out_unlock;
len = nfs_page_length(page); len = nfs_page_length(page);
if (len == 0) if (len == 0)
return nfs_return_empty_page(page); return nfs_return_empty_page(page);
......
...@@ -75,6 +75,7 @@ enum { ...@@ -75,6 +75,7 @@ enum {
Opt_acl, Opt_noacl, Opt_acl, Opt_noacl,
Opt_rdirplus, Opt_nordirplus, Opt_rdirplus, Opt_nordirplus,
Opt_sharecache, Opt_nosharecache, Opt_sharecache, Opt_nosharecache,
Opt_resvport, Opt_noresvport,
/* Mount options that take integer arguments */ /* Mount options that take integer arguments */
Opt_port, Opt_port,
...@@ -129,6 +130,8 @@ static const match_table_t nfs_mount_option_tokens = { ...@@ -129,6 +130,8 @@ static const match_table_t nfs_mount_option_tokens = {
{ Opt_nordirplus, "nordirplus" }, { Opt_nordirplus, "nordirplus" },
{ Opt_sharecache, "sharecache" }, { Opt_sharecache, "sharecache" },
{ Opt_nosharecache, "nosharecache" }, { Opt_nosharecache, "nosharecache" },
{ Opt_resvport, "resvport" },
{ Opt_noresvport, "noresvport" },
{ Opt_port, "port=%u" }, { Opt_port, "port=%u" },
{ Opt_rsize, "rsize=%u" }, { Opt_rsize, "rsize=%u" },
...@@ -512,7 +515,8 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, ...@@ -512,7 +515,8 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
{ NFS_MOUNT_NONLM, ",nolock", "" }, { NFS_MOUNT_NONLM, ",nolock", "" },
{ NFS_MOUNT_NOACL, ",noacl", "" }, { NFS_MOUNT_NOACL, ",noacl", "" },
{ NFS_MOUNT_NORDIRPLUS, ",nordirplus", "" }, { NFS_MOUNT_NORDIRPLUS, ",nordirplus", "" },
{ NFS_MOUNT_UNSHARED, ",nosharecache", ""}, { NFS_MOUNT_UNSHARED, ",nosharecache", "" },
{ NFS_MOUNT_NORESVPORT, ",noresvport", "" },
{ 0, NULL, NULL } { 0, NULL, NULL }
}; };
const struct proc_nfs_info *nfs_infop; const struct proc_nfs_info *nfs_infop;
...@@ -1033,6 +1037,12 @@ static int nfs_parse_mount_options(char *raw, ...@@ -1033,6 +1037,12 @@ static int nfs_parse_mount_options(char *raw,
case Opt_nosharecache: case Opt_nosharecache:
mnt->flags |= NFS_MOUNT_UNSHARED; mnt->flags |= NFS_MOUNT_UNSHARED;
break; break;
case Opt_resvport:
mnt->flags &= ~NFS_MOUNT_NORESVPORT;
break;
case Opt_noresvport:
mnt->flags |= NFS_MOUNT_NORESVPORT;
break;
/* /*
* options that take numeric values * options that take numeric values
...@@ -1327,8 +1337,14 @@ static int nfs_parse_mount_options(char *raw, ...@@ -1327,8 +1337,14 @@ static int nfs_parse_mount_options(char *raw,
static int nfs_try_mount(struct nfs_parsed_mount_data *args, static int nfs_try_mount(struct nfs_parsed_mount_data *args,
struct nfs_fh *root_fh) struct nfs_fh *root_fh)
{ {
struct sockaddr *sap = (struct sockaddr *)&args->mount_server.address; struct nfs_mount_request request = {
char *hostname; .sap = (struct sockaddr *)
&args->mount_server.address,
.dirpath = args->nfs_server.export_path,
.protocol = args->mount_server.protocol,
.fh = root_fh,
.noresvport = args->flags & NFS_MOUNT_NORESVPORT,
};
int status; int status;
if (args->mount_server.version == 0) { if (args->mount_server.version == 0) {
...@@ -1337,42 +1353,38 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args, ...@@ -1337,42 +1353,38 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
else else
args->mount_server.version = NFS_MNT_VERSION; args->mount_server.version = NFS_MNT_VERSION;
} }
request.version = args->mount_server.version;
if (args->mount_server.hostname) if (args->mount_server.hostname)
hostname = args->mount_server.hostname; request.hostname = args->mount_server.hostname;
else else
hostname = args->nfs_server.hostname; request.hostname = args->nfs_server.hostname;
/* /*
* Construct the mount server's address. * Construct the mount server's address.
*/ */
if (args->mount_server.address.ss_family == AF_UNSPEC) { if (args->mount_server.address.ss_family == AF_UNSPEC) {
memcpy(sap, &args->nfs_server.address, memcpy(request.sap, &args->nfs_server.address,
args->nfs_server.addrlen); args->nfs_server.addrlen);
args->mount_server.addrlen = args->nfs_server.addrlen; args->mount_server.addrlen = args->nfs_server.addrlen;
} }
request.salen = args->mount_server.addrlen;
/* /*
* autobind will be used if mount_server.port == 0 * autobind will be used if mount_server.port == 0
*/ */
nfs_set_port(sap, args->mount_server.port); nfs_set_port(request.sap, args->mount_server.port);
/* /*
* Now ask the mount server to map our export path * Now ask the mount server to map our export path
* to a file handle. * to a file handle.
*/ */
status = nfs_mount(sap, status = nfs_mount(&request);
args->mount_server.addrlen,
hostname,
args->nfs_server.export_path,
args->mount_server.version,
args->mount_server.protocol,
root_fh);
if (status == 0) if (status == 0)
return 0; return 0;
dfprintk(MOUNT, "NFS: unable to mount server %s, error %d\n", dfprintk(MOUNT, "NFS: unable to mount server %s, error %d\n",
hostname, status); request.hostname, status);
return status; return status;
} }
...@@ -2419,7 +2431,7 @@ static void nfs4_kill_super(struct super_block *sb) ...@@ -2419,7 +2431,7 @@ static void nfs4_kill_super(struct super_block *sb)
{ {
struct nfs_server *server = NFS_SB(sb); struct nfs_server *server = NFS_SB(sb);
nfs_return_all_delegations(sb); nfs_super_return_all_delegations(sb);
kill_anon_super(sb); kill_anon_super(sb);
nfs4_renewd_prepare_shutdown(server); nfs4_renewd_prepare_shutdown(server);
......
...@@ -29,8 +29,8 @@ ...@@ -29,8 +29,8 @@
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
EXPORT_SYMBOL(nfsacl_encode); EXPORT_SYMBOL_GPL(nfsacl_encode);
EXPORT_SYMBOL(nfsacl_decode); EXPORT_SYMBOL_GPL(nfsacl_decode);
struct nfsacl_encode_desc { struct nfsacl_encode_desc {
struct xdr_array2_desc desc; struct xdr_array2_desc desc;
......
...@@ -358,6 +358,7 @@ static struct rpc_program cb_program = { ...@@ -358,6 +358,7 @@ static struct rpc_program cb_program = {
.nrvers = ARRAY_SIZE(nfs_cb_version), .nrvers = ARRAY_SIZE(nfs_cb_version),
.version = nfs_cb_version, .version = nfs_cb_version,
.stats = &cb_stats, .stats = &cb_stats,
.pipe_dir_name = "/nfsd4_cb",
}; };
/* Reference counting, callback cleanup, etc., all look racy as heck. /* Reference counting, callback cleanup, etc., all look racy as heck.
...@@ -382,8 +383,9 @@ static int do_probe_callback(void *data) ...@@ -382,8 +383,9 @@ static int do_probe_callback(void *data)
.program = &cb_program, .program = &cb_program,
.prognumber = cb->cb_prog, .prognumber = cb->cb_prog,
.version = nfs_cb_version[1]->number, .version = nfs_cb_version[1]->number,
.authflavor = RPC_AUTH_UNIX, /* XXX: need AUTH_GSS... */ .authflavor = clp->cl_flavor,
.flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET), .flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET),
.client_name = clp->cl_principal,
}; };
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL], .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
...@@ -392,6 +394,11 @@ static int do_probe_callback(void *data) ...@@ -392,6 +394,11 @@ static int do_probe_callback(void *data)
struct rpc_clnt *client; struct rpc_clnt *client;
int status; int status;
if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5)) {
status = nfserr_cb_path_down;
goto out_err;
}
/* Initialize address */ /* Initialize address */
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET; addr.sin_family = AF_INET;
......
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/lockd/bind.h> #include <linux/lockd/bind.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/sunrpc/svcauth_gss.h>
#define NFSDDBG_FACILITY NFSDDBG_PROC #define NFSDDBG_FACILITY NFSDDBG_PROC
...@@ -377,6 +378,7 @@ free_client(struct nfs4_client *clp) ...@@ -377,6 +378,7 @@ free_client(struct nfs4_client *clp)
shutdown_callback_client(clp); shutdown_callback_client(clp);
if (clp->cl_cred.cr_group_info) if (clp->cl_cred.cr_group_info)
put_group_info(clp->cl_cred.cr_group_info); put_group_info(clp->cl_cred.cr_group_info);
kfree(clp->cl_principal);
kfree(clp->cl_name.data); kfree(clp->cl_name.data);
kfree(clp); kfree(clp);
} }
...@@ -696,6 +698,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -696,6 +698,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
unsigned int strhashval; unsigned int strhashval;
struct nfs4_client *conf, *unconf, *new; struct nfs4_client *conf, *unconf, *new;
__be32 status; __be32 status;
char *princ;
char dname[HEXDIR_LEN]; char dname[HEXDIR_LEN];
if (!check_name(clname)) if (!check_name(clname))
...@@ -783,6 +786,15 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -783,6 +786,15 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
} }
copy_verf(new, &clverifier); copy_verf(new, &clverifier);
new->cl_addr = sin->sin_addr.s_addr; new->cl_addr = sin->sin_addr.s_addr;
new->cl_flavor = rqstp->rq_flavor;
princ = svc_gss_principal(rqstp);
if (princ) {
new->cl_principal = kstrdup(princ, GFP_KERNEL);
if (new->cl_principal == NULL) {
free_client(new);
goto out;
}
}
copy_cred(&new->cl_cred, &rqstp->rq_cred); copy_cred(&new->cl_cred, &rqstp->rq_cred);
gen_confirm(new); gen_confirm(new);
gen_callback(new, setclid); gen_callback(new, setclid);
......
...@@ -115,10 +115,20 @@ static inline u64 get_jiffies_64(void) ...@@ -115,10 +115,20 @@ static inline u64 get_jiffies_64(void)
((long)(a) - (long)(b) >= 0)) ((long)(a) - (long)(b) >= 0))
#define time_before_eq(a,b) time_after_eq(b,a) #define time_before_eq(a,b) time_after_eq(b,a)
/*
* Calculate whether a is in the range of [b, c].
*/
#define time_in_range(a,b,c) \ #define time_in_range(a,b,c) \
(time_after_eq(a,b) && \ (time_after_eq(a,b) && \
time_before_eq(a,c)) time_before_eq(a,c))
/*
* Calculate whether a is in the range of [b, c).
*/
#define time_in_range_open(a,b,c) \
(time_after_eq(a,b) && \
time_before(a,c))
/* Same as above, but does so with platform independent 64bit types. /* Same as above, but does so with platform independent 64bit types.
* These must be used when utilizing jiffies_64 (i.e. return value of * These must be used when utilizing jiffies_64 (i.e. return value of
* get_jiffies_64() */ * get_jiffies_64() */
......
...@@ -41,6 +41,7 @@ struct nlmclnt_initdata { ...@@ -41,6 +41,7 @@ struct nlmclnt_initdata {
size_t addrlen; size_t addrlen;
unsigned short protocol; unsigned short protocol;
u32 nfs_version; u32 nfs_version;
int noresvport;
}; };
/* /*
......
...@@ -49,6 +49,7 @@ struct nlm_host { ...@@ -49,6 +49,7 @@ struct nlm_host {
unsigned short h_proto; /* transport proto */ unsigned short h_proto; /* transport proto */
unsigned short h_reclaiming : 1, unsigned short h_reclaiming : 1,
h_server : 1, /* server side, not client side */ h_server : 1, /* server side, not client side */
h_noresvport : 1,
h_inuse : 1; h_inuse : 1;
wait_queue_head_t h_gracewait; /* wait while reclaiming */ wait_queue_head_t h_gracewait; /* wait while reclaiming */
struct rw_semaphore h_rwsem; /* Reboot recovery lock */ struct rw_semaphore h_rwsem; /* Reboot recovery lock */
...@@ -220,7 +221,8 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap, ...@@ -220,7 +221,8 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
const size_t salen, const size_t salen,
const unsigned short protocol, const unsigned short protocol,
const u32 version, const u32 version,
const char *hostname); const char *hostname,
int noresvport);
struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp, struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
const char *hostname, const char *hostname,
const size_t hostname_len); const size_t hostname_len);
......
...@@ -83,7 +83,7 @@ struct nfs_open_context { ...@@ -83,7 +83,7 @@ struct nfs_open_context {
struct rpc_cred *cred; struct rpc_cred *cred;
struct nfs4_state *state; struct nfs4_state *state;
fl_owner_t lockowner; fl_owner_t lockowner;
int mode; fmode_t mode;
unsigned long flags; unsigned long flags;
#define NFS_CONTEXT_ERROR_WRITE (0) #define NFS_CONTEXT_ERROR_WRITE (0)
...@@ -130,7 +130,10 @@ struct nfs_inode { ...@@ -130,7 +130,10 @@ struct nfs_inode {
* *
* We need to revalidate the cached attrs for this inode if * We need to revalidate the cached attrs for this inode if
* *
* jiffies - read_cache_jiffies > attrtimeo * jiffies - read_cache_jiffies >= attrtimeo
*
* Please note the comparison is greater than or equal
* so that zero timeout values can be specified.
*/ */
unsigned long read_cache_jiffies; unsigned long read_cache_jiffies;
unsigned long attrtimeo; unsigned long attrtimeo;
...@@ -180,7 +183,7 @@ struct nfs_inode { ...@@ -180,7 +183,7 @@ struct nfs_inode {
/* NFSv4 state */ /* NFSv4 state */
struct list_head open_states; struct list_head open_states;
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
int delegation_state; fmode_t delegation_state;
struct rw_semaphore rwsem; struct rw_semaphore rwsem;
#endif /* CONFIG_NFS_V4*/ #endif /* CONFIG_NFS_V4*/
struct inode vfs_inode; struct inode vfs_inode;
...@@ -342,7 +345,7 @@ extern int nfs_setattr(struct dentry *, struct iattr *); ...@@ -342,7 +345,7 @@ extern int nfs_setattr(struct dentry *, struct iattr *);
extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr); extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr);
extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx); extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx);
extern void put_nfs_open_context(struct nfs_open_context *ctx); extern void put_nfs_open_context(struct nfs_open_context *ctx);
extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, int mode); extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, fmode_t mode);
extern u64 nfs_compat_user_ino64(u64 fileid); extern u64 nfs_compat_user_ino64(u64 fileid);
extern void nfs_fattr_init(struct nfs_fattr *fattr); extern void nfs_fattr_init(struct nfs_fattr *fattr);
...@@ -532,12 +535,6 @@ static inline void nfs3_forget_cached_acls(struct inode *inode) ...@@ -532,12 +535,6 @@ static inline void nfs3_forget_cached_acls(struct inode *inode)
} }
#endif /* CONFIG_NFS_V3_ACL */ #endif /* CONFIG_NFS_V3_ACL */
/*
* linux/fs/mount_clnt.c
*/
extern int nfs_mount(struct sockaddr *, size_t, char *, char *,
int, int, struct nfs_fh *);
/* /*
* inline functions * inline functions
*/ */
......
...@@ -42,12 +42,6 @@ struct nfs_client { ...@@ -42,12 +42,6 @@ struct nfs_client {
struct rb_root cl_openowner_id; struct rb_root cl_openowner_id;
struct rb_root cl_lockowner_id; struct rb_root cl_lockowner_id;
/*
* The following rwsem ensures exclusive access to the server
* while we recover the state following a lease expiration.
*/
struct rw_semaphore cl_sem;
struct list_head cl_delegations; struct list_head cl_delegations;
struct rb_root cl_state_owners; struct rb_root cl_state_owners;
spinlock_t cl_lock; spinlock_t cl_lock;
......
...@@ -45,7 +45,7 @@ struct nfs_mount_data { ...@@ -45,7 +45,7 @@ struct nfs_mount_data {
char context[NFS_MAX_CONTEXT_LEN + 1]; /* 6 */ char context[NFS_MAX_CONTEXT_LEN + 1]; /* 6 */
}; };
/* bits in the flags field */ /* bits in the flags field visible to user space */
#define NFS_MOUNT_SOFT 0x0001 /* 1 */ #define NFS_MOUNT_SOFT 0x0001 /* 1 */
#define NFS_MOUNT_INTR 0x0002 /* 1 */ /* now unused, but ABI */ #define NFS_MOUNT_INTR 0x0002 /* 1 */ /* now unused, but ABI */
...@@ -68,5 +68,6 @@ struct nfs_mount_data { ...@@ -68,5 +68,6 @@ struct nfs_mount_data {
/* The following are for internal use only */ /* The following are for internal use only */
#define NFS_MOUNT_LOOKUP_CACHE_NONEG 0x10000 #define NFS_MOUNT_LOOKUP_CACHE_NONEG 0x10000
#define NFS_MOUNT_LOOKUP_CACHE_NONE 0x20000 #define NFS_MOUNT_LOOKUP_CACHE_NONE 0x20000
#define NFS_MOUNT_NORESVPORT 0x40000
#endif #endif
...@@ -120,13 +120,14 @@ struct nfs_openargs { ...@@ -120,13 +120,14 @@ struct nfs_openargs {
const struct nfs_fh * fh; const struct nfs_fh * fh;
struct nfs_seqid * seqid; struct nfs_seqid * seqid;
int open_flags; int open_flags;
fmode_t fmode;
__u64 clientid; __u64 clientid;
__u64 id; __u64 id;
union { union {
struct iattr * attrs; /* UNCHECKED, GUARDED */ struct iattr * attrs; /* UNCHECKED, GUARDED */
nfs4_verifier verifier; /* EXCLUSIVE */ nfs4_verifier verifier; /* EXCLUSIVE */
nfs4_stateid delegation; /* CLAIM_DELEGATE_CUR */ nfs4_stateid delegation; /* CLAIM_DELEGATE_CUR */
int delegation_type; /* CLAIM_PREVIOUS */ fmode_t delegation_type; /* CLAIM_PREVIOUS */
} u; } u;
const struct qstr * name; const struct qstr * name;
const struct nfs_server *server; /* Needed for ID mapping */ const struct nfs_server *server; /* Needed for ID mapping */
...@@ -143,7 +144,7 @@ struct nfs_openres { ...@@ -143,7 +144,7 @@ struct nfs_openres {
struct nfs_fattr * dir_attr; struct nfs_fattr * dir_attr;
struct nfs_seqid * seqid; struct nfs_seqid * seqid;
const struct nfs_server *server; const struct nfs_server *server;
int delegation_type; fmode_t delegation_type;
nfs4_stateid delegation; nfs4_stateid delegation;
__u32 do_recall; __u32 do_recall;
__u64 maxsize; __u64 maxsize;
...@@ -171,7 +172,7 @@ struct nfs_closeargs { ...@@ -171,7 +172,7 @@ struct nfs_closeargs {
struct nfs_fh * fh; struct nfs_fh * fh;
nfs4_stateid * stateid; nfs4_stateid * stateid;
struct nfs_seqid * seqid; struct nfs_seqid * seqid;
int open_flags; fmode_t fmode;
const u32 * bitmask; const u32 * bitmask;
}; };
......
...@@ -124,6 +124,8 @@ struct nfs4_client { ...@@ -124,6 +124,8 @@ struct nfs4_client {
nfs4_verifier cl_verifier; /* generated by client */ nfs4_verifier cl_verifier; /* generated by client */
time_t cl_time; /* time of last lease renewal */ time_t cl_time; /* time of last lease renewal */
__be32 cl_addr; /* client ipaddress */ __be32 cl_addr; /* client ipaddress */
u32 cl_flavor; /* setclientid pseudoflavor */
char *cl_principal; /* setclientid principal name */
struct svc_cred cl_cred; /* setclientid principal */ struct svc_cred cl_cred; /* setclientid principal */
clientid_t cl_clientid; /* generated by server */ clientid_t cl_clientid; /* generated by server */
nfs4_verifier cl_confirm; /* generated by server */ nfs4_verifier cl_confirm; /* generated by server */
......
...@@ -58,6 +58,7 @@ struct rpc_clnt { ...@@ -58,6 +58,7 @@ struct rpc_clnt {
struct rpc_timeout cl_timeout_default; struct rpc_timeout cl_timeout_default;
struct rpc_program * cl_program; struct rpc_program * cl_program;
char cl_inline_name[32]; char cl_inline_name[32];
char *cl_principal; /* target to authenticate to */
}; };
/* /*
...@@ -108,6 +109,7 @@ struct rpc_create_args { ...@@ -108,6 +109,7 @@ struct rpc_create_args {
u32 version; u32 version;
rpc_authflavor_t authflavor; rpc_authflavor_t authflavor;
unsigned long flags; unsigned long flags;
char *client_name;
}; };
/* Values for "flags" field */ /* Values for "flags" field */
......
...@@ -15,6 +15,7 @@ struct rpc_pipe_ops { ...@@ -15,6 +15,7 @@ struct rpc_pipe_ops {
ssize_t (*upcall)(struct file *, struct rpc_pipe_msg *, char __user *, size_t); ssize_t (*upcall)(struct file *, struct rpc_pipe_msg *, char __user *, size_t);
ssize_t (*downcall)(struct file *, const char __user *, size_t); ssize_t (*downcall)(struct file *, const char __user *, size_t);
void (*release_pipe)(struct inode *); void (*release_pipe)(struct inode *);
int (*open_pipe)(struct inode *);
void (*destroy_msg)(struct rpc_pipe_msg *); void (*destroy_msg)(struct rpc_pipe_msg *);
}; };
......
...@@ -20,6 +20,7 @@ int gss_svc_init(void); ...@@ -20,6 +20,7 @@ int gss_svc_init(void);
void gss_svc_shutdown(void); void gss_svc_shutdown(void);
int svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name); int svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name);
u32 svcauth_gss_flavor(struct auth_domain *dom); u32 svcauth_gss_flavor(struct auth_domain *dom);
char *svc_gss_principal(struct svc_rqst *);
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* _LINUX_SUNRPC_SVCAUTH_GSS_H */ #endif /* _LINUX_SUNRPC_SVCAUTH_GSS_H */
...@@ -36,21 +36,6 @@ struct xdr_netobj { ...@@ -36,21 +36,6 @@ struct xdr_netobj {
*/ */
typedef int (*kxdrproc_t)(void *rqstp, __be32 *data, void *obj); typedef int (*kxdrproc_t)(void *rqstp, __be32 *data, void *obj);
/*
* We're still requiring the BKL in the xdr code until it's been
* more carefully audited, at which point this wrapper will become
* unnecessary.
*/
static inline int rpc_call_xdrproc(kxdrproc_t xdrproc, void *rqstp, __be32 *data, void *obj)
{
int ret;
lock_kernel();
ret = xdrproc(rqstp, data, obj);
unlock_kernel();
return ret;
}
/* /*
* Basic structure for transmission/reception of a client XDR message. * Basic structure for transmission/reception of a client XDR message.
* Features a header (for a linear buffer containing RPC headers * Features a header (for a linear buffer containing RPC headers
......
...@@ -76,8 +76,7 @@ struct rpc_rqst { ...@@ -76,8 +76,7 @@ struct rpc_rqst {
struct list_head rq_list; struct list_head rq_list;
__u32 * rq_buffer; /* XDR encode buffer */ __u32 * rq_buffer; /* XDR encode buffer */
size_t rq_bufsize, size_t rq_callsize,
rq_callsize,
rq_rcvsize; rq_rcvsize;
struct xdr_buf rq_private_buf; /* The receive buffer struct xdr_buf rq_private_buf; /* The receive buffer
......
...@@ -234,7 +234,7 @@ rpcauth_prune_expired(struct list_head *free, int nr_to_scan) ...@@ -234,7 +234,7 @@ rpcauth_prune_expired(struct list_head *free, int nr_to_scan)
list_for_each_entry_safe(cred, next, &cred_unused, cr_lru) { list_for_each_entry_safe(cred, next, &cred_unused, cr_lru) {
/* Enforce a 60 second garbage collection moratorium */ /* Enforce a 60 second garbage collection moratorium */
if (time_in_range(cred->cr_expire, expired, jiffies) && if (time_in_range_open(cred->cr_expire, expired, jiffies) &&
test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0) test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0)
continue; continue;
...@@ -515,7 +515,7 @@ rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp, ...@@ -515,7 +515,7 @@ rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp,
if (cred->cr_ops->crwrap_req) if (cred->cr_ops->crwrap_req)
return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj); return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj);
/* By default, we encode the arguments normally. */ /* By default, we encode the arguments normally. */
return rpc_call_xdrproc(encode, rqstp, data, obj); return encode(rqstp, data, obj);
} }
int int
...@@ -530,7 +530,7 @@ rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp, ...@@ -530,7 +530,7 @@ rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp,
return cred->cr_ops->crunwrap_resp(task, decode, rqstp, return cred->cr_ops->crunwrap_resp(task, decode, rqstp,
data, obj); data, obj);
/* By default, we decode the arguments normally. */ /* By default, we decode the arguments normally. */
return rpc_call_xdrproc(decode, rqstp, data, obj); return decode(rqstp, data, obj);
} }
int int
......
...@@ -72,11 +72,25 @@ struct gss_auth { ...@@ -72,11 +72,25 @@ struct gss_auth {
struct gss_api_mech *mech; struct gss_api_mech *mech;
enum rpc_gss_svc service; enum rpc_gss_svc service;
struct rpc_clnt *client; struct rpc_clnt *client;
struct dentry *dentry; /*
* There are two upcall pipes; dentry[1], named "gssd", is used
* for the new text-based upcall; dentry[0] is named after the
* mechanism (for example, "krb5") and exists for
* backwards-compatibility with older gssd's.
*/
struct dentry *dentry[2];
}; };
/* pipe_version >= 0 if and only if someone has a pipe open. */
static int pipe_version = -1;
static atomic_t pipe_users = ATOMIC_INIT(0);
static DEFINE_SPINLOCK(pipe_version_lock);
static struct rpc_wait_queue pipe_version_rpc_waitqueue;
static DECLARE_WAIT_QUEUE_HEAD(pipe_version_waitqueue);
static void gss_free_ctx(struct gss_cl_ctx *); static void gss_free_ctx(struct gss_cl_ctx *);
static struct rpc_pipe_ops gss_upcall_ops; static struct rpc_pipe_ops gss_upcall_ops_v0;
static struct rpc_pipe_ops gss_upcall_ops_v1;
static inline struct gss_cl_ctx * static inline struct gss_cl_ctx *
gss_get_ctx(struct gss_cl_ctx *ctx) gss_get_ctx(struct gss_cl_ctx *ctx)
...@@ -220,6 +234,7 @@ gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct ...@@ -220,6 +234,7 @@ gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct
return p; return p;
} }
#define UPCALL_BUF_LEN 128
struct gss_upcall_msg { struct gss_upcall_msg {
atomic_t count; atomic_t count;
...@@ -227,16 +242,41 @@ struct gss_upcall_msg { ...@@ -227,16 +242,41 @@ struct gss_upcall_msg {
struct rpc_pipe_msg msg; struct rpc_pipe_msg msg;
struct list_head list; struct list_head list;
struct gss_auth *auth; struct gss_auth *auth;
struct rpc_inode *inode;
struct rpc_wait_queue rpc_waitqueue; struct rpc_wait_queue rpc_waitqueue;
wait_queue_head_t waitqueue; wait_queue_head_t waitqueue;
struct gss_cl_ctx *ctx; struct gss_cl_ctx *ctx;
char databuf[UPCALL_BUF_LEN];
}; };
static int get_pipe_version(void)
{
int ret;
spin_lock(&pipe_version_lock);
if (pipe_version >= 0) {
atomic_inc(&pipe_users);
ret = pipe_version;
} else
ret = -EAGAIN;
spin_unlock(&pipe_version_lock);
return ret;
}
static void put_pipe_version(void)
{
if (atomic_dec_and_lock(&pipe_users, &pipe_version_lock)) {
pipe_version = -1;
spin_unlock(&pipe_version_lock);
}
}
static void static void
gss_release_msg(struct gss_upcall_msg *gss_msg) gss_release_msg(struct gss_upcall_msg *gss_msg)
{ {
if (!atomic_dec_and_test(&gss_msg->count)) if (!atomic_dec_and_test(&gss_msg->count))
return; return;
put_pipe_version();
BUG_ON(!list_empty(&gss_msg->list)); BUG_ON(!list_empty(&gss_msg->list));
if (gss_msg->ctx != NULL) if (gss_msg->ctx != NULL)
gss_put_ctx(gss_msg->ctx); gss_put_ctx(gss_msg->ctx);
...@@ -266,8 +306,8 @@ __gss_find_upcall(struct rpc_inode *rpci, uid_t uid) ...@@ -266,8 +306,8 @@ __gss_find_upcall(struct rpc_inode *rpci, uid_t uid)
static inline struct gss_upcall_msg * static inline struct gss_upcall_msg *
gss_add_msg(struct gss_auth *gss_auth, struct gss_upcall_msg *gss_msg) gss_add_msg(struct gss_auth *gss_auth, struct gss_upcall_msg *gss_msg)
{ {
struct inode *inode = gss_auth->dentry->d_inode; struct rpc_inode *rpci = gss_msg->inode;
struct rpc_inode *rpci = RPC_I(inode); struct inode *inode = &rpci->vfs_inode;
struct gss_upcall_msg *old; struct gss_upcall_msg *old;
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
...@@ -293,8 +333,7 @@ __gss_unhash_msg(struct gss_upcall_msg *gss_msg) ...@@ -293,8 +333,7 @@ __gss_unhash_msg(struct gss_upcall_msg *gss_msg)
static void static void
gss_unhash_msg(struct gss_upcall_msg *gss_msg) gss_unhash_msg(struct gss_upcall_msg *gss_msg)
{ {
struct gss_auth *gss_auth = gss_msg->auth; struct inode *inode = &gss_msg->inode->vfs_inode;
struct inode *inode = gss_auth->dentry->d_inode;
if (list_empty(&gss_msg->list)) if (list_empty(&gss_msg->list))
return; return;
...@@ -310,7 +349,7 @@ gss_upcall_callback(struct rpc_task *task) ...@@ -310,7 +349,7 @@ gss_upcall_callback(struct rpc_task *task)
struct gss_cred *gss_cred = container_of(task->tk_msg.rpc_cred, struct gss_cred *gss_cred = container_of(task->tk_msg.rpc_cred,
struct gss_cred, gc_base); struct gss_cred, gc_base);
struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall; struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall;
struct inode *inode = gss_msg->auth->dentry->d_inode; struct inode *inode = &gss_msg->inode->vfs_inode;
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
if (gss_msg->ctx) if (gss_msg->ctx)
...@@ -323,22 +362,75 @@ gss_upcall_callback(struct rpc_task *task) ...@@ -323,22 +362,75 @@ gss_upcall_callback(struct rpc_task *task)
gss_release_msg(gss_msg); gss_release_msg(gss_msg);
} }
static void gss_encode_v0_msg(struct gss_upcall_msg *gss_msg)
{
gss_msg->msg.data = &gss_msg->uid;
gss_msg->msg.len = sizeof(gss_msg->uid);
}
static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg,
struct rpc_clnt *clnt, int machine_cred)
{
char *p = gss_msg->databuf;
int len = 0;
gss_msg->msg.len = sprintf(gss_msg->databuf, "mech=%s uid=%d ",
gss_msg->auth->mech->gm_name,
gss_msg->uid);
p += gss_msg->msg.len;
if (clnt->cl_principal) {
len = sprintf(p, "target=%s ", clnt->cl_principal);
p += len;
gss_msg->msg.len += len;
}
if (machine_cred) {
len = sprintf(p, "service=* ");
p += len;
gss_msg->msg.len += len;
} else if (!strcmp(clnt->cl_program->name, "nfs4_cb")) {
len = sprintf(p, "service=nfs ");
p += len;
gss_msg->msg.len += len;
}
len = sprintf(p, "\n");
gss_msg->msg.len += len;
gss_msg->msg.data = gss_msg->databuf;
BUG_ON(gss_msg->msg.len > UPCALL_BUF_LEN);
}
static void gss_encode_msg(struct gss_upcall_msg *gss_msg,
struct rpc_clnt *clnt, int machine_cred)
{
if (pipe_version == 0)
gss_encode_v0_msg(gss_msg);
else /* pipe_version == 1 */
gss_encode_v1_msg(gss_msg, clnt, machine_cred);
}
static inline struct gss_upcall_msg * static inline struct gss_upcall_msg *
gss_alloc_msg(struct gss_auth *gss_auth, uid_t uid) gss_alloc_msg(struct gss_auth *gss_auth, uid_t uid, struct rpc_clnt *clnt,
int machine_cred)
{ {
struct gss_upcall_msg *gss_msg; struct gss_upcall_msg *gss_msg;
int vers;
gss_msg = kzalloc(sizeof(*gss_msg), GFP_NOFS); gss_msg = kzalloc(sizeof(*gss_msg), GFP_NOFS);
if (gss_msg != NULL) { if (gss_msg == NULL)
return ERR_PTR(-ENOMEM);
vers = get_pipe_version();
if (vers < 0) {
kfree(gss_msg);
return ERR_PTR(vers);
}
gss_msg->inode = RPC_I(gss_auth->dentry[vers]->d_inode);
INIT_LIST_HEAD(&gss_msg->list); INIT_LIST_HEAD(&gss_msg->list);
rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq"); rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq");
init_waitqueue_head(&gss_msg->waitqueue); init_waitqueue_head(&gss_msg->waitqueue);
atomic_set(&gss_msg->count, 1); atomic_set(&gss_msg->count, 1);
gss_msg->msg.data = &gss_msg->uid;
gss_msg->msg.len = sizeof(gss_msg->uid);
gss_msg->uid = uid; gss_msg->uid = uid;
gss_msg->auth = gss_auth; gss_msg->auth = gss_auth;
} gss_encode_msg(gss_msg, clnt, machine_cred);
return gss_msg; return gss_msg;
} }
...@@ -350,16 +442,13 @@ gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cr ...@@ -350,16 +442,13 @@ gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cr
struct gss_upcall_msg *gss_new, *gss_msg; struct gss_upcall_msg *gss_new, *gss_msg;
uid_t uid = cred->cr_uid; uid_t uid = cred->cr_uid;
/* Special case: rpc.gssd assumes that uid == 0 implies machine creds */ gss_new = gss_alloc_msg(gss_auth, uid, clnt, gss_cred->gc_machine_cred);
if (gss_cred->gc_machine_cred != 0) if (IS_ERR(gss_new))
uid = 0; return gss_new;
gss_new = gss_alloc_msg(gss_auth, uid);
if (gss_new == NULL)
return ERR_PTR(-ENOMEM);
gss_msg = gss_add_msg(gss_auth, gss_new); gss_msg = gss_add_msg(gss_auth, gss_new);
if (gss_msg == gss_new) { if (gss_msg == gss_new) {
int res = rpc_queue_upcall(gss_auth->dentry->d_inode, &gss_new->msg); struct inode *inode = &gss_new->inode->vfs_inode;
int res = rpc_queue_upcall(inode, &gss_new->msg);
if (res) { if (res) {
gss_unhash_msg(gss_new); gss_unhash_msg(gss_new);
gss_msg = ERR_PTR(res); gss_msg = ERR_PTR(res);
...@@ -369,6 +458,18 @@ gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cr ...@@ -369,6 +458,18 @@ gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cr
return gss_msg; return gss_msg;
} }
static void warn_gssd(void)
{
static unsigned long ratelimit;
unsigned long now = jiffies;
if (time_after(now, ratelimit)) {
printk(KERN_WARNING "RPC: AUTH_GSS upcall timed out.\n"
"Please check user daemon is running.\n");
ratelimit = now + 15*HZ;
}
}
static inline int static inline int
gss_refresh_upcall(struct rpc_task *task) gss_refresh_upcall(struct rpc_task *task)
{ {
...@@ -378,16 +479,25 @@ gss_refresh_upcall(struct rpc_task *task) ...@@ -378,16 +479,25 @@ gss_refresh_upcall(struct rpc_task *task)
struct gss_cred *gss_cred = container_of(cred, struct gss_cred *gss_cred = container_of(cred,
struct gss_cred, gc_base); struct gss_cred, gc_base);
struct gss_upcall_msg *gss_msg; struct gss_upcall_msg *gss_msg;
struct inode *inode = gss_auth->dentry->d_inode; struct inode *inode;
int err = 0; int err = 0;
dprintk("RPC: %5u gss_refresh_upcall for uid %u\n", task->tk_pid, dprintk("RPC: %5u gss_refresh_upcall for uid %u\n", task->tk_pid,
cred->cr_uid); cred->cr_uid);
gss_msg = gss_setup_upcall(task->tk_client, gss_auth, cred); gss_msg = gss_setup_upcall(task->tk_client, gss_auth, cred);
if (IS_ERR(gss_msg) == -EAGAIN) {
/* XXX: warning on the first, under the assumption we
* shouldn't normally hit this case on a refresh. */
warn_gssd();
task->tk_timeout = 15*HZ;
rpc_sleep_on(&pipe_version_rpc_waitqueue, task, NULL);
return 0;
}
if (IS_ERR(gss_msg)) { if (IS_ERR(gss_msg)) {
err = PTR_ERR(gss_msg); err = PTR_ERR(gss_msg);
goto out; goto out;
} }
inode = &gss_msg->inode->vfs_inode;
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
if (gss_cred->gc_upcall != NULL) if (gss_cred->gc_upcall != NULL)
rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL); rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL);
...@@ -414,18 +524,29 @@ gss_refresh_upcall(struct rpc_task *task) ...@@ -414,18 +524,29 @@ gss_refresh_upcall(struct rpc_task *task)
static inline int static inline int
gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred) gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred)
{ {
struct inode *inode = gss_auth->dentry->d_inode; struct inode *inode;
struct rpc_cred *cred = &gss_cred->gc_base; struct rpc_cred *cred = &gss_cred->gc_base;
struct gss_upcall_msg *gss_msg; struct gss_upcall_msg *gss_msg;
DEFINE_WAIT(wait); DEFINE_WAIT(wait);
int err = 0; int err = 0;
dprintk("RPC: gss_upcall for uid %u\n", cred->cr_uid); dprintk("RPC: gss_upcall for uid %u\n", cred->cr_uid);
retry:
gss_msg = gss_setup_upcall(gss_auth->client, gss_auth, cred); gss_msg = gss_setup_upcall(gss_auth->client, gss_auth, cred);
if (PTR_ERR(gss_msg) == -EAGAIN) {
err = wait_event_interruptible_timeout(pipe_version_waitqueue,
pipe_version >= 0, 15*HZ);
if (err)
goto out;
if (pipe_version < 0)
warn_gssd();
goto retry;
}
if (IS_ERR(gss_msg)) { if (IS_ERR(gss_msg)) {
err = PTR_ERR(gss_msg); err = PTR_ERR(gss_msg);
goto out; goto out;
} }
inode = &gss_msg->inode->vfs_inode;
for (;;) { for (;;) {
prepare_to_wait(&gss_msg->waitqueue, &wait, TASK_INTERRUPTIBLE); prepare_to_wait(&gss_msg->waitqueue, &wait, TASK_INTERRUPTIBLE);
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
...@@ -543,6 +664,38 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) ...@@ -543,6 +664,38 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
return err; return err;
} }
static int gss_pipe_open(struct inode *inode, int new_version)
{
int ret = 0;
spin_lock(&pipe_version_lock);
if (pipe_version < 0) {
/* First open of any gss pipe determines the version: */
pipe_version = new_version;
rpc_wake_up(&pipe_version_rpc_waitqueue);
wake_up(&pipe_version_waitqueue);
} else if (pipe_version != new_version) {
/* Trying to open a pipe of a different version */
ret = -EBUSY;
goto out;
}
atomic_inc(&pipe_users);
out:
spin_unlock(&pipe_version_lock);
return ret;
}
static int gss_pipe_open_v0(struct inode *inode)
{
return gss_pipe_open(inode, 0);
}
static int gss_pipe_open_v1(struct inode *inode)
{
return gss_pipe_open(inode, 1);
}
static void static void
gss_pipe_release(struct inode *inode) gss_pipe_release(struct inode *inode)
{ {
...@@ -562,27 +715,22 @@ gss_pipe_release(struct inode *inode) ...@@ -562,27 +715,22 @@ gss_pipe_release(struct inode *inode)
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
} }
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
put_pipe_version();
} }
static void static void
gss_pipe_destroy_msg(struct rpc_pipe_msg *msg) gss_pipe_destroy_msg(struct rpc_pipe_msg *msg)
{ {
struct gss_upcall_msg *gss_msg = container_of(msg, struct gss_upcall_msg, msg); struct gss_upcall_msg *gss_msg = container_of(msg, struct gss_upcall_msg, msg);
static unsigned long ratelimit;
if (msg->errno < 0) { if (msg->errno < 0) {
dprintk("RPC: gss_pipe_destroy_msg releasing msg %p\n", dprintk("RPC: gss_pipe_destroy_msg releasing msg %p\n",
gss_msg); gss_msg);
atomic_inc(&gss_msg->count); atomic_inc(&gss_msg->count);
gss_unhash_msg(gss_msg); gss_unhash_msg(gss_msg);
if (msg->errno == -ETIMEDOUT) { if (msg->errno == -ETIMEDOUT)
unsigned long now = jiffies; warn_gssd();
if (time_after(now, ratelimit)) {
printk(KERN_WARNING "RPC: AUTH_GSS upcall timed out.\n"
"Please check user daemon is running!\n");
ratelimit = now + 15*HZ;
}
}
gss_release_msg(gss_msg); gss_release_msg(gss_msg);
} }
} }
...@@ -623,20 +771,38 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) ...@@ -623,20 +771,38 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
atomic_set(&auth->au_count, 1); atomic_set(&auth->au_count, 1);
kref_init(&gss_auth->kref); kref_init(&gss_auth->kref);
gss_auth->dentry = rpc_mkpipe(clnt->cl_dentry, gss_auth->mech->gm_name, /*
clnt, &gss_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN); * Note: if we created the old pipe first, then someone who
if (IS_ERR(gss_auth->dentry)) { * examined the directory at the right moment might conclude
err = PTR_ERR(gss_auth->dentry); * that we supported only the old pipe. So we instead create
* the new pipe first.
*/
gss_auth->dentry[1] = rpc_mkpipe(clnt->cl_dentry,
"gssd",
clnt, &gss_upcall_ops_v1,
RPC_PIPE_WAIT_FOR_OPEN);
if (IS_ERR(gss_auth->dentry[1])) {
err = PTR_ERR(gss_auth->dentry[1]);
goto err_put_mech; goto err_put_mech;
} }
gss_auth->dentry[0] = rpc_mkpipe(clnt->cl_dentry,
gss_auth->mech->gm_name,
clnt, &gss_upcall_ops_v0,
RPC_PIPE_WAIT_FOR_OPEN);
if (IS_ERR(gss_auth->dentry[0])) {
err = PTR_ERR(gss_auth->dentry[0]);
goto err_unlink_pipe_1;
}
err = rpcauth_init_credcache(auth); err = rpcauth_init_credcache(auth);
if (err) if (err)
goto err_unlink_pipe; goto err_unlink_pipe_0;
return auth; return auth;
err_unlink_pipe: err_unlink_pipe_0:
rpc_unlink(gss_auth->dentry); rpc_unlink(gss_auth->dentry[0]);
err_unlink_pipe_1:
rpc_unlink(gss_auth->dentry[1]);
err_put_mech: err_put_mech:
gss_mech_put(gss_auth->mech); gss_mech_put(gss_auth->mech);
err_free: err_free:
...@@ -649,8 +815,8 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) ...@@ -649,8 +815,8 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
static void static void
gss_free(struct gss_auth *gss_auth) gss_free(struct gss_auth *gss_auth)
{ {
rpc_unlink(gss_auth->dentry); rpc_unlink(gss_auth->dentry[1]);
gss_auth->dentry = NULL; rpc_unlink(gss_auth->dentry[0]);
gss_mech_put(gss_auth->mech); gss_mech_put(gss_auth->mech);
kfree(gss_auth); kfree(gss_auth);
...@@ -693,7 +859,7 @@ gss_destroying_context(struct rpc_cred *cred) ...@@ -693,7 +859,7 @@ gss_destroying_context(struct rpc_cred *cred)
struct rpc_task *task; struct rpc_task *task;
if (gss_cred->gc_ctx == NULL || if (gss_cred->gc_ctx == NULL ||
test_and_clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0) test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0)
return 0; return 0;
gss_cred->gc_ctx->gc_proc = RPC_GSS_PROC_DESTROY; gss_cred->gc_ctx->gc_proc = RPC_GSS_PROC_DESTROY;
...@@ -757,14 +923,12 @@ gss_free_cred_callback(struct rcu_head *head) ...@@ -757,14 +923,12 @@ gss_free_cred_callback(struct rcu_head *head)
} }
static void static void
gss_destroy_cred(struct rpc_cred *cred) gss_destroy_nullcred(struct rpc_cred *cred)
{ {
struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth); struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth);
struct gss_cl_ctx *ctx = gss_cred->gc_ctx; struct gss_cl_ctx *ctx = gss_cred->gc_ctx;
if (gss_destroying_context(cred))
return;
rcu_assign_pointer(gss_cred->gc_ctx, NULL); rcu_assign_pointer(gss_cred->gc_ctx, NULL);
call_rcu(&cred->cr_rcu, gss_free_cred_callback); call_rcu(&cred->cr_rcu, gss_free_cred_callback);
if (ctx) if (ctx)
...@@ -772,6 +936,15 @@ gss_destroy_cred(struct rpc_cred *cred) ...@@ -772,6 +936,15 @@ gss_destroy_cred(struct rpc_cred *cred)
kref_put(&gss_auth->kref, gss_free_callback); kref_put(&gss_auth->kref, gss_free_callback);
} }
static void
gss_destroy_cred(struct rpc_cred *cred)
{
if (gss_destroying_context(cred))
return;
gss_destroy_nullcred(cred);
}
/* /*
* Lookup RPCSEC_GSS cred for the current process * Lookup RPCSEC_GSS cred for the current process
*/ */
...@@ -1017,7 +1190,7 @@ gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, ...@@ -1017,7 +1190,7 @@ gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;
*p++ = htonl(rqstp->rq_seqno); *p++ = htonl(rqstp->rq_seqno);
status = rpc_call_xdrproc(encode, rqstp, p, obj); status = encode(rqstp, p, obj);
if (status) if (status)
return status; return status;
...@@ -1111,7 +1284,7 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, ...@@ -1111,7 +1284,7 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;
*p++ = htonl(rqstp->rq_seqno); *p++ = htonl(rqstp->rq_seqno);
status = rpc_call_xdrproc(encode, rqstp, p, obj); status = encode(rqstp, p, obj);
if (status) if (status)
return status; return status;
...@@ -1170,12 +1343,12 @@ gss_wrap_req(struct rpc_task *task, ...@@ -1170,12 +1343,12 @@ gss_wrap_req(struct rpc_task *task,
/* The spec seems a little ambiguous here, but I think that not /* The spec seems a little ambiguous here, but I think that not
* wrapping context destruction requests makes the most sense. * wrapping context destruction requests makes the most sense.
*/ */
status = rpc_call_xdrproc(encode, rqstp, p, obj); status = encode(rqstp, p, obj);
goto out; goto out;
} }
switch (gss_cred->gc_service) { switch (gss_cred->gc_service) {
case RPC_GSS_SVC_NONE: case RPC_GSS_SVC_NONE:
status = rpc_call_xdrproc(encode, rqstp, p, obj); status = encode(rqstp, p, obj);
break; break;
case RPC_GSS_SVC_INTEGRITY: case RPC_GSS_SVC_INTEGRITY:
status = gss_wrap_req_integ(cred, ctx, encode, status = gss_wrap_req_integ(cred, ctx, encode,
...@@ -1291,7 +1464,7 @@ gss_unwrap_resp(struct rpc_task *task, ...@@ -1291,7 +1464,7 @@ gss_unwrap_resp(struct rpc_task *task,
cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + (p - savedp) cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + (p - savedp)
+ (savedlen - head->iov_len); + (savedlen - head->iov_len);
out_decode: out_decode:
status = rpc_call_xdrproc(decode, rqstp, p, obj); status = decode(rqstp, p, obj);
out: out:
gss_put_ctx(ctx); gss_put_ctx(ctx);
dprintk("RPC: %5u gss_unwrap_resp returning %d\n", task->tk_pid, dprintk("RPC: %5u gss_unwrap_resp returning %d\n", task->tk_pid,
...@@ -1324,7 +1497,7 @@ static const struct rpc_credops gss_credops = { ...@@ -1324,7 +1497,7 @@ static const struct rpc_credops gss_credops = {
static const struct rpc_credops gss_nullops = { static const struct rpc_credops gss_nullops = {
.cr_name = "AUTH_GSS", .cr_name = "AUTH_GSS",
.crdestroy = gss_destroy_cred, .crdestroy = gss_destroy_nullcred,
.crbind = rpcauth_generic_bind_cred, .crbind = rpcauth_generic_bind_cred,
.crmatch = gss_match, .crmatch = gss_match,
.crmarshal = gss_marshal, .crmarshal = gss_marshal,
...@@ -1334,10 +1507,19 @@ static const struct rpc_credops gss_nullops = { ...@@ -1334,10 +1507,19 @@ static const struct rpc_credops gss_nullops = {
.crunwrap_resp = gss_unwrap_resp, .crunwrap_resp = gss_unwrap_resp,
}; };
static struct rpc_pipe_ops gss_upcall_ops = { static struct rpc_pipe_ops gss_upcall_ops_v0 = {
.upcall = gss_pipe_upcall,
.downcall = gss_pipe_downcall,
.destroy_msg = gss_pipe_destroy_msg,
.open_pipe = gss_pipe_open_v0,
.release_pipe = gss_pipe_release,
};
static struct rpc_pipe_ops gss_upcall_ops_v1 = {
.upcall = gss_pipe_upcall, .upcall = gss_pipe_upcall,
.downcall = gss_pipe_downcall, .downcall = gss_pipe_downcall,
.destroy_msg = gss_pipe_destroy_msg, .destroy_msg = gss_pipe_destroy_msg,
.open_pipe = gss_pipe_open_v1,
.release_pipe = gss_pipe_release, .release_pipe = gss_pipe_release,
}; };
...@@ -1354,6 +1536,7 @@ static int __init init_rpcsec_gss(void) ...@@ -1354,6 +1536,7 @@ static int __init init_rpcsec_gss(void)
err = gss_svc_init(); err = gss_svc_init();
if (err) if (err)
goto out_unregister; goto out_unregister;
rpc_init_wait_queue(&pipe_version_rpc_waitqueue, "gss pipe version");
return 0; return 0;
out_unregister: out_unregister:
rpcauth_unregister(&authgss_ops); rpcauth_unregister(&authgss_ops);
......
...@@ -152,7 +152,7 @@ g_token_size(struct xdr_netobj *mech, unsigned int body_size) ...@@ -152,7 +152,7 @@ g_token_size(struct xdr_netobj *mech, unsigned int body_size)
return(1 + der_length_size(body_size) + body_size); return(1 + der_length_size(body_size) + body_size);
} }
EXPORT_SYMBOL(g_token_size); EXPORT_SYMBOL_GPL(g_token_size);
/* fills in a buffer with the token header. The buffer is assumed to /* fills in a buffer with the token header. The buffer is assumed to
be the right size. buf is advanced past the token header */ be the right size. buf is advanced past the token header */
...@@ -167,7 +167,7 @@ g_make_token_header(struct xdr_netobj *mech, int body_size, unsigned char **buf) ...@@ -167,7 +167,7 @@ g_make_token_header(struct xdr_netobj *mech, int body_size, unsigned char **buf)
TWRITE_STR(*buf, mech->data, ((int) mech->len)); TWRITE_STR(*buf, mech->data, ((int) mech->len));
} }
EXPORT_SYMBOL(g_make_token_header); EXPORT_SYMBOL_GPL(g_make_token_header);
/* /*
* Given a buffer containing a token, reads and verifies the token, * Given a buffer containing a token, reads and verifies the token,
...@@ -231,5 +231,5 @@ g_verify_token_header(struct xdr_netobj *mech, int *body_size, ...@@ -231,5 +231,5 @@ g_verify_token_header(struct xdr_netobj *mech, int *body_size,
return(ret); return(ret);
} }
EXPORT_SYMBOL(g_verify_token_header); EXPORT_SYMBOL_GPL(g_verify_token_header);
...@@ -117,7 +117,7 @@ gss_mech_register(struct gss_api_mech *gm) ...@@ -117,7 +117,7 @@ gss_mech_register(struct gss_api_mech *gm)
return 0; return 0;
} }
EXPORT_SYMBOL(gss_mech_register); EXPORT_SYMBOL_GPL(gss_mech_register);
void void
gss_mech_unregister(struct gss_api_mech *gm) gss_mech_unregister(struct gss_api_mech *gm)
...@@ -129,7 +129,7 @@ gss_mech_unregister(struct gss_api_mech *gm) ...@@ -129,7 +129,7 @@ gss_mech_unregister(struct gss_api_mech *gm)
gss_mech_free(gm); gss_mech_free(gm);
} }
EXPORT_SYMBOL(gss_mech_unregister); EXPORT_SYMBOL_GPL(gss_mech_unregister);
struct gss_api_mech * struct gss_api_mech *
gss_mech_get(struct gss_api_mech *gm) gss_mech_get(struct gss_api_mech *gm)
...@@ -138,7 +138,7 @@ gss_mech_get(struct gss_api_mech *gm) ...@@ -138,7 +138,7 @@ gss_mech_get(struct gss_api_mech *gm)
return gm; return gm;
} }
EXPORT_SYMBOL(gss_mech_get); EXPORT_SYMBOL_GPL(gss_mech_get);
struct gss_api_mech * struct gss_api_mech *
gss_mech_get_by_name(const char *name) gss_mech_get_by_name(const char *name)
...@@ -158,7 +158,7 @@ gss_mech_get_by_name(const char *name) ...@@ -158,7 +158,7 @@ gss_mech_get_by_name(const char *name)
} }
EXPORT_SYMBOL(gss_mech_get_by_name); EXPORT_SYMBOL_GPL(gss_mech_get_by_name);
static inline int static inline int
mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor) mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor)
...@@ -191,7 +191,7 @@ gss_mech_get_by_pseudoflavor(u32 pseudoflavor) ...@@ -191,7 +191,7 @@ gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
return gm; return gm;
} }
EXPORT_SYMBOL(gss_mech_get_by_pseudoflavor); EXPORT_SYMBOL_GPL(gss_mech_get_by_pseudoflavor);
u32 u32
gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 service) gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 service)
...@@ -205,7 +205,7 @@ gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 service) ...@@ -205,7 +205,7 @@ gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 service)
} }
return RPC_AUTH_MAXFLAVOR; /* illegal value */ return RPC_AUTH_MAXFLAVOR; /* illegal value */
} }
EXPORT_SYMBOL(gss_svc_to_pseudoflavor); EXPORT_SYMBOL_GPL(gss_svc_to_pseudoflavor);
u32 u32
gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor) gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
...@@ -219,7 +219,7 @@ gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor) ...@@ -219,7 +219,7 @@ gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
return 0; return 0;
} }
EXPORT_SYMBOL(gss_pseudoflavor_to_service); EXPORT_SYMBOL_GPL(gss_pseudoflavor_to_service);
char * char *
gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service) gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service)
...@@ -233,7 +233,7 @@ gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service) ...@@ -233,7 +233,7 @@ gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service)
return NULL; return NULL;
} }
EXPORT_SYMBOL(gss_service_to_auth_domain_name); EXPORT_SYMBOL_GPL(gss_service_to_auth_domain_name);
void void
gss_mech_put(struct gss_api_mech * gm) gss_mech_put(struct gss_api_mech * gm)
...@@ -242,7 +242,7 @@ gss_mech_put(struct gss_api_mech * gm) ...@@ -242,7 +242,7 @@ gss_mech_put(struct gss_api_mech * gm)
module_put(gm->gm_owner); module_put(gm->gm_owner);
} }
EXPORT_SYMBOL(gss_mech_put); EXPORT_SYMBOL_GPL(gss_mech_put);
/* The mech could probably be determined from the token instead, but it's just /* The mech could probably be determined from the token instead, but it's just
* as easy for now to pass it in. */ * as easy for now to pass it in. */
......
...@@ -332,6 +332,7 @@ struct rsc { ...@@ -332,6 +332,7 @@ struct rsc {
struct svc_cred cred; struct svc_cred cred;
struct gss_svc_seq_data seqdata; struct gss_svc_seq_data seqdata;
struct gss_ctx *mechctx; struct gss_ctx *mechctx;
char *client_name;
}; };
static struct cache_head *rsc_table[RSC_HASHMAX]; static struct cache_head *rsc_table[RSC_HASHMAX];
...@@ -346,6 +347,7 @@ static void rsc_free(struct rsc *rsci) ...@@ -346,6 +347,7 @@ static void rsc_free(struct rsc *rsci)
gss_delete_sec_context(&rsci->mechctx); gss_delete_sec_context(&rsci->mechctx);
if (rsci->cred.cr_group_info) if (rsci->cred.cr_group_info)
put_group_info(rsci->cred.cr_group_info); put_group_info(rsci->cred.cr_group_info);
kfree(rsci->client_name);
} }
static void rsc_put(struct kref *ref) static void rsc_put(struct kref *ref)
...@@ -383,6 +385,7 @@ rsc_init(struct cache_head *cnew, struct cache_head *ctmp) ...@@ -383,6 +385,7 @@ rsc_init(struct cache_head *cnew, struct cache_head *ctmp)
tmp->handle.data = NULL; tmp->handle.data = NULL;
new->mechctx = NULL; new->mechctx = NULL;
new->cred.cr_group_info = NULL; new->cred.cr_group_info = NULL;
new->client_name = NULL;
} }
static void static void
...@@ -397,6 +400,8 @@ update_rsc(struct cache_head *cnew, struct cache_head *ctmp) ...@@ -397,6 +400,8 @@ update_rsc(struct cache_head *cnew, struct cache_head *ctmp)
spin_lock_init(&new->seqdata.sd_lock); spin_lock_init(&new->seqdata.sd_lock);
new->cred = tmp->cred; new->cred = tmp->cred;
tmp->cred.cr_group_info = NULL; tmp->cred.cr_group_info = NULL;
new->client_name = tmp->client_name;
tmp->client_name = NULL;
} }
static struct cache_head * static struct cache_head *
...@@ -486,6 +491,15 @@ static int rsc_parse(struct cache_detail *cd, ...@@ -486,6 +491,15 @@ static int rsc_parse(struct cache_detail *cd,
status = gss_import_sec_context(buf, len, gm, &rsci.mechctx); status = gss_import_sec_context(buf, len, gm, &rsci.mechctx);
if (status) if (status)
goto out; goto out;
/* get client name */
len = qword_get(&mesg, buf, mlen);
if (len > 0) {
rsci.client_name = kstrdup(buf, GFP_KERNEL);
if (!rsci.client_name)
goto out;
}
} }
rsci.h.expiry_time = expiry; rsci.h.expiry_time = expiry;
rscp = rsc_update(&rsci, rscp); rscp = rsc_update(&rsci, rscp);
...@@ -746,7 +760,7 @@ u32 svcauth_gss_flavor(struct auth_domain *dom) ...@@ -746,7 +760,7 @@ u32 svcauth_gss_flavor(struct auth_domain *dom)
return gd->pseudoflavor; return gd->pseudoflavor;
} }
EXPORT_SYMBOL(svcauth_gss_flavor); EXPORT_SYMBOL_GPL(svcauth_gss_flavor);
int int
svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name) svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name)
...@@ -780,7 +794,7 @@ svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name) ...@@ -780,7 +794,7 @@ svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name)
return stat; return stat;
} }
EXPORT_SYMBOL(svcauth_gss_register_pseudoflavor); EXPORT_SYMBOL_GPL(svcauth_gss_register_pseudoflavor);
static inline int static inline int
read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj) read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj)
...@@ -913,6 +927,16 @@ struct gss_svc_data { ...@@ -913,6 +927,16 @@ struct gss_svc_data {
struct rsc *rsci; struct rsc *rsci;
}; };
char *svc_gss_principal(struct svc_rqst *rqstp)
{
struct gss_svc_data *gd = (struct gss_svc_data *)rqstp->rq_auth_data;
if (gd && gd->rsci)
return gd->rsci->client_name;
return NULL;
}
EXPORT_SYMBOL_GPL(svc_gss_principal);
static int static int
svcauth_gss_set_client(struct svc_rqst *rqstp) svcauth_gss_set_client(struct svc_rqst *rqstp)
{ {
......
...@@ -197,6 +197,12 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru ...@@ -197,6 +197,12 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
clnt->cl_rtt = &clnt->cl_rtt_default; clnt->cl_rtt = &clnt->cl_rtt_default;
rpc_init_rtt(&clnt->cl_rtt_default, clnt->cl_timeout->to_initval); rpc_init_rtt(&clnt->cl_rtt_default, clnt->cl_timeout->to_initval);
clnt->cl_principal = NULL;
if (args->client_name) {
clnt->cl_principal = kstrdup(args->client_name, GFP_KERNEL);
if (!clnt->cl_principal)
goto out_no_principal;
}
kref_init(&clnt->cl_kref); kref_init(&clnt->cl_kref);
...@@ -226,6 +232,8 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru ...@@ -226,6 +232,8 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
rpc_put_mount(); rpc_put_mount();
} }
out_no_path: out_no_path:
kfree(clnt->cl_principal);
out_no_principal:
rpc_free_iostats(clnt->cl_metrics); rpc_free_iostats(clnt->cl_metrics);
out_no_stats: out_no_stats:
if (clnt->cl_server != clnt->cl_inline_name) if (clnt->cl_server != clnt->cl_inline_name)
...@@ -354,6 +362,11 @@ rpc_clone_client(struct rpc_clnt *clnt) ...@@ -354,6 +362,11 @@ rpc_clone_client(struct rpc_clnt *clnt)
new->cl_metrics = rpc_alloc_iostats(clnt); new->cl_metrics = rpc_alloc_iostats(clnt);
if (new->cl_metrics == NULL) if (new->cl_metrics == NULL)
goto out_no_stats; goto out_no_stats;
if (clnt->cl_principal) {
new->cl_principal = kstrdup(clnt->cl_principal, GFP_KERNEL);
if (new->cl_principal == NULL)
goto out_no_principal;
}
kref_init(&new->cl_kref); kref_init(&new->cl_kref);
err = rpc_setup_pipedir(new, clnt->cl_program->pipe_dir_name); err = rpc_setup_pipedir(new, clnt->cl_program->pipe_dir_name);
if (err != 0) if (err != 0)
...@@ -366,6 +379,8 @@ rpc_clone_client(struct rpc_clnt *clnt) ...@@ -366,6 +379,8 @@ rpc_clone_client(struct rpc_clnt *clnt)
rpciod_up(); rpciod_up();
return new; return new;
out_no_path: out_no_path:
kfree(new->cl_principal);
out_no_principal:
rpc_free_iostats(new->cl_metrics); rpc_free_iostats(new->cl_metrics);
out_no_stats: out_no_stats:
kfree(new); kfree(new);
...@@ -417,6 +432,7 @@ rpc_free_client(struct kref *kref) ...@@ -417,6 +432,7 @@ rpc_free_client(struct kref *kref)
out_free: out_free:
rpc_unregister_client(clnt); rpc_unregister_client(clnt);
rpc_free_iostats(clnt->cl_metrics); rpc_free_iostats(clnt->cl_metrics);
kfree(clnt->cl_principal);
clnt->cl_metrics = NULL; clnt->cl_metrics = NULL;
xprt_put(clnt->cl_xprt); xprt_put(clnt->cl_xprt);
rpciod_down(); rpciod_down();
......
...@@ -113,7 +113,7 @@ rpc_queue_upcall(struct inode *inode, struct rpc_pipe_msg *msg) ...@@ -113,7 +113,7 @@ rpc_queue_upcall(struct inode *inode, struct rpc_pipe_msg *msg)
wake_up(&rpci->waitq); wake_up(&rpci->waitq);
return res; return res;
} }
EXPORT_SYMBOL(rpc_queue_upcall); EXPORT_SYMBOL_GPL(rpc_queue_upcall);
static inline void static inline void
rpc_inode_setowner(struct inode *inode, void *private) rpc_inode_setowner(struct inode *inode, void *private)
...@@ -126,13 +126,14 @@ rpc_close_pipes(struct inode *inode) ...@@ -126,13 +126,14 @@ rpc_close_pipes(struct inode *inode)
{ {
struct rpc_inode *rpci = RPC_I(inode); struct rpc_inode *rpci = RPC_I(inode);
struct rpc_pipe_ops *ops; struct rpc_pipe_ops *ops;
int need_release;
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
ops = rpci->ops; ops = rpci->ops;
if (ops != NULL) { if (ops != NULL) {
LIST_HEAD(free_list); LIST_HEAD(free_list);
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
need_release = rpci->nreaders != 0 || rpci->nwriters != 0;
rpci->nreaders = 0; rpci->nreaders = 0;
list_splice_init(&rpci->in_upcall, &free_list); list_splice_init(&rpci->in_upcall, &free_list);
list_splice_init(&rpci->pipe, &free_list); list_splice_init(&rpci->pipe, &free_list);
...@@ -141,7 +142,7 @@ rpc_close_pipes(struct inode *inode) ...@@ -141,7 +142,7 @@ rpc_close_pipes(struct inode *inode)
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
rpc_purge_list(rpci, &free_list, ops->destroy_msg, -EPIPE); rpc_purge_list(rpci, &free_list, ops->destroy_msg, -EPIPE);
rpci->nwriters = 0; rpci->nwriters = 0;
if (ops->release_pipe) if (need_release && ops->release_pipe)
ops->release_pipe(inode); ops->release_pipe(inode);
cancel_delayed_work_sync(&rpci->queue_timeout); cancel_delayed_work_sync(&rpci->queue_timeout);
} }
...@@ -169,16 +170,24 @@ static int ...@@ -169,16 +170,24 @@ static int
rpc_pipe_open(struct inode *inode, struct file *filp) rpc_pipe_open(struct inode *inode, struct file *filp)
{ {
struct rpc_inode *rpci = RPC_I(inode); struct rpc_inode *rpci = RPC_I(inode);
int first_open;
int res = -ENXIO; int res = -ENXIO;
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
if (rpci->ops != NULL) { if (rpci->ops == NULL)
goto out;
first_open = rpci->nreaders == 0 && rpci->nwriters == 0;
if (first_open && rpci->ops->open_pipe) {
res = rpci->ops->open_pipe(inode);
if (res)
goto out;
}
if (filp->f_mode & FMODE_READ) if (filp->f_mode & FMODE_READ)
rpci->nreaders ++; rpci->nreaders++;
if (filp->f_mode & FMODE_WRITE) if (filp->f_mode & FMODE_WRITE)
rpci->nwriters ++; rpci->nwriters++;
res = 0; res = 0;
} out:
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
return res; return res;
} }
...@@ -188,6 +197,7 @@ rpc_pipe_release(struct inode *inode, struct file *filp) ...@@ -188,6 +197,7 @@ rpc_pipe_release(struct inode *inode, struct file *filp)
{ {
struct rpc_inode *rpci = RPC_I(inode); struct rpc_inode *rpci = RPC_I(inode);
struct rpc_pipe_msg *msg; struct rpc_pipe_msg *msg;
int last_close;
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
if (rpci->ops == NULL) if (rpci->ops == NULL)
...@@ -214,7 +224,8 @@ rpc_pipe_release(struct inode *inode, struct file *filp) ...@@ -214,7 +224,8 @@ rpc_pipe_release(struct inode *inode, struct file *filp)
rpci->ops->destroy_msg, -EAGAIN); rpci->ops->destroy_msg, -EAGAIN);
} }
} }
if (rpci->ops->release_pipe) last_close = rpci->nwriters == 0 && rpci->nreaders == 0;
if (last_close && rpci->ops->release_pipe)
rpci->ops->release_pipe(inode); rpci->ops->release_pipe(inode);
out: out:
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
...@@ -396,6 +407,7 @@ enum { ...@@ -396,6 +407,7 @@ enum {
RPCAUTH_nfs, RPCAUTH_nfs,
RPCAUTH_portmap, RPCAUTH_portmap,
RPCAUTH_statd, RPCAUTH_statd,
RPCAUTH_nfsd4_cb,
RPCAUTH_RootEOF RPCAUTH_RootEOF
}; };
...@@ -429,6 +441,10 @@ static struct rpc_filelist files[] = { ...@@ -429,6 +441,10 @@ static struct rpc_filelist files[] = {
.name = "statd", .name = "statd",
.mode = S_IFDIR | S_IRUGO | S_IXUGO, .mode = S_IFDIR | S_IRUGO | S_IXUGO,
}, },
[RPCAUTH_nfsd4_cb] = {
.name = "nfsd4_cb",
.mode = S_IFDIR | S_IRUGO | S_IXUGO,
},
}; };
enum { enum {
...@@ -748,7 +764,7 @@ rpc_rmdir(struct dentry *dentry) ...@@ -748,7 +764,7 @@ rpc_rmdir(struct dentry *dentry)
* @name: name of pipe * @name: name of pipe
* @private: private data to associate with the pipe, for the caller's use * @private: private data to associate with the pipe, for the caller's use
* @ops: operations defining the behavior of the pipe: upcall, downcall, * @ops: operations defining the behavior of the pipe: upcall, downcall,
* release_pipe, and destroy_msg. * release_pipe, open_pipe, and destroy_msg.
* @flags: rpc_inode flags * @flags: rpc_inode flags
* *
* Data is made available for userspace to read by calls to * Data is made available for userspace to read by calls to
...@@ -808,7 +824,7 @@ rpc_mkpipe(struct dentry *parent, const char *name, void *private, struct rpc_pi ...@@ -808,7 +824,7 @@ rpc_mkpipe(struct dentry *parent, const char *name, void *private, struct rpc_pi
-ENOMEM); -ENOMEM);
goto out; goto out;
} }
EXPORT_SYMBOL(rpc_mkpipe); EXPORT_SYMBOL_GPL(rpc_mkpipe);
/** /**
* rpc_unlink - remove a pipe * rpc_unlink - remove a pipe
...@@ -839,7 +855,7 @@ rpc_unlink(struct dentry *dentry) ...@@ -839,7 +855,7 @@ rpc_unlink(struct dentry *dentry)
dput(parent); dput(parent);
return error; return error;
} }
EXPORT_SYMBOL(rpc_unlink); EXPORT_SYMBOL_GPL(rpc_unlink);
/* /*
* populate the filesystem * populate the filesystem
......
...@@ -28,7 +28,7 @@ xdr_encode_netobj(__be32 *p, const struct xdr_netobj *obj) ...@@ -28,7 +28,7 @@ xdr_encode_netobj(__be32 *p, const struct xdr_netobj *obj)
memcpy(p, obj->data, obj->len); memcpy(p, obj->data, obj->len);
return p + XDR_QUADLEN(obj->len); return p + XDR_QUADLEN(obj->len);
} }
EXPORT_SYMBOL(xdr_encode_netobj); EXPORT_SYMBOL_GPL(xdr_encode_netobj);
__be32 * __be32 *
xdr_decode_netobj(__be32 *p, struct xdr_netobj *obj) xdr_decode_netobj(__be32 *p, struct xdr_netobj *obj)
...@@ -41,7 +41,7 @@ xdr_decode_netobj(__be32 *p, struct xdr_netobj *obj) ...@@ -41,7 +41,7 @@ xdr_decode_netobj(__be32 *p, struct xdr_netobj *obj)
obj->data = (u8 *) p; obj->data = (u8 *) p;
return p + XDR_QUADLEN(len); return p + XDR_QUADLEN(len);
} }
EXPORT_SYMBOL(xdr_decode_netobj); EXPORT_SYMBOL_GPL(xdr_decode_netobj);
/** /**
* xdr_encode_opaque_fixed - Encode fixed length opaque data * xdr_encode_opaque_fixed - Encode fixed length opaque data
...@@ -71,7 +71,7 @@ __be32 *xdr_encode_opaque_fixed(__be32 *p, const void *ptr, unsigned int nbytes) ...@@ -71,7 +71,7 @@ __be32 *xdr_encode_opaque_fixed(__be32 *p, const void *ptr, unsigned int nbytes)
} }
return p; return p;
} }
EXPORT_SYMBOL(xdr_encode_opaque_fixed); EXPORT_SYMBOL_GPL(xdr_encode_opaque_fixed);
/** /**
* xdr_encode_opaque - Encode variable length opaque data * xdr_encode_opaque - Encode variable length opaque data
...@@ -86,14 +86,14 @@ __be32 *xdr_encode_opaque(__be32 *p, const void *ptr, unsigned int nbytes) ...@@ -86,14 +86,14 @@ __be32 *xdr_encode_opaque(__be32 *p, const void *ptr, unsigned int nbytes)
*p++ = htonl(nbytes); *p++ = htonl(nbytes);
return xdr_encode_opaque_fixed(p, ptr, nbytes); return xdr_encode_opaque_fixed(p, ptr, nbytes);
} }
EXPORT_SYMBOL(xdr_encode_opaque); EXPORT_SYMBOL_GPL(xdr_encode_opaque);
__be32 * __be32 *
xdr_encode_string(__be32 *p, const char *string) xdr_encode_string(__be32 *p, const char *string)
{ {
return xdr_encode_array(p, string, strlen(string)); return xdr_encode_array(p, string, strlen(string));
} }
EXPORT_SYMBOL(xdr_encode_string); EXPORT_SYMBOL_GPL(xdr_encode_string);
__be32 * __be32 *
xdr_decode_string_inplace(__be32 *p, char **sp, xdr_decode_string_inplace(__be32 *p, char **sp,
...@@ -108,7 +108,7 @@ xdr_decode_string_inplace(__be32 *p, char **sp, ...@@ -108,7 +108,7 @@ xdr_decode_string_inplace(__be32 *p, char **sp,
*sp = (char *) p; *sp = (char *) p;
return p + XDR_QUADLEN(len); return p + XDR_QUADLEN(len);
} }
EXPORT_SYMBOL(xdr_decode_string_inplace); EXPORT_SYMBOL_GPL(xdr_decode_string_inplace);
void void
xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base, xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base,
...@@ -136,7 +136,7 @@ xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base, ...@@ -136,7 +136,7 @@ xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base,
xdr->buflen += len; xdr->buflen += len;
xdr->len += len; xdr->len += len;
} }
EXPORT_SYMBOL(xdr_encode_pages); EXPORT_SYMBOL_GPL(xdr_encode_pages);
void void
xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset, xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset,
...@@ -158,7 +158,7 @@ xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset, ...@@ -158,7 +158,7 @@ xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset,
xdr->buflen += len; xdr->buflen += len;
} }
EXPORT_SYMBOL(xdr_inline_pages); EXPORT_SYMBOL_GPL(xdr_inline_pages);
/* /*
* Helper routines for doing 'memmove' like operations on a struct xdr_buf * Helper routines for doing 'memmove' like operations on a struct xdr_buf
...@@ -428,7 +428,7 @@ xdr_shift_buf(struct xdr_buf *buf, size_t len) ...@@ -428,7 +428,7 @@ xdr_shift_buf(struct xdr_buf *buf, size_t len)
{ {
xdr_shrink_bufhead(buf, len); xdr_shrink_bufhead(buf, len);
} }
EXPORT_SYMBOL(xdr_shift_buf); EXPORT_SYMBOL_GPL(xdr_shift_buf);
/** /**
* xdr_init_encode - Initialize a struct xdr_stream for sending data. * xdr_init_encode - Initialize a struct xdr_stream for sending data.
...@@ -465,7 +465,7 @@ void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p) ...@@ -465,7 +465,7 @@ void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p)
iov->iov_len += len; iov->iov_len += len;
} }
} }
EXPORT_SYMBOL(xdr_init_encode); EXPORT_SYMBOL_GPL(xdr_init_encode);
/** /**
* xdr_reserve_space - Reserve buffer space for sending * xdr_reserve_space - Reserve buffer space for sending
...@@ -492,7 +492,7 @@ __be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes) ...@@ -492,7 +492,7 @@ __be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes)
xdr->buf->len += nbytes; xdr->buf->len += nbytes;
return p; return p;
} }
EXPORT_SYMBOL(xdr_reserve_space); EXPORT_SYMBOL_GPL(xdr_reserve_space);
/** /**
* xdr_write_pages - Insert a list of pages into an XDR buffer for sending * xdr_write_pages - Insert a list of pages into an XDR buffer for sending
...@@ -527,7 +527,7 @@ void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int b ...@@ -527,7 +527,7 @@ void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int b
buf->buflen += len; buf->buflen += len;
buf->len += len; buf->len += len;
} }
EXPORT_SYMBOL(xdr_write_pages); EXPORT_SYMBOL_GPL(xdr_write_pages);
/** /**
* xdr_init_decode - Initialize an xdr_stream for decoding data. * xdr_init_decode - Initialize an xdr_stream for decoding data.
...@@ -547,7 +547,7 @@ void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p) ...@@ -547,7 +547,7 @@ void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p)
xdr->p = p; xdr->p = p;
xdr->end = (__be32 *)((char *)iov->iov_base + len); xdr->end = (__be32 *)((char *)iov->iov_base + len);
} }
EXPORT_SYMBOL(xdr_init_decode); EXPORT_SYMBOL_GPL(xdr_init_decode);
/** /**
* xdr_inline_decode - Retrieve non-page XDR data to decode * xdr_inline_decode - Retrieve non-page XDR data to decode
...@@ -569,7 +569,7 @@ __be32 * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) ...@@ -569,7 +569,7 @@ __be32 * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
xdr->p = q; xdr->p = q;
return p; return p;
} }
EXPORT_SYMBOL(xdr_inline_decode); EXPORT_SYMBOL_GPL(xdr_inline_decode);
/** /**
* xdr_read_pages - Ensure page-based XDR data to decode is aligned at current pointer position * xdr_read_pages - Ensure page-based XDR data to decode is aligned at current pointer position
...@@ -613,7 +613,7 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) ...@@ -613,7 +613,7 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len)
xdr->p = (__be32 *)((char *)iov->iov_base + padding); xdr->p = (__be32 *)((char *)iov->iov_base + padding);
xdr->end = (__be32 *)((char *)iov->iov_base + end); xdr->end = (__be32 *)((char *)iov->iov_base + end);
} }
EXPORT_SYMBOL(xdr_read_pages); EXPORT_SYMBOL_GPL(xdr_read_pages);
/** /**
* xdr_enter_page - decode data from the XDR page * xdr_enter_page - decode data from the XDR page
...@@ -638,7 +638,7 @@ void xdr_enter_page(struct xdr_stream *xdr, unsigned int len) ...@@ -638,7 +638,7 @@ void xdr_enter_page(struct xdr_stream *xdr, unsigned int len)
xdr->p = (__be32 *)(kaddr + xdr->buf->page_base); xdr->p = (__be32 *)(kaddr + xdr->buf->page_base);
xdr->end = (__be32 *)((char *)xdr->p + len); xdr->end = (__be32 *)((char *)xdr->p + len);
} }
EXPORT_SYMBOL(xdr_enter_page); EXPORT_SYMBOL_GPL(xdr_enter_page);
static struct kvec empty_iov = {.iov_base = NULL, .iov_len = 0}; static struct kvec empty_iov = {.iov_base = NULL, .iov_len = 0};
...@@ -650,7 +650,7 @@ xdr_buf_from_iov(struct kvec *iov, struct xdr_buf *buf) ...@@ -650,7 +650,7 @@ xdr_buf_from_iov(struct kvec *iov, struct xdr_buf *buf)
buf->page_len = 0; buf->page_len = 0;
buf->buflen = buf->len = iov->iov_len; buf->buflen = buf->len = iov->iov_len;
} }
EXPORT_SYMBOL(xdr_buf_from_iov); EXPORT_SYMBOL_GPL(xdr_buf_from_iov);
/* Sets subbuf to the portion of buf of length len beginning base bytes /* Sets subbuf to the portion of buf of length len beginning base bytes
* from the start of buf. Returns -1 if base of length are out of bounds. */ * from the start of buf. Returns -1 if base of length are out of bounds. */
...@@ -699,7 +699,7 @@ xdr_buf_subsegment(struct xdr_buf *buf, struct xdr_buf *subbuf, ...@@ -699,7 +699,7 @@ xdr_buf_subsegment(struct xdr_buf *buf, struct xdr_buf *subbuf,
return -1; return -1;
return 0; return 0;
} }
EXPORT_SYMBOL(xdr_buf_subsegment); EXPORT_SYMBOL_GPL(xdr_buf_subsegment);
static void __read_bytes_from_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned int len) static void __read_bytes_from_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned int len)
{ {
...@@ -730,7 +730,7 @@ int read_bytes_from_xdr_buf(struct xdr_buf *buf, unsigned int base, void *obj, u ...@@ -730,7 +730,7 @@ int read_bytes_from_xdr_buf(struct xdr_buf *buf, unsigned int base, void *obj, u
__read_bytes_from_xdr_buf(&subbuf, obj, len); __read_bytes_from_xdr_buf(&subbuf, obj, len);
return 0; return 0;
} }
EXPORT_SYMBOL(read_bytes_from_xdr_buf); EXPORT_SYMBOL_GPL(read_bytes_from_xdr_buf);
static void __write_bytes_to_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned int len) static void __write_bytes_to_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned int len)
{ {
...@@ -774,7 +774,7 @@ xdr_decode_word(struct xdr_buf *buf, unsigned int base, u32 *obj) ...@@ -774,7 +774,7 @@ xdr_decode_word(struct xdr_buf *buf, unsigned int base, u32 *obj)
*obj = ntohl(raw); *obj = ntohl(raw);
return 0; return 0;
} }
EXPORT_SYMBOL(xdr_decode_word); EXPORT_SYMBOL_GPL(xdr_decode_word);
int int
xdr_encode_word(struct xdr_buf *buf, unsigned int base, u32 obj) xdr_encode_word(struct xdr_buf *buf, unsigned int base, u32 obj)
...@@ -783,7 +783,7 @@ xdr_encode_word(struct xdr_buf *buf, unsigned int base, u32 obj) ...@@ -783,7 +783,7 @@ xdr_encode_word(struct xdr_buf *buf, unsigned int base, u32 obj)
return write_bytes_to_xdr_buf(buf, base, &raw, sizeof(obj)); return write_bytes_to_xdr_buf(buf, base, &raw, sizeof(obj));
} }
EXPORT_SYMBOL(xdr_encode_word); EXPORT_SYMBOL_GPL(xdr_encode_word);
/* If the netobj starting offset bytes from the start of xdr_buf is contained /* If the netobj starting offset bytes from the start of xdr_buf is contained
* entirely in the head or the tail, set object to point to it; otherwise * entirely in the head or the tail, set object to point to it; otherwise
...@@ -821,7 +821,7 @@ int xdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, unsigned in ...@@ -821,7 +821,7 @@ int xdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, unsigned in
__read_bytes_from_xdr_buf(&subbuf, obj->data, obj->len); __read_bytes_from_xdr_buf(&subbuf, obj->data, obj->len);
return 0; return 0;
} }
EXPORT_SYMBOL(xdr_buf_read_netobj); EXPORT_SYMBOL_GPL(xdr_buf_read_netobj);
/* Returns 0 on success, or else a negative error code. */ /* Returns 0 on success, or else a negative error code. */
static int static int
...@@ -1027,7 +1027,7 @@ xdr_decode_array2(struct xdr_buf *buf, unsigned int base, ...@@ -1027,7 +1027,7 @@ xdr_decode_array2(struct xdr_buf *buf, unsigned int base,
return xdr_xcode_array2(buf, base, desc, 0); return xdr_xcode_array2(buf, base, desc, 0);
} }
EXPORT_SYMBOL(xdr_decode_array2); EXPORT_SYMBOL_GPL(xdr_decode_array2);
int int
xdr_encode_array2(struct xdr_buf *buf, unsigned int base, xdr_encode_array2(struct xdr_buf *buf, unsigned int base,
...@@ -1039,7 +1039,7 @@ xdr_encode_array2(struct xdr_buf *buf, unsigned int base, ...@@ -1039,7 +1039,7 @@ xdr_encode_array2(struct xdr_buf *buf, unsigned int base,
return xdr_xcode_array2(buf, base, desc, 1); return xdr_xcode_array2(buf, base, desc, 1);
} }
EXPORT_SYMBOL(xdr_encode_array2); EXPORT_SYMBOL_GPL(xdr_encode_array2);
int int
xdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len, xdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len,
...@@ -1106,5 +1106,5 @@ xdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len, ...@@ -1106,5 +1106,5 @@ xdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len,
out: out:
return ret; return ret;
} }
EXPORT_SYMBOL(xdr_process_buf); EXPORT_SYMBOL_GPL(xdr_process_buf);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment