Commit 0b48d422 authored by Linus Torvalds's avatar Linus Torvalds

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

* 'for-3.3' of git://linux-nfs.org/~bfields/linux: (31 commits)
  nfsd4: nfsd4_create_clid_dir return value is unused
  NFSD: Change name of extended attribute containing junction
  svcrpc: don't revert to SVC_POOL_DEFAULT on nfsd shutdown
  svcrpc: fix double-free on shutdown of nfsd after changing pool mode
  nfsd4: be forgiving in the absence of the recovery directory
  nfsd4: fix spurious 4.1 post-reboot failures
  NFSD: forget_delegations should use list_for_each_entry_safe
  NFSD: Only reinitilize the recall_lru list under the recall lock
  nfsd4: initialize special stateid's at compile time
  NFSd: use network-namespace-aware cache registering routines
  SUNRPC: create svc_xprt in proper network namespace
  svcrpc: update outdated BKL comment
  nfsd41: allow non-reclaim open-by-fh's in 4.1
  svcrpc: avoid memory-corruption on pool shutdown
  svcrpc: destroy server sockets all at once
  svcrpc: make svc_delete_xprt static
  nfsd: Fix oops when parsing a 0 length export
  nfsd4: Use kmemdup rather than duplicating its implementation
  nfsd4: add a separate (lockowner, inode) lookup
  nfsd4: fix CONFIG_NFSD_FAULT_INJECTION compile error
  ...
