summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2008-12-30 17:45:45 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2008-12-30 17:45:45 -0800
commitf57fa1d6a6b3414e853d3d17e339ac48816e4406 (patch)
treee1d3acdb12f902e916765915a4f9a65cbae909cc /fs
parent6094c85a935f7eadb4c607c6dc6d86c0a9f09a4b (diff)
parent08cc36cbd1ee7d86422713bb21551eed1326b894 (diff)
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 ...
Diffstat (limited to 'fs')
-rw-r--r--fs/lockd/clntlock.c23
-rw-r--r--fs/lockd/host.c10
-rw-r--r--fs/lockd/svc.c6
-rw-r--r--fs/nfs/callback.c36
-rw-r--r--fs/nfs/client.c95
-rw-r--r--fs/nfs/delegation.c260
-rw-r--r--fs/nfs/delegation.h33
-rw-r--r--fs/nfs/dir.c24
-rw-r--r--fs/nfs/inode.c13
-rw-r--r--fs/nfs/internal.h14
-rw-r--r--fs/nfs/mount_clnt.c34
-rw-r--r--fs/nfs/nfs4_fs.h32
-rw-r--r--fs/nfs/nfs4proc.c431
-rw-r--r--fs/nfs/nfs4renewd.c22
-rw-r--r--fs/nfs/nfs4state.c415
-rw-r--r--fs/nfs/nfs4xdr.c1235
-rw-r--r--fs/nfs/nfsroot.c27
-rw-r--r--fs/nfs/read.c6
-rw-r--r--fs/nfs/super.c44
-rw-r--r--fs/nfs_common/nfsacl.c4
-rw-r--r--fs/nfsd/nfs4callback.c9
-rw-r--r--fs/nfsd/nfs4state.c12
22 files changed, 1492 insertions, 1293 deletions
diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c
index 8307dd64bf46..1f3b0fc0d351 100644
--- a/fs/lockd/clntlock.c
+++ b/fs/lockd/clntlock.c
@@ -14,6 +14,7 @@
#include <linux/sunrpc/svc.h>
#include <linux/lockd/lockd.h>
#include <linux/smp_lock.h>
+#include <linux/kthread.h>
#define NLMDBG_FACILITY NLMDBG_CLIENT
@@ -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,
nlm_init->protocol, nlm_version,
- nlm_init->hostname);
+ nlm_init->hostname, nlm_init->noresvport);
if (host == NULL) {
lockd_down();
return ERR_PTR(-ENOLCK);
@@ -191,11 +192,15 @@ __be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock)
void
nlmclnt_recovery(struct nlm_host *host)
{
+ struct task_struct *task;
+
if (!host->h_reclaiming++) {
nlm_get_host(host);
- __module_get(THIS_MODULE);
- if (kernel_thread(reclaimer, host, CLONE_FS | CLONE_FILES) < 0)
- module_put(THIS_MODULE);
+ task = kthread_run(reclaimer, host, "%s-reclaim", host->h_name);
+ if (IS_ERR(task))
+ 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)
struct file_lock *fl, *next;
u32 nsmstate;
- daemonize("%s-reclaim", host->h_name);
allow_signal(SIGKILL);
down_write(&host->h_rwsem);
@@ -233,7 +237,12 @@ restart:
list_for_each_entry_safe(fl, next, &host->h_reclaim, 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())
continue;
if (nlmclnt_reclaim(host, fl) != 0)
@@ -261,5 +270,5 @@ restart:
nlm_release_host(host);
lockd_down();
unlock_kernel();
- module_put_and_exit(0);
+ return 0;
}
diff --git a/fs/lockd/host.c b/fs/lockd/host.c
index e05d04416037..abdebf76b820 100644
--- a/fs/lockd/host.c
+++ b/fs/lockd/host.c
@@ -48,6 +48,7 @@ struct nlm_lookup_host_info {
const size_t hostname_len; /* it's length */
const struct sockaddr *src_sap; /* our address (optional) */
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)
host->h_nsmstate = 0; /* real NSM state */
host->h_nsmhandle = nsm;
host->h_server = ni->server;
+ host->h_noresvport = ni->noresvport;
hlist_add_head(&host->h_hash, chain);
INIT_LIST_HEAD(&host->h_lockowners);
spin_lock_init(&host->h_lock);
@@ -272,6 +274,7 @@ nlm_destroy_host(struct nlm_host *host)
* @protocol: transport protocol to use
* @version: NLM protocol version
* @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
* [server address, transport protocol, NLM version, server hostname].
@@ -281,7 +284,9 @@ nlm_destroy_host(struct nlm_host *host)
struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
const size_t salen,
const unsigned short protocol,
- const u32 version, const char *hostname)
+ const u32 version,
+ const char *hostname,
+ int noresvport)
{
const struct sockaddr source = {
.sa_family = AF_UNSPEC,
@@ -296,6 +301,7 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
.hostname_len = strlen(hostname),
.src_sap = &source,
.src_len = sizeof(source),
+ .noresvport = noresvport,
};
dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__,
@@ -417,6 +423,8 @@ nlm_bind_host(struct nlm_host *host)
*/
if (!host->h_server)
args.flags |= RPC_CLNT_CREATE_HARDRTRY;
+ if (host->h_noresvport)
+ args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
clnt = rpc_create(&args);
if (!IS_ERR(clnt))
diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
index 56b076736b56..252d80163d02 100644
--- a/fs/lockd/svc.c
+++ b/fs/lockd/svc.c
@@ -45,7 +45,7 @@
static struct svc_program nlmsvc_program;
struct nlmsvc_binding * nlmsvc_ops;
-EXPORT_SYMBOL(nlmsvc_ops);
+EXPORT_SYMBOL_GPL(nlmsvc_ops);
static DEFINE_MUTEX(nlmsvc_mutex);
static unsigned int nlmsvc_users;
@@ -300,7 +300,7 @@ out:
mutex_unlock(&nlmsvc_mutex);
return error;
}
-EXPORT_SYMBOL(lockd_up);
+EXPORT_SYMBOL_GPL(lockd_up);
/*
* Decrement the user count and bring down lockd if we're the last.
@@ -329,7 +329,7 @@ lockd_down(void)
out:
mutex_unlock(&nlmsvc_mutex);
}
-EXPORT_SYMBOL(lockd_down);
+EXPORT_SYMBOL_GPL(lockd_down);
#ifdef CONFIG_SYSCTL
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
index c2e9cfd9e5a4..3e634f2a1083 100644
--- a/fs/nfs/callback.c
+++ b/fs/nfs/callback.c
@@ -16,6 +16,7 @@
#include <linux/mutex.h>
#include <linux/freezer.h>
#include <linux/kthread.h>
+#include <linux/sunrpc/svcauth_gss.h>
#include <net/inet_sock.h>
@@ -182,10 +183,34 @@ void nfs_callback_down(void)
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)
{
struct nfs_client *clp;
RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
+ int ret = SVC_OK;
/* Don't talk to strangers */
clp = nfs_find_client(svc_addr(rqstp), 4);
@@ -194,21 +219,22 @@ static int nfs_callback_authenticate(struct svc_rqst *rqstp)
dprintk("%s: %s NFSv4 callback!\n", __func__,
svc_print_addr(rqstp, buf, sizeof(buf)));
- nfs_put_client(clp);
switch (rqstp->rq_authop->flavour) {
case RPC_AUTH_NULL:
if (rqstp->rq_proc != CB_NULL)
- return SVC_DENIED;
+ ret = SVC_DENIED;
break;
case RPC_AUTH_UNIX:
break;
case RPC_AUTH_GSS:
- /* FIXME: RPCSEC_GSS handling? */
+ ret = check_gss_callback_principal(clp, rqstp);
+ break;
default:
- return SVC_DENIED;
+ ret = SVC_DENIED;
}
- return SVC_OK;
+ nfs_put_client(clp);
+ return ret;
}
/*
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 7547600b6174..9b728f3565a1 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -143,7 +143,6 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
clp->cl_proto = cl_init->proto;
#ifdef CONFIG_NFS_V4
- init_rwsem(&clp->cl_sem);
INIT_LIST_HEAD(&clp->cl_delegations);
spin_lock_init(&clp->cl_lock);
INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state);
@@ -224,31 +223,54 @@ void nfs_put_client(struct nfs_client *clp)
}
}
-static int nfs_sockaddr_match_ipaddr4(const struct sockaddr_in *sa1,
- const struct sockaddr_in *sa2)
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+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,
- const struct sockaddr_in6 *sa2)
+static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1,
+ 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,
const struct sockaddr *sa2)
{
- switch (sa1->sa_family) {
- case AF_INET:
- return nfs_sockaddr_match_ipaddr4((const struct sockaddr_in *)sa1,
- (const struct sockaddr_in *)sa2);
- case AF_INET6:
- return nfs_sockaddr_match_ipaddr6((const struct sockaddr_in6 *)sa1,
- (const struct sockaddr_in6 *)sa2);
- }
- BUG();
+ if (unlikely(sa1->sa_family != AF_INET || sa2->sa_family != AF_INET))
+ return 0;
+ return nfs_sockaddr_match_ipaddr4((const struct sockaddr_in *)sa1,
+ (const struct sockaddr_in *)sa2);
}
+#endif
/*
* 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)
if (clp->rpc_ops->version != nfsversion)
continue;
- if (addr->sa_family != clap->sa_family)
- continue;
/* Match only the IP address, not the port number */
if (!nfs_sockaddr_match_ipaddr(addr, clap))
continue;
@@ -305,8 +325,6 @@ struct nfs_client *nfs_find_client_next(struct nfs_client *clp)
if (clp->rpc_ops->version != nfsvers)
continue;
- if (sap->sa_family != clap->sa_family)
- continue;
/* Match only the IP address, not the port number */
if (!nfs_sockaddr_match_ipaddr(sap, clap))
continue;
@@ -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,
const struct rpc_timeout *timeparms,
rpc_authflavor_t flavor,
- int flags)
+ int discrtry, int noresvport)
{
struct rpc_clnt *clnt = NULL;
struct rpc_create_args args = {
@@ -482,9 +500,13 @@ static int nfs_create_rpc_client(struct nfs_client *clp,
.program = &nfs_program,
.version = clp->rpc_ops->version,
.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))
return 0;
@@ -522,6 +544,8 @@ static int nfs_start_lockd(struct nfs_server *server)
.protocol = server->flags & NFS_MOUNT_TCP ?
IPPROTO_TCP : IPPROTO_UDP,
.nfs_version = clp->rpc_ops->version,
+ .noresvport = server->flags & NFS_MOUNT_NORESVPORT ?
+ 1 : 0,
};
if (nlm_init.nfs_version > 3)
@@ -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
* - 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)
goto error;
nfs_mark_client_ready(clp, NFS_CS_READY);
@@ -965,7 +990,8 @@ error:
static int nfs4_init_client(struct nfs_client *clp,
const struct rpc_timeout *timeparms,
const char *ip_addr,
- rpc_authflavor_t authflavour)
+ rpc_authflavor_t authflavour,
+ int flags)
{
int error;
@@ -979,7 +1005,7 @@ static int nfs4_init_client(struct nfs_client *clp,
clp->rpc_ops = &nfs_v4_clientops;
error = nfs_create_rpc_client(clp, timeparms, authflavour,
- RPC_CLNT_CREATE_DISCRTRY);
+ 1, flags & NFS_MOUNT_NORESVPORT);
if (error < 0)
goto error;
memcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr));
@@ -1030,7 +1056,8 @@ static int nfs4_set_client(struct nfs_server *server,
error = PTR_ERR(clp);
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)
goto error_put;
@@ -1059,6 +1086,10 @@ static int nfs4_init_server(struct nfs_server *server,
nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
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 */
error = nfs4_set_client(server,
data->nfs_server.hostname,
@@ -1071,10 +1102,6 @@ static int nfs4_init_server(struct nfs_server *server,
if (error < 0)
goto error;
- /* Initialise the client representation from the mount data */
- server->flags = data->flags;
- server->caps |= NFS_CAP_ATOMIC_OPEN;
-
if (data->rsize)
server->rsize = nfs_block_size(data->rsize, NULL);
if (data->wsize)
@@ -1177,6 +1204,10 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
parent_server = NFS_SB(data->sb);
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.
* Note: NFSv4 always uses TCP, */
error = nfs4_set_client(server, data->hostname,
@@ -1189,10 +1220,6 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
if (error < 0)
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);
if (error < 0)
goto error;
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index cc563cfa6940..968225a88015 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -43,6 +43,27 @@ static void nfs_free_delegation(struct nfs_delegation *delegation)
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)
{
struct inode *inode = state->inode;
@@ -119,7 +140,7 @@ void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, st
delegation->maxsize = res->maxsize;
oldcred = delegation->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;
smp_wmb();
put_rpccred(oldcred);
@@ -134,19 +155,35 @@ static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *
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)
{
struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation);
if (delegation == NULL)
goto nomatch;
+ spin_lock(&delegation->lock);
if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data,
sizeof(delegation->stateid.data)) != 0)
- goto nomatch;
+ goto nomatch_unlock;
list_del_rcu(&delegation->super_list);
+ delegation->inode = NULL;
nfsi->delegation_state = 0;
rcu_assign_pointer(nfsi->delegation, NULL);
+ spin_unlock(&delegation->lock);
return delegation;
+nomatch_unlock:
+ spin_unlock(&delegation->lock);
nomatch:
return NULL;
}
@@ -172,6 +209,8 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
delegation->change_attr = nfsi->change_attr;
delegation->cred = get_rpccred(cred);
delegation->inode = inode;
+ delegation->flags = 1<<NFS_DELEGATION_REFERENCED;
+ spin_lock_init(&delegation->lock);
spin_lock(&clp->cl_lock);
if (rcu_dereference(nfsi->delegation) != NULL) {
@@ -226,22 +265,47 @@ static void nfs_msync_inode(struct inode *inode)
*/
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);
nfs_msync_inode(inode);
- down_read(&clp->cl_sem);
/* Guard against new delegated open calls */
down_write(&nfsi->rwsem);
nfs_delegation_claim_opens(inode, &delegation->stateid);
up_write(&nfsi->rwsem);
- up_read(&clp->cl_sem);
nfs_msync_inode(inode);
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
* or protecting against delegation reclaims.
* It is therefore really only safe to be called from
@@ -279,83 +343,55 @@ int nfs_inode_return_delegation(struct inode *inode)
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
*/
-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_delegation *delegation;
- struct inode *inode;
if (clp == NULL)
return;
-restart:
rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
- if (delegation->inode->i_sb != sb)
- continue;
- inode = igrab(delegation->inode);
- 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;
+ spin_lock(&delegation->lock);
+ if (delegation->inode != NULL && delegation->inode->i_sb == sb)
+ set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
+ spin_unlock(&delegation->lock);
}
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 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();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
- inode = igrab(delegation->inode);
- 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)
- __nfs_inode_return_delegation(inode, delegation);
- iput(inode);
- goto restart;
+ set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
+ set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
}
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)
{
- struct task_struct *task;
-
- __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);
+ nfs_client_mark_return_all_delegations(clp);
+ nfs_delegation_run_state_manager(clp);
}
/*
@@ -363,68 +399,29 @@ void nfs_expire_all_delegations(struct nfs_client *clp)
*/
void nfs_handle_cb_pathdown(struct nfs_client *clp)
{
- struct nfs_delegation *delegation;
- struct inode *inode;
-
if (clp == NULL)
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();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
- inode = igrab(delegation->inode);
- if (inode == NULL)
+ if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
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;
+ set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
+ set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
}
rcu_read_unlock();
}
-struct recall_threadargs {
- struct inode *inode;
- struct nfs_client *clp;
- const nfs4_stateid *stateid;
-
- struct completion started;
- int result;
-};
-
-static int recall_thread(void *data)
+void nfs_expire_unreferenced_delegations(struct nfs_client *clp)
{
- struct recall_threadargs *args = (struct recall_threadargs *)data;
- struct inode *inode = igrab(args->inode);
- 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);
+ nfs_client_mark_return_unreferenced_delegations(clp);
+ nfs_delegation_run_state_manager(clp);
}
/*
@@ -432,22 +429,20 @@ static int recall_thread(void *data)
*/
int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid)
{
- struct recall_threadargs data = {
- .inode = inode,
- .stateid = stateid,
- };
- int status;
+ struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
+ struct nfs_delegation *delegation;
- init_completion(&data.started);
- __module_get(THIS_MODULE);
- status = kernel_thread(recall_thread, &data, CLONE_KERNEL);
- if (status < 0)
- goto out_module_put;
- wait_for_completion(&data.started);
- return data.result;
-out_module_put:
- module_put(THIS_MODULE);
- return status;
+ rcu_read_lock();
+ delegation = rcu_dereference(NFS_I(inode)->delegation);
+ if (delegation == NULL || memcmp(delegation->stateid.data, stateid->data,
+ sizeof(delegation->stateid.data)) != 0) {
+ rcu_read_unlock();
+ return -ENOENT;
+ }
+ nfs_mark_return_delegation(clp, delegation);
+ rcu_read_unlock();
+ 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
struct inode *res = NULL;
rcu_read_lock();
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);
- break;
}
+ spin_unlock(&delegation->lock);
+ if (res != NULL)
+ break;
}
rcu_read_unlock();
return res;
@@ -476,7 +475,7 @@ void nfs_delegation_mark_reclaim(struct nfs_client *clp)
struct nfs_delegation *delegation;
rcu_read_lock();
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();
}
@@ -486,17 +485,22 @@ void nfs_delegation_mark_reclaim(struct nfs_client *clp)
void nfs_delegation_reap_unclaimed(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 ((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;
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);
rcu_read_unlock();
if (delegation != NULL)
nfs_free_delegation(delegation);
+ iput(inode);
goto restart;
}
rcu_read_unlock();
diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h
index f1c5e2a5d88e..09f383795174 100644
--- a/fs/nfs/delegation.h
+++ b/fs/nfs/delegation.h
@@ -17,14 +17,20 @@ struct nfs_delegation {
struct rpc_cred *cred;
struct inode *inode;
nfs4_stateid stateid;
- int type;
-#define NFS_DELEGATION_NEED_RECLAIM 1
- long flags;
+ fmode_t type;
loff_t maxsize;
__u64 change_attr;
+ unsigned long flags;
+ spinlock_t lock;
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);
void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
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
void nfs_inode_return_delegation_noreclaim(struct inode *inode);
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_unreferenced_delegations(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_reap_unclaimed(struct nfs_cl