2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* fs/nfs/nfs4state.c
|
|
|
|
*
|
|
|
|
* Client-side XDR for NFSv4.
|
|
|
|
*
|
|
|
|
* Copyright (c) 2002 The Regents of the University of Michigan.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Kendrick Smith <kmsmith@umich.edu>
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
*
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. Neither the name of the University nor the names of its
|
|
|
|
* contributors may be used to endorse or promote products derived
|
|
|
|
* from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
|
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
|
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
* Implementation of the NFSv4 state model. For the time being,
|
|
|
|
* this is minimal, but will be made much more complex in a
|
|
|
|
* subsequent patch.
|
|
|
|
*/
|
|
|
|
|
2007-07-09 03:49:11 +07:00
|
|
|
#include <linux/kernel.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/slab.h>
|
2010-09-18 20:09:31 +07:00
|
|
|
#include <linux/fs.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/nfs_fs.h>
|
|
|
|
#include <linux/nfs_idmap.h>
|
2006-01-03 15:55:23 +07:00
|
|
|
#include <linux/kthread.h>
|
|
|
|
#include <linux/module.h>
|
2007-07-03 00:58:33 +07:00
|
|
|
#include <linux/random.h>
|
2010-10-23 06:18:52 +07:00
|
|
|
#include <linux/ratelimit.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/workqueue.h>
|
|
|
|
#include <linux/bitops.h>
|
NFS: Cache state owners after files are closed
Servers have a finite amount of memory to store NFSv4 open and lock
owners. Moreover, servers may have a difficult time determining when
they can reap their state owner table, thanks to gray areas in the
NFSv4 protocol specification. Thus clients should be careful to reuse
state owners when possible.
Currently Linux is not too careful. When a user has closed all her
files on one mount point, the state owner's reference count goes to
zero, and it is released. The next OPEN allocates a new one. A
workload that serially opens and closes files can run through a large
number of open owners this way.
When a state owner's reference count goes to zero, slap it onto a free
list for that nfs_server, with an expiry time. Garbage collect before
looking for a state owner. This makes state owners for active users
available for re-use.
Now that there can be unused state owners remaining at umount time,
purge the state owner free list when a server is destroyed. Also be
sure not to reclaim unused state owners during state recovery.
This change has benefits for the client as well. For some workloads,
this approach drops the number of OPEN_CONFIRM calls from the same as
the number of OPEN calls, down to just one. This reduces wire traffic
and thus open(2) latency. Before this patch, untarring a kernel
source tarball shows the OPEN_CONFIRM call counter steadily increasing
through the test. With the patch, the OPEN_CONFIRM count remains at 1
throughout the entire untar.
As long as the expiry time is kept short, I don't think garbage
collection should be terribly expensive, although it does bounce the
clp->cl_lock around a bit.
[ At some point we should rationalize the use of the nfs_server
->destroy method. ]
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
[Trond: Fixed a garbage collection race and a few efficiency issues]
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2011-12-07 04:13:48 +07:00
|
|
|
#include <linux/jiffies.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2012-09-15 04:24:32 +07:00
|
|
|
#include <linux/sunrpc/clnt.h>
|
|
|
|
|
2005-06-23 00:16:21 +07:00
|
|
|
#include "nfs4_fs.h"
|
2005-04-17 05:20:36 +07:00
|
|
|
#include "callback.h"
|
|
|
|
#include "delegation.h"
|
2006-08-23 07:06:10 +07:00
|
|
|
#include "internal.h"
|
2012-11-27 02:20:49 +07:00
|
|
|
#include "nfs4session.h"
|
2010-10-20 11:18:02 +07:00
|
|
|
#include "pnfs.h"
|
2012-08-20 21:00:36 +07:00
|
|
|
#include "netns.h"
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2012-05-22 09:45:24 +07:00
|
|
|
#define NFSDBG_FACILITY NFSDBG_STATE
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
#define OPENOWNER_POOL_SIZE 8
|
|
|
|
|
2005-06-23 00:16:21 +07:00
|
|
|
const nfs4_stateid zero_stateid;
|
2012-09-15 04:24:32 +07:00
|
|
|
static DEFINE_MUTEX(nfs_clid_init_mutex);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-04-01 20:22:47 +07:00
|
|
|
int nfs4_init_clientid(struct nfs_client *clp, struct rpc_cred *cred)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2011-04-25 01:28:18 +07:00
|
|
|
struct nfs4_setclientid_res clid = {
|
|
|
|
.clientid = clp->cl_clientid,
|
|
|
|
.confirm = clp->cl_confirm,
|
|
|
|
};
|
2009-03-19 07:48:06 +07:00
|
|
|
unsigned short port;
|
|
|
|
int status;
|
2012-08-20 21:00:36 +07:00
|
|
|
struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
|
2009-03-19 07:48:06 +07:00
|
|
|
|
2011-04-25 01:28:18 +07:00
|
|
|
if (test_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state))
|
|
|
|
goto do_confirm;
|
2012-08-20 21:00:36 +07:00
|
|
|
port = nn->nfs_callback_tcpport;
|
2009-03-19 07:48:06 +07:00
|
|
|
if (clp->cl_addr.ss_family == AF_INET6)
|
2012-08-20 21:00:41 +07:00
|
|
|
port = nn->nfs_callback_tcpport6;
|
2009-03-19 07:48:06 +07:00
|
|
|
|
2010-04-17 03:43:06 +07:00
|
|
|
status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid);
|
|
|
|
if (status != 0)
|
|
|
|
goto out;
|
2011-04-25 01:28:18 +07:00
|
|
|
clp->cl_clientid = clid.clientid;
|
|
|
|
clp->cl_confirm = clid.confirm;
|
|
|
|
set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
|
|
|
|
do_confirm:
|
2010-04-17 03:43:06 +07:00
|
|
|
status = nfs4_proc_setclientid_confirm(clp, &clid, cred);
|
|
|
|
if (status != 0)
|
|
|
|
goto out;
|
2011-04-25 01:28:18 +07:00
|
|
|
clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
|
2010-04-17 03:43:06 +07:00
|
|
|
nfs4_schedule_state_renewal(clp);
|
|
|
|
out:
|
2005-04-17 05:20:36 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2012-09-15 04:24:32 +07:00
|
|
|
/**
|
|
|
|
* nfs40_discover_server_trunking - Detect server IP address trunking (mv0)
|
|
|
|
*
|
|
|
|
* @clp: nfs_client under test
|
|
|
|
* @result: OUT: found nfs_client, or clp
|
|
|
|
* @cred: credential to use for trunking test
|
|
|
|
*
|
|
|
|
* Returns zero, a negative errno, or a negative NFS4ERR status.
|
|
|
|
* If zero is returned, an nfs_client pointer is planted in
|
|
|
|
* "result".
|
|
|
|
*
|
|
|
|
* Note: The returned client may not yet be marked ready.
|
|
|
|
*/
|
|
|
|
int nfs40_discover_server_trunking(struct nfs_client *clp,
|
|
|
|
struct nfs_client **result,
|
|
|
|
struct rpc_cred *cred)
|
|
|
|
{
|
|
|
|
struct nfs4_setclientid_res clid = {
|
|
|
|
.clientid = clp->cl_clientid,
|
|
|
|
.confirm = clp->cl_confirm,
|
|
|
|
};
|
2012-10-02 06:17:31 +07:00
|
|
|
struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
|
2012-09-15 04:24:32 +07:00
|
|
|
unsigned short port;
|
|
|
|
int status;
|
|
|
|
|
2012-10-02 06:17:31 +07:00
|
|
|
port = nn->nfs_callback_tcpport;
|
2012-09-15 04:24:32 +07:00
|
|
|
if (clp->cl_addr.ss_family == AF_INET6)
|
2012-10-02 06:17:31 +07:00
|
|
|
port = nn->nfs_callback_tcpport6;
|
2012-09-15 04:24:32 +07:00
|
|
|
|
|
|
|
status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid);
|
|
|
|
if (status != 0)
|
|
|
|
goto out;
|
|
|
|
clp->cl_clientid = clid.clientid;
|
|
|
|
clp->cl_confirm = clid.confirm;
|
|
|
|
|
|
|
|
status = nfs40_walk_client_list(clp, result, cred);
|
2013-01-19 10:56:23 +07:00
|
|
|
if (status == 0) {
|
2012-09-15 04:24:32 +07:00
|
|
|
/* Sustain the lease, even if it's empty. If the clientid4
|
|
|
|
* goes stale it's of no use for trunking discovery. */
|
|
|
|
nfs4_schedule_state_renewal(*result);
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2009-04-01 20:22:46 +07:00
|
|
|
struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp)
|
2008-04-09 03:02:17 +07:00
|
|
|
{
|
|
|
|
struct rpc_cred *cred = NULL;
|
|
|
|
|
|
|
|
if (clp->cl_machine_cred != NULL)
|
|
|
|
cred = get_rpccred(clp->cl_machine_cred);
|
|
|
|
return cred;
|
|
|
|
}
|
|
|
|
|
NFS: Use root's credential for lease management when keytab is missing
Commit 05f4c350 "NFS: Discover NFSv4 server trunking when mounting"
Fri Sep 14 17:24:32 2012 introduced Uniform Client String support,
which forces our NFS client to establish a client ID immediately
during a mount operation rather than waiting until a user wants to
open a file.
Normally machine credentials (eg. from a keytab) are used to perform
a mount operation that is protected by Kerberos. Before 05fc350,
SETCLIENTID used a machine credential, or fell back to a regular
user's credential if no keytab is available.
On clients that don't have a keytab, performing SETCLIENTID early
means there's no user credential to fall back on, since no regular
user has kinit'd yet. 05f4c350 seems to have broken the ability
to mount with sec=krb5 on clients that don't have a keytab in
kernels 3.7 - 3.10.
To address this regression, commit 4edaa308 (NFS: Use "krb5i" to
establish NFSv4 state whenever possible), Sat Mar 16 15:56:20 2013,
was merged in 3.10. This commit forces the NFS client to fall back
to AUTH_SYS for lease management operations if no keytab is
available.
Neil Brown noticed that, since root is required to kinit to do a
sec=krb5 mount when a client doesn't have a keytab, we can try to
use root's Kerberos credential before AUTH_SYS.
Now, when determining a principal and flavor to use for lease
management, the NFS client tries in this order:
1. Flavor: AUTH_GSS, krb5i
Principal: service principal (via keytab)
2. Flavor: AUTH_GSS, krb5i
Principal: user principal established for UID 0 (via kinit)
3. Flavor: AUTH_SYS
Principal: UID 0 / GID 0
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2013-07-24 23:28:28 +07:00
|
|
|
static void nfs4_root_machine_cred(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
struct rpc_cred *cred, *new;
|
|
|
|
|
|
|
|
new = rpc_lookup_machine_cred(NULL);
|
|
|
|
spin_lock(&clp->cl_lock);
|
|
|
|
cred = clp->cl_machine_cred;
|
|
|
|
clp->cl_machine_cred = new;
|
|
|
|
spin_unlock(&clp->cl_lock);
|
|
|
|
if (cred != NULL)
|
|
|
|
put_rpccred(cred);
|
|
|
|
}
|
|
|
|
|
2010-12-24 08:32:43 +07:00
|
|
|
static struct rpc_cred *
|
|
|
|
nfs4_get_renew_cred_server_locked(struct nfs_server *server)
|
2006-01-03 15:55:25 +07:00
|
|
|
{
|
2010-12-24 08:32:43 +07:00
|
|
|
struct rpc_cred *cred = NULL;
|
2006-01-03 15:55:25 +07:00
|
|
|
struct nfs4_state_owner *sp;
|
2007-07-03 00:58:33 +07:00
|
|
|
struct rb_node *pos;
|
2006-01-03 15:55:25 +07:00
|
|
|
|
2010-12-24 08:32:43 +07:00
|
|
|
for (pos = rb_first(&server->state_owners);
|
|
|
|
pos != NULL;
|
|
|
|
pos = rb_next(pos)) {
|
|
|
|
sp = rb_entry(pos, struct nfs4_state_owner, so_server_node);
|
2006-01-03 15:55:25 +07:00
|
|
|
if (list_empty(&sp->so_states))
|
|
|
|
continue;
|
|
|
|
cred = get_rpccred(sp->so_cred);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return cred;
|
|
|
|
}
|
|
|
|
|
2010-12-24 08:32:43 +07:00
|
|
|
/**
|
|
|
|
* nfs4_get_renew_cred_locked - Acquire credential for a renew operation
|
|
|
|
* @clp: client state handle
|
|
|
|
*
|
|
|
|
* Returns an rpc_cred with reference count bumped, or NULL.
|
|
|
|
* Caller must hold clp->cl_lock.
|
|
|
|
*/
|
|
|
|
struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
struct rpc_cred *cred = NULL;
|
|
|
|
struct nfs_server *server;
|
|
|
|
|
2012-03-17 02:25:52 +07:00
|
|
|
/* Use machine credentials if available */
|
|
|
|
cred = nfs4_get_machine_cred_locked(clp);
|
|
|
|
if (cred != NULL)
|
|
|
|
goto out;
|
|
|
|
|
2010-12-24 08:32:43 +07:00
|
|
|
rcu_read_lock();
|
|
|
|
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
|
|
|
|
cred = nfs4_get_renew_cred_server_locked(server);
|
|
|
|
if (cred != NULL)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
2012-03-17 02:25:52 +07:00
|
|
|
|
|
|
|
out:
|
2010-12-24 08:32:43 +07:00
|
|
|
return cred;
|
|
|
|
}
|
|
|
|
|
2013-06-20 03:39:44 +07:00
|
|
|
static void nfs4_end_drain_slot_table(struct nfs4_slot_table *tbl)
|
2009-12-07 00:57:34 +07:00
|
|
|
{
|
2013-05-21 01:13:50 +07:00
|
|
|
if (test_and_clear_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state)) {
|
2012-01-18 10:57:37 +07:00
|
|
|
spin_lock(&tbl->slot_tbl_lock);
|
2012-11-30 05:27:47 +07:00
|
|
|
nfs41_wake_slot_table(tbl);
|
2012-01-18 10:57:37 +07:00
|
|
|
spin_unlock(&tbl->slot_tbl_lock);
|
2009-12-15 12:27:56 +07:00
|
|
|
}
|
2009-12-07 00:57:34 +07:00
|
|
|
}
|
|
|
|
|
2013-06-20 03:39:44 +07:00
|
|
|
static void nfs4_end_drain_session(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
struct nfs4_session *ses = clp->cl_session;
|
|
|
|
|
2013-08-09 23:49:56 +07:00
|
|
|
if (clp->cl_slot_tbl) {
|
|
|
|
nfs4_end_drain_slot_table(clp->cl_slot_tbl);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-20 03:39:44 +07:00
|
|
|
if (ses != NULL) {
|
|
|
|
nfs4_end_drain_slot_table(&ses->bc_slot_table);
|
|
|
|
nfs4_end_drain_slot_table(&ses->fc_slot_table);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-21 01:13:50 +07:00
|
|
|
static int nfs4_drain_slot_tbl(struct nfs4_slot_table *tbl)
|
2009-12-07 00:57:34 +07:00
|
|
|
{
|
2013-05-21 01:13:50 +07:00
|
|
|
set_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state);
|
2009-12-07 00:57:34 +07:00
|
|
|
spin_lock(&tbl->slot_tbl_lock);
|
2012-02-18 01:05:23 +07:00
|
|
|
if (tbl->highest_used_slotid != NFS4_NO_SLOT) {
|
2013-11-15 05:32:02 +07:00
|
|
|
reinit_completion(&tbl->complete);
|
2009-12-07 00:57:34 +07:00
|
|
|
spin_unlock(&tbl->slot_tbl_lock);
|
2011-01-06 09:04:34 +07:00
|
|
|
return wait_for_completion_interruptible(&tbl->complete);
|
2009-12-07 00:57:34 +07:00
|
|
|
}
|
|
|
|
spin_unlock(&tbl->slot_tbl_lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-01-06 09:04:34 +07:00
|
|
|
static int nfs4_begin_drain_session(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
struct nfs4_session *ses = clp->cl_session;
|
|
|
|
int ret = 0;
|
|
|
|
|
2013-08-09 23:49:56 +07:00
|
|
|
if (clp->cl_slot_tbl)
|
|
|
|
return nfs4_drain_slot_tbl(clp->cl_slot_tbl);
|
|
|
|
|
2011-01-06 09:04:34 +07:00
|
|
|
/* back channel */
|
2013-05-21 01:13:50 +07:00
|
|
|
ret = nfs4_drain_slot_tbl(&ses->bc_slot_table);
|
2011-01-06 09:04:34 +07:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
/* fore channel */
|
2013-05-21 01:13:50 +07:00
|
|
|
return nfs4_drain_slot_tbl(&ses->fc_slot_table);
|
2011-01-06 09:04:34 +07:00
|
|
|
}
|
|
|
|
|
2013-10-18 01:13:02 +07:00
|
|
|
#if defined(CONFIG_NFS_V4_1)
|
|
|
|
|
2013-08-09 23:49:56 +07:00
|
|
|
static int nfs41_setup_state_renewal(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
struct nfs_fsinfo fsinfo;
|
|
|
|
|
|
|
|
if (!test_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state)) {
|
|
|
|
nfs4_schedule_state_renewal(clp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = nfs4_proc_get_lease_time(clp, &fsinfo);
|
|
|
|
if (status == 0) {
|
|
|
|
/* Update lease time and schedule renewal */
|
|
|
|
spin_lock(&clp->cl_lock);
|
|
|
|
clp->cl_lease_time = fsinfo.lease_time * HZ;
|
|
|
|
clp->cl_last_renewal = jiffies;
|
|
|
|
spin_unlock(&clp->cl_lock);
|
|
|
|
|
|
|
|
nfs4_schedule_state_renewal(clp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2012-06-05 21:22:14 +07:00
|
|
|
static void nfs41_finish_session_reset(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
|
|
|
|
clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
|
|
|
|
/* create_session negotiated new slot table */
|
|
|
|
clear_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
|
|
|
|
nfs41_setup_state_renewal(clp);
|
|
|
|
}
|
|
|
|
|
2009-12-05 03:52:24 +07:00
|
|
|
int nfs41_init_clientid(struct nfs_client *clp, struct rpc_cred *cred)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
2011-04-25 01:28:18 +07:00
|
|
|
if (test_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state))
|
|
|
|
goto do_confirm;
|
2009-12-16 05:36:57 +07:00
|
|
|
nfs4_begin_drain_session(clp);
|
2009-12-05 03:52:24 +07:00
|
|
|
status = nfs4_proc_exchange_id(clp, cred);
|
2009-12-07 00:23:46 +07:00
|
|
|
if (status != 0)
|
|
|
|
goto out;
|
2011-04-25 01:28:18 +07:00
|
|
|
set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
|
|
|
|
do_confirm:
|
2012-05-26 04:51:23 +07:00
|
|
|
status = nfs4_proc_create_session(clp, cred);
|
2009-12-07 00:23:46 +07:00
|
|
|
if (status != 0)
|
|
|
|
goto out;
|
2012-06-05 21:22:14 +07:00
|
|
|
nfs41_finish_session_reset(clp);
|
2009-12-07 00:23:46 +07:00
|
|
|
nfs_mark_client_ready(clp, NFS_CS_READY);
|
|
|
|
out:
|
2009-12-05 03:52:24 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2012-09-15 04:24:32 +07:00
|
|
|
/**
|
|
|
|
* nfs41_discover_server_trunking - Detect server IP address trunking (mv1)
|
|
|
|
*
|
|
|
|
* @clp: nfs_client under test
|
|
|
|
* @result: OUT: found nfs_client, or clp
|
|
|
|
* @cred: credential to use for trunking test
|
|
|
|
*
|
|
|
|
* Returns NFS4_OK, a negative errno, or a negative NFS4ERR status.
|
|
|
|
* If NFS4_OK is returned, an nfs_client pointer is planted in
|
|
|
|
* "result".
|
|
|
|
*
|
|
|
|
* Note: The returned client may not yet be marked ready.
|
|
|
|
*/
|
|
|
|
int nfs41_discover_server_trunking(struct nfs_client *clp,
|
|
|
|
struct nfs_client **result,
|
|
|
|
struct rpc_cred *cred)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
status = nfs4_proc_exchange_id(clp, cred);
|
|
|
|
if (status != NFS4_OK)
|
|
|
|
return status;
|
2012-10-03 01:33:22 +07:00
|
|
|
set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
|
2012-09-15 04:24:32 +07:00
|
|
|
|
|
|
|
return nfs41_walk_client_list(clp, result, cred);
|
|
|
|
}
|
|
|
|
|
2009-04-01 20:22:49 +07:00
|
|
|
#endif /* CONFIG_NFS_V4_1 */
|
|
|
|
|
2010-12-24 08:32:43 +07:00
|
|
|
/**
|
2013-07-24 23:28:37 +07:00
|
|
|
* nfs4_get_clid_cred - Acquire credential for a setclientid operation
|
2010-12-24 08:32:43 +07:00
|
|
|
* @clp: client state handle
|
|
|
|
*
|
|
|
|
* Returns an rpc_cred with reference count bumped, or NULL.
|
|
|
|
*/
|
2013-07-24 23:28:37 +07:00
|
|
|
struct rpc_cred *nfs4_get_clid_cred(struct nfs_client *clp)
|
2010-12-24 08:32:43 +07:00
|
|
|
{
|
2008-04-09 03:02:17 +07:00
|
|
|
struct rpc_cred *cred;
|
2006-01-03 15:55:26 +07:00
|
|
|
|
2008-12-24 03:21:41 +07:00
|
|
|
spin_lock(&clp->cl_lock);
|
|
|
|
cred = nfs4_get_machine_cred_locked(clp);
|
2010-12-24 08:32:43 +07:00
|
|
|
spin_unlock(&clp->cl_lock);
|
2008-04-09 03:02:17 +07:00
|
|
|
return cred;
|
2006-01-03 15:55:26 +07:00
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static struct nfs4_state_owner *
|
2010-12-24 08:32:43 +07:00
|
|
|
nfs4_find_state_owner_locked(struct nfs_server *server, struct rpc_cred *cred)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2010-12-24 08:32:43 +07:00
|
|
|
struct rb_node **p = &server->state_owners.rb_node,
|
2007-07-03 00:58:33 +07:00
|
|
|
*parent = NULL;
|
2011-12-07 04:13:39 +07:00
|
|
|
struct nfs4_state_owner *sp;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-07-03 00:58:33 +07:00
|
|
|
while (*p != NULL) {
|
|
|
|
parent = *p;
|
2010-12-24 08:32:43 +07:00
|
|
|
sp = rb_entry(parent, struct nfs4_state_owner, so_server_node);
|
2007-07-03 00:58:33 +07:00
|
|
|
|
|
|
|
if (cred < sp->so_cred)
|
|
|
|
p = &parent->rb_left;
|
|
|
|
else if (cred > sp->so_cred)
|
|
|
|
p = &parent->rb_right;
|
|
|
|
else {
|
NFS: Cache state owners after files are closed
Servers have a finite amount of memory to store NFSv4 open and lock
owners. Moreover, servers may have a difficult time determining when
they can reap their state owner table, thanks to gray areas in the
NFSv4 protocol specification. Thus clients should be careful to reuse
state owners when possible.
Currently Linux is not too careful. When a user has closed all her
files on one mount point, the state owner's reference count goes to
zero, and it is released. The next OPEN allocates a new one. A
workload that serially opens and closes files can run through a large
number of open owners this way.
When a state owner's reference count goes to zero, slap it onto a free
list for that nfs_server, with an expiry time. Garbage collect before
looking for a state owner. This makes state owners for active users
available for re-use.
Now that there can be unused state owners remaining at umount time,
purge the state owner free list when a server is destroyed. Also be
sure not to reclaim unused state owners during state recovery.
This change has benefits for the client as well. For some workloads,
this approach drops the number of OPEN_CONFIRM calls from the same as
the number of OPEN calls, down to just one. This reduces wire traffic
and thus open(2) latency. Before this patch, untarring a kernel
source tarball shows the OPEN_CONFIRM call counter steadily increasing
through the test. With the patch, the OPEN_CONFIRM count remains at 1
throughout the entire untar.
As long as the expiry time is kept short, I don't think garbage
collection should be terribly expensive, although it does bounce the
clp->cl_lock around a bit.
[ At some point we should rationalize the use of the nfs_server
->destroy method. ]
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
[Trond: Fixed a garbage collection race and a few efficiency issues]
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2011-12-07 04:13:48 +07:00
|
|
|
if (!list_empty(&sp->so_lru))
|
|
|
|
list_del_init(&sp->so_lru);
|
2007-07-03 00:58:33 +07:00
|
|
|
atomic_inc(&sp->so_count);
|
2011-12-07 04:13:39 +07:00
|
|
|
return sp;
|
2007-07-03 00:58:33 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2011-12-07 04:13:39 +07:00
|
|
|
return NULL;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2007-07-03 00:58:33 +07:00
|
|
|
static struct nfs4_state_owner *
|
2010-12-24 08:32:43 +07:00
|
|
|
nfs4_insert_state_owner_locked(struct nfs4_state_owner *new)
|
2007-07-03 00:58:33 +07:00
|
|
|
{
|
2010-12-24 08:32:43 +07:00
|
|
|
struct nfs_server *server = new->so_server;
|
|
|
|
struct rb_node **p = &server->state_owners.rb_node,
|
2007-07-03 00:58:33 +07:00
|
|
|
*parent = NULL;
|
|
|
|
struct nfs4_state_owner *sp;
|
2012-01-18 10:04:24 +07:00
|
|
|
int err;
|
2007-07-03 00:58:33 +07:00
|
|
|
|
|
|
|
while (*p != NULL) {
|
|
|
|
parent = *p;
|
2010-12-24 08:32:43 +07:00
|
|
|
sp = rb_entry(parent, struct nfs4_state_owner, so_server_node);
|
2007-07-03 00:58:33 +07:00
|
|
|
|
|
|
|
if (new->so_cred < sp->so_cred)
|
|
|
|
p = &parent->rb_left;
|
|
|
|
else if (new->so_cred > sp->so_cred)
|
|
|
|
p = &parent->rb_right;
|
|
|
|
else {
|
NFS: Cache state owners after files are closed
Servers have a finite amount of memory to store NFSv4 open and lock
owners. Moreover, servers may have a difficult time determining when
they can reap their state owner table, thanks to gray areas in the
NFSv4 protocol specification. Thus clients should be careful to reuse
state owners when possible.
Currently Linux is not too careful. When a user has closed all her
files on one mount point, the state owner's reference count goes to
zero, and it is released. The next OPEN allocates a new one. A
workload that serially opens and closes files can run through a large
number of open owners this way.
When a state owner's reference count goes to zero, slap it onto a free
list for that nfs_server, with an expiry time. Garbage collect before
looking for a state owner. This makes state owners for active users
available for re-use.
Now that there can be unused state owners remaining at umount time,
purge the state owner free list when a server is destroyed. Also be
sure not to reclaim unused state owners during state recovery.
This change has benefits for the client as well. For some workloads,
this approach drops the number of OPEN_CONFIRM calls from the same as
the number of OPEN calls, down to just one. This reduces wire traffic
and thus open(2) latency. Before this patch, untarring a kernel
source tarball shows the OPEN_CONFIRM call counter steadily increasing
through the test. With the patch, the OPEN_CONFIRM count remains at 1
throughout the entire untar.
As long as the expiry time is kept short, I don't think garbage
collection should be terribly expensive, although it does bounce the
clp->cl_lock around a bit.
[ At some point we should rationalize the use of the nfs_server
->destroy method. ]
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
[Trond: Fixed a garbage collection race and a few efficiency issues]
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2011-12-07 04:13:48 +07:00
|
|
|
if (!list_empty(&sp->so_lru))
|
|
|
|
list_del_init(&sp->so_lru);
|
2007-07-03 00:58:33 +07:00
|
|
|
atomic_inc(&sp->so_count);
|
|
|
|
return sp;
|
|
|
|
}
|
|
|
|
}
|
2012-01-18 10:04:25 +07:00
|
|
|
err = ida_get_new(&server->openowner_id, &new->so_seqid.owner_id);
|
2012-01-18 10:04:24 +07:00
|
|
|
if (err)
|
|
|
|
return ERR_PTR(err);
|
2010-12-24 08:32:43 +07:00
|
|
|
rb_link_node(&new->so_server_node, parent, p);
|
|
|
|
rb_insert_color(&new->so_server_node, &server->state_owners);
|
2007-07-03 00:58:33 +07:00
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2010-12-24 08:32:43 +07:00
|
|
|
nfs4_remove_state_owner_locked(struct nfs4_state_owner *sp)
|
2007-07-03 00:58:33 +07:00
|
|
|
{
|
2010-12-24 08:32:43 +07:00
|
|
|
struct nfs_server *server = sp->so_server;
|
|
|
|
|
|
|
|
if (!RB_EMPTY_NODE(&sp->so_server_node))
|
|
|
|
rb_erase(&sp->so_server_node, &server->state_owners);
|
2012-01-18 10:04:25 +07:00
|
|
|
ida_remove(&server->openowner_id, sp->so_seqid.owner_id);
|
2007-07-03 00:58:33 +07:00
|
|
|
}
|
|
|
|
|
2012-01-18 10:04:25 +07:00
|
|
|
static void
|
|
|
|
nfs4_init_seqid_counter(struct nfs_seqid_counter *sc)
|
|
|
|
{
|
2012-04-21 06:24:51 +07:00
|
|
|
sc->create_time = ktime_get();
|
2012-01-18 10:04:25 +07:00
|
|
|
sc->flags = 0;
|
|
|
|
sc->counter = 0;
|
|
|
|
spin_lock_init(&sc->lock);
|
|
|
|
INIT_LIST_HEAD(&sc->list);
|
|
|
|
rpc_init_wait_queue(&sc->wait, "Seqid_waitqueue");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nfs4_destroy_seqid_counter(struct nfs_seqid_counter *sc)
|
|
|
|
{
|
|
|
|
rpc_destroy_wait_queue(&sc->wait);
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* nfs4_alloc_state_owner(): this is called on the OPEN or CREATE path to
|
|
|
|
* create a new state_owner.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static struct nfs4_state_owner *
|
2012-01-18 10:04:24 +07:00
|
|
|
nfs4_alloc_state_owner(struct nfs_server *server,
|
|
|
|
struct rpc_cred *cred,
|
|
|
|
gfp_t gfp_flags)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct nfs4_state_owner *sp;
|
|
|
|
|
2012-01-18 10:04:24 +07:00
|
|
|
sp = kzalloc(sizeof(*sp), gfp_flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!sp)
|
|
|
|
return NULL;
|
2012-01-18 10:04:24 +07:00
|
|
|
sp->so_server = server;
|
|
|
|
sp->so_cred = get_rpccred(cred);
|
2005-10-21 04:22:47 +07:00
|
|
|
spin_lock_init(&sp->so_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
INIT_LIST_HEAD(&sp->so_states);
|
2012-01-18 10:04:25 +07:00
|
|
|
nfs4_init_seqid_counter(&sp->so_seqid);
|
2005-04-17 05:20:36 +07:00
|
|
|
atomic_set(&sp->so_count, 1);
|
NFS: Cache state owners after files are closed
Servers have a finite amount of memory to store NFSv4 open and lock
owners. Moreover, servers may have a difficult time determining when
they can reap their state owner table, thanks to gray areas in the
NFSv4 protocol specification. Thus clients should be careful to reuse
state owners when possible.
Currently Linux is not too careful. When a user has closed all her
files on one mount point, the state owner's reference count goes to
zero, and it is released. The next OPEN allocates a new one. A
workload that serially opens and closes files can run through a large
number of open owners this way.
When a state owner's reference count goes to zero, slap it onto a free
list for that nfs_server, with an expiry time. Garbage collect before
looking for a state owner. This makes state owners for active users
available for re-use.
Now that there can be unused state owners remaining at umount time,
purge the state owner free list when a server is destroyed. Also be
sure not to reclaim unused state owners during state recovery.
This change has benefits for the client as well. For some workloads,
this approach drops the number of OPEN_CONFIRM calls from the same as
the number of OPEN calls, down to just one. This reduces wire traffic
and thus open(2) latency. Before this patch, untarring a kernel
source tarball shows the OPEN_CONFIRM call counter steadily increasing
through the test. With the patch, the OPEN_CONFIRM count remains at 1
throughout the entire untar.
As long as the expiry time is kept short, I don't think garbage
collection should be terribly expensive, although it does bounce the
clp->cl_lock around a bit.
[ At some point we should rationalize the use of the nfs_server
->destroy method. ]
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
[Trond: Fixed a garbage collection race and a few efficiency issues]
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2011-12-07 04:13:48 +07:00
|
|
|
INIT_LIST_HEAD(&sp->so_lru);
|
2013-02-08 02:41:11 +07:00
|
|
|
seqcount_init(&sp->so_reclaim_seqcount);
|
2013-02-07 22:54:07 +07:00
|
|
|
mutex_init(&sp->so_delegreturn_mutex);
|
2005-04-17 05:20:36 +07:00
|
|
|
return sp;
|
|
|
|
}
|
|
|
|
|
2008-05-03 03:42:45 +07:00
|
|
|
static void
|
2005-04-17 05:20:36 +07:00
|
|
|
nfs4_drop_state_owner(struct nfs4_state_owner *sp)
|
|
|
|
{
|
2012-04-21 23:31:05 +07:00
|
|
|
struct rb_node *rb_node = &sp->so_server_node;
|
|
|
|
|
|
|
|
if (!RB_EMPTY_NODE(rb_node)) {
|
2010-12-24 08:32:43 +07:00
|
|
|
struct nfs_server *server = sp->so_server;
|
|
|
|
struct nfs_client *clp = server->nfs_client;
|
2007-07-03 00:58:33 +07:00
|
|
|
|
|
|
|
spin_lock(&clp->cl_lock);
|
2012-04-21 23:31:05 +07:00
|
|
|
if (!RB_EMPTY_NODE(rb_node)) {
|
|
|
|
rb_erase(rb_node, &server->state_owners);
|
|
|
|
RB_CLEAR_NODE(rb_node);
|
|
|
|
}
|
2007-07-03 00:58:33 +07:00
|
|
|
spin_unlock(&clp->cl_lock);
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
NFS: Cache state owners after files are closed
Servers have a finite amount of memory to store NFSv4 open and lock
owners. Moreover, servers may have a difficult time determining when
they can reap their state owner table, thanks to gray areas in the
NFSv4 protocol specification. Thus clients should be careful to reuse
state owners when possible.
Currently Linux is not too careful. When a user has closed all her
files on one mount point, the state owner's reference count goes to
zero, and it is released. The next OPEN allocates a new one. A
workload that serially opens and closes files can run through a large
number of open owners this way.
When a state owner's reference count goes to zero, slap it onto a free
list for that nfs_server, with an expiry time. Garbage collect before
looking for a state owner. This makes state owners for active users
available for re-use.
Now that there can be unused state owners remaining at umount time,
purge the state owner free list when a server is destroyed. Also be
sure not to reclaim unused state owners during state recovery.
This change has benefits for the client as well. For some workloads,
this approach drops the number of OPEN_CONFIRM calls from the same as
the number of OPEN calls, down to just one. This reduces wire traffic
and thus open(2) latency. Before this patch, untarring a kernel
source tarball shows the OPEN_CONFIRM call counter steadily increasing
through the test. With the patch, the OPEN_CONFIRM count remains at 1
throughout the entire untar.
As long as the expiry time is kept short, I don't think garbage
collection should be terribly expensive, although it does bounce the
clp->cl_lock around a bit.
[ At some point we should rationalize the use of the nfs_server
->destroy method. ]
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
[Trond: Fixed a garbage collection race and a few efficiency issues]
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2011-12-07 04:13:48 +07:00
|
|
|
static void nfs4_free_state_owner(struct nfs4_state_owner *sp)
|
|
|
|
{
|
2012-01-18 10:04:25 +07:00
|
|
|
nfs4_destroy_seqid_counter(&sp->so_seqid);
|
NFS: Cache state owners after files are closed
Servers have a finite amount of memory to store NFSv4 open and lock
owners. Moreover, servers may have a difficult time determining when
they can reap their state owner table, thanks to gray areas in the
NFSv4 protocol specification. Thus clients should be careful to reuse
state owners when possible.
Currently Linux is not too careful. When a user has closed all her
files on one mount point, the state owner's reference count goes to
zero, and it is released. The next OPEN allocates a new one. A
workload that serially opens and closes files can run through a large
number of open owners this way.
When a state owner's reference count goes to zero, slap it onto a free
list for that nfs_server, with an expiry time. Garbage collect before
looking for a state owner. This makes state owners for active users
available for re-use.
Now that there can be unused state owners remaining at umount time,
purge the state owner free list when a server is destroyed. Also be
sure not to reclaim unused state owners during state recovery.
This change has benefits for the client as well. For some workloads,
this approach drops the number of OPEN_CONFIRM calls from the same as
the number of OPEN calls, down to just one. This reduces wire traffic
and thus open(2) latency. Before this patch, untarring a kernel
source tarball shows the OPEN_CONFIRM call counter steadily increasing
through the test. With the patch, the OPEN_CONFIRM count remains at 1
throughout the entire untar.
As long as the expiry time is kept short, I don't think garbage
collection should be terribly expensive, although it does bounce the
clp->cl_lock around a bit.
[ At some point we should rationalize the use of the nfs_server
->destroy method. ]
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
[Trond: Fixed a garbage collection race and a few efficiency issues]
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2011-12-07 04:13:48 +07:00
|
|
|
put_rpccred(sp->so_cred);
|
|
|
|
kfree(sp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs4_gc_state_owners(struct nfs_server *server)
|
|
|
|
{
|
|
|
|
struct nfs_client *clp = server->nfs_client;
|
|
|
|
struct nfs4_state_owner *sp, *tmp;
|
|
|
|
unsigned long time_min, time_max;
|
|
|
|
LIST_HEAD(doomed);
|
|
|
|
|
|
|
|
spin_lock(&clp->cl_lock);
|
|
|
|
time_max = jiffies;
|
|
|
|
time_min = (long)time_max - (long)clp->cl_lease_time;
|
|
|
|
list_for_each_entry_safe(sp, tmp, &server->state_owners_lru, so_lru) {
|
|
|
|
/* NB: LRU is sorted so that oldest is at the head */
|
|
|
|
if (time_in_range(sp->so_expires, time_min, time_max))
|
|
|
|
break;
|
|
|
|
list_move(&sp->so_lru, &doomed);
|
|
|
|
nfs4_remove_state_owner_locked(sp);
|
|
|
|
}
|
|
|
|
spin_unlock(&clp->cl_lock);
|
|
|
|
|
|
|
|
list_for_each_entry_safe(sp, tmp, &doomed, so_lru) {
|
|
|
|
list_del(&sp->so_lru);
|
|
|
|
nfs4_free_state_owner(sp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-24 08:32:43 +07:00
|
|
|
/**
|
|
|
|
* nfs4_get_state_owner - Look up a state owner given a credential
|
|
|
|
* @server: nfs_server to search
|
|
|
|
* @cred: RPC credential to match
|
|
|
|
*
|
|
|
|
* Returns a pointer to an instantiated nfs4_state_owner struct, or NULL.
|
|
|
|
*/
|
|
|
|
struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server,
|
2012-01-18 10:04:24 +07:00
|
|
|
struct rpc_cred *cred,
|
|
|
|
gfp_t gfp_flags)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-08-23 07:06:09 +07:00
|
|
|
struct nfs_client *clp = server->nfs_client;
|
2005-04-17 05:20:36 +07:00
|
|
|
struct nfs4_state_owner *sp, *new;
|
|
|
|
|
|
|
|
spin_lock(&clp->cl_lock);
|
2010-12-24 08:32:43 +07:00
|
|
|
sp = nfs4_find_state_owner_locked(server, cred);
|
2005-04-17 05:20:36 +07:00
|
|
|
spin_unlock(&clp->cl_lock);
|
|
|
|
if (sp != NULL)
|
NFS: Cache state owners after files are closed
Servers have a finite amount of memory to store NFSv4 open and lock
owners. Moreover, servers may have a difficult time determining when
they can reap their state owner table, thanks to gray areas in the
NFSv4 protocol specification. Thus clients should be careful to reuse
state owners when possible.
Currently Linux is not too careful. When a user has closed all her
files on one mount point, the state owner's reference count goes to
zero, and it is released. The next OPEN allocates a new one. A
workload that serially opens and closes files can run through a large
number of open owners this way.
When a state owner's reference count goes to zero, slap it onto a free
list for that nfs_server, with an expiry time. Garbage collect before
looking for a state owner. This makes state owners for active users
available for re-use.
Now that there can be unused state owners remaining at umount time,
purge the state owner free list when a server is destroyed. Also be
sure not to reclaim unused state owners during state recovery.
This change has benefits for the client as well. For some workloads,
this approach drops the number of OPEN_CONFIRM calls from the same as
the number of OPEN calls, down to just one. This reduces wire traffic
and thus open(2) latency. Before this patch, untarring a kernel
source tarball shows the OPEN_CONFIRM call counter steadily increasing
through the test. With the patch, the OPEN_CONFIRM count remains at 1
throughout the entire untar.
As long as the expiry time is kept short, I don't think garbage
collection should be terribly expensive, although it does bounce the
clp->cl_lock around a bit.
[ At some point we should rationalize the use of the nfs_server
->destroy method. ]
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
[Trond: Fixed a garbage collection race and a few efficiency issues]
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2011-12-07 04:13:48 +07:00
|
|
|
goto out;
|
2012-01-18 10:04:24 +07:00
|
|
|
new = nfs4_alloc_state_owner(server, cred, gfp_flags);
|
2007-07-03 00:58:33 +07:00
|
|
|
if (new == NULL)
|
NFS: Cache state owners after files are closed
Servers have a finite amount of memory to store NFSv4 open and lock
owners. Moreover, servers may have a difficult time determining when
they can reap their state owner table, thanks to gray areas in the
NFSv4 protocol specification. Thus clients should be careful to reuse
state owners when possible.
Currently Linux is not too careful. When a user has closed all her
files on one mount point, the state owner's reference count goes to
zero, and it is released. The next OPEN allocates a new one. A
workload that serially opens and closes files can run through a large
number of open owners this way.
When a state owner's reference count goes to zero, slap it onto a free
list for that nfs_server, with an expiry time. Garbage collect before
looking for a state owner. This makes state owners for active users
available for re-use.
Now that there can be unused state owners remaining at umount time,
purge the state owner free list when a server is destroyed. Also be
sure not to reclaim unused state owners during state recovery.
This change has benefits for the client as well. For some workloads,
this approach drops the number of OPEN_CONFIRM calls from the same as
the number of OPEN calls, down to just one. This reduces wire traffic
and thus open(2) latency. Before this patch, untarring a kernel
source tarball shows the OPEN_CONFIRM call counter steadily increasing
through the test. With the patch, the OPEN_CONFIRM count remains at 1
throughout the entire untar.
As long as the expiry time is kept short, I don't think garbage
collection should be terribly expensive, although it does bounce the
clp->cl_lock around a bit.
[ At some point we should rationalize the use of the nfs_server
->destroy method. ]
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
[Trond: Fixed a garbage collection race and a few efficiency issues]
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2011-12-07 04:13:48 +07:00
|
|
|
goto out;
|
2012-01-18 10:04:24 +07:00
|
|
|
do {
|
|
|
|
if (ida_pre_get(&server->openowner_id, gfp_flags) == 0)
|
|
|
|
break;
|
|
|
|
spin_lock(&clp->cl_lock);
|
|
|
|
sp = nfs4_insert_state_owner_locked(new);
|
|
|
|
spin_unlock(&clp->cl_lock);
|
|
|
|
} while (sp == ERR_PTR(-EAGAIN));
|
2012-01-18 10:04:24 +07:00
|
|
|
if (sp != new)
|
|
|
|
nfs4_free_state_owner(new);
|
NFS: Cache state owners after files are closed
Servers have a finite amount of memory to store NFSv4 open and lock
owners. Moreover, servers may have a difficult time determining when
they can reap their state owner table, thanks to gray areas in the
NFSv4 protocol specification. Thus clients should be careful to reuse
state owners when possible.
Currently Linux is not too careful. When a user has closed all her
files on one mount point, the state owner's reference count goes to
zero, and it is released. The next OPEN allocates a new one. A
workload that serially opens and closes files can run through a large
number of open owners this way.
When a state owner's reference count goes to zero, slap it onto a free
list for that nfs_server, with an expiry time. Garbage collect before
looking for a state owner. This makes state owners for active users
available for re-use.
Now that there can be unused state owners remaining at umount time,
purge the state owner free list when a server is destroyed. Also be
sure not to reclaim unused state owners during state recovery.
This change has benefits for the client as well. For some workloads,
this approach drops the number of OPEN_CONFIRM calls from the same as
the number of OPEN calls, down to just one. This reduces wire traffic
and thus open(2) latency. Before this patch, untarring a kernel
source tarball shows the OPEN_CONFIRM call counter steadily increasing
through the test. With the patch, the OPEN_CONFIRM count remains at 1
throughout the entire untar.
As long as the expiry time is kept short, I don't think garbage
collection should be terribly expensive, although it does bounce the
clp->cl_lock around a bit.
[ At some point we should rationalize the use of the nfs_server
->destroy method. ]
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
[Trond: Fixed a garbage collection race and a few efficiency issues]
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2011-12-07 04:13:48 +07:00
|
|
|
out:
|
|
|
|
nfs4_gc_state_owners(server);
|
2007-07-03 00:58:33 +07:00
|
|
|
return sp;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2010-12-24 08:32:43 +07:00
|
|
|
/**
|
|
|
|
* nfs4_put_state_owner - Release a nfs4_state_owner
|
|
|
|
* @sp: state owner data to release
|
2012-04-21 23:36:19 +07:00
|
|
|
*
|
|
|
|
* Note that we keep released state owners on an LRU
|
|
|
|
* list.
|
|
|
|
* This caches valid state owners so that they can be
|
|
|
|
* reused, to avoid the OPEN_CONFIRM on minor version 0.
|
|
|
|
* It also pins the uniquifier of dropped state owners for
|
|
|
|
* a while, to ensure that those state owner names are
|
|
|
|
* never reused.
|
2010-12-24 08:32:43 +07:00
|
|
|
*/
|
2005-04-17 05:20:36 +07:00
|
|
|
void nfs4_put_state_owner(struct nfs4_state_owner *sp)
|
|
|
|
{
|
NFS: Cache state owners after files are closed
Servers have a finite amount of memory to store NFSv4 open and lock
owners. Moreover, servers may have a difficult time determining when
they can reap their state owner table, thanks to gray areas in the
NFSv4 protocol specification. Thus clients should be careful to reuse
state owners when possible.
Currently Linux is not too careful. When a user has closed all her
files on one mount point, the state owner's reference count goes to
zero, and it is released. The next OPEN allocates a new one. A
workload that serially opens and closes files can run through a large
number of open owners this way.
When a state owner's reference count goes to zero, slap it onto a free
list for that nfs_server, with an expiry time. Garbage collect before
looking for a state owner. This makes state owners for active users
available for re-use.
Now that there can be unused state owners remaining at umount time,
purge the state owner free list when a server is destroyed. Also be
sure not to reclaim unused state owners during state recovery.
This change has benefits for the client as well. For some workloads,
this approach drops the number of OPEN_CONFIRM calls from the same as
the number of OPEN calls, down to just one. This reduces wire traffic
and thus open(2) latency. Before this patch, untarring a kernel
source tarball shows the OPEN_CONFIRM call counter steadily increasing
through the test. With the patch, the OPEN_CONFIRM count remains at 1
throughout the entire untar.
As long as the expiry time is kept short, I don't think garbage
collection should be terribly expensive, although it does bounce the
clp->cl_lock around a bit.
[ At some point we should rationalize the use of the nfs_server
->destroy method. ]
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
[Trond: Fixed a garbage collection race and a few efficiency issues]
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2011-12-07 04:13:48 +07:00
|
|
|
struct nfs_server *server = sp->so_server;
|
|
|
|
struct nfs_client *clp = server->nfs_client;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (!atomic_dec_and_lock(&sp->so_count, &clp->cl_lock))
|
|
|
|
return;
|
NFS: Cache state owners after files are closed
Servers have a finite amount of memory to store NFSv4 open and lock
owners. Moreover, servers may have a difficult time determining when
they can reap their state owner table, thanks to gray areas in the
NFSv4 protocol specification. Thus clients should be careful to reuse
state owners when possible.
Currently Linux is not too careful. When a user has closed all her
files on one mount point, the state owner's reference count goes to
zero, and it is released. The next OPEN allocates a new one. A
workload that serially opens and closes files can run through a large
number of open owners this way.
When a state owner's reference count goes to zero, slap it onto a free
list for that nfs_server, with an expiry time. Garbage collect before
looking for a state owner. This makes state owners for active users
available for re-use.
Now that there can be unused state owners remaining at umount time,
purge the state owner free list when a server is destroyed. Also be
sure not to reclaim unused state owners during state recovery.
This change has benefits for the client as well. For some workloads,
this approach drops the number of OPEN_CONFIRM calls from the same as
the number of OPEN calls, down to just one. This reduces wire traffic
and thus open(2) latency. Before this patch, untarring a kernel
source tarball shows the OPEN_CONFIRM call counter steadily increasing
through the test. With the patch, the OPEN_CONFIRM count remains at 1
throughout the entire untar.
As long as the expiry time is kept short, I don't think garbage
collection should be terribly expensive, although it does bounce the
clp->cl_lock around a bit.
[ At some point we should rationalize the use of the nfs_server
->destroy method. ]
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
[Trond: Fixed a garbage collection race and a few efficiency issues]
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2011-12-07 04:13:48 +07:00
|
|
|
|
2012-04-21 23:36:19 +07:00
|
|
|
sp->so_expires = jiffies;
|
|
|
|
list_add_tail(&sp->so_lru, &server->state_owners_lru);
|
|
|
|
spin_unlock(&clp->cl_lock);
|
NFS: Cache state owners after files are closed
Servers have a finite amount of memory to store NFSv4 open and lock
owners. Moreover, servers may have a difficult time determining when
they can reap their state owner table, thanks to gray areas in the
NFSv4 protocol specification. Thus clients should be careful to reuse
state owners when possible.
Currently Linux is not too careful. When a user has closed all her
files on one mount point, the state owner's reference count goes to
zero, and it is released. The next OPEN allocates a new one. A
workload that serially opens and closes files can run through a large
number of open owners this way.
When a state owner's reference count goes to zero, slap it onto a free
list for that nfs_server, with an expiry time. Garbage collect before
looking for a state owner. This makes state owners for active users
available for re-use.
Now that there can be unused state owners remaining at umount time,
purge the state owner free list when a server is destroyed. Also be
sure not to reclaim unused state owners during state recovery.
This change has benefits for the client as well. For some workloads,
this approach drops the number of OPEN_CONFIRM calls from the same as
the number of OPEN calls, down to just one. This reduces wire traffic
and thus open(2) latency. Before this patch, untarring a kernel
source tarball shows the OPEN_CONFIRM call counter steadily increasing
through the test. With the patch, the OPEN_CONFIRM count remains at 1
throughout the entire untar.
As long as the expiry time is kept short, I don't think garbage
collection should be terribly expensive, although it does bounce the
clp->cl_lock around a bit.
[ At some point we should rationalize the use of the nfs_server
->destroy method. ]
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
[Trond: Fixed a garbage collection race and a few efficiency issues]
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2011-12-07 04:13:48 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nfs4_purge_state_owners - Release all cached state owners
|
|
|
|
* @server: nfs_server with cached state owners to release
|
|
|
|
*
|
|
|
|
* Called at umount time. Remaining state owners will be on
|
|
|
|
* the LRU with ref count of zero.
|
|
|
|
*/
|
|
|
|
void nfs4_purge_state_owners(struct nfs_server *server)
|
|
|
|
{
|
|
|
|
struct nfs_client *clp = server->nfs_client;
|
|
|
|
struct nfs4_state_owner *sp, *tmp;
|
|
|
|
LIST_HEAD(doomed);
|
|
|
|
|
|
|
|
spin_lock(&clp->cl_lock);
|
|
|
|
list_for_each_entry_safe(sp, tmp, &server->state_owners_lru, so_lru) {
|
|
|
|
list_move(&sp->so_lru, &doomed);
|
|
|
|
nfs4_remove_state_owner_locked(sp);
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
spin_unlock(&clp->cl_lock);
|
NFS: Cache state owners after files are closed
Servers have a finite amount of memory to store NFSv4 open and lock
owners. Moreover, servers may have a difficult time determining when
they can reap their state owner table, thanks to gray areas in the
NFSv4 protocol specification. Thus clients should be careful to reuse
state owners when possible.
Currently Linux is not too careful. When a user has closed all her
files on one mount point, the state owner's reference count goes to
zero, and it is released. The next OPEN allocates a new one. A
workload that serially opens and closes files can run through a large
number of open owners this way.
When a state owner's reference count goes to zero, slap it onto a free
list for that nfs_server, with an expiry time. Garbage collect before
looking for a state owner. This makes state owners for active users
available for re-use.
Now that there can be unused state owners remaining at umount time,
purge the state owner free list when a server is destroyed. Also be
sure not to reclaim unused state owners during state recovery.
This change has benefits for the client as well. For some workloads,
this approach drops the number of OPEN_CONFIRM calls from the same as
the number of OPEN calls, down to just one. This reduces wire traffic
and thus open(2) latency. Before this patch, untarring a kernel
source tarball shows the OPEN_CONFIRM call counter steadily increasing
through the test. With the patch, the OPEN_CONFIRM count remains at 1
throughout the entire untar.
As long as the expiry time is kept short, I don't think garbage
collection should be terribly expensive, although it does bounce the
clp->cl_lock around a bit.
[ At some point we should rationalize the use of the nfs_server
->destroy method. ]
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
[Trond: Fixed a garbage collection race and a few efficiency issues]
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2011-12-07 04:13:48 +07:00
|
|
|
|
|
|
|
list_for_each_entry_safe(sp, tmp, &doomed, so_lru) {
|
|
|
|
list_del(&sp->so_lru);
|
|
|
|
nfs4_free_state_owner(sp);
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct nfs4_state *
|
|
|
|
nfs4_alloc_open_state(void)
|
|
|
|
{
|
|
|
|
struct nfs4_state *state;
|
|
|
|
|
2010-05-13 23:51:01 +07:00
|
|
|
state = kzalloc(sizeof(*state), GFP_NOFS);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!state)
|
|
|
|
return NULL;
|
|
|
|
atomic_set(&state->count, 1);
|
|
|
|
INIT_LIST_HEAD(&state->lock_states);
|
2005-06-23 00:16:32 +07:00
|
|
|
spin_lock_init(&state->state_lock);
|
2007-07-09 21:45:42 +07:00
|
|
|
seqlock_init(&state->seqlock);
|
2005-04-17 05:20:36 +07:00
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
2005-11-05 03:32:58 +07:00
|
|
|
void
|
2008-12-24 03:21:56 +07:00
|
|
|
nfs4_state_set_mode_locked(struct nfs4_state *state, fmode_t fmode)
|
2005-11-05 03:32:58 +07:00
|
|
|
{
|
2008-12-24 03:21:56 +07:00
|
|
|
if (state->state == fmode)
|
2005-11-05 03:32:58 +07:00
|
|
|
return;
|
|
|
|
/* NB! List reordering - see the reclaim code for why. */
|
2008-12-24 03:21:56 +07:00
|
|
|
if ((fmode & FMODE_WRITE) != (state->state & FMODE_WRITE)) {
|
|
|
|
if (fmode & FMODE_WRITE)
|
2005-11-05 03:32:58 +07:00
|
|
|
list_move(&state->open_states, &state->owner->so_states);
|
|
|
|
else
|
|
|
|
list_move_tail(&state->open_states, &state->owner->so_states);
|
|
|
|
}
|
2008-12-24 03:21:56 +07:00
|
|
|
state->state = fmode;
|
2005-11-05 03:32:58 +07:00
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static struct nfs4_state *
|
|
|
|
__nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner)
|
|
|
|
{
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
|
|
|
struct nfs4_state *state;
|
|
|
|
|
|
|
|
list_for_each_entry(state, &nfsi->open_states, inode_states) {
|
2007-07-04 01:41:19 +07:00
|
|
|
if (state->owner != owner)
|
2005-04-17 05:20:36 +07:00
|
|
|
continue;
|
2013-03-15 03:57:48 +07:00
|
|
|
if (!nfs4_valid_open_stateid(state))
|
|
|
|
continue;
|
2007-07-04 01:41:19 +07:00
|
|
|
if (atomic_inc_not_zero(&state->count))
|
2005-04-17 05:20:36 +07:00
|
|
|
return state;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nfs4_free_open_state(struct nfs4_state *state)
|
|
|
|
{
|
|
|
|
kfree(state);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct nfs4_state *
|
|
|
|
nfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner)
|
|
|
|
{
|
|
|
|
struct nfs4_state *state, *new;
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
|
|
|
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
state = __nfs4_find_state_byowner(inode, owner);
|
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
if (state)
|
|
|
|
goto out;
|
|
|
|
new = nfs4_alloc_open_state();
|
2005-10-21 04:22:47 +07:00
|
|
|
spin_lock(&owner->so_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
state = __nfs4_find_state_byowner(inode, owner);
|
|
|
|
if (state == NULL && new != NULL) {
|
|
|
|
state = new;
|
|
|
|
state->owner = owner;
|
|
|
|
atomic_inc(&owner->so_count);
|
|
|
|
list_add(&state->inode_states, &nfsi->open_states);
|
2011-03-29 14:08:50 +07:00
|
|
|
ihold(inode);
|
|
|
|
state->inode = inode;
|
2005-04-17 05:20:36 +07:00
|
|
|
spin_unlock(&inode->i_lock);
|
2005-10-21 04:22:47 +07:00
|
|
|
/* Note: The reclaim code dictates that we add stateless
|
|
|
|
* and read-only stateids to the end of the list */
|
|
|
|
list_add_tail(&state->open_states, &owner->so_states);
|
|
|
|
spin_unlock(&owner->so_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
} else {
|
|
|
|
spin_unlock(&inode->i_lock);
|
2005-10-21 04:22:47 +07:00
|
|
|
spin_unlock(&owner->so_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (new)
|
|
|
|
nfs4_free_open_state(new);
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nfs4_put_open_state(struct nfs4_state *state)
|
|
|
|
{
|
|
|
|
struct inode *inode = state->inode;
|
|
|
|
struct nfs4_state_owner *owner = state->owner;
|
|
|
|
|
2005-10-21 04:22:47 +07:00
|
|
|
if (!atomic_dec_and_lock(&state->count, &owner->so_lock))
|
2005-04-17 05:20:36 +07:00
|
|
|
return;
|
2005-10-21 04:22:47 +07:00
|
|
|
spin_lock(&inode->i_lock);
|
2007-07-27 21:23:05 +07:00
|
|
|
list_del(&state->inode_states);
|
2005-04-17 05:20:36 +07:00
|
|
|
list_del(&state->open_states);
|
2005-10-21 04:22:47 +07:00
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
spin_unlock(&owner->so_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
iput(inode);
|
|
|
|
nfs4_free_open_state(state);
|
|
|
|
nfs4_put_state_owner(owner);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-10-19 04:20:13 +07:00
|
|
|
* Close the current file.
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2011-06-23 05:20:23 +07:00
|
|
|
static void __nfs4_close(struct nfs4_state *state,
|
2010-05-13 23:51:01 +07:00
|
|
|
fmode_t fmode, gfp_t gfp_mask, int wait)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct nfs4_state_owner *owner = state->owner;
|
2007-07-06 05:07:55 +07:00
|
|
|
int call_close = 0;
|
2008-12-24 03:21:56 +07:00
|
|
|
fmode_t newstate;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
atomic_inc(&owner->so_count);
|
|
|
|
/* Protect against nfs4_find_state() */
|
2005-10-21 04:22:47 +07:00
|
|
|
spin_lock(&owner->so_lock);
|
2008-12-24 03:21:56 +07:00
|
|
|
switch (fmode & (FMODE_READ | FMODE_WRITE)) {
|
2006-01-03 15:55:13 +07:00
|
|
|
case FMODE_READ:
|
|
|
|
state->n_rdonly--;
|
|
|
|
break;
|
|
|
|
case FMODE_WRITE:
|
|
|
|
state->n_wronly--;
|
|
|
|
break;
|
|
|
|
case FMODE_READ|FMODE_WRITE:
|
|
|
|
state->n_rdwr--;
|
|
|
|
}
|
2007-07-06 05:07:55 +07:00
|
|
|
newstate = FMODE_READ|FMODE_WRITE;
|
2006-01-03 15:55:13 +07:00
|
|
|
if (state->n_rdwr == 0) {
|
2007-07-06 05:07:55 +07:00
|
|
|
if (state->n_rdonly == 0) {
|
2006-01-03 15:55:13 +07:00
|
|
|
newstate &= ~FMODE_READ;
|
2007-07-06 05:07:55 +07:00
|
|
|
call_close |= test_bit(NFS_O_RDONLY_STATE, &state->flags);
|
|
|
|
call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags);
|
|
|
|
}
|
|
|
|
if (state->n_wronly == 0) {
|
2006-01-03 15:55:13 +07:00
|
|
|
newstate &= ~FMODE_WRITE;
|
2007-07-06 05:07:55 +07:00
|
|
|
call_close |= test_bit(NFS_O_WRONLY_STATE, &state->flags);
|
|
|
|
call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags);
|
|
|
|
}
|
|
|
|
if (newstate == 0)
|
|
|
|
clear_bit(NFS_DELEGATED_STATE, &state->flags);
|
2006-01-03 15:55:13 +07:00
|
|
|
}
|
2007-07-06 05:07:55 +07:00
|
|
|
nfs4_state_set_mode_locked(state, newstate);
|
2005-10-21 04:22:47 +07:00
|
|
|
spin_unlock(&owner->so_lock);
|
2005-11-05 03:32:58 +07:00
|
|
|
|
2007-07-06 05:07:55 +07:00
|
|
|
if (!call_close) {
|
2007-06-12 10:05:07 +07:00
|
|
|
nfs4_put_open_state(state);
|
|
|
|
nfs4_put_state_owner(owner);
|
2012-09-21 07:31:51 +07:00
|
|
|
} else
|
|
|
|
nfs4_do_close(state, gfp_mask, wait);
|
2007-10-19 05:03:27 +07:00
|
|
|
}
|
|
|
|
|
2011-06-23 05:20:23 +07:00
|
|
|
void nfs4_close_state(struct nfs4_state *state, fmode_t fmode)
|
2007-10-19 05:03:27 +07:00
|
|
|
{
|
2011-06-23 05:20:23 +07:00
|
|
|
__nfs4_close(state, fmode, GFP_NOFS, 0);
|
2007-10-19 05:03:27 +07:00
|
|
|
}
|
|
|
|
|
2011-06-23 05:20:23 +07:00
|
|
|
void nfs4_close_sync(struct nfs4_state *state, fmode_t fmode)
|
2007-10-19 05:03:27 +07:00
|
|
|
{
|
2011-06-23 05:20:23 +07:00
|
|
|
__nfs4_close(state, fmode, GFP_KERNEL, 1);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Search the state->lock_states for an existing lock_owner
|
|
|
|
* that is compatible with current->files
|
|
|
|
*/
|
|
|
|
static struct nfs4_lock_state *
|
2014-05-01 17:28:45 +07:00
|
|
|
__nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct nfs4_lock_state *pos;
|
|
|
|
list_for_each_entry(pos, &state->lock_states, ls_locks) {
|
2014-05-01 17:28:45 +07:00
|
|
|
if (pos->ls_owner != fl_owner)
|
2005-04-17 05:20:36 +07:00
|
|
|
continue;
|
|
|
|
atomic_inc(&pos->ls_count);
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-05-01 17:28:46 +07:00
|
|
|
static void
|
|
|
|
free_lock_state_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct nfs4_lock_state *lsp = container_of(work,
|
|
|
|
struct nfs4_lock_state, ls_release);
|
|
|
|
struct nfs4_state *state = lsp->ls_state;
|
|
|
|
struct nfs_server *server = state->owner->so_server;
|
|
|
|
struct nfs_client *clp = server->nfs_client;
|
|
|
|
|
|
|
|
clp->cl_mvops->free_lock_state(server, lsp);
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* Return a compatible lock_state. If no initialized lock_state structure
|
|
|
|
* exists, return an uninitialized one.
|
|
|
|
*
|
|
|
|
*/
|
2014-05-01 17:28:45 +07:00
|
|
|
static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct nfs4_lock_state *lsp;
|
2010-12-24 08:32:43 +07:00
|
|
|
struct nfs_server *server = state->owner->so_server;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-05-13 23:51:01 +07:00
|
|
|
lsp = kzalloc(sizeof(*lsp), GFP_NOFS);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (lsp == NULL)
|
|
|
|
return NULL;
|
2012-01-18 10:04:25 +07:00
|
|
|
nfs4_init_seqid_counter(&lsp->ls_seqid);
|
2005-04-17 05:20:36 +07:00
|
|
|
atomic_set(&lsp->ls_count, 1);
|
2009-07-22 03:47:46 +07:00
|
|
|
lsp->ls_state = state;
|
2014-05-01 17:28:45 +07:00
|
|
|
lsp->ls_owner = fl_owner;
|
2012-01-18 10:04:25 +07:00
|
|
|
lsp->ls_seqid.owner_id = ida_simple_get(&server->lockowner_id, 0, 0, GFP_NOFS);
|
|
|
|
if (lsp->ls_seqid.owner_id < 0)
|
2012-01-18 10:04:25 +07:00
|
|
|
goto out_free;
|
2005-06-23 00:16:32 +07:00
|
|
|
INIT_LIST_HEAD(&lsp->ls_locks);
|
2014-05-01 17:28:46 +07:00
|
|
|
INIT_WORK(&lsp->ls_release, free_lock_state_work);
|
2005-04-17 05:20:36 +07:00
|
|
|
return lsp;
|
2012-01-18 10:04:25 +07:00
|
|
|
out_free:
|
|
|
|
kfree(lsp);
|
|
|
|
return NULL;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2012-03-20 03:17:18 +07:00
|
|
|
void nfs4_free_lock_state(struct nfs_server *server, struct nfs4_lock_state *lsp)
|
2007-07-03 00:58:33 +07:00
|
|
|
{
|
2012-01-18 10:04:25 +07:00
|
|
|
ida_simple_remove(&server->lockowner_id, lsp->ls_seqid.owner_id);
|
2012-01-18 10:04:25 +07:00
|
|
|
nfs4_destroy_seqid_counter(&lsp->ls_seqid);
|
2007-07-03 00:58:33 +07:00
|
|
|
kfree(lsp);
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* Return a compatible lock_state. If no initialized lock_state structure
|
|
|
|
* exists, return an uninitialized one.
|
|
|
|
*
|
|
|
|
*/
|
2014-05-01 17:28:45 +07:00
|
|
|
static struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-06-23 00:16:32 +07:00
|
|
|
struct nfs4_lock_state *lsp, *new = NULL;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-06-23 00:16:32 +07:00
|
|
|
for(;;) {
|
|
|
|
spin_lock(&state->state_lock);
|
2014-05-01 17:28:45 +07:00
|
|
|
lsp = __nfs4_find_lock_state(state, owner);
|
2005-06-23 00:16:32 +07:00
|
|
|
if (lsp != NULL)
|
|
|
|
break;
|
|
|
|
if (new != NULL) {
|
|
|
|
list_add(&new->ls_locks, &state->lock_states);
|
|
|
|
set_bit(LK_STATE_IN_USE, &state->flags);
|
|
|
|
lsp = new;
|
|
|
|
new = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
spin_unlock(&state->state_lock);
|
2014-05-01 17:28:45 +07:00
|
|
|
new = nfs4_alloc_lock_state(state, owner);
|
2005-06-23 00:16:32 +07:00
|
|
|
if (new == NULL)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
spin_unlock(&state->state_lock);
|
2007-07-03 00:58:33 +07:00
|
|
|
if (new != NULL)
|
2012-03-20 03:17:18 +07:00
|
|
|
nfs4_free_lock_state(state->owner->so_server, new);
|
2005-04-17 05:20:36 +07:00
|
|
|
return lsp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-06-23 00:16:32 +07:00
|
|
|
* Release reference to lock_state, and free it if we see that
|
|
|
|
* it is no longer in use
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2005-10-19 04:20:15 +07:00
|
|
|
void nfs4_put_lock_state(struct nfs4_lock_state *lsp)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2013-05-04 03:22:55 +07:00
|
|
|
struct nfs_server *server;
|
2005-06-23 00:16:32 +07:00
|
|
|
struct nfs4_state *state;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-06-23 00:16:32 +07:00
|
|
|
if (lsp == NULL)
|
|
|
|
return;
|
|
|
|
state = lsp->ls_state;
|
|
|
|
if (!atomic_dec_and_lock(&lsp->ls_count, &state->state_lock))
|
|
|
|
return;
|
|
|
|
list_del(&lsp->ls_locks);
|
|
|
|
if (list_empty(&state->lock_states))
|
|
|
|
clear_bit(LK_STATE_IN_USE, &state->flags);
|
|
|
|
spin_unlock(&state->state_lock);
|
2014-05-01 17:28:46 +07:00
|
|
|
if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags))
|
|
|
|
queue_work(nfsiod_workqueue, &lsp->ls_release);
|
|
|
|
else {
|
|
|
|
server = state->owner->so_server;
|
2013-05-04 03:22:55 +07:00
|
|
|
nfs4_free_lock_state(server, lsp);
|
2014-05-01 17:28:46 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2005-06-23 00:16:32 +07:00
|
|
|
static void nfs4_fl_copy_lock(struct file_lock *dst, struct file_lock *src)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-06-23 00:16:32 +07:00
|
|
|
struct nfs4_lock_state *lsp = src->fl_u.nfs4_fl.owner;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-06-23 00:16:32 +07:00
|
|
|
dst->fl_u.nfs4_fl.owner = lsp;
|
|
|
|
atomic_inc(&lsp->ls_count);
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-06-23 00:16:32 +07:00
|
|
|
static void nfs4_fl_release_lock(struct file_lock *fl)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-06-23 00:16:32 +07:00
|
|
|
nfs4_put_lock_state(fl->fl_u.nfs4_fl.owner);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2009-09-22 07:01:11 +07:00
|
|
|
static const struct file_lock_operations nfs4_fl_lock_ops = {
|
2005-06-23 00:16:32 +07:00
|
|
|
.fl_copy_lock = nfs4_fl_copy_lock,
|
|
|
|
.fl_release_private = nfs4_fl_release_lock,
|
|
|
|
};
|
|
|
|
|
|
|
|
int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-06-23 00:16:32 +07:00
|
|
|
struct nfs4_lock_state *lsp;
|
|
|
|
|
|
|
|
if (fl->fl_ops != NULL)
|
|
|
|
return 0;
|
2014-05-01 17:28:45 +07:00
|
|
|
lsp = nfs4_get_lock_state(state, fl->fl_owner);
|
2005-06-23 00:16:32 +07:00
|
|
|
if (lsp == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
fl->fl_u.nfs4_fl.owner = lsp;
|
|
|
|
fl->fl_ops = &nfs4_fl_lock_ops;
|
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2013-03-17 07:54:34 +07:00
|
|
|
static int nfs4_copy_lock_stateid(nfs4_stateid *dst,
|
|
|
|
struct nfs4_state *state,
|
2012-08-14 05:54:45 +07:00
|
|
|
const struct nfs_lockowner *lockowner)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-06-23 00:16:32 +07:00
|
|
|
struct nfs4_lock_state *lsp;
|
2012-08-14 05:54:45 +07:00
|
|
|
fl_owner_t fl_owner;
|
2013-03-17 07:54:34 +07:00
|
|
|
int ret = -ENOENT;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2012-08-14 05:54:45 +07:00
|
|
|
|
|
|
|
if (lockowner == NULL)
|
|
|
|
goto out;
|
|
|
|
|
2005-06-23 00:16:32 +07:00
|
|
|
if (test_bit(LK_STATE_IN_USE, &state->flags) == 0)
|
2012-03-09 05:42:01 +07:00
|
|
|
goto out;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2012-08-14 05:54:45 +07:00
|
|
|
fl_owner = lockowner->l_owner;
|
2005-06-23 00:16:32 +07:00
|
|
|
spin_lock(&state->state_lock);
|
2014-05-01 17:28:45 +07:00
|
|
|
lsp = __nfs4_find_lock_state(state, fl_owner);
|
2013-09-04 14:04:49 +07:00
|
|
|
if (lsp && test_bit(NFS_LOCK_LOST, &lsp->ls_flags))
|
|
|
|
ret = -EIO;
|
|
|
|
else if (lsp != NULL && test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) != 0) {
|
2012-03-05 06:13:56 +07:00
|
|
|
nfs4_stateid_copy(dst, &lsp->ls_stateid);
|
2013-03-17 07:54:34 +07:00
|
|
|
ret = 0;
|
2012-03-09 05:42:01 +07:00
|
|
|
}
|
2005-06-23 00:16:32 +07:00
|
|
|
spin_unlock(&state->state_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
nfs4_put_lock_state(lsp);
|
2012-03-09 05:42:01 +07:00
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-03-05 01:12:03 +07:00
|
|
|
static void nfs4_copy_open_stateid(nfs4_stateid *dst, struct nfs4_state *state)
|
2012-03-09 05:42:01 +07:00
|
|
|
{
|
2013-04-20 12:25:45 +07:00
|
|
|
const nfs4_stateid *src;
|
2012-03-09 05:42:01 +07:00
|
|
|
int seq;
|
|
|
|
|
|
|
|
do {
|
2013-04-20 12:25:45 +07:00
|
|
|
src = &zero_stateid;
|
2012-03-09 05:42:01 +07:00
|
|
|
seq = read_seqbegin(&state->seqlock);
|
2013-04-20 12:25:45 +07:00
|
|
|
if (test_bit(NFS_OPEN_STATE, &state->flags))
|
|
|
|
src = &state->open_stateid;
|
|
|
|
nfs4_stateid_copy(dst, src);
|
2012-03-09 05:42:01 +07:00
|
|
|
} while (read_seqretry(&state->seqlock, seq));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Byte-range lock aware utility to initialize the stateid of read/write
|
|
|
|
* requests.
|
|
|
|
*/
|
2013-03-17 07:54:34 +07:00
|
|
|
int nfs4_select_rw_stateid(nfs4_stateid *dst, struct nfs4_state *state,
|
2012-08-14 05:54:45 +07:00
|
|
|
fmode_t fmode, const struct nfs_lockowner *lockowner)
|
2012-03-09 05:42:01 +07:00
|
|
|
{
|
2013-09-04 14:04:49 +07:00
|
|
|
int ret = nfs4_copy_lock_stateid(dst, state, lockowner);
|
|
|
|
if (ret == -EIO)
|
|
|
|
/* A lost lock - don't even consider delegations */
|
|
|
|
goto out;
|
2014-02-18 22:36:05 +07:00
|
|
|
/* returns true if delegation stateid found and copied */
|
|
|
|
if (nfs4_copy_delegation_stateid(dst, state->inode, fmode)) {
|
|
|
|
ret = 0;
|
2013-03-17 07:54:34 +07:00
|
|
|
goto out;
|
2014-02-18 22:36:05 +07:00
|
|
|
}
|
2013-03-17 07:54:34 +07:00
|
|
|
if (ret != -ENOENT)
|
2013-09-04 14:04:49 +07:00
|
|
|
/* nfs4_copy_delegation_stateid() didn't over-write
|
|
|
|
* dst, so it still has the lock stateid which we now
|
|
|
|
* choose to use.
|
|
|
|
*/
|
2013-03-17 07:54:34 +07:00
|
|
|
goto out;
|
2014-03-05 01:12:03 +07:00
|
|
|
nfs4_copy_open_stateid(dst, state);
|
|
|
|
ret = 0;
|
2013-03-17 07:54:34 +07:00
|
|
|
out:
|
2013-03-18 02:31:15 +07:00
|
|
|
if (nfs_server_capable(state->inode, NFS_CAP_STATEID_NFSV41))
|
|
|
|
dst->seqid = 0;
|
2013-03-17 07:54:34 +07:00
|
|
|
return ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2010-05-13 23:51:01 +07:00
|
|
|
struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter, gfp_t gfp_mask)
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
{
|
|
|
|
struct nfs_seqid *new;
|
|
|
|
|
2010-05-13 23:51:01 +07:00
|
|
|
new = kmalloc(sizeof(*new), gfp_mask);
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
if (new != NULL) {
|
|
|
|
new->sequence = counter;
|
2008-01-09 05:56:07 +07:00
|
|
|
INIT_LIST_HEAD(&new->list);
|
2012-01-21 06:47:05 +07:00
|
|
|
new->task = NULL;
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
}
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
2009-12-16 02:47:36 +07:00
|
|
|
void nfs_release_seqid(struct nfs_seqid *seqid)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2012-01-21 06:47:05 +07:00
|
|
|
struct nfs_seqid_counter *sequence;
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
|
2012-01-21 06:47:05 +07:00
|
|
|
if (list_empty(&seqid->list))
|
|
|
|
return;
|
|
|
|
sequence = seqid->sequence;
|
|
|
|
spin_lock(&sequence->lock);
|
|
|
|
list_del_init(&seqid->list);
|
|
|
|
if (!list_empty(&sequence->list)) {
|
|
|
|
struct nfs_seqid *next;
|
|
|
|
|
|
|
|
next = list_first_entry(&sequence->list,
|
|
|
|
struct nfs_seqid, list);
|
|
|
|
rpc_wake_up_queued_task(&sequence->wait, next->task);
|
2008-01-09 05:56:07 +07:00
|
|
|
}
|
2012-01-21 06:47:05 +07:00
|
|
|
spin_unlock(&sequence->lock);
|
2009-12-16 02:47:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
void nfs_free_seqid(struct nfs_seqid *seqid)
|
|
|
|
{
|
|
|
|
nfs_release_seqid(seqid);
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
kfree(seqid);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
* Increment the seqid if the OPEN/OPEN_DOWNGRADE/CLOSE succeeded, or
|
|
|
|
* failed with a seqid incrementing error -
|
2013-12-13 01:09:00 +07:00
|
|
|
* see comments nfs4.h:seqid_mutating_error()
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
*/
|
2007-07-03 01:03:03 +07:00
|
|
|
static void nfs_increment_seqid(int status, struct nfs_seqid *seqid)
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
{
|
|
|
|
switch (status) {
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case -NFS4ERR_BAD_SEQID:
|
2007-07-09 03:49:11 +07:00
|
|
|
if (seqid->sequence->flags & NFS_SEQID_CONFIRMED)
|
|
|
|
return;
|
2012-03-13 05:01:48 +07:00
|
|
|
pr_warn_ratelimited("NFS: v4 server returned a bad"
|
2008-02-14 04:09:35 +07:00
|
|
|
" sequence-id error on an"
|
|
|
|
" unconfirmed sequence %p!\n",
|
2007-07-09 03:49:11 +07:00
|
|
|
seqid->sequence);
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
case -NFS4ERR_STALE_CLIENTID:
|
|
|
|
case -NFS4ERR_STALE_STATEID:
|
|
|
|
case -NFS4ERR_BAD_STATEID:
|
|
|
|
case -NFS4ERR_BADXDR:
|
|
|
|
case -NFS4ERR_RESOURCE:
|
|
|
|
case -NFS4ERR_NOFILEHANDLE:
|
|
|
|
/* Non-seqid mutating errors */
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
/*
|
|
|
|
* Note: no locking needed as we are guaranteed to be first
|
|
|
|
* on the sequence list
|
|
|
|
*/
|
|
|
|
seqid->sequence->counter++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid)
|
|
|
|
{
|
2009-04-01 20:22:52 +07:00
|
|
|
struct nfs4_state_owner *sp = container_of(seqid->sequence,
|
|
|
|
struct nfs4_state_owner, so_seqid);
|
|
|
|
struct nfs_server *server = sp->so_server;
|
|
|
|
|
|
|
|
if (status == -NFS4ERR_BAD_SEQID)
|
2005-04-17 05:20:36 +07:00
|
|
|
nfs4_drop_state_owner(sp);
|
2009-04-01 20:22:52 +07:00
|
|
|
if (!nfs4_has_session(server->nfs_client))
|
|
|
|
nfs_increment_seqid(status, seqid);
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Increment the seqid if the LOCK/LOCKU succeeded, or
|
|
|
|
* failed with a seqid incrementing error -
|
2013-12-13 01:09:00 +07:00
|
|
|
* see comments nfs4.h:seqid_mutating_error()
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
*/
|
|
|
|
void nfs_increment_lock_seqid(int status, struct nfs_seqid *seqid)
|
|
|
|
{
|
2007-07-03 01:03:03 +07:00
|
|
|
nfs_increment_seqid(status, seqid);
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
int nfs_wait_on_sequence(struct nfs_seqid *seqid, struct rpc_task *task)
|
|
|
|
{
|
2012-01-18 10:04:25 +07:00
|
|
|
struct nfs_seqid_counter *sequence = seqid->sequence;
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
spin_lock(&sequence->lock);
|
2012-01-21 06:47:05 +07:00
|
|
|
seqid->task = task;
|
2008-01-09 05:56:07 +07:00
|
|
|
if (list_empty(&seqid->list))
|
|
|
|
list_add_tail(&seqid->list, &sequence->list);
|
|
|
|
if (list_first_entry(&sequence->list, struct nfs_seqid, list) == seqid)
|
|
|
|
goto unlock;
|
2008-02-23 04:34:17 +07:00
|
|
|
rpc_sleep_on(&sequence->wait, task, NULL);
|
2008-01-09 05:56:07 +07:00
|
|
|
status = -EAGAIN;
|
|
|
|
unlock:
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
spin_unlock(&sequence->lock);
|
|
|
|
return status;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-12-24 03:21:48 +07:00
|
|
|
static int nfs4_run_state_manager(void *);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-12-24 03:21:48 +07:00
|
|
|
static void nfs4_clear_state_manager_bit(struct nfs_client *clp)
|
2006-01-03 15:55:22 +07:00
|
|
|
{
|
2014-03-18 00:06:10 +07:00
|
|
|
smp_mb__before_atomic();
|
2008-12-24 03:21:48 +07:00
|
|
|
clear_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state);
|
2014-03-18 00:06:10 +07:00
|
|
|
smp_mb__after_atomic();
|
2008-12-24 03:21:48 +07:00
|
|
|
wake_up_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING);
|
2006-01-03 15:55:22 +07:00
|
|
|
rpc_wake_up(&clp->cl_rpcwaitq);
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
2008-12-24 03:21:48 +07:00
|
|
|
* Schedule the nfs_client asynchronous state management routine
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2008-12-24 03:21:50 +07:00
|
|
|
void nfs4_schedule_state_manager(struct nfs_client *clp)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-01-03 15:55:23 +07:00
|
|
|
struct task_struct *task;
|
2012-03-02 05:00:56 +07:00
|
|
|
char buf[INET6_ADDRSTRLEN + sizeof("-manager") + 1];
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-12-24 03:21:48 +07:00
|
|
|
if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
|
|
|
|
return;
|
2006-01-03 15:55:23 +07:00
|
|
|
__module_get(THIS_MODULE);
|
|
|
|
atomic_inc(&clp->cl_count);
|
2012-03-02 05:00:56 +07:00
|
|
|
|
|
|
|
/* The rcu_read_lock() is not strictly necessary, as the state
|
|
|
|
* manager is the only thread that ever changes the rpc_xprt
|
|
|
|
* after it's initialized. At this point, we're single threaded. */
|
|
|
|
rcu_read_lock();
|
|
|
|
snprintf(buf, sizeof(buf), "%s-manager",
|
|
|
|
rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR));
|
|
|
|
rcu_read_unlock();
|
2013-07-04 05:04:58 +07:00
|
|
|
task = kthread_run(nfs4_run_state_manager, clp, "%s", buf);
|
2012-03-02 05:00:56 +07:00
|
|
|
if (IS_ERR(task)) {
|
|
|
|
printk(KERN_ERR "%s: kthread_run: %ld\n",
|
|
|
|
__func__, PTR_ERR(task));
|
|
|
|
nfs4_clear_state_manager_bit(clp);
|
|
|
|
nfs_put_client(clp);
|
|
|
|
module_put(THIS_MODULE);
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-03-10 04:00:53 +07:00
|
|
|
* Schedule a lease recovery attempt
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2011-03-10 04:00:53 +07:00
|
|
|
void nfs4_schedule_lease_recovery(struct nfs_client *clp)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
if (!clp)
|
|
|
|
return;
|
2008-12-24 03:21:42 +07:00
|
|
|
if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
|
|
|
|
set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
|
2012-05-29 02:12:27 +07:00
|
|
|
dprintk("%s: scheduling lease recovery for server %s\n", __func__,
|
|
|
|
clp->cl_hostname);
|
2008-12-24 03:21:48 +07:00
|
|
|
nfs4_schedule_state_manager(clp);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2012-03-07 22:49:41 +07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs4_schedule_lease_recovery);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2013-10-18 01:13:02 +07:00
|
|
|
/**
|
|
|
|
* nfs4_schedule_migration_recovery - trigger migration recovery
|
|
|
|
*
|
|
|
|
* @server: FSID that is migrating
|
|
|
|
*
|
|
|
|
* Returns zero if recovery has started, otherwise a negative NFS4ERR
|
|
|
|
* value is returned.
|
|
|
|
*/
|
|
|
|
int nfs4_schedule_migration_recovery(const struct nfs_server *server)
|
|
|
|
{
|
|
|
|
struct nfs_client *clp = server->nfs_client;
|
|
|
|
|
|
|
|
if (server->fh_expire_type != NFS4_FH_PERSISTENT) {
|
|
|
|
pr_err("NFS: volatile file handles not supported (server %s)\n",
|
|
|
|
clp->cl_hostname);
|
|
|
|
return -NFS4ERR_IO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (test_bit(NFS_MIG_FAILED, &server->mig_status))
|
|
|
|
return -NFS4ERR_IO;
|
|
|
|
|
|
|
|
dprintk("%s: scheduling migration recovery for (%llx:%llx) on %s\n",
|
|
|
|
__func__,
|
|
|
|
(unsigned long long)server->fsid.major,
|
|
|
|
(unsigned long long)server->fsid.minor,
|
|
|
|
clp->cl_hostname);
|
|
|
|
|
|
|
|
set_bit(NFS_MIG_IN_TRANSITION,
|
|
|
|
&((struct nfs_server *)server)->mig_status);
|
|
|
|
set_bit(NFS4CLNT_MOVED, &clp->cl_state);
|
|
|
|
|
|
|
|
nfs4_schedule_state_manager(clp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nfs4_schedule_migration_recovery);
|
|
|
|
|
2013-10-18 01:13:35 +07:00
|
|
|
/**
|
|
|
|
* nfs4_schedule_lease_moved_recovery - start lease-moved recovery
|
|
|
|
*
|
|
|
|
* @clp: server to check for moved leases
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
void nfs4_schedule_lease_moved_recovery(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
dprintk("%s: scheduling lease-moved recovery for client ID %llx on %s\n",
|
|
|
|
__func__, clp->cl_clientid, clp->cl_hostname);
|
|
|
|
|
|
|
|
set_bit(NFS4CLNT_LEASE_MOVED, &clp->cl_state);
|
|
|
|
nfs4_schedule_state_manager(clp);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nfs4_schedule_lease_moved_recovery);
|
|
|
|
|
2012-11-27 01:13:29 +07:00
|
|
|
int nfs4_wait_clnt_recover(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
|
|
|
|
might_sleep();
|
|
|
|
|
2013-10-18 01:14:10 +07:00
|
|
|
atomic_inc(&clp->cl_count);
|
sched: Remove proliferation of wait_on_bit() action functions
The current "wait_on_bit" interface requires an 'action'
function to be provided which does the actual waiting.
There are over 20 such functions, many of them identical.
Most cases can be satisfied by one of just two functions, one
which uses io_schedule() and one which just uses schedule().
So:
Rename wait_on_bit and wait_on_bit_lock to
wait_on_bit_action and wait_on_bit_lock_action
to make it explicit that they need an action function.
Introduce new wait_on_bit{,_lock} and wait_on_bit{,_lock}_io
which are *not* given an action function but implicitly use
a standard one.
The decision to error-out if a signal is pending is now made
based on the 'mode' argument rather than being encoded in the action
function.
All instances of the old wait_on_bit and wait_on_bit_lock which
can use the new version have been changed accordingly and their
action functions have been discarded.
wait_on_bit{_lock} does not return any specific error code in the
event of a signal so the caller must check for non-zero and
interpolate their own error code as appropriate.
The wait_on_bit() call in __fscache_wait_on_invalidate() was
ambiguous as it specified TASK_UNINTERRUPTIBLE but used
fscache_wait_bit_interruptible as an action function.
David Howells confirms this should be uniformly
"uninterruptible"
The main remaining user of wait_on_bit{,_lock}_action is NFS
which needs to use a freezer-aware schedule() call.
A comment in fs/gfs2/glock.c notes that having multiple 'action'
functions is useful as they display differently in the 'wchan'
field of 'ps'. (and /proc/$PID/wchan).
As the new bit_wait{,_io} functions are tagged "__sched", they
will not show up at all, but something higher in the stack. So
the distinction will still be visible, only with different
function names (gds2_glock_wait versus gfs2_glock_dq_wait in the
gfs2/glock.c case).
Since first version of this patch (against 3.15) two new action
functions appeared, on in NFS and one in CIFS. CIFS also now
uses an action function that makes the same freezer aware
schedule call as NFS.
Signed-off-by: NeilBrown <neilb@suse.de>
Acked-by: David Howells <dhowells@redhat.com> (fscache, keys)
Acked-by: Steven Whitehouse <swhiteho@redhat.com> (gfs2)
Acked-by: Peter Zijlstra <peterz@infradead.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Steve French <sfrench@samba.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Link: http://lkml.kernel.org/r/20140707051603.28027.72349.stgit@notabene.brown
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2014-07-07 12:16:04 +07:00
|
|
|
res = wait_on_bit_action(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING,
|
|
|
|
nfs_wait_bit_killable, TASK_KILLABLE);
|
2012-11-27 01:13:29 +07:00
|
|
|
if (res)
|
2013-10-18 01:14:10 +07:00
|
|
|
goto out;
|
2012-11-27 01:13:29 +07:00
|
|
|
if (clp->cl_cons_state < 0)
|
2013-10-18 01:14:10 +07:00
|
|
|
res = clp->cl_cons_state;
|
|
|
|
out:
|
|
|
|
nfs_put_client(clp);
|
|
|
|
return res;
|
2012-11-27 01:13:29 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
int nfs4_client_recover_expired_lease(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
unsigned int loop;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
for (loop = NFS4_MAX_LOOP_ON_RECOVER; loop != 0; loop--) {
|
|
|
|
ret = nfs4_wait_clnt_recover(clp);
|
|
|
|
if (ret != 0)
|
|
|
|
break;
|
|
|
|
if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) &&
|
|
|
|
!test_bit(NFS4CLNT_CHECK_LEASE,&clp->cl_state))
|
|
|
|
break;
|
|
|
|
nfs4_schedule_state_manager(clp);
|
|
|
|
ret = -EIO;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-03-10 23:23:15 +07:00
|
|
|
/*
|
|
|
|
* nfs40_handle_cb_pathdown - return all delegations after NFS4ERR_CB_PATH_DOWN
|
|
|
|
* @clp: client to process
|
|
|
|
*
|
|
|
|
* Set the NFS4CLNT_LEASE_EXPIRED state in order to force a
|
|
|
|
* resend of the SETCLIENTID and hence re-establish the
|
|
|
|
* callback channel. Then return all existing delegations.
|
|
|
|
*/
|
|
|
|
static void nfs40_handle_cb_pathdown(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
|
|
|
|
nfs_expire_all_delegations(clp);
|
2012-05-29 02:12:27 +07:00
|
|
|
dprintk("%s: handling CB_PATHDOWN recovery for server %s\n", __func__,
|
|
|
|
clp->cl_hostname);
|
2012-03-10 23:23:15 +07:00
|
|
|
}
|
|
|
|
|
2011-08-25 02:07:37 +07:00
|
|
|
void nfs4_schedule_path_down_recovery(struct nfs_client *clp)
|
|
|
|
{
|
2012-03-10 23:23:15 +07:00
|
|
|
nfs40_handle_cb_pathdown(clp);
|
2011-08-25 02:07:37 +07:00
|
|
|
nfs4_schedule_state_manager(clp);
|
|
|
|
}
|
|
|
|
|
2011-03-10 04:12:46 +07:00
|
|
|
static int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_state *state)
|
2008-12-24 03:21:41 +07:00
|
|
|
{
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2008-12-24 03:21:43 +07:00
|
|
|
set_bit(NFS_OWNER_RECLAIM_REBOOT, &state->owner->so_flags);
|
2008-12-24 03:21:41 +07:00
|
|
|
set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-02-13 07:15:06 +07:00
|
|
|
int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_state *state)
|
2008-12-24 03:21:41 +07:00
|
|
|
{
|
|
|
|
set_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags);
|
|
|
|
clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
|
2008-12-24 03:21:43 +07:00
|
|
|
set_bit(NFS_OWNER_RECLAIM_NOGRACE, &state->owner->so_flags);
|
2008-12-24 03:21:41 +07:00
|
|
|
set_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-03-15 03:57:48 +07:00
|
|
|
int nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4_state *state)
|
2011-03-10 04:00:53 +07:00
|
|
|
{
|
|
|
|
struct nfs_client *clp = server->nfs_client;
|
|
|
|
|
2013-03-15 03:57:48 +07:00
|
|
|
if (!nfs4_valid_open_stateid(state))
|
|
|
|
return -EBADF;
|
2011-03-10 04:00:53 +07:00
|
|
|
nfs4_state_mark_reclaim_nograce(clp, state);
|
2012-05-29 02:12:27 +07:00
|
|
|
dprintk("%s: scheduling stateid recovery for server %s\n", __func__,
|
|
|
|
clp->cl_hostname);
|
2011-03-10 04:00:53 +07:00
|
|
|
nfs4_schedule_state_manager(clp);
|
2013-03-15 03:57:48 +07:00
|
|
|
return 0;
|
2011-03-10 04:00:53 +07:00
|
|
|
}
|
2012-03-07 22:49:41 +07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs4_schedule_stateid_recovery);
|
2011-03-10 04:00:53 +07:00
|
|
|
|
2012-03-06 07:56:44 +07:00
|
|
|
void nfs_inode_find_state_and_recover(struct inode *inode,
|
|
|
|
const nfs4_stateid *stateid)
|
|
|
|
{
|
|
|
|
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
|
|
|
struct nfs_open_context *ctx;
|
|
|
|
struct nfs4_state *state;
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
list_for_each_entry(ctx, &nfsi->open_files, list) {
|
|
|
|
state = ctx->state;
|
|
|
|
if (state == NULL)
|
|
|
|
continue;
|
|
|
|
if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
|
|
|
|
continue;
|
2012-03-05 06:13:56 +07:00
|
|
|
if (!nfs4_stateid_match(&state->stateid, stateid))
|
2012-03-06 07:56:44 +07:00
|
|
|
continue;
|
|
|
|
nfs4_state_mark_reclaim_nograce(clp, state);
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
if (found)
|
|
|
|
nfs4_schedule_state_manager(clp);
|
|
|
|
}
|
|
|
|
|
2013-03-19 06:45:14 +07:00
|
|
|
static void nfs4_state_mark_open_context_bad(struct nfs4_state *state)
|
|
|
|
{
|
|
|
|
struct inode *inode = state->inode;
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
|
|
|
struct nfs_open_context *ctx;
|
|
|
|
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
list_for_each_entry(ctx, &nfsi->open_files, list) {
|
|
|
|
if (ctx->state != state)
|
|
|
|
continue;
|
|
|
|
set_bit(NFS_CONTEXT_BAD, &ctx->flags);
|
|
|
|
}
|
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
}
|
|
|
|
|
2013-03-15 03:57:48 +07:00
|
|
|
static void nfs4_state_mark_recovery_failed(struct nfs4_state *state, int error)
|
|
|
|
{
|
|
|
|
set_bit(NFS_STATE_RECOVERY_FAILED, &state->flags);
|
2013-03-19 06:45:14 +07:00
|
|
|
nfs4_state_mark_open_context_bad(state);
|
2013-03-15 03:57:48 +07:00
|
|
|
}
|
|
|
|
|
2012-03-06 07:56:44 +07:00
|
|
|
|
2008-12-24 03:21:40 +07:00
|
|
|
static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_recovery_ops *ops)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct inode *inode = state->inode;
|
2008-12-24 03:21:44 +07:00
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
2005-04-17 05:20:36 +07:00
|
|
|
struct file_lock *fl;
|
|
|
|
int status = 0;
|
|
|
|
|
2009-06-18 03:23:00 +07:00
|
|
|
if (inode->i_flock == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Guard against delegation returns and new lock/unlock calls */
|
2008-12-24 03:21:44 +07:00
|
|
|
down_write(&nfsi->rwsem);
|
2009-06-18 03:23:00 +07:00
|
|
|
/* Protect inode->i_flock using the BKL */
|
2013-06-21 19:58:15 +07:00
|
|
|
spin_lock(&inode->i_lock);
|
2008-02-21 04:03:05 +07:00
|
|
|
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
|
2005-11-05 03:35:30 +07:00
|
|
|
if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
|
2005-04-17 05:20:36 +07:00
|
|
|
continue;
|
2007-08-11 04:44:32 +07:00
|
|
|
if (nfs_file_open_context(fl->fl_file)->state != state)
|
2005-04-17 05:20:36 +07:00
|
|
|
continue;
|
2013-06-21 19:58:15 +07:00
|
|
|
spin_unlock(&inode->i_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
status = ops->recover_lock(state, fl);
|
|
|
|
switch (status) {
|
2009-06-18 03:22:59 +07:00
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case -ESTALE:
|
|
|
|
case -NFS4ERR_ADMIN_REVOKED:
|
|
|
|
case -NFS4ERR_STALE_STATEID:
|
|
|
|
case -NFS4ERR_BAD_STATEID:
|
|
|
|
case -NFS4ERR_EXPIRED:
|
|
|
|
case -NFS4ERR_NO_GRACE:
|
|
|
|
case -NFS4ERR_STALE_CLIENTID:
|
2009-12-04 03:58:56 +07:00
|
|
|
case -NFS4ERR_BADSESSION:
|
|
|
|
case -NFS4ERR_BADSLOT:
|
|
|
|
case -NFS4ERR_BAD_HIGH_SLOT:
|
|
|
|
case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
|
2009-06-18 03:22:59 +07:00
|
|
|
goto out;
|
2005-04-17 05:20:36 +07:00
|
|
|
default:
|
2013-10-03 23:23:24 +07:00
|
|
|
printk(KERN_ERR "NFS: %s: unhandled error %d\n",
|
|
|
|
__func__, status);
|
2009-06-18 03:22:59 +07:00
|
|
|
case -ENOMEM:
|
|
|
|
case -NFS4ERR_DENIED:
|
2005-04-17 05:20:36 +07:00
|
|
|
case -NFS4ERR_RECLAIM_BAD:
|
|
|
|
case -NFS4ERR_RECLAIM_CONFLICT:
|
2005-11-05 03:35:30 +07:00
|
|
|
/* kill_proc(fl->fl_pid, SIGLOST, 1); */
|
2009-06-18 03:22:59 +07:00
|
|
|
status = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2013-06-21 19:58:15 +07:00
|
|
|
spin_lock(&inode->i_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2013-06-21 19:58:15 +07:00
|
|
|
spin_unlock(&inode->i_lock);
|
2009-06-18 03:22:59 +07:00
|
|
|
out:
|
2008-12-24 03:21:44 +07:00
|
|
|
up_write(&nfsi->rwsem);
|
2005-04-17 05:20:36 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2008-12-24 03:21:40 +07:00
|
|
|
static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs4_state_recovery_ops *ops)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct nfs4_state *state;
|
|
|
|
struct nfs4_lock_state *lock;
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
/* Note: we rely on the sp->so_states list being ordered
|
|
|
|
* so that we always reclaim open(O_RDWR) and/or open(O_WRITE)
|
|
|
|
* states first.
|
|
|
|
* This is needed to ensure that the server won't give us any
|
|
|
|
* read delegations that we have to return if, say, we are
|
|
|
|
* recovering after a network partition or a reboot from a
|
|
|
|
* server that doesn't support a grace period.
|
|
|
|
*/
|
2008-12-24 03:21:43 +07:00
|
|
|
spin_lock(&sp->so_lock);
|
2014-06-05 21:42:37 +07:00
|
|
|
raw_write_seqcount_begin(&sp->so_reclaim_seqcount);
|
2013-02-08 02:41:11 +07:00
|
|
|
restart:
|
2005-04-17 05:20:36 +07:00
|
|
|
list_for_each_entry(state, &sp->so_states, open_states) {
|
2008-12-24 03:21:41 +07:00
|
|
|
if (!test_and_clear_bit(ops->state_flag_bit, &state->flags))
|
|
|
|
continue;
|
2013-03-15 03:57:48 +07:00
|
|
|
if (!nfs4_valid_open_stateid(state))
|
|
|
|
continue;
|
2005-04-17 05:20:36 +07:00
|
|
|
if (state->state == 0)
|
|
|
|
continue;
|
2008-12-24 03:21:43 +07:00
|
|
|
atomic_inc(&state->count);
|
|
|
|
spin_unlock(&sp->so_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
status = ops->recover_open(sp, state);
|
|
|
|
if (status >= 0) {
|
2008-12-24 03:21:40 +07:00
|
|
|
status = nfs4_reclaim_locks(state, ops);
|
|
|
|
if (status >= 0) {
|
2013-11-01 00:03:04 +07:00
|
|
|
if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) {
|
2013-08-12 13:52:47 +07:00
|
|
|
spin_lock(&state->state_lock);
|
|
|
|
list_for_each_entry(lock, &state->lock_states, ls_locks) {
|
|
|
|
if (!test_bit(NFS_LOCK_INITIALIZED, &lock->ls_flags))
|
|
|
|
pr_warn_ratelimited("NFS: "
|
|
|
|
"%s: Lock reclaim "
|
|
|
|
"failed!\n", __func__);
|
|
|
|
}
|
|
|
|
spin_unlock(&state->state_lock);
|
2008-12-24 03:21:40 +07:00
|
|
|
}
|
2008-12-24 03:21:43 +07:00
|
|
|
nfs4_put_open_state(state);
|
2013-02-08 02:41:11 +07:00
|
|
|
spin_lock(&sp->so_lock);
|
2008-12-24 03:21:43 +07:00
|
|
|
goto restart;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
switch (status) {
|
|
|
|
default:
|
2013-10-03 23:23:24 +07:00
|
|
|
printk(KERN_ERR "NFS: %s: unhandled error %d\n",
|
|
|
|
__func__, status);
|
2005-04-17 05:20:36 +07:00
|
|
|
case -ENOENT:
|
2009-06-18 03:22:59 +07:00
|
|
|
case -ENOMEM:
|
2008-12-24 03:21:41 +07:00
|
|
|
case -ESTALE:
|
2013-10-03 23:23:24 +07:00
|
|
|
/* Open state on this file cannot be recovered */
|
2013-03-15 03:57:48 +07:00
|
|
|
nfs4_state_mark_recovery_failed(state, status);
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
2013-03-29 01:01:33 +07:00
|
|
|
case -EAGAIN:
|
|
|
|
ssleep(1);
|
2009-06-18 03:22:59 +07:00
|
|
|
case -NFS4ERR_ADMIN_REVOKED:
|
|
|
|
case -NFS4ERR_STALE_STATEID:
|
|
|
|
case -NFS4ERR_BAD_STATEID:
|
2008-12-24 03:21:41 +07:00
|
|
|
case -NFS4ERR_RECLAIM_BAD:
|
|
|
|
case -NFS4ERR_RECLAIM_CONFLICT:
|
2010-06-25 02:11:43 +07:00
|
|
|
nfs4_state_mark_reclaim_nograce(sp->so_server->nfs_client, state);
|
2008-12-24 03:21:41 +07:00
|
|
|
break;
|
2005-04-17 05:20:36 +07:00
|
|
|
case -NFS4ERR_EXPIRED:
|
|
|
|
case -NFS4ERR_NO_GRACE:
|
2010-06-25 02:11:43 +07:00
|
|
|
nfs4_state_mark_reclaim_nograce(sp->so_server->nfs_client, state);
|
2005-04-17 05:20:36 +07:00
|
|
|
case -NFS4ERR_STALE_CLIENTID:
|
2009-12-04 03:58:56 +07:00
|
|
|
case -NFS4ERR_BADSESSION:
|
|
|
|
case -NFS4ERR_BADSLOT:
|
|
|
|
case -NFS4ERR_BAD_HIGH_SLOT:
|
|
|
|
case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
|
2005-04-17 05:20:36 +07:00
|
|
|
goto out_err;
|
|
|
|
}
|
2008-12-24 03:21:43 +07:00
|
|
|
nfs4_put_open_state(state);
|
2013-02-08 02:41:11 +07:00
|
|
|
spin_lock(&sp->so_lock);
|
2008-12-24 03:21:43 +07:00
|
|
|
goto restart;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2014-06-05 21:42:37 +07:00
|
|
|
raw_write_seqcount_end(&sp->so_reclaim_seqcount);
|
2008-12-24 03:21:43 +07:00
|
|
|
spin_unlock(&sp->so_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
out_err:
|
2008-12-24 03:21:43 +07:00
|
|
|
nfs4_put_open_state(state);
|
2013-02-08 02:41:11 +07:00
|
|
|
spin_lock(&sp->so_lock);
|
2014-06-05 21:42:37 +07:00
|
|
|
raw_write_seqcount_end(&sp->so_reclaim_seqcount);
|
2013-02-08 02:41:11 +07:00
|
|
|
spin_unlock(&sp->so_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2008-12-24 03:21:41 +07:00
|
|
|
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);
|
2011-12-10 04:31:52 +07:00
|
|
|
spin_lock(&state->state_lock);
|
2008-12-24 03:21:41 +07:00
|
|
|
list_for_each_entry(lock, &state->lock_states, ls_locks) {
|
|
|
|
lock->ls_seqid.flags = 0;
|
2012-09-11 00:26:49 +07:00
|
|
|
clear_bit(NFS_LOCK_INITIALIZED, &lock->ls_flags);
|
2008-12-24 03:21:41 +07:00
|
|
|
}
|
2011-12-10 04:31:52 +07:00
|
|
|
spin_unlock(&state->state_lock);
|
2008-12-24 03:21:41 +07:00
|
|
|
}
|
|
|
|
|
2010-12-24 08:32:43 +07:00
|
|
|
static void nfs4_reset_seqids(struct nfs_server *server,
|
|
|
|
int (*mark_reclaim)(struct nfs_client *clp, struct nfs4_state *state))
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
{
|
2010-12-24 08:32:43 +07:00
|
|
|
struct nfs_client *clp = server->nfs_client;
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
struct nfs4_state_owner *sp;
|
2007-07-03 00:58:33 +07:00
|
|
|
struct rb_node *pos;
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
struct nfs4_state *state;
|
|
|
|
|
2010-12-24 08:32:43 +07:00
|
|
|
spin_lock(&clp->cl_lock);
|
|
|
|
for (pos = rb_first(&server->state_owners);
|
|
|
|
pos != NULL;
|
|
|
|
pos = rb_next(pos)) {
|
|
|
|
sp = rb_entry(pos, struct nfs4_state_owner, so_server_node);
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
sp->so_seqid.flags = 0;
|
2005-10-21 04:22:47 +07:00
|
|
|
spin_lock(&sp->so_lock);
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
list_for_each_entry(state, &sp->so_states, open_states) {
|
2008-12-24 03:21:41 +07:00
|
|
|
if (mark_reclaim(clp, state))
|
|
|
|
nfs4_clear_open_state(state);
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
}
|
2005-10-21 04:22:47 +07:00
|
|
|
spin_unlock(&sp->so_lock);
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
}
|
2010-12-24 08:32:43 +07:00
|
|
|
spin_unlock(&clp->cl_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs4_state_mark_reclaim_helper(struct nfs_client *clp,
|
|
|
|
int (*mark_reclaim)(struct nfs_client *clp, struct nfs4_state *state))
|
|
|
|
{
|
|
|
|
struct nfs_server *server;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
|
|
|
|
nfs4_reset_seqids(server, mark_reclaim);
|
|
|
|
rcu_read_unlock();
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
}
|
|
|
|
|
2008-12-24 03:21:41 +07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2009-12-06 04:08:41 +07:00
|
|
|
static void nfs4_reclaim_complete(struct nfs_client *clp,
|
2013-05-20 22:05:17 +07:00
|
|
|
const struct nfs4_state_recovery_ops *ops,
|
|
|
|
struct rpc_cred *cred)
|
2009-12-06 04:08:41 +07:00
|
|
|
{
|
|
|
|
/* Notify the server we're done reclaiming our state */
|
|
|
|
if (ops->reclaim_complete)
|
2013-05-20 22:05:17 +07:00
|
|
|
(void)ops->reclaim_complete(clp, cred);
|
2009-12-06 04:08:41 +07:00
|
|
|
}
|
|
|
|
|
2010-12-24 08:32:43 +07:00
|
|
|
static void nfs4_clear_reclaim_server(struct nfs_server *server)
|
2008-12-24 03:21:41 +07:00
|
|
|
{
|
2010-12-24 08:32:43 +07:00
|
|
|
struct nfs_client *clp = server->nfs_client;
|
2008-12-24 03:21:41 +07:00
|
|
|
struct nfs4_state_owner *sp;
|
|
|
|
struct rb_node *pos;
|
|
|
|
struct nfs4_state *state;
|
|
|
|
|
2010-12-24 08:32:43 +07:00
|
|
|
spin_lock(&clp->cl_lock);
|
|
|
|
for (pos = rb_first(&server->state_owners);
|
|
|
|
pos != NULL;
|
|
|
|
pos = rb_next(pos)) {
|
|
|
|
sp = rb_entry(pos, struct nfs4_state_owner, so_server_node);
|
2008-12-24 03:21:41 +07:00
|
|
|
spin_lock(&sp->so_lock);
|
|
|
|
list_for_each_entry(state, &sp->so_states, open_states) {
|
2010-12-24 08:32:43 +07:00
|
|
|
if (!test_and_clear_bit(NFS_STATE_RECLAIM_REBOOT,
|
|
|
|
&state->flags))
|
2008-12-24 03:21:41 +07:00
|
|
|
continue;
|
|
|
|
nfs4_state_mark_reclaim_nograce(clp, state);
|
|
|
|
}
|
|
|
|
spin_unlock(&sp->so_lock);
|
|
|
|
}
|
2010-12-24 08:32:43 +07:00
|
|
|
spin_unlock(&clp->cl_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_state_clear_reclaim_reboot(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
struct nfs_server *server;
|
|
|
|
|
|
|
|
if (!test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
|
|
|
|
nfs4_clear_reclaim_server(server);
|
|
|
|
rcu_read_unlock();
|
2008-12-24 03:21:41 +07:00
|
|
|
|
|
|
|
nfs_delegation_reap_unclaimed(clp);
|
2010-10-05 04:59:08 +07:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp)
|
|
|
|
{
|
2013-05-20 22:05:17 +07:00
|
|
|
const struct nfs4_state_recovery_ops *ops;
|
|
|
|
struct rpc_cred *cred;
|
|
|
|
|
2010-10-05 04:59:08 +07:00
|
|
|
if (!nfs4_state_clear_reclaim_reboot(clp))
|
|
|
|
return;
|
2013-05-20 22:05:17 +07:00
|
|
|
ops = clp->cl_mvops->reboot_recovery_ops;
|
2013-07-24 23:28:37 +07:00
|
|
|
cred = nfs4_get_clid_cred(clp);
|
2013-05-20 22:05:17 +07:00
|
|
|
nfs4_reclaim_complete(clp, ops, cred);
|
|
|
|
put_rpccred(cred);
|
2008-12-24 03:21:41 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2009-12-04 03:53:20 +07:00
|
|
|
static int nfs4_recovery_handle_error(struct nfs_client *clp, int error)
|
2008-12-24 03:21:42 +07:00
|
|
|
{
|
|
|
|
switch (error) {
|
2011-12-02 04:31:34 +07:00
|
|
|
case 0:
|
|
|
|
break;
|
2008-12-24 03:21:42 +07:00
|
|
|
case -NFS4ERR_CB_PATH_DOWN:
|
2012-03-10 23:23:15 +07:00
|
|
|
nfs40_handle_cb_pathdown(clp);
|
2011-12-02 04:31:34 +07:00
|
|
|
break;
|
2009-12-04 03:53:21 +07:00
|
|
|
case -NFS4ERR_NO_GRACE:
|
|
|
|
nfs4_state_end_reclaim_reboot(clp);
|
2011-12-02 04:31:34 +07:00
|
|
|
break;
|
2008-12-24 03:21:42 +07:00
|
|
|
case -NFS4ERR_STALE_CLIENTID:
|
|
|
|
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
|
2010-10-05 04:59:08 +07:00
|
|
|
nfs4_state_clear_reclaim_reboot(clp);
|
2008-12-24 03:21:42 +07:00
|
|
|
nfs4_state_start_reclaim_reboot(clp);
|
|
|
|
break;
|
|
|
|
case -NFS4ERR_EXPIRED:
|
|
|
|
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
|
|
|
|
nfs4_state_start_reclaim_nograce(clp);
|
2009-12-05 03:55:34 +07:00
|
|
|
break;
|
2009-04-01 20:22:39 +07:00
|
|
|
case -NFS4ERR_BADSESSION:
|
|
|
|
case -NFS4ERR_BADSLOT:
|
|
|
|
case -NFS4ERR_BAD_HIGH_SLOT:
|
|
|
|
case -NFS4ERR_DEADSESSION:
|
|
|
|
case -NFS4ERR_SEQ_FALSE_RETRY:
|
|
|
|
case -NFS4ERR_SEQ_MISORDERED:
|
2009-12-05 03:55:05 +07:00
|
|
|
set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
|
2009-12-05 04:02:14 +07:00
|
|
|
/* Zero session reset errors */
|
2011-12-02 04:31:34 +07:00
|
|
|
break;
|
2012-05-27 23:58:48 +07:00
|
|
|
case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
|
|
|
|
set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
|
|
|
|
break;
|
2011-12-02 04:31:34 +07:00
|
|
|
default:
|
2012-05-29 02:12:27 +07:00
|
|
|
dprintk("%s: failed to handle error %d for server %s\n",
|
|
|
|
__func__, error, clp->cl_hostname);
|
2011-12-02 04:31:34 +07:00
|
|
|
return error;
|
2008-12-24 03:21:42 +07:00
|
|
|
}
|
2012-05-29 02:12:27 +07:00
|
|
|
dprintk("%s: handled error %d for server %s\n", __func__, error,
|
|
|
|
clp->cl_hostname);
|
2011-12-02 04:31:34 +07:00
|
|
|
return 0;
|
2008-12-24 03:21:42 +07:00
|
|
|
}
|
|
|
|
|
2008-12-24 03:21:40 +07:00
|
|
|
static int nfs4_do_reclaim(struct nfs_client *clp, const struct nfs4_state_recovery_ops *ops)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2010-12-24 08:32:43 +07:00
|
|
|
struct nfs4_state_owner *sp;
|
|
|
|
struct nfs_server *server;
|
2007-07-03 00:58:33 +07:00
|
|
|
struct rb_node *pos;
|
2005-04-17 05:20:36 +07:00
|
|
|
int status = 0;
|
|
|
|
|
2008-12-24 03:21:43 +07:00
|
|
|
restart:
|
2010-12-24 08:32:43 +07:00
|
|
|
rcu_read_lock();
|
|
|
|
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
|
NFS: Cache state owners after files are closed
Servers have a finite amount of memory to store NFSv4 open and lock
owners. Moreover, servers may have a difficult time determining when
they can reap their state owner table, thanks to gray areas in the
NFSv4 protocol specification. Thus clients should be careful to reuse
state owners when possible.
Currently Linux is not too careful. When a user has closed all her
files on one mount point, the state owner's reference count goes to
zero, and it is released. The next OPEN allocates a new one. A
workload that serially opens and closes files can run through a large
number of open owners this way.
When a state owner's reference count goes to zero, slap it onto a free
list for that nfs_server, with an expiry time. Garbage collect before
looking for a state owner. This makes state owners for active users
available for re-use.
Now that there can be unused state owners remaining at umount time,
purge the state owner free list when a server is destroyed. Also be
sure not to reclaim unused state owners during state recovery.
This change has benefits for the client as well. For some workloads,
this approach drops the number of OPEN_CONFIRM calls from the same as
the number of OPEN calls, down to just one. This reduces wire traffic
and thus open(2) latency. Before this patch, untarring a kernel
source tarball shows the OPEN_CONFIRM call counter steadily increasing
through the test. With the patch, the OPEN_CONFIRM count remains at 1
throughout the entire untar.
As long as the expiry time is kept short, I don't think garbage
collection should be terribly expensive, although it does bounce the
clp->cl_lock around a bit.
[ At some point we should rationalize the use of the nfs_server
->destroy method. ]
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
[Trond: Fixed a garbage collection race and a few efficiency issues]
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2011-12-07 04:13:48 +07:00
|
|
|
nfs4_purge_state_owners(server);
|
2010-12-24 08:32:43 +07:00
|
|
|
spin_lock(&clp->cl_lock);
|
|
|
|
for (pos = rb_first(&server->state_owners);
|
|
|
|
pos != NULL;
|
|
|
|
pos = rb_next(pos)) {
|
|
|
|
sp = rb_entry(pos,
|
|
|
|
struct nfs4_state_owner, so_server_node);
|
|
|
|
if (!test_and_clear_bit(ops->owner_flag_bit,
|
|
|
|
&sp->so_flags))
|
|
|
|
continue;
|
|
|
|
atomic_inc(&sp->so_count);
|
|
|
|
spin_unlock(&clp->cl_lock);
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
status = nfs4_reclaim_open_state(sp, ops);
|
|
|
|
if (status < 0) {
|
|
|
|
set_bit(ops->owner_flag_bit, &sp->so_flags);
|
|
|
|
nfs4_put_state_owner(sp);
|
|
|
|
return nfs4_recovery_handle_error(clp, status);
|
|
|
|
}
|
|
|
|
|
2008-12-24 03:21:43 +07:00
|
|
|
nfs4_put_state_owner(sp);
|
2010-12-24 08:32:43 +07:00
|
|
|
goto restart;
|
2008-12-24 03:21:43 +07:00
|
|
|
}
|
2010-12-24 08:32:43 +07:00
|
|
|
spin_unlock(&clp->cl_lock);
|
2008-12-24 03:21:40 +07:00
|
|
|
}
|
2010-12-24 08:32:43 +07:00
|
|
|
rcu_read_unlock();
|
2008-12-24 03:21:40 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_check_lease(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
struct rpc_cred *cred;
|
2010-06-16 20:52:27 +07:00
|
|
|
const struct nfs4_state_maintenance_ops *ops =
|
|
|
|
clp->cl_mvops->state_renewal_ops;
|
2011-12-02 04:31:34 +07:00
|
|
|
int status;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-12-24 03:21:42 +07:00
|
|
|
/* Is the client already known to have an expired lease? */
|
|
|
|
if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
|
|
|
|
return 0;
|
2009-04-01 20:22:46 +07:00
|
|
|
spin_lock(&clp->cl_lock);
|
|
|
|
cred = ops->get_state_renewal_cred_locked(clp);
|
|
|
|
spin_unlock(&clp->cl_lock);
|
2008-12-24 03:21:42 +07:00
|
|
|
if (cred == NULL) {
|
2013-07-24 23:28:37 +07:00
|
|
|
cred = nfs4_get_clid_cred(clp);
|
2011-12-02 04:31:34 +07:00
|
|
|
status = -ENOKEY;
|
2008-12-24 03:21:42 +07:00
|
|
|
if (cred == NULL)
|
|
|
|
goto out;
|
2006-01-03 15:55:26 +07:00
|
|
|
}
|
2009-04-01 20:22:45 +07:00
|
|
|
status = ops->renew_lease(clp, cred);
|
2008-12-24 03:21:42 +07:00
|
|
|
put_rpccred(cred);
|
2013-04-09 04:50:28 +07:00
|
|
|
if (status == -ETIMEDOUT) {
|
|
|
|
set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
|
|
|
|
return 0;
|
|
|
|
}
|
2008-12-24 03:21:42 +07:00
|
|
|
out:
|
2009-12-04 03:53:20 +07:00
|
|
|
return nfs4_recovery_handle_error(clp, status);
|
2008-12-24 03:21:40 +07:00
|
|
|
}
|
|
|
|
|
2012-10-02 07:42:32 +07:00
|
|
|
/* Set NFS4CLNT_LEASE_EXPIRED and reclaim reboot state for all v4.0 errors
|
|
|
|
* and for recoverable errors on EXCHANGE_ID for v4.1
|
2012-05-26 02:00:06 +07:00
|
|
|
*/
|
|
|
|
static int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status)
|
|
|
|
{
|
|
|
|
switch (status) {
|
2012-05-26 02:00:06 +07:00
|
|
|
case -NFS4ERR_SEQ_MISORDERED:
|
|
|
|
if (test_and_set_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state))
|
|
|
|
return -ESERVERFAULT;
|
|
|
|
/* Lease confirmation error: retry after purging the lease */
|
|
|
|
ssleep(1);
|
2012-10-02 07:42:32 +07:00
|
|
|
clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
|
|
|
|
break;
|
2012-05-26 02:00:06 +07:00
|
|
|
case -NFS4ERR_STALE_CLIENTID:
|
|
|
|
clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
|
2012-10-02 07:42:32 +07:00
|
|
|
nfs4_state_clear_reclaim_reboot(clp);
|
|
|
|
nfs4_state_start_reclaim_reboot(clp);
|
2012-05-26 02:00:06 +07:00
|
|
|
break;
|
2012-07-12 03:30:50 +07:00
|
|
|
case -NFS4ERR_CLID_INUSE:
|
|
|
|
pr_err("NFS: Server %s reports our clientid is in use\n",
|
|
|
|
clp->cl_hostname);
|
|
|
|
nfs_mark_client_ready(clp, -EPERM);
|
|
|
|
clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
|
|
|
|
return -EPERM;
|
2012-05-26 02:00:06 +07:00
|
|
|
case -EACCES:
|
|
|
|
case -NFS4ERR_DELAY:
|
|
|
|
case -ETIMEDOUT:
|
|
|
|
case -EAGAIN:
|
|
|
|
ssleep(1);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case -NFS4ERR_MINOR_VERS_MISMATCH:
|
|
|
|
if (clp->cl_cons_state == NFS_CS_SESSION_INITING)
|
|
|
|
nfs_mark_client_ready(clp, -EPROTONOSUPPORT);
|
2012-05-29 02:12:27 +07:00
|
|
|
dprintk("%s: exit with error %d for server %s\n",
|
|
|
|
__func__, -EPROTONOSUPPORT, clp->cl_hostname);
|
2012-05-26 02:00:06 +07:00
|
|
|
return -EPROTONOSUPPORT;
|
|
|
|
case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
|
|
|
|
* in nfs4_exchange_id */
|
|
|
|
default:
|
2012-05-29 02:12:27 +07:00
|
|
|
dprintk("%s: exit with error %d for server %s\n", __func__,
|
|
|
|
status, clp->cl_hostname);
|
2012-05-26 02:00:06 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
|
2012-05-29 02:12:27 +07:00
|
|
|
dprintk("%s: handled error %d for server %s\n", __func__, status,
|
|
|
|
clp->cl_hostname);
|
2012-05-26 02:00:06 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-06-05 22:19:47 +07:00
|
|
|
static int nfs4_establish_lease(struct nfs_client *clp)
|
2008-12-24 03:21:40 +07:00
|
|
|
{
|
|
|
|
struct rpc_cred *cred;
|
2010-06-16 20:52:27 +07:00
|
|
|
const struct nfs4_state_recovery_ops *ops =
|
|
|
|
clp->cl_mvops->reboot_recovery_ops;
|
2012-05-26 02:00:06 +07:00
|
|
|
int status;
|
2008-12-24 03:21:40 +07:00
|
|
|
|
2013-07-24 23:28:37 +07:00
|
|
|
cred = nfs4_get_clid_cred(clp);
|
2012-05-26 02:00:06 +07:00
|
|
|
if (cred == NULL)
|
|
|
|
return -ENOENT;
|
|
|
|
status = ops->establish_clid(clp, cred);
|
|
|
|
put_rpccred(cred);
|
|
|
|
if (status != 0)
|
2012-06-05 22:19:47 +07:00
|
|
|
return status;
|
|
|
|
pnfs_destroy_all_layouts(clp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-12 03:30:59 +07:00
|
|
|
/*
|
|
|
|
* Returns zero or a negative errno. NFS4ERR values are converted
|
|
|
|
* to local errno values.
|
|
|
|
*/
|
2012-06-05 22:19:47 +07:00
|
|
|
static int nfs4_reclaim_lease(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
status = nfs4_establish_lease(clp);
|
|
|
|
if (status < 0)
|
|
|
|
return nfs4_handle_reclaim_lease_error(clp, status);
|
|
|
|
if (test_and_clear_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, &clp->cl_state))
|
|
|
|
nfs4_state_start_reclaim_nograce(clp);
|
|
|
|
if (!test_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state))
|
|
|
|
set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state);
|
|
|
|
clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
|
|
|
|
clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_purge_lease(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
status = nfs4_establish_lease(clp);
|
|
|
|
if (status < 0)
|
2012-05-26 02:00:06 +07:00
|
|
|
return nfs4_handle_reclaim_lease_error(clp, status);
|
2012-06-05 22:19:47 +07:00
|
|
|
clear_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state);
|
|
|
|
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
|
|
|
|
nfs4_state_start_reclaim_nograce(clp);
|
2012-05-26 02:00:06 +07:00
|
|
|
return 0;
|
2008-12-24 03:21:40 +07:00
|
|
|
}
|
|
|
|
|
2013-10-18 01:13:02 +07:00
|
|
|
/*
|
|
|
|
* Try remote migration of one FSID from a source server to a
|
|
|
|
* destination server. The source server provides a list of
|
|
|
|
* potential destinations.
|
|
|
|
*
|
|
|
|
* Returns zero or a negative NFS4ERR status code.
|
|
|
|
*/
|
|
|
|
static int nfs4_try_migration(struct nfs_server *server, struct rpc_cred *cred)
|
|
|
|
{
|
|
|
|
struct nfs_client *clp = server->nfs_client;
|
|
|
|
struct nfs4_fs_locations *locations = NULL;
|
|
|
|
struct inode *inode;
|
|
|
|
struct page *page;
|
|
|
|
int status, result;
|
|
|
|
|
|
|
|
dprintk("--> %s: FSID %llx:%llx on \"%s\"\n", __func__,
|
|
|
|
(unsigned long long)server->fsid.major,
|
|
|
|
(unsigned long long)server->fsid.minor,
|
|
|
|
clp->cl_hostname);
|
|
|
|
|
|
|
|
result = 0;
|
|
|
|
page = alloc_page(GFP_KERNEL);
|
|
|
|
locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL);
|
|
|
|
if (page == NULL || locations == NULL) {
|
|
|
|
dprintk("<-- %s: no memory\n", __func__);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
inode = server->super->s_root->d_inode;
|
|
|
|
result = nfs4_proc_get_locations(inode, locations, page, cred);
|
|
|
|
if (result) {
|
|
|
|
dprintk("<-- %s: failed to retrieve fs_locations: %d\n",
|
|
|
|
__func__, result);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = -NFS4ERR_NXIO;
|
|
|
|
if (!(locations->fattr.valid & NFS_ATTR_FATTR_V4_LOCATIONS)) {
|
|
|
|
dprintk("<-- %s: No fs_locations data, migration skipped\n",
|
|
|
|
__func__);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
nfs4_begin_drain_session(clp);
|
|
|
|
|
|
|
|
status = nfs4_replace_transport(server, locations);
|
|
|
|
if (status != 0) {
|
|
|
|
dprintk("<-- %s: failed to replace transport: %d\n",
|
|
|
|
__func__, status);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = 0;
|
|
|
|
dprintk("<-- %s: migration succeeded\n", __func__);
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (page != NULL)
|
|
|
|
__free_page(page);
|
|
|
|
kfree(locations);
|
|
|
|
if (result) {
|
|
|
|
pr_err("NFS: migration recovery failed (server %s)\n",
|
|
|
|
clp->cl_hostname);
|
|
|
|
set_bit(NFS_MIG_FAILED, &server->mig_status);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns zero or a negative NFS4ERR status code.
|
|
|
|
*/
|
|
|
|
static int nfs4_handle_migration(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
const struct nfs4_state_maintenance_ops *ops =
|
|
|
|
clp->cl_mvops->state_renewal_ops;
|
|
|
|
struct nfs_server *server;
|
|
|
|
struct rpc_cred *cred;
|
|
|
|
|
|
|
|
dprintk("%s: migration reported on \"%s\"\n", __func__,
|
|
|
|
clp->cl_hostname);
|
|
|
|
|
|
|
|
spin_lock(&clp->cl_lock);
|
|
|
|
cred = ops->get_state_renewal_cred_locked(clp);
|
|
|
|
spin_unlock(&clp->cl_lock);
|
|
|
|
if (cred == NULL)
|
|
|
|
return -NFS4ERR_NOENT;
|
|
|
|
|
|
|
|
clp->cl_mig_gen++;
|
|
|
|
restart:
|
|
|
|
rcu_read_lock();
|
|
|
|
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
|
|
|
|
int status;
|
|
|
|
|
|
|
|
if (server->mig_gen == clp->cl_mig_gen)
|
|
|
|
continue;
|
|
|
|
server->mig_gen = clp->cl_mig_gen;
|
|
|
|
|
|
|
|
if (!test_and_clear_bit(NFS_MIG_IN_TRANSITION,
|
|
|
|
&server->mig_status))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
rcu_read_unlock();
|
|
|
|
status = nfs4_try_migration(server, cred);
|
|
|
|
if (status < 0) {
|
|
|
|
put_rpccred(cred);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
goto restart;
|
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
put_rpccred(cred);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-10-18 01:13:35 +07:00
|
|
|
/*
|
|
|
|
* Test each nfs_server on the clp's cl_superblocks list to see
|
|
|
|
* if it's moved to another server. Stop when the server no longer
|
|
|
|
* returns NFS4ERR_LEASE_MOVED.
|
|
|
|
*/
|
|
|
|
static int nfs4_handle_lease_moved(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
const struct nfs4_state_maintenance_ops *ops =
|
|
|
|
clp->cl_mvops->state_renewal_ops;
|
|
|
|
struct nfs_server *server;
|
|
|
|
struct rpc_cred *cred;
|
|
|
|
|
|
|
|
dprintk("%s: lease moved reported on \"%s\"\n", __func__,
|
|
|
|
clp->cl_hostname);
|
|
|
|
|
|
|
|
spin_lock(&clp->cl_lock);
|
|
|
|
cred = ops->get_state_renewal_cred_locked(clp);
|
|
|
|
spin_unlock(&clp->cl_lock);
|
|
|
|
if (cred == NULL)
|
|
|
|
return -NFS4ERR_NOENT;
|
|
|
|
|
|
|
|
clp->cl_mig_gen++;
|
|
|
|
restart:
|
|
|
|
rcu_read_lock();
|
|
|
|
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
|
|
|
|
struct inode *inode;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
if (server->mig_gen == clp->cl_mig_gen)
|
|
|
|
continue;
|
|
|
|
server->mig_gen = clp->cl_mig_gen;
|
|
|
|
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
inode = server->super->s_root->d_inode;
|
|
|
|
status = nfs4_proc_fsid_present(inode, cred);
|
|
|
|
if (status != -NFS4ERR_MOVED)
|
|
|
|
goto restart; /* wasn't this one */
|
|
|
|
if (nfs4_try_migration(server, cred) == -NFS4ERR_LEASE_MOVED)
|
|
|
|
goto restart; /* there are more */
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
out:
|
|
|
|
put_rpccred(cred);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-15 04:24:32 +07:00
|
|
|
/**
|
|
|
|
* nfs4_discover_server_trunking - Detect server IP address trunking
|
|
|
|
*
|
|
|
|
* @clp: nfs_client under test
|
|
|
|
* @result: OUT: found nfs_client, or clp
|
|
|
|
*
|
|
|
|
* Returns zero or a negative errno. If zero is returned,
|
|
|
|
* an nfs_client pointer is planted in "result".
|
|
|
|
*
|
|
|
|
* Note: since we are invoked in process context, and
|
|
|
|
* not from inside the state manager, we cannot use
|
|
|
|
* nfs4_handle_reclaim_lease_error().
|
|
|
|
*/
|
|
|
|
int nfs4_discover_server_trunking(struct nfs_client *clp,
|
|
|
|
struct nfs_client **result)
|
|
|
|
{
|
|
|
|
const struct nfs4_state_recovery_ops *ops =
|
|
|
|
clp->cl_mvops->reboot_recovery_ops;
|
|
|
|
struct rpc_clnt *clnt;
|
|
|
|
struct rpc_cred *cred;
|
2013-03-17 02:56:20 +07:00
|
|
|
int i, status;
|
2012-09-15 04:24:32 +07:00
|
|
|
|
|
|
|
dprintk("NFS: %s: testing '%s'\n", __func__, clp->cl_hostname);
|
|
|
|
|
|
|
|
clnt = clp->cl_rpcclient;
|
|
|
|
i = 0;
|
|
|
|
|
|
|
|
mutex_lock(&nfs_clid_init_mutex);
|
|
|
|
again:
|
2013-04-06 00:22:50 +07:00
|
|
|
status = -ENOENT;
|
2013-07-24 23:28:37 +07:00
|
|
|
cred = nfs4_get_clid_cred(clp);
|
2012-09-15 04:24:32 +07:00
|
|
|
if (cred == NULL)
|
|
|
|
goto out_unlock;
|
|
|
|
|
|
|
|
status = ops->detect_trunking(clp, result, cred);
|
|
|
|
put_rpccred(cred);
|
|
|
|
switch (status) {
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case -ETIMEDOUT:
|
2014-03-19 01:23:11 +07:00
|
|
|
if (clnt->cl_softrtry)
|
|
|
|
break;
|
|
|
|
case -NFS4ERR_DELAY:
|
2012-09-15 04:24:32 +07:00
|
|
|
case -EAGAIN:
|
|
|
|
ssleep(1);
|
2013-01-19 10:56:23 +07:00
|
|
|
case -NFS4ERR_STALE_CLIENTID:
|
2012-09-15 04:24:32 +07:00
|
|
|
dprintk("NFS: %s after status %d, retrying\n",
|
|
|
|
__func__, status);
|
|
|
|
goto again;
|
2013-03-17 02:56:20 +07:00
|
|
|
case -EACCES:
|
NFS: Use root's credential for lease management when keytab is missing
Commit 05f4c350 "NFS: Discover NFSv4 server trunking when mounting"
Fri Sep 14 17:24:32 2012 introduced Uniform Client String support,
which forces our NFS client to establish a client ID immediately
during a mount operation rather than waiting until a user wants to
open a file.
Normally machine credentials (eg. from a keytab) are used to perform
a mount operation that is protected by Kerberos. Before 05fc350,
SETCLIENTID used a machine credential, or fell back to a regular
user's credential if no keytab is available.
On clients that don't have a keytab, performing SETCLIENTID early
means there's no user credential to fall back on, since no regular
user has kinit'd yet. 05f4c350 seems to have broken the ability
to mount with sec=krb5 on clients that don't have a keytab in
kernels 3.7 - 3.10.
To address this regression, commit 4edaa308 (NFS: Use "krb5i" to
establish NFSv4 state whenever possible), Sat Mar 16 15:56:20 2013,
was merged in 3.10. This commit forces the NFS client to fall back
to AUTH_SYS for lease management operations if no keytab is
available.
Neil Brown noticed that, since root is required to kinit to do a
sec=krb5 mount when a client doesn't have a keytab, we can try to
use root's Kerberos credential before AUTH_SYS.
Now, when determining a principal and flavor to use for lease
management, the NFS client tries in this order:
1. Flavor: AUTH_GSS, krb5i
Principal: service principal (via keytab)
2. Flavor: AUTH_GSS, krb5i
Principal: user principal established for UID 0 (via kinit)
3. Flavor: AUTH_SYS
Principal: UID 0 / GID 0
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2013-07-24 23:28:28 +07:00
|
|
|
if (i++ == 0) {
|
|
|
|
nfs4_root_machine_cred(clp);
|
|
|
|
goto again;
|
|
|
|
}
|
2013-11-13 21:08:21 +07:00
|
|
|
if (clnt->cl_auth->au_flavor == RPC_AUTH_UNIX)
|
2013-03-17 02:56:20 +07:00
|
|
|
break;
|
2012-09-15 04:24:32 +07:00
|
|
|
case -NFS4ERR_CLID_INUSE:
|
|
|
|
case -NFS4ERR_WRONGSEC:
|
2013-11-13 21:08:21 +07:00
|
|
|
/* No point in retrying if we already used RPC_AUTH_UNIX */
|
|
|
|
if (clnt->cl_auth->au_flavor == RPC_AUTH_UNIX) {
|
|
|
|
status = -EPERM;
|
|
|
|
break;
|
|
|
|
}
|
2013-04-23 02:42:48 +07:00
|
|
|
clnt = rpc_clone_client_set_auth(clnt, RPC_AUTH_UNIX);
|
2012-09-15 04:24:32 +07:00
|
|
|
if (IS_ERR(clnt)) {
|
|
|
|
status = PTR_ERR(clnt);
|
|
|
|
break;
|
|
|
|
}
|
2013-04-05 02:55:00 +07:00
|
|
|
/* Note: this is safe because we haven't yet marked the
|
|
|
|
* client as ready, so we are the only user of
|
|
|
|
* clp->cl_rpcclient
|
|
|
|
*/
|
|
|
|
clnt = xchg(&clp->cl_rpcclient, clnt);
|
|
|
|
rpc_shutdown_client(clnt);
|
|
|
|
clnt = clp->cl_rpcclient;
|
2012-09-15 04:24:32 +07:00
|
|
|
goto again;
|
|
|
|
|
|
|
|
case -NFS4ERR_MINOR_VERS_MISMATCH:
|
|
|
|
status = -EPROTONOSUPPORT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case -EKEYEXPIRED:
|
|
|
|
case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
|
|
|
|
* in nfs4_exchange_id */
|
|
|
|
status = -EKEYEXPIRED;
|
2013-04-06 00:22:50 +07:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
pr_warn("NFS: %s unhandled error %d. Exiting with error EIO\n",
|
|
|
|
__func__, status);
|
|
|
|
status = -EIO;
|
2012-09-15 04:24:32 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
out_unlock:
|
|
|
|
mutex_unlock(&nfs_clid_init_mutex);
|
|
|
|
dprintk("NFS: %s: status = %d\n", __func__, status);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2009-04-01 20:22:38 +07:00
|
|
|
#ifdef CONFIG_NFS_V4_1
|
2012-05-28 00:02:53 +07:00
|
|
|
void nfs4_schedule_session_recovery(struct nfs4_session *session, int err)
|
2011-03-10 04:00:53 +07:00
|
|
|
{
|
2011-05-27 01:26:35 +07:00
|
|
|
struct nfs_client *clp = session->clp;
|
|
|
|
|
2012-05-28 00:02:53 +07:00
|
|
|
switch (err) {
|
|
|
|
default:
|
|
|
|
set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
|
|
|
|
break;
|
|
|
|
case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
|
|
|
|
set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
|
|
|
|
}
|
2011-05-27 01:26:35 +07:00
|
|
|
nfs4_schedule_lease_recovery(clp);
|
2011-03-10 04:00:53 +07:00
|
|
|
}
|
2011-03-01 08:34:20 +07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs4_schedule_session_recovery);
|
2011-03-10 04:00:53 +07:00
|
|
|
|
2012-11-21 21:06:11 +07:00
|
|
|
static void nfs41_ping_server(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
/* Use CHECK_LEASE to ping the server with a SEQUENCE */
|
|
|
|
set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
|
|
|
|
nfs4_schedule_state_manager(clp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void nfs41_server_notify_target_slotid_update(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
nfs41_ping_server(clp);
|
|
|
|
}
|
|
|
|
|
2012-11-27 04:16:54 +07:00
|
|
|
void nfs41_server_notify_highest_slotid_update(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
nfs41_ping_server(clp);
|
|
|
|
}
|
|
|
|
|
2010-03-03 01:06:21 +07:00
|
|
|
static void nfs4_reset_all_state(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
if (test_and_set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) {
|
2012-05-22 09:45:33 +07:00
|
|
|
set_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state);
|
2012-05-26 03:02:15 +07:00
|
|
|
clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
|
2010-03-03 01:06:21 +07:00
|
|
|
nfs4_state_start_reclaim_nograce(clp);
|
2012-05-29 02:12:27 +07:00
|
|
|
dprintk("%s: scheduling reset of all state for server %s!\n",
|
|
|
|
__func__, clp->cl_hostname);
|
2011-03-10 04:00:53 +07:00
|
|
|
nfs4_schedule_state_manager(clp);
|
2010-03-03 01:06:21 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs41_handle_server_reboot(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
if (test_and_set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) {
|
|
|
|
nfs4_state_start_reclaim_reboot(clp);
|
2012-05-29 02:12:27 +07:00
|
|
|
dprintk("%s: server %s rebooted!\n", __func__,
|
|
|
|
clp->cl_hostname);
|
2011-03-10 04:00:53 +07:00
|
|
|
nfs4_schedule_state_manager(clp);
|
2010-03-03 01:06:21 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs41_handle_state_revoked(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
nfs4_reset_all_state(clp);
|
2012-05-29 02:12:27 +07:00
|
|
|
dprintk("%s: state revoked on server %s\n", __func__, clp->cl_hostname);
|
2010-03-03 01:06:21 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs41_handle_recallable_state_revoked(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
/* This will need to handle layouts too */
|
|
|
|
nfs_expire_all_delegations(clp);
|
2012-05-29 02:12:27 +07:00
|
|
|
dprintk("%s: Recallable state revoked on server %s!\n", __func__,
|
|
|
|
clp->cl_hostname);
|
2010-03-03 01:06:21 +07:00
|
|
|
}
|
|
|
|
|
2012-05-24 23:26:37 +07:00
|
|
|
static void nfs41_handle_backchannel_fault(struct nfs_client *clp)
|
2010-03-03 01:06:21 +07:00
|
|
|
{
|
|
|
|
nfs_expire_all_delegations(clp);
|
|
|
|
if (test_and_set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state) == 0)
|
2011-03-10 04:00:53 +07:00
|
|
|
nfs4_schedule_state_manager(clp);
|
2012-05-29 02:12:27 +07:00
|
|
|
dprintk("%s: server %s declared a backchannel fault\n", __func__,
|
|
|
|
clp->cl_hostname);
|
2010-03-03 01:06:21 +07:00
|
|
|
}
|
|
|
|
|
2012-05-24 23:26:37 +07:00
|
|
|
static void nfs41_handle_cb_path_down(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
if (test_and_set_bit(NFS4CLNT_BIND_CONN_TO_SESSION,
|
|
|
|
&clp->cl_state) == 0)
|
|
|
|
nfs4_schedule_state_manager(clp);
|
|
|
|
}
|
|
|
|
|
2009-12-06 01:46:14 +07:00
|
|
|
void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags)
|
|
|
|
{
|
|
|
|
if (!flags)
|
|
|
|
return;
|
2012-05-22 09:45:33 +07:00
|
|
|
|
|
|
|
dprintk("%s: \"%s\" (client ID %llx) flags=0x%08x\n",
|
|
|
|
__func__, clp->cl_hostname, clp->cl_clientid, flags);
|
|
|
|
|
2011-12-02 04:37:42 +07:00
|
|
|
if (flags & SEQ4_STATUS_RESTART_RECLAIM_NEEDED)
|
2010-03-03 01:06:21 +07:00
|
|
|
nfs41_handle_server_reboot(clp);
|
2011-12-02 04:37:42 +07:00
|
|
|
if (flags & (SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED |
|
2009-12-06 01:46:14 +07:00
|
|
|
SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED |
|
2013-10-18 01:13:58 +07:00
|
|
|
SEQ4_STATUS_ADMIN_STATE_REVOKED))
|
2010-03-03 01:06:21 +07:00
|
|
|
nfs41_handle_state_revoked(clp);
|
2013-10-18 01:13:58 +07:00
|
|
|
if (flags & SEQ4_STATUS_LEASE_MOVED)
|
|
|
|
nfs4_schedule_lease_moved_recovery(clp);
|
2011-12-02 04:37:42 +07:00
|
|
|
if (flags & SEQ4_STATUS_RECALLABLE_STATE_REVOKED)
|
2010-03-03 01:06:21 +07:00
|
|
|
nfs41_handle_recallable_state_revoked(clp);
|
2012-05-24 23:26:37 +07:00
|
|
|
if (flags & SEQ4_STATUS_BACKCHANNEL_FAULT)
|
|
|
|
nfs41_handle_backchannel_fault(clp);
|
|
|
|
else if (flags & (SEQ4_STATUS_CB_PATH_DOWN |
|
|
|
|
SEQ4_STATUS_CB_PATH_DOWN_SESSION))
|
2010-03-03 01:06:21 +07:00
|
|
|
nfs41_handle_cb_path_down(clp);
|
2009-12-06 01:46:14 +07:00
|
|
|
}
|
|
|
|
|
2009-04-01 20:22:39 +07:00
|
|
|
static int nfs4_reset_session(struct nfs_client *clp)
|
|
|
|
{
|
2012-05-26 04:51:23 +07:00
|
|
|
struct rpc_cred *cred;
|
2009-04-01 20:22:39 +07:00
|
|
|
int status;
|
|
|
|
|
2012-06-05 21:53:38 +07:00
|
|
|
if (!nfs4_has_session(clp))
|
|
|
|
return 0;
|
2009-12-16 05:36:57 +07:00
|
|
|
nfs4_begin_drain_session(clp);
|
2013-07-24 23:28:37 +07:00
|
|
|
cred = nfs4_get_clid_cred(clp);
|
2012-05-26 04:51:23 +07:00
|
|
|
status = nfs4_proc_destroy_session(clp->cl_session, cred);
|
2013-01-31 01:04:10 +07:00
|
|
|
switch (status) {
|
|
|
|
case 0:
|
|
|
|
case -NFS4ERR_BADSESSION:
|
|
|
|
case -NFS4ERR_DEADSESSION:
|
|
|
|
break;
|
|
|
|
case -NFS4ERR_BACK_CHAN_BUSY:
|
|
|
|
case -NFS4ERR_DELAY:
|
|
|
|
set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
|
|
|
|
status = 0;
|
|
|
|
ssleep(1);
|
|
|
|
goto out;
|
|
|
|
default:
|
2009-12-07 21:16:09 +07:00
|
|
|
status = nfs4_recovery_handle_error(clp, status);
|
2009-04-01 20:22:39 +07:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(clp->cl_session->sess_id.data, 0, NFS4_MAX_SESSIONID_LEN);
|
2012-05-26 04:51:23 +07:00
|
|
|
status = nfs4_proc_create_session(clp, cred);
|
2010-01-22 02:54:13 +07:00
|
|
|
if (status) {
|
2012-05-29 02:12:27 +07:00
|
|
|
dprintk("%s: session reset failed with status %d for server %s!\n",
|
|
|
|
__func__, status, clp->cl_hostname);
|
2012-05-28 01:46:46 +07:00
|
|
|
status = nfs4_handle_reclaim_lease_error(clp, status);
|
2010-01-22 02:54:13 +07:00
|
|
|
goto out;
|
|
|
|
}
|
2012-06-05 21:22:14 +07:00
|
|
|
nfs41_finish_session_reset(clp);
|
2012-05-29 02:12:27 +07:00
|
|
|
dprintk("%s: session reset was successful for server %s!\n",
|
|
|
|
__func__, clp->cl_hostname);
|
2010-01-22 02:54:13 +07:00
|
|
|
out:
|
2012-05-26 04:51:23 +07:00
|
|
|
if (cred)
|
|
|
|
put_rpccred(cred);
|
2009-04-01 20:22:39 +07:00
|
|
|
return status;
|
|
|
|
}
|
2009-04-01 20:22:38 +07:00
|
|
|
|
2012-05-24 23:26:37 +07:00
|
|
|
static int nfs4_bind_conn_to_session(struct nfs_client *clp)
|
|
|
|
{
|
2012-05-26 04:57:41 +07:00
|
|
|
struct rpc_cred *cred;
|
|
|
|
int ret;
|
|
|
|
|
2012-06-05 21:53:38 +07:00
|
|
|
if (!nfs4_has_session(clp))
|
|
|
|
return 0;
|
2012-05-28 00:47:21 +07:00
|
|
|
nfs4_begin_drain_session(clp);
|
2013-07-24 23:28:37 +07:00
|
|
|
cred = nfs4_get_clid_cred(clp);
|
2012-05-26 04:57:41 +07:00
|
|
|
ret = nfs4_proc_bind_conn_to_session(clp, cred);
|
|
|
|
if (cred)
|
|
|
|
put_rpccred(cred);
|
2012-05-28 00:47:21 +07:00
|
|
|
clear_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
|
2012-05-27 23:53:10 +07:00
|
|
|
switch (ret) {
|
|
|
|
case 0:
|
2012-05-29 02:12:27 +07:00
|
|
|
dprintk("%s: bind_conn_to_session was successful for server %s!\n",
|
|
|
|
__func__, clp->cl_hostname);
|
2012-05-27 23:53:10 +07:00
|
|
|
break;
|
|
|
|
case -NFS4ERR_DELAY:
|
|
|
|
ssleep(1);
|
|
|
|
set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return nfs4_recovery_handle_error(clp, ret);
|
|
|
|
}
|
|
|
|
return 0;
|
2012-05-24 23:26:37 +07:00
|
|
|
}
|
2009-04-01 20:22:38 +07:00
|
|
|
#else /* CONFIG_NFS_V4_1 */
|
2009-04-01 20:22:39 +07:00
|
|
|
static int nfs4_reset_session(struct nfs_client *clp) { return 0; }
|
2012-05-24 23:26:37 +07:00
|
|
|
|
|
|
|
static int nfs4_bind_conn_to_session(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2009-04-01 20:22:38 +07:00
|
|
|
#endif /* CONFIG_NFS_V4_1 */
|
|
|
|
|
2008-12-24 03:21:48 +07:00
|
|
|
static void nfs4_state_manager(struct nfs_client *clp)
|
2008-12-24 03:21:40 +07:00
|
|
|
{
|
|
|
|
int status = 0;
|
2012-05-30 02:57:59 +07:00
|
|
|
const char *section = "", *section_sep = "";
|
2008-12-24 03:21:40 +07:00
|
|
|
|
|
|
|
/* Ensure exclusive access to NFSv4 state */
|
2011-04-16 04:34:18 +07:00
|
|
|
do {
|
2012-05-22 09:45:33 +07:00
|
|
|
if (test_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state)) {
|
2012-05-30 02:57:59 +07:00
|
|
|
section = "purge state";
|
2012-06-05 22:19:47 +07:00
|
|
|
status = nfs4_purge_lease(clp);
|
2012-05-26 02:00:06 +07:00
|
|
|
if (status < 0)
|
|
|
|
goto out_error;
|
2012-06-05 22:19:47 +07:00
|
|
|
continue;
|
2012-05-22 09:45:33 +07:00
|
|
|
}
|
|
|
|
|
2012-06-05 22:19:47 +07:00
|
|
|
if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) {
|
2012-05-30 02:57:59 +07:00
|
|
|
section = "lease expired";
|
2008-12-24 03:21:41 +07:00
|
|
|
/* We're going to have to re-establish a clientid */
|
|
|
|
status = nfs4_reclaim_lease(clp);
|
2012-05-26 02:00:06 +07:00
|
|
|
if (status < 0)
|
2008-12-24 03:21:41 +07:00
|
|
|
goto out_error;
|
2012-06-05 22:19:47 +07:00
|
|
|
continue;
|
2008-12-24 03:21:42 +07:00
|
|
|
}
|
|
|
|
|
2009-04-01 20:22:39 +07:00
|
|
|
/* Initialize or reset the session */
|
2012-06-05 21:53:38 +07:00
|
|
|
if (test_and_clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state)) {
|
2012-05-30 02:57:59 +07:00
|
|
|
section = "reset session";
|
2009-12-05 03:52:24 +07:00
|
|
|
status = nfs4_reset_session(clp);
|
2009-12-04 03:53:22 +07:00
|
|
|
if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
|
|
|
|
continue;
|
|
|
|
if (status < 0)
|
2009-04-01 20:22:38 +07:00
|
|
|
goto out_error;
|
|
|
|
}
|
2009-12-04 03:53:22 +07:00
|
|
|
|
2012-05-24 23:26:37 +07:00
|
|
|
/* Send BIND_CONN_TO_SESSION */
|
|
|
|
if (test_and_clear_bit(NFS4CLNT_BIND_CONN_TO_SESSION,
|
2012-06-05 21:53:38 +07:00
|
|
|
&clp->cl_state)) {
|
2012-05-30 02:57:59 +07:00
|
|
|
section = "bind conn to session";
|
2012-05-24 23:26:37 +07:00
|
|
|
status = nfs4_bind_conn_to_session(clp);
|
|
|
|
if (status < 0)
|
|
|
|
goto out_error;
|
2012-05-27 23:53:10 +07:00
|
|
|
continue;
|
2012-05-24 23:26:37 +07:00
|
|
|
}
|
|
|
|
|
2012-11-21 21:22:14 +07:00
|
|
|
if (test_and_clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state)) {
|
|
|
|
section = "check lease";
|
|
|
|
status = nfs4_check_lease(clp);
|
|
|
|
if (status < 0)
|
|
|
|
goto out_error;
|
2013-10-18 01:13:02 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (test_and_clear_bit(NFS4CLNT_MOVED, &clp->cl_state)) {
|
|
|
|
section = "migration";
|
|
|
|
status = nfs4_handle_migration(clp);
|
|
|
|
if (status < 0)
|
|
|
|
goto out_error;
|
2012-11-21 21:22:14 +07:00
|
|
|
}
|
|
|
|
|
2013-10-18 01:13:35 +07:00
|
|
|
if (test_and_clear_bit(NFS4CLNT_LEASE_MOVED, &clp->cl_state)) {
|
|
|
|
section = "lease moved";
|
|
|
|
status = nfs4_handle_lease_moved(clp);
|
|
|
|
if (status < 0)
|
|
|
|
goto out_error;
|
|
|
|
}
|
|
|
|
|
2008-12-24 03:21:41 +07:00
|
|
|
/* First recover reboot state... */
|
2009-12-04 03:52:41 +07:00
|
|
|
if (test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) {
|
2012-05-30 02:57:59 +07:00
|
|
|
section = "reclaim reboot";
|
2009-04-01 20:22:47 +07:00
|
|
|
status = nfs4_do_reclaim(clp,
|
2010-06-16 20:52:27 +07:00
|
|
|
clp->cl_mvops->reboot_recovery_ops);
|
2009-12-04 03:53:22 +07:00
|
|
|
if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) ||
|
2009-12-05 03:55:05 +07:00
|
|
|
test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state))
|
2009-04-01 20:22:39 +07:00
|
|
|
continue;
|
2008-12-24 03:21:41 +07:00
|
|
|
nfs4_state_end_reclaim_reboot(clp);
|
2009-12-04 03:53:22 +07:00
|
|
|
if (test_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state))
|
|
|
|
continue;
|
|
|
|
if (status < 0)
|
|
|
|
goto out_error;
|
2008-12-24 03:21:40 +07:00
|
|
|
}
|
|
|
|
|
2008-12-24 03:21:41 +07:00
|
|
|
/* Now recover expired state... */
|
|
|
|
if (test_and_clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) {
|
2012-05-30 02:57:59 +07:00
|
|
|
section = "reclaim nograce";
|
2009-04-01 20:22:47 +07:00
|
|
|
status = nfs4_do_reclaim(clp,
|
2010-06-16 20:52:27 +07:00
|
|
|
clp->cl_mvops->nograce_recovery_ops);
|
2009-12-04 03:53:22 +07:00
|
|
|
if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) ||
|
2009-12-05 03:55:05 +07:00
|
|
|
test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state) ||
|
2009-12-04 03:53:22 +07:00
|
|
|
test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state))
|
|
|
|
continue;
|
|
|
|
if (status < 0)
|
2008-12-24 03:21:41 +07:00
|
|
|
goto out_error;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2008-12-24 03:21:47 +07:00
|
|
|
|
2009-12-15 12:27:58 +07:00
|
|
|
nfs4_end_drain_session(clp);
|
2008-12-24 03:21:47 +07:00
|
|
|
if (test_and_clear_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) {
|
|
|
|
nfs_client_return_marked_delegations(clp);
|
|
|
|
continue;
|
|
|
|
}
|
2008-12-24 03:21:48 +07:00
|
|
|
|
|
|
|
nfs4_clear_state_manager_bit(clp);
|
2008-12-24 03:21:48 +07:00
|
|
|
/* 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;
|
2011-04-16 04:34:18 +07:00
|
|
|
} while (atomic_read(&clp->cl_count) > 1);
|
2008-12-24 03:21:48 +07:00
|
|
|
return;
|
2005-04-17 05:20:36 +07:00
|
|
|
out_error:
|
2012-05-30 02:57:59 +07:00
|
|
|
if (strlen(section))
|
|
|
|
section_sep = ": ";
|
|
|
|
pr_warn_ratelimited("NFS: state manager%s%s failed on NFSv4 server %s"
|
|
|
|
" with error %d\n", section_sep, section,
|
|
|
|
clp->cl_hostname, -status);
|
2012-09-15 04:23:23 +07:00
|
|
|
ssleep(1);
|
2009-12-15 12:27:58 +07:00
|
|
|
nfs4_end_drain_session(clp);
|
2008-12-24 03:21:48 +07:00
|
|
|
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);
|
|
|
|
module_put_and_exit(0);
|
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Local variables:
|
|
|
|
* c-basic-offset: 8
|
|
|
|
* End:
|
|
|
|
*/
|