提交 08cc36cb 编写于 作者: T Trond Myklebust

Merge branch 'devel' into next

...@@ -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);
......
此差异已折叠。
...@@ -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) {
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); if (list_empty(&clp->cl_delegations)) {
spin_unlock(&clp->cl_lock); set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
goto out;
}
nfs_expire_all_delegations(clp); nfs_expire_all_delegations(clp);
goto out; } else {
/* Queue an asynchronous RENEW. */
nfs4_proc_async_renew(clp, cred);
put_rpccred(cred);
} }
spin_unlock(&clp->cl_lock);
/* Queue an asynchronous RENEW. */
nfs4_proc_async_renew(clp, 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__);
}
nfs4_put_open_state(state);
goto restart;
} }
continue;
} }
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_lock_state *lock;
clear_bit(NFS_DELEGATED_STATE, &state->flags);
clear_bit(NFS_O_RDONLY_STATE, &state->flags);
clear_bit(NFS_O_WRONLY_STATE, &state->flags);
clear_bit(NFS_O_RDWR_STATE, &state->flags);
list_for_each_entry(lock, &state->lock_states, ls_locks) {
lock->ls_seqid.flags = 0;
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 nfs4_state_owner *sp;
struct rb_node *pos; struct rb_node *pos;
struct nfs4_state *state; struct nfs4_state *state;
struct nfs4_lock_state *lock;
/* Reset all sequence ids to zero */ /* Reset all sequence ids to zero */
for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) { 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 = rb_entry(pos, struct nfs4_state_owner, so_client_node);
sp->so_seqid.counter = 0;
sp->so_seqid.flags = 0; sp->so_seqid.flags = 0;
spin_lock(&sp->so_lock); 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) {
clear_bit(NFS_DELEGATED_STATE, &state->flags); if (mark_reclaim(clp, state))
clear_bit(NFS_O_RDONLY_STATE, &state->flags); nfs4_clear_open_state(state);
clear_bit(NFS_O_WRONLY_STATE, &state->flags);
clear_bit(NFS_O_RDWR_STATE, &state->flags);
list_for_each_entry(lock, &state->lock_states, ls_locks) {
lock->ls_seqid.counter = 0;
lock->ls_seqid.flags = 0;
lock->ls_flags &= ~NFS_LOCK_INITIALIZED;
}
} }
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;
if (!test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state))
return;
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);
spin_lock(&sp->so_lock);
list_for_each_entry(state, &sp->so_states, open_states) {
if (!test_and_clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags))
continue;
nfs4_state_mark_reclaim_nograce(clp, state);
}
spin_unlock(&sp->so_lock);
}
nfs_delegation_reap_unclaimed(clp);
}
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:
nfs_handle_cb_pathdown(clp);
break;
case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_LEASE_MOVED:
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);
}
}
static int nfs4_do_reclaim(struct nfs_client *clp, const struct nfs4_state_recovery_ops *ops)
{
struct rb_node *pos;
int status = 0; int status = 0;
allow_signal(SIGKILL); 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;
}
nfs4_put_state_owner(sp);
goto restart;
}
spin_unlock(&clp->cl_lock);
return status;
}
/* Ensure exclusive access to NFSv4 state */ static int nfs4_check_lease(struct nfs_client *clp)
down_write(&clp->cl_sem); {
/* Are there any NFS mounts out there? */ struct rpc_cred *cred;
if (list_empty(&clp->cl_superblocks)) int status = -NFS4ERR_EXPIRED;
goto out;
restart_loop: /* Is the client already known to have an expired lease? */
ops = &nfs4_network_partition_recovery_ops; if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
/* Are there any open files on this volume? */ return 0;
cred = nfs4_get_renew_cred(clp); cred = nfs4_get_renew_cred(clp);
if (cred != NULL) { if (cred == NULL) {
/* Yes there are: try to renew the old lease */ cred = nfs4_get_setclientid_cred(clp);
status = nfs4_proc_renew(clp, cred); if (cred == NULL)
put_rpccred(cred); goto out;
switch (status) {
case 0:
case -NFS4ERR_CB_PATH_DOWN:
goto out;
case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_LEASE_MOVED:
ops = &nfs4_reboot_recovery_ops;
}
} else {
/* "reboot" to ensure we clear all state on the server */
clp->cl_boot_time = CURRENT_TIME;
} }
/* We're going to have to re-establish a clientid */ status = nfs4_proc_renew(clp, cred);
nfs4_state_mark_reclaim(clp); put_rpccred(cred);
status = -ENOENT; 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;
goto out_error; }
/* Mark all delegations for reclaim */
nfs_delegation_mark_reclaim(clp); static void nfs4_state_manager(struct nfs_client *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)) { int status = 0;
sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
status = nfs4_reclaim_open_state(ops, sp); /* Ensure exclusive access to NFSv4 state */
if (status < 0) { for(;;) {
if (status == -NFS4ERR_NO_GRACE) { if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) {
ops = &nfs4_network_partition_recovery_ops; /* We're going to have to re-establish a clientid */
status = nfs4_reclaim_open_state(ops, sp); status = nfs4_reclaim_lease(clp);
if (status) {
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
if (status == -EAGAIN)
continue;
goto out_error;
} }
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;
if (status == -NFS4ERR_EXPIRED) nfs4_state_end_reclaim_reboot(clp);
goto restart_loop; 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)
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;
}
nfs4_clear_state_manager_bit(clp);
/* Did we race with an attempt to give us more work? */
if (clp->cl_state == 0)
break;
if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
break;
} }
nfs_delegation_reap_unclaimed(clp); return;
out: out_error:
up_write(&clp->cl_sem); printk(KERN_WARNING "Error: state manager failed on NFSv4 server %s"
if (status == -NFS4ERR_CB_PATH_DOWN) " with error %d\n", clp->cl_hostname, -status);
nfs_handle_cb_pathdown(clp); if (test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state))
nfs4_clear_recover_bit(clp); 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;
} }
/* /*
......
此差异已折叠。
...@@ -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);
......
此差异已折叠。
...@@ -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;
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
...@@ -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;
}; };
/* /*
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
...@@ -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 */
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册