parents 8e63dd6e 7a6ef8c7
...@@ -514,6 +514,11 @@ S: Bessemerstraat 21 ...@@ -514,6 +514,11 @@ S: Bessemerstraat 21
S: Amsterdam S: Amsterdam
S: The Netherlands S: The Netherlands
N: NeilBrown
E: neil@brown.name
P: 4096R/566281B9 1BC6 29EB D390 D870 7B5F 497A 39EC 9EDD 5662 81B9
D: NFSD Maintainer 2000-2007
N: Zach Brown N: Zach Brown
E: zab@zabbo.net E: zab@zabbo.net
D: maestro pci sound D: maestro pci sound
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
- this file (nfs-related documentation). - this file (nfs-related documentation).
Exporting Exporting
- explanation of how to make filesystems exportable. - explanation of how to make filesystems exportable.
fault_injection.txt
- information for using fault injection on the server
knfsd-stats.txt knfsd-stats.txt
- statistics which the NFS server makes available to user space. - statistics which the NFS server makes available to user space.
nfs.txt nfs.txt
......
Fault Injection
===============
Fault injection is a method for forcing errors that may not normally occur, or
may be difficult to reproduce. Forcing these errors in a controlled environment
can help the developer find and fix bugs before their code is shipped in a
production system. Injecting an error on the Linux NFS server will allow us to
observe how the client reacts and if it manages to recover its state correctly.
NFSD_FAULT_INJECTION must be selected when configuring the kernel to use this
feature.
Using Fault Injection
=====================
On the client, mount the fault injection server through NFS v4.0+ and do some
work over NFS (open files, take locks, ...).
On the server, mount the debugfs filesystem to <debug_dir> and ls
<debug_dir>/nfsd. This will show a list of files that will be used for
injecting faults on the NFS server. As root, write a number n to the file
corresponding to the action you want the server to take. The server will then
process the first n items it finds. So if you want to forget 5 locks, echo '5'
to <debug_dir>/nfsd/forget_locks. A value of 0 will tell the server to forget
all corresponding items. A log message will be created containing the number
of items forgotten (check dmesg).
Go back to work on the client and check if the client recovered from the error
correctly.
Available Faults
================
forget_clients:
The NFS server keeps a list of clients that have placed a mount call. If
this list is cleared, the server will have no knowledge of who the client
is, forcing the client to reauthenticate with the server.
forget_openowners:
The NFS server keeps a list of what files are currently opened and who
they were opened by. Clearing this list will force the client to reopen
its files.
forget_locks:
The NFS server keeps a list of what files are currently locked in the VFS.
Clearing this list will force the client to reclaim its locks (files are
unlocked through the VFS as they are cleared from this list).
forget_delegations:
A delegation is used to assure the client that a file, or part of a file,
has not changed since the delegation was awarded. Clearing this list will
force the client to reaquire its delegation before accessing the file
again.
recall_delegations:
Delegations can be recalled by the server when another client attempts to
access a file. This test will notify the client that its delegation has
been revoked, forcing the client to reaquire the delegation before using
the file again.
tools/nfs/inject_faults.sh script
=================================
This script has been created to ease the fault injection process. This script
will detect the mounted debugfs directory and write to the files located there
based on the arguments passed by the user. For example, running
`inject_faults.sh forget_locks 1` as root will instruct the server to forget
one lock. Running `inject_faults forget_locks` will instruct the server to
forgetall locks.
...@@ -3775,7 +3775,6 @@ S: Odd Fixes ...@@ -3775,7 +3775,6 @@ S: Odd Fixes
KERNEL NFSD, SUNRPC, AND LOCKD SERVERS KERNEL NFSD, SUNRPC, AND LOCKD SERVERS
M: "J. Bruce Fields" <bfields@fieldses.org> M: "J. Bruce Fields" <bfields@fieldses.org>
M: Neil Brown <neilb@suse.de>
L: linux-nfs@vger.kernel.org L: linux-nfs@vger.kernel.org
W: http://nfs.sourceforge.net/ W: http://nfs.sourceforge.net/
S: Supported S: Supported
......
...@@ -80,3 +80,13 @@ config NFSD_V4 ...@@ -80,3 +80,13 @@ config NFSD_V4
available from http://linux-nfs.org/. available from http://linux-nfs.org/.
If unsure, say N. If unsure, say N.
config NFSD_FAULT_INJECTION
bool "NFS server manual fault injection"
depends on NFSD_V4 && DEBUG_KERNEL
help
This option enables support for manually injecting faults
into the NFS server. This is intended to be used for
testing error recovery on the NFS client.
If unsure, say N.
...@@ -6,6 +6,7 @@ obj-$(CONFIG_NFSD) += nfsd.o ...@@ -6,6 +6,7 @@ obj-$(CONFIG_NFSD) += nfsd.o
nfsd-y := nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \ nfsd-y := nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \
export.o auth.o lockd.o nfscache.o nfsxdr.o stats.o export.o auth.o lockd.o nfscache.o nfsxdr.o stats.o
nfsd-$(CONFIG_NFSD_FAULT_INJECTION) += fault_inject.o
nfsd-$(CONFIG_NFSD_V2_ACL) += nfs2acl.o nfsd-$(CONFIG_NFSD_V2_ACL) += nfs2acl.o
nfsd-$(CONFIG_NFSD_V3) += nfs3proc.o nfs3xdr.o nfsd-$(CONFIG_NFSD_V3) += nfs3proc.o nfs3xdr.o
nfsd-$(CONFIG_NFSD_V3_ACL) += nfs3acl.o nfsd-$(CONFIG_NFSD_V3_ACL) += nfs3acl.o
......
...@@ -87,7 +87,7 @@ static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen) ...@@ -87,7 +87,7 @@ static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen)
struct svc_expkey key; struct svc_expkey key;
struct svc_expkey *ek = NULL; struct svc_expkey *ek = NULL;
if (mesg[mlen-1] != '\n') if (mlen < 1 || mesg[mlen-1] != '\n')
return -EINVAL; return -EINVAL;
mesg[mlen-1] = 0; mesg[mlen-1] = 0;
...@@ -1226,12 +1226,12 @@ nfsd_export_init(void) ...@@ -1226,12 +1226,12 @@ nfsd_export_init(void)
int rv; int rv;
dprintk("nfsd: initializing export module.\n"); dprintk("nfsd: initializing export module.\n");
rv = cache_register(&svc_export_cache); rv = cache_register_net(&svc_export_cache, &init_net);
if (rv) if (rv)
return rv; return rv;
rv = cache_register(&svc_expkey_cache); rv = cache_register_net(&svc_expkey_cache, &init_net);
if (rv) if (rv)
cache_unregister(&svc_export_cache); cache_unregister_net(&svc_export_cache, &init_net);
return rv; return rv;
} }
...@@ -1255,8 +1255,8 @@ nfsd_export_shutdown(void) ...@@ -1255,8 +1255,8 @@ nfsd_export_shutdown(void)
dprintk("nfsd: shutting down export module.\n"); dprintk("nfsd: shutting down export module.\n");
cache_unregister(&svc_expkey_cache); cache_unregister_net(&svc_expkey_cache, &init_net);
cache_unregister(&svc_export_cache); cache_unregister_net(&svc_export_cache, &init_net);
svcauth_unix_purge(); svcauth_unix_purge();
dprintk("nfsd: export shutdown complete.\n"); dprintk("nfsd: export shutdown complete.\n");
......
/*
* Copyright (c) 2011 Bryan Schumaker <bjschuma@netapp.com>
*
* Uses debugfs to create fault injection points for client testing
*/
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <linux/module.h>
#include "state.h"
#include "fault_inject.h"
struct nfsd_fault_inject_op {
char *file;
void (*func)(u64);
};
static struct nfsd_fault_inject_op inject_ops[] = {
{
.file = "forget_clients",
.func = nfsd_forget_clients,
},
{
.file = "forget_locks",
.func = nfsd_forget_locks,
},
{
.file = "forget_openowners",
.func = nfsd_forget_openowners,
},
{
.file = "forget_delegations",
.func = nfsd_forget_delegations,
},
{
.file = "recall_delegations",
.func = nfsd_recall_delegations,
},
};
static long int NUM_INJECT_OPS = sizeof(inject_ops) / sizeof(struct nfsd_fault_inject_op);
static struct dentry *debug_dir;
static int nfsd_inject_set(void *op_ptr, u64 val)
{
struct nfsd_fault_inject_op *op = op_ptr;
if (val == 0)
printk(KERN_INFO "NFSD Fault Injection: %s (all)", op->file);
else
printk(KERN_INFO "NFSD Fault Injection: %s (n = %llu)", op->file, val);
op->func(val);
return 0;
}
static int nfsd_inject_get(void *data, u64 *val)
{
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(fops_nfsd, nfsd_inject_get, nfsd_inject_set, "%llu\n");
void nfsd_fault_inject_cleanup(void)
{
debugfs_remove_recursive(debug_dir);
}
int nfsd_fault_inject_init(void)
{
unsigned int i;
struct nfsd_fault_inject_op *op;
mode_t mode = S_IFREG | S_IRUSR | S_IWUSR;
debug_dir = debugfs_create_dir("nfsd", NULL);
if (!debug_dir)
goto fail;
for (i = 0; i < NUM_INJECT_OPS; i++) {
op = &inject_ops[i];
if (!debugfs_create_file(op->file, mode, debug_dir, op, &fops_nfsd))
goto fail;
}
return 0;
fail:
nfsd_fault_inject_cleanup();
return -ENOMEM;
}
/*
* Copyright (c) 2011 Bryan Schumaker <bjschuma@netapp.com>
*
* Function definitions for fault injection
*/
#ifndef LINUX_NFSD_FAULT_INJECT_H
#define LINUX_NFSD_FAULT_INJECT_H
#ifdef CONFIG_NFSD_FAULT_INJECTION
int nfsd_fault_inject_init(void);
void nfsd_fault_inject_cleanup(void);
void nfsd_forget_clients(u64);
void nfsd_forget_locks(u64);
void nfsd_forget_openowners(u64);
void nfsd_forget_delegations(u64);
void nfsd_recall_delegations(u64);
#else /* CONFIG_NFSD_FAULT_INJECTION */
static inline int nfsd_fault_inject_init(void) { return 0; }
static inline void nfsd_fault_inject_cleanup(void) {}
static inline void nfsd_forget_clients(u64 num) {}
static inline void nfsd_forget_locks(u64 num) {}
static inline void nfsd_forget_openowners(u64 num) {}
static inline void nfsd_forget_delegations(u64 num) {}
static inline void nfsd_recall_delegations(u64 num) {}
#endif /* CONFIG_NFSD_FAULT_INJECTION */
#endif /* LINUX_NFSD_FAULT_INJECT_H */
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <net/net_namespace.h>
#include "idmap.h" #include "idmap.h"
#include "nfsd.h" #include "nfsd.h"
...@@ -466,20 +467,20 @@ nfsd_idmap_init(void) ...@@ -466,20 +467,20 @@ nfsd_idmap_init(void)
{ {
int rv; int rv;
rv = cache_register(&idtoname_cache); rv = cache_register_net(&idtoname_cache, &init_net);
if (rv) if (rv)
return rv; return rv;
rv = cache_register(&nametoid_cache); rv = cache_register_net(&nametoid_cache, &init_net);
if (rv) if (rv)
cache_unregister(&idtoname_cache); cache_unregister_net(&idtoname_cache, &init_net);
return rv; return rv;
} }
void void
nfsd_idmap_shutdown(void) nfsd_idmap_shutdown(void)
{ {
cache_unregister(&idtoname_cache); cache_unregister_net(&idtoname_cache, &init_net);
cache_unregister(&nametoid_cache); cache_unregister_net(&nametoid_cache, &init_net);
} }
static int static int
......
...@@ -266,10 +266,6 @@ do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_ ...@@ -266,10 +266,6 @@ do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_
{ {
__be32 status; __be32 status;
/* Only reclaims from previously confirmed clients are valid */
if ((status = nfs4_check_open_reclaim(&open->op_clientid)))
return status;
/* We don't know the target directory, and therefore can not /* We don't know the target directory, and therefore can not
* set the change info * set the change info
*/ */
...@@ -373,6 +369,9 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -373,6 +369,9 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
break; break;
case NFS4_OPEN_CLAIM_PREVIOUS: case NFS4_OPEN_CLAIM_PREVIOUS:
open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED; open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED;
status = nfs4_check_open_reclaim(&open->op_clientid);
if (status)
goto out;
case NFS4_OPEN_CLAIM_FH: case NFS4_OPEN_CLAIM_FH:
case NFS4_OPEN_CLAIM_DELEG_CUR_FH: case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
status = do_open_fhandle(rqstp, &cstate->current_fh, status = do_open_fhandle(rqstp, &cstate->current_fh,
......
...@@ -117,8 +117,7 @@ nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname) ...@@ -117,8 +117,7 @@ nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname)
return status; return status;
} }
int void nfsd4_create_clid_dir(struct nfs4_client *clp)
nfsd4_create_clid_dir(struct nfs4_client *clp)
{ {
const struct cred *original_cred; const struct cred *original_cred;
char *dname = clp->cl_recdir; char *dname = clp->cl_recdir;
...@@ -127,13 +126,14 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) ...@@ -127,13 +126,14 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname); dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname);
if (!rec_file || clp->cl_firststate) if (clp->cl_firststate)
return 0; return;
clp->cl_firststate = 1; clp->cl_firststate = 1;
if (!rec_file)
return;
status = nfs4_save_creds(&original_cred); status = nfs4_save_creds(&original_cred);
if (status < 0) if (status < 0)
return status; return;
dir = rec_file->f_path.dentry; dir = rec_file->f_path.dentry;
/* lock the parent */ /* lock the parent */
...@@ -144,8 +144,15 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) ...@@ -144,8 +144,15 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
status = PTR_ERR(dentry); status = PTR_ERR(dentry);
goto out_unlock; goto out_unlock;
} }
status = -EEXIST;
if (dentry->d_inode) if (dentry->d_inode)
/*
* In the 4.1 case, where we're called from
* reclaim_complete(), records from the previous reboot
* may still be left, so this is OK.
*
* In the 4.0 case, we should never get here; but we may
* as well be forgiving and just succeed silently.
*/
goto out_put; goto out_put;
status = mnt_want_write_file(rec_file); status = mnt_want_write_file(rec_file);
if (status) if (status)
...@@ -164,7 +171,6 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) ...@@ -164,7 +171,6 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
" and is writeable", status, " and is writeable", status,
user_recovery_dirname); user_recovery_dirname);
nfs4_reset_creds(original_cred); nfs4_reset_creds(original_cred);
return status;
} }
typedef int (recdir_func)(struct dentry *, struct dentry *); typedef int (recdir_func)(struct dentry *, struct dentry *);
......
This diff is collapsed.
...@@ -215,10 +215,9 @@ defer_free(struct nfsd4_compoundargs *argp, ...@@ -215,10 +215,9 @@ defer_free(struct nfsd4_compoundargs *argp,
static char *savemem(struct nfsd4_compoundargs *argp, __be32 *p, int nbytes) static char *savemem(struct nfsd4_compoundargs *argp, __be32 *p, int nbytes)
{ {
if (p == argp->tmp) { if (p == argp->tmp) {
p = kmalloc(nbytes, GFP_KERNEL); p = kmemdup(argp->tmp, nbytes, GFP_KERNEL);
if (!p) if (!p)
return NULL; return NULL;
memcpy(p, argp->tmp, nbytes);
} else { } else {
BUG_ON(p != argp->tmpp); BUG_ON(p != argp->tmpp);
argp->tmpp = NULL; argp->tmpp = NULL;
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "idmap.h" #include "idmap.h"
#include "nfsd.h" #include "nfsd.h"
#include "cache.h" #include "cache.h"
#include "fault_inject.h"
/* /*
* We have a single directory with several nodes in it. * We have a single directory with several nodes in it.
...@@ -1128,9 +1129,13 @@ static int __init init_nfsd(void) ...@@ -1128,9 +1129,13 @@ static int __init init_nfsd(void)
int retval; int retval;
printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n"); printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
retval = nfs4_state_init(); /* nfs4 locking state */ retval = nfsd4_init_slabs();
if (retval) if (retval)
return retval; return retval;
nfs4_state_init();
retval = nfsd_fault_inject_init(); /* nfsd fault injection controls */
if (retval)
goto out_free_slabs;
nfsd_stat_init(); /* Statistics */ nfsd_stat_init(); /* Statistics */
retval = nfsd_reply_cache_init(); retval = nfsd_reply_cache_init();
if (retval) if (retval)
...@@ -1161,6 +1166,8 @@ static int __init init_nfsd(void) ...@@ -1161,6 +1166,8 @@ static int __init init_nfsd(void)
nfsd_reply_cache_shutdown(); nfsd_reply_cache_shutdown();
out_free_stat: out_free_stat:
nfsd_stat_shutdown(); nfsd_stat_shutdown();
nfsd_fault_inject_cleanup();
out_free_slabs:
nfsd4_free_slabs(); nfsd4_free_slabs();
return retval; return retval;
} }
...@@ -1175,6 +1182,7 @@ static void __exit exit_nfsd(void) ...@@ -1175,6 +1182,7 @@ static void __exit exit_nfsd(void)
nfsd_lockd_shutdown(); nfsd_lockd_shutdown();
nfsd_idmap_shutdown(); nfsd_idmap_shutdown();
nfsd4_free_slabs(); nfsd4_free_slabs();
nfsd_fault_inject_cleanup();
unregister_filesystem(&nfsd_fs_type); unregister_filesystem(&nfsd_fs_type);
} }
......
...@@ -104,14 +104,16 @@ static inline int nfsd_v4client(struct svc_rqst *rq) ...@@ -104,14 +104,16 @@ static inline int nfsd_v4client(struct svc_rqst *rq)
*/ */
#ifdef CONFIG_NFSD_V4 #ifdef CONFIG_NFSD_V4
extern unsigned int max_delegations; extern unsigned int max_delegations;
int nfs4_state_init(void); void nfs4_state_init(void);
int nfsd4_init_slabs(void);
void nfsd4_free_slabs(void); void nfsd4_free_slabs(void);
int nfs4_state_start(void); int nfs4_state_start(void);
void nfs4_state_shutdown(void); void nfs4_state_shutdown(void);
void nfs4_reset_lease(time_t leasetime); void nfs4_reset_lease(time_t leasetime);
int nfs4_reset_recoverydir(char *recdir); int nfs4_reset_recoverydir(char *recdir);
#else #else
static inline int nfs4_state_init(void) { return 0; } static inline void nfs4_state_init(void) { }
static inline int nfsd4_init_slabs(void) { return 0; }
static inline void nfsd4_free_slabs(void) { } static inline void nfsd4_free_slabs(void) { }
static inline int nfs4_state_start(void) { return 0; } static inline int nfs4_state_start(void) { return 0; }
static inline void nfs4_state_shutdown(void) { } static inline void nfs4_state_shutdown(void) { }
...@@ -338,15 +340,15 @@ static inline u32 nfsd_suppattrs2(u32 minorversion) ...@@ -338,15 +340,15 @@ static inline u32 nfsd_suppattrs2(u32 minorversion)
} }
/* These will return ERR_INVAL if specified in GETATTR or READDIR. */ /* These will return ERR_INVAL if specified in GETATTR or READDIR. */
#define NFSD_WRITEONLY_ATTRS_WORD1 \ #define NFSD_WRITEONLY_ATTRS_WORD1 \
(FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET) (FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET)
/* These are the only attrs allowed in CREATE/OPEN/SETATTR. */ /* These are the only attrs allowed in CREATE/OPEN/SETATTR. */
#define NFSD_WRITEABLE_ATTRS_WORD0 \ #define NFSD_WRITEABLE_ATTRS_WORD0 \
(FATTR4_WORD0_SIZE | FATTR4_WORD0_ACL ) (FATTR4_WORD0_SIZE | FATTR4_WORD0_ACL)
#define NFSD_WRITEABLE_ATTRS_WORD1 \ #define NFSD_WRITEABLE_ATTRS_WORD1 \
(FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \ (FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \
| FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET) | FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET)
#define NFSD_WRITEABLE_ATTRS_WORD2 0 #define NFSD_WRITEABLE_ATTRS_WORD2 0
#define NFSD_SUPPATTR_EXCLCREAT_WORD0 \ #define NFSD_SUPPATTR_EXCLCREAT_WORD0 \
......
...@@ -366,6 +366,7 @@ struct nfs4_openowner { ...@@ -366,6 +366,7 @@ struct nfs4_openowner {
struct nfs4_lockowner { struct nfs4_lockowner {
struct nfs4_stateowner lo_owner; /* must be first element */ struct nfs4_stateowner lo_owner; /* must be first element */
struct list_head lo_owner_ino_hash; /* hash by owner,file */
struct list_head lo_perstateid; /* for lockowners only */ struct list_head lo_perstateid; /* for lockowners only */
struct list_head lo_list; /* for temporary uses */ struct list_head lo_list; /* for temporary uses */
}; };
...@@ -482,7 +483,7 @@ extern void nfsd4_shutdown_recdir(void); ...@@ -482,7 +483,7 @@ extern void nfsd4_shutdown_recdir(void);
extern int nfs4_client_to_reclaim(const char *name); extern int nfs4_client_to_reclaim(const char *name);
extern int nfs4_has_reclaimed_state(const char *name, bool use_exchange_id); extern int nfs4_has_reclaimed_state(const char *name, bool use_exchange_id);
extern void nfsd4_recdir_purge_old(void); extern void nfsd4_recdir_purge_old(void);
extern int nfsd4_create_clid_dir(struct nfs4_client *clp); extern void nfsd4_create_clid_dir(struct nfs4_client *clp);
extern void nfsd4_remove_clid_dir(struct nfs4_client *clp); extern void nfsd4_remove_clid_dir(struct nfs4_client *clp);
extern void release_session_client(struct nfsd4_session *); extern void release_session_client(struct nfsd4_session *);
extern __be32 nfs4_validate_stateid(struct nfs4_client *, stateid_t *); extern __be32 nfs4_validate_stateid(struct nfs4_client *, stateid_t *);
......
...@@ -594,8 +594,19 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_ac ...@@ -594,8 +594,19 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_ac
return error; return error;
} }
#define NFSD_XATTR_JUNCTION_PREFIX XATTR_TRUSTED_PREFIX "junction." /*
#define NFSD_XATTR_JUNCTION_TYPE NFSD_XATTR_JUNCTION_PREFIX "type" * NFS junction information is stored in an extended attribute.
*/
#define NFSD_JUNCTION_XATTR_NAME XATTR_TRUSTED_PREFIX "junction.nfs"
/**
* nfsd4_is_junction - Test if an object could be an NFS junction
*
* @dentry: object to test
*
* Returns 1 if "dentry" appears to contain NFS junction information.
* Otherwise 0 is returned.
*/
int nfsd4_is_junction(struct dentry *dentry) int nfsd4_is_junction(struct dentry *dentry)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
...@@ -606,7 +617,7 @@ int nfsd4_is_junction(struct dentry *dentry) ...@@ -606,7 +617,7 @@ int nfsd4_is_junction(struct dentry *dentry)
return 0; return 0;
if (!(inode->i_mode & S_ISVTX)) if (!(inode->i_mode & S_ISVTX))
return 0; return 0;
if (vfs_getxattr(dentry, NFSD_XATTR_JUNCTION_TYPE, NULL, 0) <= 0) if (vfs_getxattr(dentry, NFSD_JUNCTION_XATTR_NAME, NULL, 0) <= 0)
return 0; return 0;
return 1; return 1;
} }
......
...@@ -109,7 +109,7 @@ static inline int register_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u ...@@ -109,7 +109,7 @@ static inline int register_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u
int svc_reg_xprt_class(struct svc_xprt_class *); int svc_reg_xprt_class(struct svc_xprt_class *);
void svc_unreg_xprt_class(struct svc_xprt_class *); void svc_unreg_xprt_class(struct svc_xprt_class *);
void svc_xprt_init(struct svc_xprt_class *, struct svc_xprt *, void svc_xprt_init(struct net *, struct svc_xprt_class *, struct svc_xprt *,
struct svc_serv *); struct svc_serv *);
int svc_create_xprt(struct svc_serv *, const char *, struct net *, int svc_create_xprt(struct svc_serv *, const char *, struct net *,
const int, const unsigned short, int); const int, const unsigned short, int);
...@@ -118,7 +118,6 @@ void svc_xprt_received(struct svc_xprt *); ...@@ -118,7 +118,6 @@ void svc_xprt_received(struct svc_xprt *);
void svc_xprt_put(struct svc_xprt *xprt); void svc_xprt_put(struct svc_xprt *xprt);
void svc_xprt_copy_addrs(struct svc_rqst *rqstp, struct svc_xprt *xprt); void svc_xprt_copy_addrs(struct svc_rqst *rqstp, struct svc_xprt *xprt);
void svc_close_xprt(struct svc_xprt *xprt); void svc_close_xprt(struct svc_xprt *xprt);
void svc_delete_xprt(struct svc_xprt *xprt);
int svc_port_is_privileged(struct sockaddr *sin); int svc_port_is_privileged(struct sockaddr *sin);
int svc_print_xprts(char *buf, int maxlen); int svc_print_xprts(char *buf, int maxlen);
struct svc_xprt *svc_find_xprt(struct svc_serv *serv, const char *xcl_name, struct svc_xprt *svc_find_xprt(struct svc_serv *serv, const char *xcl_name,
......
...@@ -34,7 +34,7 @@ struct svc_sock { ...@@ -34,7 +34,7 @@ struct svc_sock {
/* /*
* Function prototypes. * Function prototypes.
*/ */
void svc_close_all(struct list_head *); void svc_close_all(struct svc_serv *);
int svc_recv(struct svc_rqst *, long); int svc_recv(struct svc_rqst *, long);
int svc_send(struct svc_rqst *); int svc_send(struct svc_rqst *);
void svc_drop(struct svc_rqst *); void svc_drop(struct svc_rqst *);
......
...@@ -1641,6 +1641,7 @@ int cache_register_net(struct cache_detail *cd, struct net *net) ...@@ -1641,6 +1641,7 @@ int cache_register_net(struct cache_detail *cd, struct net *net)
sunrpc_destroy_cache_detail(cd); sunrpc_destroy_cache_detail(cd);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(cache_register_net);
int cache_register(struct cache_detail *cd) int cache_register(struct cache_detail *cd)
{ {
...@@ -1653,6 +1654,7 @@ void cache_unregister_net(struct cache_detail *cd, struct net *net) ...@@ -1653,6 +1654,7 @@ void cache_unregister_net(struct cache_detail *cd, struct net *net)
remove_cache_proc_entries(cd, net); remove_cache_proc_entries(cd, net);
sunrpc_destroy_cache_detail(cd); sunrpc_destroy_cache_detail(cd);
} }
EXPORT_SYMBOL_GPL(cache_unregister_net);
void cache_unregister(struct cache_detail *cd) void cache_unregister(struct cache_detail *cd)
{ {
......
...@@ -167,6 +167,7 @@ svc_pool_map_alloc_arrays(struct svc_pool_map *m, unsigned int maxpools) ...@@ -167,6 +167,7 @@ svc_pool_map_alloc_arrays(struct svc_pool_map *m, unsigned int maxpools)
fail_free: fail_free:
kfree(m->to_pool); kfree(m->to_pool);
m->to_pool = NULL;
fail: fail:
return -ENOMEM; return -ENOMEM;
} }
...@@ -285,9 +286,10 @@ svc_pool_map_put(void) ...@@ -285,9 +286,10 @@ svc_pool_map_put(void)
mutex_lock(&svc_pool_map_mutex); mutex_lock(&svc_pool_map_mutex);
if (!--m->count) { if (!--m->count) {
m->mode = SVC_POOL_DEFAULT;
kfree(m->to_pool); kfree(m->to_pool);
m->to_pool = NULL;
kfree(m->pool_to); kfree(m->pool_to);
m->pool_to = NULL;
m->npools = 0; m->npools = 0;
} }
...@@ -527,17 +529,20 @@ svc_destroy(struct svc_serv *serv) ...@@ -527,17 +529,20 @@ svc_destroy(struct svc_serv *serv)
printk("svc_destroy: no threads for serv=%p!\n", serv); printk("svc_destroy: no threads for serv=%p!\n", serv);
del_timer_sync(&serv->sv_temptimer); del_timer_sync(&serv->sv_temptimer);
/*
svc_close_all(&serv->sv_tempsocks); * The set of xprts (contained in the sv_tempsocks and
* sv_permsocks lists) is now constant, since it is modified
* only by accepting new sockets (done by service threads in
* svc_recv) or aging old ones (done by sv_temptimer), or
* configuration changes (excluded by whatever locking the
* caller is using--nfsd_mutex in the case of nfsd). So it's
* safe to traverse those lists and shut everything down:
*/
svc_close_all(serv);
if (serv->sv_shutdown) if (serv->sv_shutdown)
serv->sv_shutdown(serv); serv->sv_shutdown(serv);
svc_close_all(&serv->sv_permsocks);
BUG_ON(!list_empty(&serv->sv_permsocks));
BUG_ON(!list_empty(&serv->sv_tempsocks));
cache_clean_deferred(serv); cache_clean_deferred(serv);
if (svc_serv_is_pooled(serv)) if (svc_serv_is_pooled(serv))
...@@ -683,8 +688,8 @@ choose_victim(struct svc_serv *serv, struct svc_pool *pool, unsigned int *state) ...@@ -683,8 +688,8 @@ choose_victim(struct svc_serv *serv, struct svc_pool *pool, unsigned int *state)
* Create or destroy enough new threads to make the number * Create or destroy enough new threads to make the number
* of threads the given number. If `pool' is non-NULL, applies * of threads the given number. If `pool' is non-NULL, applies
* only to threads in that pool, otherwise round-robins between * only to threads in that pool, otherwise round-robins between
* all pools. Must be called with a svc_get() reference and * all pools. Caller must ensure that mutual exclusion between this and
* the BKL or another lock to protect access to svc_serv fields. * server startup or shutdown.
* *
* Destroying threads relies on the service threads filling in * Destroying threads relies on the service threads filling in
* rqstp->rq_task, which only the nfs ones do. Assumes the serv * rqstp->rq_task, which only the nfs ones do. Assumes the serv
......
...@@ -22,6 +22,7 @@ static struct svc_deferred_req *svc_deferred_dequeue(struct svc_xprt *xprt); ...@@ -22,6 +22,7 @@ static struct svc_deferred_req *svc_deferred_dequeue(struct svc_xprt *xprt);
static int svc_deferred_recv(struct svc_rqst *rqstp); static int svc_deferred_recv(struct svc_rqst *rqstp);
static struct cache_deferred_req *svc_defer(struct cache_req *req); static struct cache_deferred_req *svc_defer(struct cache_req *req);
static void svc_age_temp_xprts(unsigned long closure); static void svc_age_temp_xprts(unsigned long closure);
static void svc_delete_xprt(struct svc_xprt *xprt);
/* apparently the "standard" is that clients close /* apparently the "standard" is that clients close
* idle connections after 5 minutes, servers after * idle connections after 5 minutes, servers after
...@@ -147,8 +148,8 @@ EXPORT_SYMBOL_GPL(svc_xprt_put); ...@@ -147,8 +148,8 @@ EXPORT_SYMBOL_GPL(svc_xprt_put);
* Called by transport drivers to initialize the transport independent * Called by transport drivers to initialize the transport independent
* portion of the transport instance. * portion of the transport instance.
*/ */
void svc_xprt_init(struct svc_xprt_class *xcl, struct svc_xprt *xprt, void svc_xprt_init(struct net *net, struct svc_xprt_class *xcl,
struct svc_serv *serv) struct svc_xprt *xprt, struct svc_serv *serv)
{ {
memset(xprt, 0, sizeof(*xprt)); memset(xprt, 0, sizeof(*xprt));
xprt->xpt_class = xcl; xprt->xpt_class = xcl;
...@@ -163,7 +164,7 @@ void svc_xprt_init(struct svc_xprt_class *xcl, struct svc_xprt *xprt, ...@@ -163,7 +164,7 @@ void svc_xprt_init(struct svc_xprt_class *xcl, struct svc_xprt *xprt,
spin_lock_init(&xprt->xpt_lock); spin_lock_init(&xprt->xpt_lock);
set_bit(XPT_BUSY, &xprt->xpt_flags); set_bit(XPT_BUSY, &xprt->xpt_flags);
rpc_init_wait_queue(&xprt->xpt_bc_pending, "xpt_bc_pending"); rpc_init_wait_queue(&xprt->xpt_bc_pending, "xpt_bc_pending");
xprt->xpt_net = get_net(&init_net); xprt->xpt_net = get_net(net);
} }
EXPORT_SYMBOL_GPL(svc_xprt_init); EXPORT_SYMBOL_GPL(svc_xprt_init);
...@@ -878,7 +879,7 @@ static void call_xpt_users(struct svc_xprt *xprt) ...@@ -878,7 +879,7 @@ static void call_xpt_users(struct svc_xprt *xprt)
/* /*
* Remove a dead transport * Remove a dead transport
*/ */
void svc_delete_xprt(struct svc_xprt *xprt) static void svc_delete_xprt(struct svc_xprt *xprt)
{ {
struct svc_serv *serv = xprt->xpt_server; struct svc_serv *serv = xprt->xpt_server;
struct svc_deferred_req *dr; struct svc_deferred_req *dr;
...@@ -893,14 +894,7 @@ void svc_delete_xprt(struct svc_xprt *xprt) ...@@ -893,14 +894,7 @@ void svc_delete_xprt(struct svc_xprt *xprt)
spin_lock_bh(&serv->sv_lock); spin_lock_bh(&serv->sv_lock);
if (!test_and_set_bit(XPT_DETACHED, &xprt->xpt_flags)) if (!test_and_set_bit(XPT_DETACHED, &xprt->xpt_flags))
list_del_init(&xprt->xpt_list); list_del_init(&xprt->xpt_list);
/* BUG_ON(!list_empty(&xprt->xpt_ready));
* The only time we're called while xpt_ready is still on a list
* is while the list itself is about to be destroyed (in
* svc_destroy). BUT svc_xprt_enqueue could still be attempting
* to add new entries to the sp_sockets list, so we can't leave
* a freed xprt on it.
*/
list_del_init(&xprt->xpt_ready);
if (test_bit(XPT_TEMP, &xprt->xpt_flags)) if (test_bit(XPT_TEMP, &xprt->xpt_flags))
serv->sv_tmpcnt--; serv->sv_tmpcnt--;
spin_unlock_bh(&serv->sv_lock); spin_unlock_bh(&serv->sv_lock);
...@@ -928,22 +922,48 @@ void svc_close_xprt(struct svc_xprt *xprt) ...@@ -928,22 +922,48 @@ void svc_close_xprt(struct svc_xprt *xprt)
} }
EXPORT_SYMBOL_GPL(svc_close_xprt); EXPORT_SYMBOL_GPL(svc_close_xprt);
void svc_close_all(struct list_head *xprt_list) static void svc_close_list(struct list_head *xprt_list)
{
struct svc_xprt *xprt;
list_for_each_entry(xprt, xprt_list, xpt_list) {
set_bit(XPT_CLOSE, &xprt->xpt_flags);
set_bit(XPT_BUSY, &xprt->xpt_flags);
}
}
void svc_close_all(struct svc_serv *serv)
{ {
struct svc_pool *pool;
struct svc_xprt *xprt; struct svc_xprt *xprt;
struct svc_xprt *tmp; struct svc_xprt *tmp;
int i;
svc_close_list(&serv->sv_tempsocks);
svc_close_list(&serv->sv_permsocks);
for (i = 0; i < serv->sv_nrpools; i++) {
pool = &serv->sv_pools[i];
spin_lock_bh(&pool->sp_lock);
while (!list_empty(&pool->sp_sockets)) {
xprt = list_first_entry(&pool->sp_sockets, struct svc_xprt, xpt_ready);
list_del_init(&xprt->xpt_ready);
}
spin_unlock_bh(&pool->sp_lock);
}
/* /*
* The server is shutting down, and no more threads are running. * At this point the sp_sockets lists will stay empty, since
* svc_xprt_enqueue() might still be running, but at worst it * svc_enqueue will not add new entries without taking the
* will re-add the xprt to sp_sockets, which will soon get * sp_lock and checking XPT_BUSY.
* freed. So we don't bother with any more locking, and don't
* leave the close to the (nonexistent) server threads:
*/ */
list_for_each_entry_safe(xprt, tmp, xprt_list, xpt_list) { list_for_each_entry_safe(xprt, tmp, &serv->sv_tempsocks, xpt_list)
set_bit(XPT_CLOSE, &xprt->xpt_flags);
svc_delete_xprt(xprt); svc_delete_xprt(xprt);
} list_for_each_entry_safe(xprt, tmp, &serv->sv_permsocks, xpt_list)
svc_delete_xprt(xprt);
BUG_ON(!list_empty(&serv->sv_permsocks));
BUG_ON(!list_empty(&serv->sv_tempsocks));
} }
/* /*
......
...@@ -739,7 +739,8 @@ static void svc_udp_init(struct svc_sock *svsk, struct svc_serv *serv) ...@@ -739,7 +739,8 @@ static void svc_udp_init(struct svc_sock *svsk, struct svc_serv *serv)
{ {
int err, level, optname, one = 1; int err, level, optname, one = 1;
svc_xprt_init(&svc_udp_class, &svsk->sk_xprt, serv); svc_xprt_init(sock_net(svsk->sk_sock->sk), &svc_udp_class,
&svsk->sk_xprt, serv);
clear_bit(XPT_CACHE_AUTH, &svsk->sk_xprt.xpt_flags); clear_bit(XPT_CACHE_AUTH, &svsk->sk_xprt.xpt_flags);
svsk->sk_sk->sk_data_ready = svc_udp_data_ready; svsk->sk_sk->sk_data_ready = svc_udp_data_ready;
svsk->sk_sk->sk_write_space = svc_write_space; svsk->sk_sk->sk_write_space = svc_write_space;
...@@ -1343,7 +1344,8 @@ static void svc_tcp_init(struct svc_sock *svsk, struct svc_serv *serv) ...@@ -1343,7 +1344,8 @@ static void svc_tcp_init(struct svc_sock *svsk, struct svc_serv *serv)
{ {
struct sock *sk = svsk->sk_sk; struct sock *sk = svsk->sk_sk;
svc_xprt_init(&svc_tcp_class, &svsk->sk_xprt, serv); svc_xprt_init(sock_net(svsk->sk_sock->sk), &svc_tcp_class,
&svsk->sk_xprt, serv);
set_bit(XPT_CACHE_AUTH, &svsk->sk_xprt.xpt_flags); set_bit(XPT_CACHE_AUTH, &svsk->sk_xprt.xpt_flags);
if (sk->sk_state == TCP_LISTEN) { if (sk->sk_state == TCP_LISTEN) {
dprintk("setting up TCP socket for listening\n"); dprintk("setting up TCP socket for listening\n");
...@@ -1659,7 +1661,7 @@ static struct svc_xprt *svc_bc_create_socket(struct svc_serv *serv, ...@@ -1659,7 +1661,7 @@ static struct svc_xprt *svc_bc_create_socket(struct svc_serv *serv,
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
xprt = &svsk->sk_xprt; xprt = &svsk->sk_xprt;
svc_xprt_init(&svc_tcp_bc_class, xprt, serv); svc_xprt_init(net, &svc_tcp_bc_class, xprt, serv);
serv->sv_bc_xprt = xprt; serv->sv_bc_xprt = xprt;
......
...@@ -453,7 +453,7 @@ static struct svcxprt_rdma *rdma_create_xprt(struct svc_serv *serv, ...@@ -453,7 +453,7 @@ static struct svcxprt_rdma *rdma_create_xprt(struct svc_serv *serv,
if (!cma_xprt) if (!cma_xprt)
return NULL; return NULL;
svc_xprt_init(&svc_rdma_class, &cma_xprt->sc_xprt, serv); svc_xprt_init(&init_net, &svc_rdma_class, &cma_xprt->sc_xprt, serv);
INIT_LIST_HEAD(&cma_xprt->sc_accept_q); INIT_LIST_HEAD(&cma_xprt->sc_accept_q);
INIT_LIST_HEAD(&cma_xprt->sc_dto_q); INIT_LIST_HEAD(&cma_xprt->sc_dto_q);
INIT_LIST_HEAD(&cma_xprt->sc_rq_dto_q); INIT_LIST_HEAD(&cma_xprt->sc_rq_dto_q);
......
#!/bin/bash
#
# Copyright (c) 2011 Bryan Schumaker <bjschuma@netapp.com>
#
# Script for easier NFSD fault injection
# Check that debugfs has been mounted
DEBUGFS=`cat /proc/mounts | grep debugfs`
if [ "$DEBUGFS" == "" ]; then
echo "debugfs does not appear to be mounted!"
echo "Please mount debugfs and try again"
exit 1
fi
# Check that the fault injection directory exists
DEBUGDIR=`echo $DEBUGFS | awk '{print $2}'`/nfsd
if [ ! -d "$DEBUGDIR" ]; then
echo "$DEBUGDIR does not exist"
echo "Check that your .config selects CONFIG_NFSD_FAULT_INJECTION"
exit 1
fi
function help()
{
echo "Usage $0 injection_type [count]"
echo ""
echo "Injection types are:"
ls $DEBUGDIR
exit 1
}
if [ $# == 0 ]; then
help
elif [ ! -f $DEBUGDIR/$1 ]; then
help
elif [ $# != 2 ]; then
COUNT=0
else
COUNT=$2
fi
BEFORE=`mktemp`
AFTER=`mktemp`
dmesg > $BEFORE
echo $COUNT > $DEBUGDIR/$1
dmesg > $AFTER
# Capture lines that only exist in the $AFTER file
diff $BEFORE $AFTER | grep ">"
rm -f $BEFORE $AFTER
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment