NFS client updates for Linux 3.12

Highlights include:
 
 - Fix NFSv4 recovery so that it doesn't recover lost locks in cases such as
   lease loss due to a network partition, where doing so may result in data
   corruption. Add a kernel parameter to control choice of legacy behaviour
   or not.
 - Performance improvements when 2 processes are writing to the same file.
 - Flush data to disk when an RPCSEC_GSS session timeout is imminent.
 - Implement NFSv4.1 SP4_MACH_CRED state protection to prevent other
   NFS clients from being able to manipulate our lease and file lockingr
   state.
 - Allow sharing of RPCSEC_GSS caches between different rpc clients
 - Fix the broken NFSv4 security auto-negotiation between client and server
 - Fix rmdir() to wait for outstanding sillyrename unlinks to complete
 - Add a tracepoint framework for debugging NFSv4 state recovery issues.
 - Add tracing to the generic NFS layer.
 - Add tracing for the SUNRPC socket connection state.
 - Clean up the rpc_pipefs mount/umount event management.
 - Merge more patches from Chuck in preparation for NFSv4 migration support.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.14 (GNU/Linux)
 
 iQIcBAABAgAGBQJSLelVAAoJEGcL54qWCgDyo2IQAKOfRJyZVnf4ipxi3xLNl1QF
 w/70DVSIF1S1djWN7G3vgkxj/R8KCvJ8CcvkAD2BEgRDeZJ9TtyKAdM/jYLZ+W05
 7k2QKk8fkwZmc1Y2qDqFwKHzP5ZgP5L2nGx7FNhi/99wEAe47yFG3qd3rUWKrcOf
 mnd863zgGDE2Q10slhoq/bywwMJo6tKZNeaIE8kPjgFbBEh/jslpAWr8dSA4QgvJ
 nZ8VB5XU8L+XJ0GpHHdjYm9LvQ51DbQ6omOF+0P4fI093azKmf4ZsrjMDWT8+iu3
 XkXlnQmKLGTi7yB43hHtn2NiRqwGzCcZ1Amo9PpCFaHUt1RP9cc37UhG1T+x1xWJ
 STEKDbvCdQ3FU9FvbgrGEwBR0e8fNS4fZY3ToDBflIcfwre0aWs5RCodZMUD0nUI
 4wY5J9NsQR/bL+v8KeUR4V4cXK8YrgL0zB4u4WYzH5Npxr5KD0NEKDNqRPhrB9l2
 LLF9Haql8j76Ff0ek6UGFIZjDE0h6Fs71wLBpLj+ZWArOJ7vBuLMBSOVqNpld9+9
 f2fEG7qoGF4FGTY4myH/eakMPaWnk9Ol4Ls/svSIapJ9+rePD+a93e/qnmdofIMf
 4TuEYk6ERib1qXgaeDRQuCsm2YE1Co5skGMaOsRFWgReE1c12QoJQVst2nMtEKp3
 uV2w8LgX18aZOZXJVkCM
 =ZuW+
 -----END PGP SIGNATURE-----

Merge tag 'nfs-for-3.12-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs

Pull NFS client updates from Trond Myklebust:
 "Highlights include:

   - Fix NFSv4 recovery so that it doesn't recover lost locks in cases
     such as lease loss due to a network partition, where doing so may
     result in data corruption.  Add a kernel parameter to control
     choice of legacy behaviour or not.
   - Performance improvements when 2 processes are writing to the same
     file.
   - Flush data to disk when an RPCSEC_GSS session timeout is imminent.
   - Implement NFSv4.1 SP4_MACH_CRED state protection to prevent other
     NFS clients from being able to manipulate our lease and file
     locking state.
   - Allow sharing of RPCSEC_GSS caches between different rpc clients.
   - Fix the broken NFSv4 security auto-negotiation between client and
     server.
   - Fix rmdir() to wait for outstanding sillyrename unlinks to complete
   - Add a tracepoint framework for debugging NFSv4 state recovery
     issues.
   - Add tracing to the generic NFS layer.
   - Add tracing for the SUNRPC socket connection state.
   - Clean up the rpc_pipefs mount/umount event management.
   - Merge more patches from Chuck in preparation for NFSv4 migration
     support"

* tag 'nfs-for-3.12-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (107 commits)
  NFSv4: use mach cred for SECINFO_NO_NAME w/ integrity
  NFS: nfs_compare_super shouldn't check the auth flavour unless 'sec=' was set
  NFSv4: Allow security autonegotiation for submounts
  NFSv4: Disallow security negotiation for lookups when 'sec=' is specified
  NFSv4: Fix security auto-negotiation
  NFS: Clean up nfs_parse_security_flavors()
  NFS: Clean up the auth flavour array mess
  NFSv4.1 Use MDS auth flavor for data server connection
  NFS: Don't check lock owner compatability unless file is locked (part 2)
  NFS: Don't check lock owner compatibility in writes unless file is locked
  nfs4: Map NFS4ERR_WRONG_CRED to EPERM
  nfs4.1: Add SP4_MACH_CRED write and commit support
  nfs4.1: Add SP4_MACH_CRED stateid support
  nfs4.1: Add SP4_MACH_CRED secinfo support
  nfs4.1: Add SP4_MACH_CRED cleanup support
  nfs4.1: Add state protection handler
  nfs4.1: Minimal SP4_MACH_CRED implementation
  SUNRPC: Replace pointer values with task->tk_pid and rpc_clnt->cl_clid
  SUNRPC: Add an identifier for struct rpc_clnt
  SUNRPC: Ensure rpc_task->tk_pid is available for tracepoints
  ...
This commit is contained in:
Linus Torvalds 2013-09-09 09:19:15 -07:00
commit bf97293eb8
51 changed files with 4540 additions and 1010 deletions

View File

@ -1899,6 +1899,18 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
The default is to send the implementation identification The default is to send the implementation identification
information. information.
nfs.recover_lost_locks =
[NFSv4] Attempt to recover locks that were lost due
to a lease timeout on the server. Please note that
doing this risks data corruption, since there are
no guarantees that the file will remain unchanged
after the locks are lost.
If you want to enable the kernel legacy behaviour of
attempting to recover these locks, then set this
parameter to '1'.
The default parameter value of '0' causes the kernel
not to attempt recovery of lost locks.
nfsd.nfs4_disable_idmapping= nfsd.nfs4_disable_idmapping=
[NFSv4] When set to the default of '1', the NFSv4 [NFSv4] When set to the default of '1', the NFSv4
server will return only numeric uids and gids to server will return only numeric uids and gids to

View File

@ -4,9 +4,10 @@
obj-$(CONFIG_NFS_FS) += nfs.o obj-$(CONFIG_NFS_FS) += nfs.o
CFLAGS_nfstrace.o += -I$(src)
nfs-y := client.o dir.o file.o getroot.o inode.o super.o \ nfs-y := client.o dir.o file.o getroot.o inode.o super.o \
direct.o pagelist.o read.o symlink.o unlink.o \ direct.o pagelist.o read.o symlink.o unlink.o \
write.o namespace.o mount_clnt.o write.o namespace.o mount_clnt.o nfstrace.o
nfs-$(CONFIG_ROOT_NFS) += nfsroot.o nfs-$(CONFIG_ROOT_NFS) += nfsroot.o
nfs-$(CONFIG_SYSCTL) += sysctl.o nfs-$(CONFIG_SYSCTL) += sysctl.o
nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o
@ -19,12 +20,14 @@ nfsv3-y := nfs3super.o nfs3client.o nfs3proc.o nfs3xdr.o
nfsv3-$(CONFIG_NFS_V3_ACL) += nfs3acl.o nfsv3-$(CONFIG_NFS_V3_ACL) += nfs3acl.o
obj-$(CONFIG_NFS_V4) += nfsv4.o obj-$(CONFIG_NFS_V4) += nfsv4.o
CFLAGS_nfs4trace.o += -I$(src)
nfsv4-y := nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o nfs4super.o nfs4file.o \ nfsv4-y := nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o nfs4super.o nfs4file.o \
delegation.o idmap.o callback.o callback_xdr.o callback_proc.o \ delegation.o idmap.o callback.o callback_xdr.o callback_proc.o \
nfs4namespace.o nfs4getroot.o nfs4client.o dns_resolve.o nfs4namespace.o nfs4getroot.o nfs4client.o nfs4session.o \
dns_resolve.o nfs4trace.o
nfsv4-$(CONFIG_NFS_USE_LEGACY_DNS) += cache_lib.o nfsv4-$(CONFIG_NFS_USE_LEGACY_DNS) += cache_lib.o
nfsv4-$(CONFIG_SYSCTL) += nfs4sysctl.o nfsv4-$(CONFIG_SYSCTL) += nfs4sysctl.o
nfsv4-$(CONFIG_NFS_V4_1) += nfs4session.o pnfs.o pnfs_dev.o nfsv4-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o
obj-$(CONFIG_PNFS_FILE_LAYOUT) += nfs_layout_nfsv41_files.o obj-$(CONFIG_PNFS_FILE_LAYOUT) += nfs_layout_nfsv41_files.o
nfs_layout_nfsv41_files-y := nfs4filelayout.o nfs4filelayoutdev.o nfs_layout_nfsv41_files-y := nfs4filelayout.o nfs4filelayoutdev.o

View File

@ -15,6 +15,7 @@
#include "internal.h" #include "internal.h"
#include "pnfs.h" #include "pnfs.h"
#include "nfs4session.h" #include "nfs4session.h"
#include "nfs4trace.h"
#ifdef NFS_DEBUG #ifdef NFS_DEBUG
#define NFSDBG_FACILITY NFSDBG_CALLBACK #define NFSDBG_FACILITY NFSDBG_CALLBACK
@ -93,6 +94,7 @@ __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy,
default: default:
res = htonl(NFS4ERR_RESOURCE); res = htonl(NFS4ERR_RESOURCE);
} }
trace_nfs4_recall_delegation(inode, -ntohl(res));
iput(inode); iput(inode);
out: out:
dprintk("%s: exit with status = %d\n", __func__, ntohl(res)); dprintk("%s: exit with status = %d\n", __func__, ntohl(res));
@ -301,14 +303,14 @@ validate_seqid(struct nfs4_slot_table *tbl, struct cb_sequenceargs * args)
{ {
struct nfs4_slot *slot; struct nfs4_slot *slot;
dprintk("%s enter. slotid %d seqid %d\n", dprintk("%s enter. slotid %u seqid %u\n",
__func__, args->csa_slotid, args->csa_sequenceid); __func__, args->csa_slotid, args->csa_sequenceid);
if (args->csa_slotid >= NFS41_BC_MAX_CALLBACKS) if (args->csa_slotid >= NFS41_BC_MAX_CALLBACKS)
return htonl(NFS4ERR_BADSLOT); return htonl(NFS4ERR_BADSLOT);
slot = tbl->slots + args->csa_slotid; slot = tbl->slots + args->csa_slotid;
dprintk("%s slot table seqid: %d\n", __func__, slot->seq_nr); dprintk("%s slot table seqid: %u\n", __func__, slot->seq_nr);
/* Normal */ /* Normal */
if (likely(args->csa_sequenceid == slot->seq_nr + 1)) { if (likely(args->csa_sequenceid == slot->seq_nr + 1)) {
@ -318,7 +320,7 @@ validate_seqid(struct nfs4_slot_table *tbl, struct cb_sequenceargs * args)
/* Replay */ /* Replay */
if (args->csa_sequenceid == slot->seq_nr) { if (args->csa_sequenceid == slot->seq_nr) {
dprintk("%s seqid %d is a replay\n", dprintk("%s seqid %u is a replay\n",
__func__, args->csa_sequenceid); __func__, args->csa_sequenceid);
/* Signal process_op to set this error on next op */ /* Signal process_op to set this error on next op */
if (args->csa_cachethis == 0) if (args->csa_cachethis == 0)
@ -462,6 +464,7 @@ out:
} else } else
res->csr_status = status; res->csr_status = status;
trace_nfs4_cb_sequence(args, res, status);
dprintk("%s: exit with status = %d res->csr_status %d\n", __func__, dprintk("%s: exit with status = %d res->csr_status %d\n", __func__,
ntohl(status), ntohl(res->csr_status)); ntohl(status), ntohl(res->csr_status));
return status; return status;
@ -518,7 +521,7 @@ __be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy,
if (!cps->clp) /* set in cb_sequence */ if (!cps->clp) /* set in cb_sequence */
goto out; goto out;
dprintk_rcu("NFS: CB_RECALL_SLOT request from %s target highest slotid %d\n", dprintk_rcu("NFS: CB_RECALL_SLOT request from %s target highest slotid %u\n",
rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR), rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR),
args->crsa_target_highest_slotid); args->crsa_target_highest_slotid);

View File

@ -501,8 +501,7 @@ nfs_get_client(const struct nfs_client_initdata *cl_init,
&nn->nfs_client_list); &nn->nfs_client_list);
spin_unlock(&nn->nfs_client_lock); spin_unlock(&nn->nfs_client_lock);
new->cl_flags = cl_init->init_flags; new->cl_flags = cl_init->init_flags;
return rpc_ops->init_client(new, timeparms, ip_addr, return rpc_ops->init_client(new, timeparms, ip_addr);
authflavour);
} }
spin_unlock(&nn->nfs_client_lock); spin_unlock(&nn->nfs_client_lock);
@ -694,13 +693,12 @@ EXPORT_SYMBOL_GPL(nfs_init_server_rpcclient);
* @clp: nfs_client to initialise * @clp: nfs_client to initialise
* @timeparms: timeout parameters for underlying RPC transport * @timeparms: timeout parameters for underlying RPC transport
* @ip_addr: IP presentation address (not used) * @ip_addr: IP presentation address (not used)
* @authflavor: authentication flavor for underlying RPC transport
* *
* Returns pointer to an NFS client, or an ERR_PTR value. * Returns pointer to an NFS client, or an ERR_PTR value.
*/ */
struct nfs_client *nfs_init_client(struct nfs_client *clp, struct nfs_client *nfs_init_client(struct nfs_client *clp,
const struct rpc_timeout *timeparms, const struct rpc_timeout *timeparms,
const char *ip_addr, rpc_authflavor_t authflavour) const char *ip_addr)
{ {
int error; int error;

View File

@ -20,6 +20,7 @@
#include "nfs4_fs.h" #include "nfs4_fs.h"
#include "delegation.h" #include "delegation.h"
#include "internal.h" #include "internal.h"
#include "nfs4trace.h"
static void nfs_free_delegation(struct nfs_delegation *delegation) static void nfs_free_delegation(struct nfs_delegation *delegation)
{ {
@ -160,6 +161,7 @@ void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred,
spin_unlock(&delegation->lock); spin_unlock(&delegation->lock);
put_rpccred(oldcred); put_rpccred(oldcred);
rcu_read_unlock(); rcu_read_unlock();
trace_nfs4_reclaim_delegation(inode, res->delegation_type);
} else { } else {
/* We appear to have raced with a delegation return. */ /* We appear to have raced with a delegation return. */
spin_unlock(&delegation->lock); spin_unlock(&delegation->lock);
@ -344,6 +346,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
nfsi->cache_validity |= NFS_INO_REVAL_FORCED; nfsi->cache_validity |= NFS_INO_REVAL_FORCED;
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
trace_nfs4_set_delegation(inode, res->delegation_type);
out: out:
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);

View File

@ -43,6 +43,8 @@
#include "internal.h" #include "internal.h"
#include "fscache.h" #include "fscache.h"
#include "nfstrace.h"
/* #define NFS_DEBUG_VERBOSE 1 */ /* #define NFS_DEBUG_VERBOSE 1 */
static int nfs_opendir(struct inode *, struct file *); static int nfs_opendir(struct inode *, struct file *);
@ -1100,7 +1102,9 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
if (IS_ERR(label)) if (IS_ERR(label))
goto out_error; goto out_error;
trace_nfs_lookup_revalidate_enter(dir, dentry, flags);
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label); error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label);
trace_nfs_lookup_revalidate_exit(dir, dentry, flags, error);
if (error) if (error)
goto out_bad; goto out_bad;
if (nfs_compare_fh(NFS_FH(inode), fhandle)) if (nfs_compare_fh(NFS_FH(inode), fhandle))
@ -1312,6 +1316,7 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
parent = dentry->d_parent; parent = dentry->d_parent;
/* Protect against concurrent sillydeletes */ /* Protect against concurrent sillydeletes */
trace_nfs_lookup_enter(dir, dentry, flags);
nfs_block_sillyrename(parent); nfs_block_sillyrename(parent);
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label); error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label);
if (error == -ENOENT) if (error == -ENOENT)
@ -1338,6 +1343,7 @@ no_entry:
nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
out_unblock_sillyrename: out_unblock_sillyrename:
nfs_unblock_sillyrename(parent); nfs_unblock_sillyrename(parent);
trace_nfs_lookup_exit(dir, dentry, flags, error);
nfs4_label_free(label); nfs4_label_free(label);
out: out:
nfs_free_fattr(fattr); nfs_free_fattr(fattr);
@ -1392,7 +1398,6 @@ static int nfs_finish_open(struct nfs_open_context *ctx,
nfs_file_set_open_context(file, ctx); nfs_file_set_open_context(file, ctx);
out: out:
put_nfs_open_context(ctx);
return err; return err;
} }
@ -1404,6 +1409,7 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
struct dentry *res; struct dentry *res;
struct iattr attr = { .ia_valid = ATTR_OPEN }; struct iattr attr = { .ia_valid = ATTR_OPEN };
struct inode *inode; struct inode *inode;
unsigned int lookup_flags = 0;
int err; int err;
/* Expect a negative dentry */ /* Expect a negative dentry */
@ -1412,6 +1418,10 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
dfprintk(VFS, "NFS: atomic_open(%s/%ld), %s\n", dfprintk(VFS, "NFS: atomic_open(%s/%ld), %s\n",
dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
err = nfs_check_flags(open_flags);
if (err)
return err;
/* NFS only supports OPEN on regular files */ /* NFS only supports OPEN on regular files */
if ((open_flags & O_DIRECTORY)) { if ((open_flags & O_DIRECTORY)) {
if (!d_unhashed(dentry)) { if (!d_unhashed(dentry)) {
@ -1422,6 +1432,7 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
*/ */
return -ENOENT; return -ENOENT;
} }
lookup_flags = LOOKUP_OPEN|LOOKUP_DIRECTORY;
goto no_open; goto no_open;
} }
@ -1442,12 +1453,14 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
if (IS_ERR(ctx)) if (IS_ERR(ctx))
goto out; goto out;
trace_nfs_atomic_open_enter(dir, ctx, open_flags);
nfs_block_sillyrename(dentry->d_parent); nfs_block_sillyrename(dentry->d_parent);
inode = NFS_PROTO(dir)->open_context(dir, ctx, open_flags, &attr); inode = NFS_PROTO(dir)->open_context(dir, ctx, open_flags, &attr);
nfs_unblock_sillyrename(dentry->d_parent); nfs_unblock_sillyrename(dentry->d_parent);
if (IS_ERR(inode)) { if (IS_ERR(inode)) {
put_nfs_open_context(ctx);
err = PTR_ERR(inode); err = PTR_ERR(inode);
trace_nfs_atomic_open_exit(dir, ctx, open_flags, err);
put_nfs_open_context(ctx);
switch (err) { switch (err) {
case -ENOENT: case -ENOENT:
d_drop(dentry); d_drop(dentry);
@ -1468,11 +1481,13 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
} }
err = nfs_finish_open(ctx, ctx->dentry, file, open_flags, opened); err = nfs_finish_open(ctx, ctx->dentry, file, open_flags, opened);
trace_nfs_atomic_open_exit(dir, ctx, open_flags, err);
put_nfs_open_context(ctx);
out: out:
return err; return err;
no_open: no_open:
res = nfs_lookup(dir, dentry, 0); res = nfs_lookup(dir, dentry, lookup_flags);
err = PTR_ERR(res); err = PTR_ERR(res);
if (IS_ERR(res)) if (IS_ERR(res))
goto out; goto out;
@ -1596,7 +1611,9 @@ int nfs_create(struct inode *dir, struct dentry *dentry,
attr.ia_mode = mode; attr.ia_mode = mode;
attr.ia_valid = ATTR_MODE; attr.ia_valid = ATTR_MODE;
trace_nfs_create_enter(dir, dentry, open_flags);
error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags); error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags);
trace_nfs_create_exit(dir, dentry, open_flags, error);
if (error != 0) if (error != 0)
goto out_err; goto out_err;
return 0; return 0;
@ -1624,7 +1641,9 @@ nfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rdev)
attr.ia_mode = mode; attr.ia_mode = mode;
attr.ia_valid = ATTR_MODE; attr.ia_valid = ATTR_MODE;
trace_nfs_mknod_enter(dir, dentry);
status = NFS_PROTO(dir)->mknod(dir, dentry, &attr, rdev); status = NFS_PROTO(dir)->mknod(dir, dentry, &attr, rdev);
trace_nfs_mknod_exit(dir, dentry, status);
if (status != 0) if (status != 0)
goto out_err; goto out_err;
return 0; return 0;
@ -1648,7 +1667,9 @@ int nfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
attr.ia_valid = ATTR_MODE; attr.ia_valid = ATTR_MODE;
attr.ia_mode = mode | S_IFDIR; attr.ia_mode = mode | S_IFDIR;
trace_nfs_mkdir_enter(dir, dentry);
error = NFS_PROTO(dir)->mkdir(dir, dentry, &attr); error = NFS_PROTO(dir)->mkdir(dir, dentry, &attr);
trace_nfs_mkdir_exit(dir, dentry, error);
if (error != 0) if (error != 0)
goto out_err; goto out_err;
return 0; return 0;
@ -1671,12 +1692,21 @@ int nfs_rmdir(struct inode *dir, struct dentry *dentry)
dfprintk(VFS, "NFS: rmdir(%s/%ld), %s\n", dfprintk(VFS, "NFS: rmdir(%s/%ld), %s\n",
dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
trace_nfs_rmdir_enter(dir, dentry);
if (dentry->d_inode) {
nfs_wait_on_sillyrename(dentry);
error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name); error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
/* Ensure the VFS deletes this inode */ /* Ensure the VFS deletes this inode */
if (error == 0 && dentry->d_inode != NULL) switch (error) {
case 0:
clear_nlink(dentry->d_inode); clear_nlink(dentry->d_inode);
else if (error == -ENOENT) break;
case -ENOENT:
nfs_dentry_handle_enoent(dentry); nfs_dentry_handle_enoent(dentry);
}
} else
error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
trace_nfs_rmdir_exit(dir, dentry, error);
return error; return error;
} }
@ -1704,6 +1734,7 @@ static int nfs_safe_remove(struct dentry *dentry)
goto out; goto out;
} }
trace_nfs_remove_enter(dir, dentry);
if (inode != NULL) { if (inode != NULL) {
NFS_PROTO(inode)->return_delegation(inode); NFS_PROTO(inode)->return_delegation(inode);
error = NFS_PROTO(dir)->remove(dir, &dentry->d_name); error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
@ -1713,6 +1744,7 @@ static int nfs_safe_remove(struct dentry *dentry)
error = NFS_PROTO(dir)->remove(dir, &dentry->d_name); error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
if (error == -ENOENT) if (error == -ENOENT)
nfs_dentry_handle_enoent(dentry); nfs_dentry_handle_enoent(dentry);
trace_nfs_remove_exit(dir, dentry, error);
out: out:
return error; return error;
} }
@ -1730,13 +1762,14 @@ int nfs_unlink(struct inode *dir, struct dentry *dentry)
dfprintk(VFS, "NFS: unlink(%s/%ld, %s)\n", dir->i_sb->s_id, dfprintk(VFS, "NFS: unlink(%s/%ld, %s)\n", dir->i_sb->s_id,
dir->i_ino, dentry->d_name.name); dir->i_ino, dentry->d_name.name);
trace_nfs_unlink_enter(dir, dentry);
spin_lock(&dentry->d_lock); spin_lock(&dentry->d_lock);
if (d_count(dentry) > 1) { if (d_count(dentry) > 1) {
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
/* Start asynchronous writeout of the inode */ /* Start asynchronous writeout of the inode */
write_inode_now(dentry->d_inode, 0); write_inode_now(dentry->d_inode, 0);
error = nfs_sillyrename(dir, dentry); error = nfs_sillyrename(dir, dentry);
return error; goto out;
} }
if (!d_unhashed(dentry)) { if (!d_unhashed(dentry)) {
__d_drop(dentry); __d_drop(dentry);
@ -1748,6 +1781,8 @@ int nfs_unlink(struct inode *dir, struct dentry *dentry)
nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
} else if (need_rehash) } else if (need_rehash)
d_rehash(dentry); d_rehash(dentry);
out:
trace_nfs_unlink_exit(dir, dentry, error);
return error; return error;
} }
EXPORT_SYMBOL_GPL(nfs_unlink); EXPORT_SYMBOL_GPL(nfs_unlink);
@ -1794,7 +1829,9 @@ int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
memset(kaddr + pathlen, 0, PAGE_SIZE - pathlen); memset(kaddr + pathlen, 0, PAGE_SIZE - pathlen);
kunmap_atomic(kaddr); kunmap_atomic(kaddr);
trace_nfs_symlink_enter(dir, dentry);
error = NFS_PROTO(dir)->symlink(dir, dentry, page, pathlen, &attr); error = NFS_PROTO(dir)->symlink(dir, dentry, page, pathlen, &attr);
trace_nfs_symlink_exit(dir, dentry, error);
if (error != 0) { if (error != 0) {
dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s) error %d\n", dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s) error %d\n",
dir->i_sb->s_id, dir->i_ino, dir->i_sb->s_id, dir->i_ino,
@ -1829,6 +1866,7 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
old_dentry->d_parent->d_name.name, old_dentry->d_name.name, old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
dentry->d_parent->d_name.name, dentry->d_name.name); dentry->d_parent->d_name.name, dentry->d_name.name);
trace_nfs_link_enter(inode, dir, dentry);
NFS_PROTO(inode)->return_delegation(inode); NFS_PROTO(inode)->return_delegation(inode);
d_drop(dentry); d_drop(dentry);
@ -1837,6 +1875,7 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
ihold(inode); ihold(inode);
d_add(dentry, inode); d_add(dentry, inode);
} }
trace_nfs_link_exit(inode, dir, dentry, error);
return error; return error;
} }
EXPORT_SYMBOL_GPL(nfs_link); EXPORT_SYMBOL_GPL(nfs_link);
@ -1878,6 +1917,7 @@ int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
new_dentry->d_parent->d_name.name, new_dentry->d_name.name, new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
d_count(new_dentry)); d_count(new_dentry));
trace_nfs_rename_enter(old_dir, old_dentry, new_dir, new_dentry);
/* /*
* For non-directories, check whether the target is busy and if so, * For non-directories, check whether the target is busy and if so,
* make a copy of the dentry and then do a silly-rename. If the * make a copy of the dentry and then do a silly-rename. If the
@ -1924,6 +1964,8 @@ int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
out: out:
if (rehash) if (rehash)
d_rehash(rehash); d_rehash(rehash);
trace_nfs_rename_exit(old_dir, old_dentry,
new_dir, new_dentry, error);
if (!error) { if (!error) {
if (new_inode != NULL) if (new_inode != NULL)
nfs_drop_nlink(new_inode); nfs_drop_nlink(new_inode);
@ -2173,9 +2215,11 @@ static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask)
struct nfs_access_entry cache; struct nfs_access_entry cache;
int status; int status;
trace_nfs_access_enter(inode);
status = nfs_access_get_cached(inode, cred, &cache); status = nfs_access_get_cached(inode, cred, &cache);
if (status == 0) if (status == 0)
goto out; goto out_cached;
/* Be clever: ask server to check for all possible rights */ /* Be clever: ask server to check for all possible rights */
cache.mask = MAY_EXEC | MAY_WRITE | MAY_READ; cache.mask = MAY_EXEC | MAY_WRITE | MAY_READ;
@ -2188,13 +2232,15 @@ static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask)
if (!S_ISDIR(inode->i_mode)) if (!S_ISDIR(inode->i_mode))
set_bit(NFS_INO_STALE, &NFS_I(inode)->flags); set_bit(NFS_INO_STALE, &NFS_I(inode)->flags);
} }
return status; goto out;
} }
nfs_access_add_cache(inode, &cache); nfs_access_add_cache(inode, &cache);
out_cached:
if ((mask & ~cache.mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) != 0)
status = -EACCES;
out: out:
if ((mask & ~cache.mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0) trace_nfs_access_exit(inode, status);
return 0; return status;
return -EACCES;
} }
static int nfs_open_permission_mask(int openflags) static int nfs_open_permission_mask(int openflags)
@ -2240,11 +2286,6 @@ int nfs_permission(struct inode *inode, int mask)
case S_IFLNK: case S_IFLNK:
goto out; goto out;
case S_IFREG: case S_IFREG:
/* NFSv4 has atomic_open... */
if (nfs_server_capable(inode, NFS_CAP_ATOMIC_OPEN)
&& (mask & MAY_OPEN)
&& !(mask & MAY_EXEC))
goto out;
break; break;
case S_IFDIR: case S_IFDIR:
/* /*

View File

@ -37,6 +37,8 @@
#include "iostat.h" #include "iostat.h"
#include "fscache.h" #include "fscache.h"
#include "nfstrace.h"
#define NFSDBG_FACILITY NFSDBG_FILE #define NFSDBG_FACILITY NFSDBG_FILE
static const struct vm_operations_struct nfs_file_vm_ops; static const struct vm_operations_struct nfs_file_vm_ops;
@ -294,6 +296,8 @@ nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
int ret; int ret;
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
trace_nfs_fsync_enter(inode);
do { do {
ret = filemap_write_and_wait_range(inode->i_mapping, start, end); ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
if (ret != 0) if (ret != 0)
@ -310,6 +314,7 @@ nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
end = LLONG_MAX; end = LLONG_MAX;
} while (ret == -EAGAIN); } while (ret == -EAGAIN);
trace_nfs_fsync_exit(inode, ret);
return ret; return ret;
} }
@ -406,6 +411,7 @@ static int nfs_write_end(struct file *file, struct address_space *mapping,
struct page *page, void *fsdata) struct page *page, void *fsdata)
{ {
unsigned offset = pos & (PAGE_CACHE_SIZE - 1); unsigned offset = pos & (PAGE_CACHE_SIZE - 1);
struct nfs_open_context *ctx = nfs_file_open_context(file);
int status; int status;
dfprintk(PAGECACHE, "NFS: write_end(%s/%s(%ld), %u@%lld)\n", dfprintk(PAGECACHE, "NFS: write_end(%s/%s(%ld), %u@%lld)\n",
@ -441,6 +447,13 @@ static int nfs_write_end(struct file *file, struct address_space *mapping,
if (status < 0) if (status < 0)
return status; return status;
NFS_I(mapping->host)->write_io += copied; NFS_I(mapping->host)->write_io += copied;
if (nfs_ctx_key_to_expire(ctx)) {
status = nfs_wb_all(mapping->host);
if (status < 0)
return status;
}
return copied; return copied;
} }
@ -637,7 +650,8 @@ static int nfs_need_sync_write(struct file *filp, struct inode *inode)
if (IS_SYNC(inode) || (filp->f_flags & O_DSYNC)) if (IS_SYNC(inode) || (filp->f_flags & O_DSYNC))
return 1; return 1;
ctx = nfs_file_open_context(filp); ctx = nfs_file_open_context(filp);
if (test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags)) if (test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags) ||
nfs_ctx_key_to_expire(ctx))
return 1; return 1;
return 0; return 0;
} }
@ -651,6 +665,10 @@ ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
ssize_t result; ssize_t result;
size_t count = iov_length(iov, nr_segs); size_t count = iov_length(iov, nr_segs);
result = nfs_key_timeout_notify(iocb->ki_filp, inode);
if (result)
return result;
if (iocb->ki_filp->f_flags & O_DIRECT) if (iocb->ki_filp->f_flags & O_DIRECT)
return nfs_file_direct_write(iocb, iov, nr_segs, pos, true); return nfs_file_direct_write(iocb, iov, nr_segs, pos, true);

View File

@ -49,6 +49,7 @@
#include "internal.h" #include "internal.h"
#include "netns.h" #include "netns.h"
#include "nfs4trace.h"
#define NFS_UINT_MAXLEN 11 #define NFS_UINT_MAXLEN 11
@ -63,6 +64,7 @@ struct idmap_legacy_upcalldata {
}; };
struct idmap { struct idmap {
struct rpc_pipe_dir_object idmap_pdo;
struct rpc_pipe *idmap_pipe; struct rpc_pipe *idmap_pipe;
struct idmap_legacy_upcalldata *idmap_upcall_data; struct idmap_legacy_upcalldata *idmap_upcall_data;
struct mutex idmap_mutex; struct mutex idmap_mutex;
@ -310,7 +312,7 @@ static ssize_t nfs_idmap_get_key(const char *name, size_t namelen,
if (ret < 0) if (ret < 0)
goto out_up; goto out_up;
payload = rcu_dereference(rkey->payload.data); payload = rcu_dereference(rkey->payload.rcudata);
if (IS_ERR_OR_NULL(payload)) { if (IS_ERR_OR_NULL(payload)) {
ret = PTR_ERR(payload); ret = PTR_ERR(payload);
goto out_up; goto out_up;
@ -401,16 +403,23 @@ static struct key_type key_type_id_resolver_legacy = {
.request_key = nfs_idmap_legacy_upcall, .request_key = nfs_idmap_legacy_upcall,
}; };
static void __nfs_idmap_unregister(struct rpc_pipe *pipe) static void nfs_idmap_pipe_destroy(struct dentry *dir,
struct rpc_pipe_dir_object *pdo)
{ {
if (pipe->dentry) struct idmap *idmap = pdo->pdo_data;
struct rpc_pipe *pipe = idmap->idmap_pipe;
if (pipe->dentry) {
rpc_unlink(pipe->dentry); rpc_unlink(pipe->dentry);
pipe->dentry = NULL;
}
} }
static int __nfs_idmap_register(struct dentry *dir, static int nfs_idmap_pipe_create(struct dentry *dir,
struct idmap *idmap, struct rpc_pipe_dir_object *pdo)
struct rpc_pipe *pipe)
{ {
struct idmap *idmap = pdo->pdo_data;
struct rpc_pipe *pipe = idmap->idmap_pipe;
struct dentry *dentry; struct dentry *dentry;
dentry = rpc_mkpipe_dentry(dir, "idmap", idmap, pipe); dentry = rpc_mkpipe_dentry(dir, "idmap", idmap, pipe);
@ -420,36 +429,10 @@ static int __nfs_idmap_register(struct dentry *dir,
return 0; return 0;
} }
static void nfs_idmap_unregister(struct nfs_client *clp, static const struct rpc_pipe_dir_object_ops nfs_idmap_pipe_dir_object_ops = {
struct rpc_pipe *pipe) .create = nfs_idmap_pipe_create,
{ .destroy = nfs_idmap_pipe_destroy,
struct net *net = clp->cl_net; };
struct super_block *pipefs_sb;
pipefs_sb = rpc_get_sb_net(net);
if (pipefs_sb) {
__nfs_idmap_unregister(pipe);
rpc_put_sb_net(net);
}
}
static int nfs_idmap_register(struct nfs_client *clp,
struct idmap *idmap,
struct rpc_pipe *pipe)
{
struct net *net = clp->cl_net;
struct super_block *pipefs_sb;
int err = 0;
pipefs_sb = rpc_get_sb_net(net);
if (pipefs_sb) {
if (clp->cl_rpcclient->cl_dentry)
err = __nfs_idmap_register(clp->cl_rpcclient->cl_dentry,
idmap, pipe);
rpc_put_sb_net(net);
}
return err;
}
int int
nfs_idmap_new(struct nfs_client *clp) nfs_idmap_new(struct nfs_client *clp)
@ -462,23 +445,31 @@ nfs_idmap_new(struct nfs_client *clp)
if (idmap == NULL) if (idmap == NULL)
return -ENOMEM; return -ENOMEM;
rpc_init_pipe_dir_object(&idmap->idmap_pdo,
&nfs_idmap_pipe_dir_object_ops,
idmap);
pipe = rpc_mkpipe_data(&idmap_upcall_ops, 0); pipe = rpc_mkpipe_data(&idmap_upcall_ops, 0);
if (IS_ERR(pipe)) { if (IS_ERR(pipe)) {
error = PTR_ERR(pipe); error = PTR_ERR(pipe);
kfree(idmap); goto err;
return error;
}
error = nfs_idmap_register(clp, idmap, pipe);
if (error) {
rpc_destroy_pipe_data(pipe);
kfree(idmap);
return error;
} }
idmap->idmap_pipe = pipe; idmap->idmap_pipe = pipe;
mutex_init(&idmap->idmap_mutex); mutex_init(&idmap->idmap_mutex);
error = rpc_add_pipe_dir_object(clp->cl_net,
&clp->cl_rpcclient->cl_pipedir_objects,
&idmap->idmap_pdo);
if (error)
goto err_destroy_pipe;
clp->cl_idmap = idmap; clp->cl_idmap = idmap;
return 0; return 0;
err_destroy_pipe:
rpc_destroy_pipe_data(idmap->idmap_pipe);
err:
kfree(idmap);
return error;
} }
void void
@ -488,130 +479,26 @@ nfs_idmap_delete(struct nfs_client *clp)
if (!idmap) if (!idmap)
return; return;
nfs_idmap_unregister(clp, idmap->idmap_pipe);
rpc_destroy_pipe_data(idmap->idmap_pipe);
clp->cl_idmap = NULL; clp->cl_idmap = NULL;
rpc_remove_pipe_dir_object(clp->cl_net,
&clp->cl_rpcclient->cl_pipedir_objects,
&idmap->idmap_pdo);
rpc_destroy_pipe_data(idmap->idmap_pipe);
kfree(idmap); kfree(idmap);
} }
static int __rpc_pipefs_event(struct nfs_client *clp, unsigned long event,
struct super_block *sb)
{
int err = 0;
switch (event) {
case RPC_PIPEFS_MOUNT:
err = __nfs_idmap_register(clp->cl_rpcclient->cl_dentry,
clp->cl_idmap,
clp->cl_idmap->idmap_pipe);
break;
case RPC_PIPEFS_UMOUNT:
if (clp->cl_idmap->idmap_pipe) {
struct dentry *parent;
parent = clp->cl_idmap->idmap_pipe->dentry->d_parent;
__nfs_idmap_unregister(clp->cl_idmap->idmap_pipe);
/*
* Note: This is a dirty hack. SUNRPC hook has been
* called already but simple_rmdir() call for the
* directory returned with error because of idmap pipe
* inside. Thus now we have to remove this directory
* here.
*/
if (rpc_rmdir(parent))
printk(KERN_ERR "NFS: %s: failed to remove "
"clnt dir!\n", __func__);
}
break;
default:
printk(KERN_ERR "NFS: %s: unknown event: %ld\n", __func__,
event);
return -ENOTSUPP;
}
return err;
}
static struct nfs_client *nfs_get_client_for_event(struct net *net, int event)
{
struct nfs_net *nn = net_generic(net, nfs_net_id);
struct dentry *cl_dentry;
struct nfs_client *clp;
int err;
restart:
spin_lock(&nn->nfs_client_lock);
list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) {
/* Wait for initialisation to finish */
if (clp->cl_cons_state == NFS_CS_INITING) {
atomic_inc(&clp->cl_count);
spin_unlock(&nn->nfs_client_lock);
err = nfs_wait_client_init_complete(clp);
nfs_put_client(clp);
if (err)
return NULL;
goto restart;
}
/* Skip nfs_clients that failed to initialise */
if (clp->cl_cons_state < 0)
continue;
smp_rmb();
if (clp->rpc_ops != &nfs_v4_clientops)
continue;
cl_dentry = clp->cl_idmap->idmap_pipe->dentry;
if (((event == RPC_PIPEFS_MOUNT) && cl_dentry) ||
((event == RPC_PIPEFS_UMOUNT) && !cl_dentry))
continue;
atomic_inc(&clp->cl_count);
spin_unlock(&nn->nfs_client_lock);
return clp;
}
spin_unlock(&nn->nfs_client_lock);
return NULL;
}
static int rpc_pipefs_event(struct notifier_block *nb, unsigned long event,
void *ptr)
{
struct super_block *sb = ptr;
struct nfs_client *clp;
int error = 0;
if (!try_module_get(THIS_MODULE))
return 0;
while ((clp = nfs_get_client_for_event(sb->s_fs_info, event))) {
error = __rpc_pipefs_event(clp, event, sb);
nfs_put_client(clp);
if (error)
break;
}
module_put(THIS_MODULE);
return error;
}
#define PIPEFS_NFS_PRIO 1
static struct notifier_block nfs_idmap_block = {
.notifier_call = rpc_pipefs_event,
.priority = SUNRPC_PIPEFS_NFS_PRIO,
};
int nfs_idmap_init(void) int nfs_idmap_init(void)
{ {
int ret; int ret;
ret = nfs_idmap_init_keyring(); ret = nfs_idmap_init_keyring();
if (ret != 0) if (ret != 0)
goto out; goto out;
ret = rpc_pipefs_notifier_register(&nfs_idmap_block);
if (ret != 0)
nfs_idmap_quit_keyring();
out: out:
return ret; return ret;
} }
void nfs_idmap_quit(void) void nfs_idmap_quit(void)
{ {
rpc_pipefs_notifier_unregister(&nfs_idmap_block);
nfs_idmap_quit_keyring(); nfs_idmap_quit_keyring();
} }
@ -849,6 +736,7 @@ int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_
if (!uid_valid(*uid)) if (!uid_valid(*uid))
ret = -ERANGE; ret = -ERANGE;
} }
trace_nfs4_map_name_to_uid(name, namelen, id, ret);
return ret; return ret;
} }
@ -865,6 +753,7 @@ int nfs_map_group_to_gid(const struct nfs_server *server, const char *name, size
if (!gid_valid(*gid)) if (!gid_valid(*gid))
ret = -ERANGE; ret = -ERANGE;
} }
trace_nfs4_map_group_to_gid(name, namelen, id, ret);
return ret; return ret;
} }
@ -879,6 +768,7 @@ int nfs_map_uid_to_name(const struct nfs_server *server, kuid_t uid, char *buf,
ret = nfs_idmap_lookup_name(id, "user", buf, buflen, idmap); ret = nfs_idmap_lookup_name(id, "user", buf, buflen, idmap);
if (ret < 0) if (ret < 0)
ret = nfs_map_numeric_to_string(id, buf, buflen); ret = nfs_map_numeric_to_string(id, buf, buflen);
trace_nfs4_map_uid_to_name(buf, ret, id, ret);
return ret; return ret;
} }
int nfs_map_gid_to_group(const struct nfs_server *server, kgid_t gid, char *buf, size_t buflen) int nfs_map_gid_to_group(const struct nfs_server *server, kgid_t gid, char *buf, size_t buflen)
@ -892,5 +782,6 @@ int nfs_map_gid_to_group(const struct nfs_server *server, kgid_t gid, char *buf,
ret = nfs_idmap_lookup_name(id, "group", buf, buflen, idmap); ret = nfs_idmap_lookup_name(id, "group", buf, buflen, idmap);
if (ret < 0) if (ret < 0)
ret = nfs_map_numeric_to_string(id, buf, buflen); ret = nfs_map_numeric_to_string(id, buf, buflen);
trace_nfs4_map_gid_to_group(buf, ret, id, ret);
return ret; return ret;
} }

View File

@ -38,7 +38,6 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/crc32.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
@ -52,6 +51,8 @@
#include "nfs.h" #include "nfs.h"
#include "netns.h" #include "netns.h"
#include "nfstrace.h"
#define NFSDBG_FACILITY NFSDBG_VFS #define NFSDBG_FACILITY NFSDBG_VFS
#define NFS_64_BIT_INODE_NUMBERS_ENABLED 1 #define NFS_64_BIT_INODE_NUMBERS_ENABLED 1
@ -503,6 +504,8 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
if ((attr->ia_valid & ~(ATTR_FILE|ATTR_OPEN)) == 0) if ((attr->ia_valid & ~(ATTR_FILE|ATTR_OPEN)) == 0)
return 0; return 0;
trace_nfs_setattr_enter(inode);
/* Write all dirty data */ /* Write all dirty data */
if (S_ISREG(inode->i_mode)) { if (S_ISREG(inode->i_mode)) {
nfs_inode_dio_wait(inode); nfs_inode_dio_wait(inode);
@ -522,6 +525,7 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
error = nfs_refresh_inode(inode, fattr); error = nfs_refresh_inode(inode, fattr);
nfs_free_fattr(fattr); nfs_free_fattr(fattr);
out: out:
trace_nfs_setattr_exit(inode, error);
return error; return error;
} }
EXPORT_SYMBOL_GPL(nfs_setattr); EXPORT_SYMBOL_GPL(nfs_setattr);
@ -591,6 +595,7 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
int need_atime = NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATIME; int need_atime = NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATIME;
int err; int err;
trace_nfs_getattr_enter(inode);
/* Flush out writes to the server in order to update c/mtime. */ /* Flush out writes to the server in order to update c/mtime. */
if (S_ISREG(inode->i_mode)) { if (S_ISREG(inode->i_mode)) {
nfs_inode_dio_wait(inode); nfs_inode_dio_wait(inode);
@ -621,6 +626,7 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
stat->ino = nfs_compat_user_ino64(NFS_FILEID(inode)); stat->ino = nfs_compat_user_ino64(NFS_FILEID(inode));
} }
out: out:
trace_nfs_getattr_exit(inode, err);
return err; return err;
} }
EXPORT_SYMBOL_GPL(nfs_getattr); EXPORT_SYMBOL_GPL(nfs_getattr);
@ -875,6 +881,8 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
dfprintk(PAGECACHE, "NFS: revalidating (%s/%Ld)\n", dfprintk(PAGECACHE, "NFS: revalidating (%s/%Ld)\n",
inode->i_sb->s_id, (long long)NFS_FILEID(inode)); inode->i_sb->s_id, (long long)NFS_FILEID(inode));
trace_nfs_revalidate_inode_enter(inode);
if (is_bad_inode(inode)) if (is_bad_inode(inode))
goto out; goto out;
if (NFS_STALE(inode)) if (NFS_STALE(inode))
@ -925,6 +933,7 @@ err_out:
nfs4_label_free(label); nfs4_label_free(label);
out: out:
nfs_free_fattr(fattr); nfs_free_fattr(fattr);
trace_nfs_revalidate_inode_exit(inode, status);
return status; return status;
} }
@ -981,6 +990,7 @@ static int nfs_invalidate_mapping(struct inode *inode, struct address_space *map
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE); nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE);
nfs_fscache_wait_on_invalidate(inode); nfs_fscache_wait_on_invalidate(inode);
dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n", dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n",
inode->i_sb->s_id, (long long)NFS_FILEID(inode)); inode->i_sb->s_id, (long long)NFS_FILEID(inode));
return 0; return 0;
@ -1014,8 +1024,12 @@ int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
if (ret < 0) if (ret < 0)
goto out; goto out;
} }
if (nfsi->cache_validity & NFS_INO_INVALID_DATA) if (nfsi->cache_validity & NFS_INO_INVALID_DATA) {
trace_nfs_invalidate_mapping_enter(inode);
ret = nfs_invalidate_mapping(inode, mapping); ret = nfs_invalidate_mapping(inode, mapping);
trace_nfs_invalidate_mapping_exit(inode, ret);
}
out: out:
return ret; return ret;
} }
@ -1195,7 +1209,7 @@ u32 _nfs_display_fhandle_hash(const struct nfs_fh *fh)
{ {
/* wireshark uses 32-bit AUTODIN crc and does a bitwise /* wireshark uses 32-bit AUTODIN crc and does a bitwise
* not on the result */ * not on the result */
return ~crc32(0xFFFFFFFF, &fh->data[0], fh->size); return nfs_fhandle_hash(fh);
} }
/* /*
@ -1274,9 +1288,17 @@ static int nfs_inode_attrs_need_update(const struct inode *inode, const struct n
static int nfs_refresh_inode_locked(struct inode *inode, struct nfs_fattr *fattr) static int nfs_refresh_inode_locked(struct inode *inode, struct nfs_fattr *fattr)
{ {
int ret;
trace_nfs_refresh_inode_enter(inode);
if (nfs_inode_attrs_need_update(inode, fattr)) if (nfs_inode_attrs_need_update(inode, fattr))
return nfs_update_inode(inode, fattr); ret = nfs_update_inode(inode, fattr);
return nfs_check_inode_attributes(inode, fattr); else
ret = nfs_check_inode_attributes(inode, fattr);
trace_nfs_refresh_inode_exit(inode, ret);
return ret;
} }
/** /**

View File

@ -5,6 +5,7 @@
#include "nfs4_fs.h" #include "nfs4_fs.h"
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/crc32.h>
#define NFS_MS_MASK (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_SYNCHRONOUS) #define NFS_MS_MASK (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_SYNCHRONOUS)
@ -185,6 +186,8 @@ extern struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp,
int ds_addrlen, int ds_proto, int ds_addrlen, int ds_proto,
unsigned int ds_timeo, unsigned int ds_timeo,
unsigned int ds_retrans); unsigned int ds_retrans);
extern struct rpc_clnt *nfs4_find_or_create_ds_client(struct nfs_client *,
struct inode *);
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
extern int __init nfs_fs_proc_init(void); extern int __init nfs_fs_proc_init(void);
extern void nfs_fs_proc_exit(void); extern void nfs_fs_proc_exit(void);
@ -267,7 +270,7 @@ extern struct rpc_procinfo nfs4_procedures[];
void nfs_close_context(struct nfs_open_context *ctx, int is_sync); void nfs_close_context(struct nfs_open_context *ctx, int is_sync);
extern struct nfs_client *nfs_init_client(struct nfs_client *clp, extern struct nfs_client *nfs_init_client(struct nfs_client *clp,
const struct rpc_timeout *timeparms, const struct rpc_timeout *timeparms,
const char *ip_addr, rpc_authflavor_t authflavour); const char *ip_addr);
/* dir.c */ /* dir.c */
extern int nfs_access_cache_shrinker(struct shrinker *shrink, extern int nfs_access_cache_shrinker(struct shrinker *shrink,
@ -355,7 +358,7 @@ extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *,
extern struct dentry *nfs4_get_root(struct super_block *, struct nfs_fh *, extern struct dentry *nfs4_get_root(struct super_block *, struct nfs_fh *,
const char *); const char *);
extern int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh); extern int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh, bool);
#endif #endif
struct nfs_pgio_completion_ops; struct nfs_pgio_completion_ops;
@ -430,6 +433,8 @@ void nfs_request_remove_commit_list(struct nfs_page *req,
void nfs_init_cinfo(struct nfs_commit_info *cinfo, void nfs_init_cinfo(struct nfs_commit_info *cinfo,
struct inode *inode, struct inode *inode,
struct nfs_direct_req *dreq); struct nfs_direct_req *dreq);
int nfs_key_timeout_notify(struct file *filp, struct inode *inode);
bool nfs_ctx_key_to_expire(struct nfs_open_context *ctx);
#ifdef CONFIG_MIGRATION #ifdef CONFIG_MIGRATION
extern int nfs_migrate_page(struct address_space *, extern int nfs_migrate_page(struct address_space *,
@ -451,8 +456,7 @@ extern ssize_t nfs_dreq_bytes_left(struct nfs_direct_req *dreq);
extern void __nfs4_read_done_cb(struct nfs_read_data *); extern void __nfs4_read_done_cb(struct nfs_read_data *);
extern struct nfs_client *nfs4_init_client(struct nfs_client *clp, extern struct nfs_client *nfs4_init_client(struct nfs_client *clp,
const struct rpc_timeout *timeparms, const struct rpc_timeout *timeparms,
const char *ip_addr, const char *ip_addr);
rpc_authflavor_t authflavour);
extern int nfs40_walk_client_list(struct nfs_client *clp, extern int nfs40_walk_client_list(struct nfs_client *clp,
struct nfs_client **result, struct nfs_client **result,
struct rpc_cred *cred); struct rpc_cred *cred);
@ -575,3 +579,22 @@ u64 nfs_timespec_to_change_attr(const struct timespec *ts)
{ {
return ((u64)ts->tv_sec << 30) + ts->tv_nsec; return ((u64)ts->tv_sec << 30) + ts->tv_nsec;
} }
#ifdef CONFIG_CRC32
/**
* nfs_fhandle_hash - calculate the crc32 hash for the filehandle
* @fh - pointer to filehandle
*
* returns a crc32 hash for the filehandle that is compatible with
* the one displayed by "wireshark".
*/
static inline u32 nfs_fhandle_hash(const struct nfs_fh *fh)
{
return ~crc32_le(0xFFFFFFFF, &fh->data[0], fh->size);
}
#else
static inline u32 nfs_fhandle_hash(const struct nfs_fh *fh)
{
return 0;
}
#endif

View File

@ -336,8 +336,8 @@ nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
data->arg.create.createmode = NFS3_CREATE_UNCHECKED; data->arg.create.createmode = NFS3_CREATE_UNCHECKED;
if (flags & O_EXCL) { if (flags & O_EXCL) {
data->arg.create.createmode = NFS3_CREATE_EXCLUSIVE; data->arg.create.createmode = NFS3_CREATE_EXCLUSIVE;
data->arg.create.verifier[0] = jiffies; data->arg.create.verifier[0] = cpu_to_be32(jiffies);
data->arg.create.verifier[1] = current->pid; data->arg.create.verifier[1] = cpu_to_be32(current->pid);
} }
sattr->ia_mode &= ~current_umask(); sattr->ia_mode &= ~current_umask();
@ -826,9 +826,10 @@ static void nfs3_proc_read_setup(struct nfs_read_data *data, struct rpc_message
msg->rpc_proc = &nfs3_procedures[NFS3PROC_READ]; msg->rpc_proc = &nfs3_procedures[NFS3PROC_READ];
} }
static void nfs3_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data) static int nfs3_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data)
{ {
rpc_call_start(task); rpc_call_start(task);
return 0;
} }
static int nfs3_write_done(struct rpc_task *task, struct nfs_write_data *data) static int nfs3_write_done(struct rpc_task *task, struct nfs_write_data *data)
@ -847,9 +848,10 @@ static void nfs3_proc_write_setup(struct nfs_write_data *data, struct rpc_messag
msg->rpc_proc = &nfs3_procedures[NFS3PROC_WRITE]; msg->rpc_proc = &nfs3_procedures[NFS3PROC_WRITE];
} }
static void nfs3_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data) static int nfs3_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data)
{ {
rpc_call_start(task); rpc_call_start(task);
return 0;
} }
static void nfs3_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data) static void nfs3_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data)

View File

@ -38,17 +38,15 @@ struct nfs4_minor_version_ops {
u32 minor_version; u32 minor_version;
unsigned init_caps; unsigned init_caps;
int (*call_sync)(struct rpc_clnt *clnt, int (*init_client)(struct nfs_client *);
struct nfs_server *server, void (*shutdown_client)(struct nfs_client *);
struct rpc_message *msg,
struct nfs4_sequence_args *args,
struct nfs4_sequence_res *res);
bool (*match_stateid)(const nfs4_stateid *, bool (*match_stateid)(const nfs4_stateid *,
const nfs4_stateid *); const nfs4_stateid *);
int (*find_root_sec)(struct nfs_server *, struct nfs_fh *, int (*find_root_sec)(struct nfs_server *, struct nfs_fh *,
struct nfs_fsinfo *); struct nfs_fsinfo *);
int (*free_lock_state)(struct nfs_server *, int (*free_lock_state)(struct nfs_server *,
struct nfs4_lock_state *); struct nfs4_lock_state *);
const struct rpc_call_ops *call_sync_ops;
const struct nfs4_state_recovery_ops *reboot_recovery_ops; const struct nfs4_state_recovery_ops *reboot_recovery_ops;
const struct nfs4_state_recovery_ops *nograce_recovery_ops; const struct nfs4_state_recovery_ops *nograce_recovery_ops;
const struct nfs4_state_maintenance_ops *state_renewal_ops; const struct nfs4_state_maintenance_ops *state_renewal_ops;
@ -135,6 +133,7 @@ struct nfs4_lock_state {
struct list_head ls_locks; /* Other lock stateids */ struct list_head ls_locks; /* Other lock stateids */
struct nfs4_state * ls_state; /* Pointer to open state */ struct nfs4_state * ls_state; /* Pointer to open state */
#define NFS_LOCK_INITIALIZED 0 #define NFS_LOCK_INITIALIZED 0
#define NFS_LOCK_LOST 1
unsigned long ls_flags; unsigned long ls_flags;
struct nfs_seqid_counter ls_seqid; struct nfs_seqid_counter ls_seqid;
nfs4_stateid ls_stateid; nfs4_stateid ls_stateid;
@ -193,7 +192,6 @@ struct nfs4_state_recovery_ops {
int (*recover_open)(struct nfs4_state_owner *, struct nfs4_state *); int (*recover_open)(struct nfs4_state_owner *, struct nfs4_state *);
int (*recover_lock)(struct nfs4_state *, struct file_lock *); int (*recover_lock)(struct nfs4_state *, struct file_lock *);
int (*establish_clid)(struct nfs_client *, struct rpc_cred *); int (*establish_clid)(struct nfs_client *, struct rpc_cred *);
struct rpc_cred * (*get_clid_cred)(struct nfs_client *);
int (*reclaim_complete)(struct nfs_client *, struct rpc_cred *); int (*reclaim_complete)(struct nfs_client *, struct rpc_cred *);
int (*detect_trunking)(struct nfs_client *, struct nfs_client **, int (*detect_trunking)(struct nfs_client *, struct nfs_client **,
struct rpc_cred *); struct rpc_cred *);
@ -223,7 +221,7 @@ struct vfsmount *nfs4_submount(struct nfs_server *, struct dentry *,
/* nfs4proc.c */ /* nfs4proc.c */
extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struct rpc_cred *, struct nfs4_setclientid_res *); extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struct rpc_cred *, struct nfs4_setclientid_res *);
extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct nfs4_setclientid_res *arg, struct rpc_cred *); extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct nfs4_setclientid_res *arg, struct rpc_cred *);
extern int nfs4_proc_get_rootfh(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *); extern int nfs4_proc_get_rootfh(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *, bool);
extern int nfs4_proc_bind_conn_to_session(struct nfs_client *, struct rpc_cred *cred); extern int nfs4_proc_bind_conn_to_session(struct nfs_client *, struct rpc_cred *cred);
extern int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred); extern int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred);
extern int nfs4_destroy_clientid(struct nfs_client *clp); extern int nfs4_destroy_clientid(struct nfs_client *clp);
@ -248,9 +246,6 @@ static inline struct nfs4_session *nfs4_get_session(const struct nfs_server *ser
return server->nfs_client->cl_session; return server->nfs_client->cl_session;
} }
extern int nfs4_setup_sequence(const struct nfs_server *server,
struct nfs4_sequence_args *args, struct nfs4_sequence_res *res,
struct rpc_task *task);
extern int nfs41_setup_sequence(struct nfs4_session *session, extern int nfs41_setup_sequence(struct nfs4_session *session,
struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res,
struct rpc_task *task); struct rpc_task *task);
@ -273,20 +268,65 @@ is_ds_client(struct nfs_client *clp)
{ {
return clp->cl_exchange_flags & EXCHGID4_FLAG_USE_PNFS_DS; return clp->cl_exchange_flags & EXCHGID4_FLAG_USE_PNFS_DS;
} }
static inline bool
_nfs4_state_protect(struct nfs_client *clp, unsigned long sp4_mode,
struct rpc_clnt **clntp, struct rpc_message *msg)
{
struct rpc_cred *newcred = NULL;
rpc_authflavor_t flavor;
if (test_bit(sp4_mode, &clp->cl_sp4_flags)) {
spin_lock(&clp->cl_lock);
if (clp->cl_machine_cred != NULL)
newcred = get_rpccred(clp->cl_machine_cred);
spin_unlock(&clp->cl_lock);
if (msg->rpc_cred)
put_rpccred(msg->rpc_cred);
msg->rpc_cred = newcred;
flavor = clp->cl_rpcclient->cl_auth->au_flavor;
WARN_ON(flavor != RPC_AUTH_GSS_KRB5I &&
flavor != RPC_AUTH_GSS_KRB5P);
*clntp = clp->cl_rpcclient;
return true;
}
return false;
}
/*
* Function responsible for determining if an rpc_message should use the
* machine cred under SP4_MACH_CRED and if so switching the credential and
* authflavor (using the nfs_client's rpc_clnt which will be krb5i/p).
* Should be called before rpc_call_sync/rpc_call_async.
*/
static inline void
nfs4_state_protect(struct nfs_client *clp, unsigned long sp4_mode,
struct rpc_clnt **clntp, struct rpc_message *msg)
{
_nfs4_state_protect(clp, sp4_mode, clntp, msg);
}
/*
* Special wrapper to nfs4_state_protect for write.
* If WRITE can use machine cred but COMMIT cannot, make sure all writes
* that use machine cred use NFS_FILE_SYNC.
*/
static inline void
nfs4_state_protect_write(struct nfs_client *clp, struct rpc_clnt **clntp,
struct rpc_message *msg, struct nfs_write_data *wdata)
{
if (_nfs4_state_protect(clp, NFS_SP4_MACH_CRED_WRITE, clntp, msg) &&
!test_bit(NFS_SP4_MACH_CRED_COMMIT, &clp->cl_sp4_flags))
wdata->args.stable = NFS_FILE_SYNC;
}
#else /* CONFIG_NFS_v4_1 */ #else /* CONFIG_NFS_v4_1 */
static inline struct nfs4_session *nfs4_get_session(const struct nfs_server *server) static inline struct nfs4_session *nfs4_get_session(const struct nfs_server *server)
{ {
return NULL; return NULL;
} }
static inline int nfs4_setup_sequence(const struct nfs_server *server,
struct nfs4_sequence_args *args, struct nfs4_sequence_res *res,
struct rpc_task *task)
{
rpc_call_start(task);
return 0;
}
static inline bool static inline bool
is_ds_only_client(struct nfs_client *clp) is_ds_only_client(struct nfs_client *clp)
{ {
@ -298,6 +338,18 @@ is_ds_client(struct nfs_client *clp)
{ {
return false; return false;
} }
static inline void
nfs4_state_protect(struct nfs_client *clp, unsigned long sp4_flags,
struct rpc_clnt **clntp, struct rpc_message *msg)
{
}
static inline void
nfs4_state_protect_write(struct nfs_client *clp, struct rpc_clnt **clntp,
struct rpc_message *msg, struct nfs_write_data *wdata)
{
}
#endif /* CONFIG_NFS_V4_1 */ #endif /* CONFIG_NFS_V4_1 */
extern const struct nfs4_minor_version_ops *nfs_v4_minor_ops[]; extern const struct nfs4_minor_version_ops *nfs_v4_minor_ops[];
@ -308,6 +360,10 @@ extern const u32 nfs4_pathconf_bitmap[3];
extern const u32 nfs4_fsinfo_bitmap[3]; extern const u32 nfs4_fsinfo_bitmap[3];
extern const u32 nfs4_fs_locations_bitmap[3]; extern const u32 nfs4_fs_locations_bitmap[3];
void nfs40_shutdown_client(struct nfs_client *);
void nfs41_shutdown_client(struct nfs_client *);
int nfs40_init_client(struct nfs_client *);
int nfs41_init_client(struct nfs_client *);
void nfs4_free_client(struct nfs_client *); void nfs4_free_client(struct nfs_client *);
struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *); struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *);
@ -319,7 +375,7 @@ extern void nfs4_kill_renewd(struct nfs_client *);
extern void nfs4_renew_state(struct work_struct *); extern void nfs4_renew_state(struct work_struct *);
/* nfs4state.c */ /* nfs4state.c */
struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp); struct rpc_cred *nfs4_get_clid_cred(struct nfs_client *clp);
struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp); struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp);
struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp); struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp);
int nfs4_discover_server_trunking(struct nfs_client *clp, int nfs4_discover_server_trunking(struct nfs_client *clp,
@ -327,7 +383,6 @@ int nfs4_discover_server_trunking(struct nfs_client *clp,
int nfs40_discover_server_trunking(struct nfs_client *clp, int nfs40_discover_server_trunking(struct nfs_client *clp,
struct nfs_client **, struct rpc_cred *); struct nfs_client **, struct rpc_cred *);
#if defined(CONFIG_NFS_V4_1) #if defined(CONFIG_NFS_V4_1)
struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp);
int nfs41_discover_server_trunking(struct nfs_client *clp, int nfs41_discover_server_trunking(struct nfs_client *clp,
struct nfs_client **, struct rpc_cred *); struct nfs_client **, struct rpc_cred *);
extern void nfs4_schedule_session_recovery(struct nfs4_session *, int); extern void nfs4_schedule_session_recovery(struct nfs4_session *, int);
@ -382,6 +437,7 @@ struct dentry *nfs4_try_mount(int, const char *, struct nfs_mount_info *, struct
extern bool nfs4_disable_idmapping; extern bool nfs4_disable_idmapping;
extern unsigned short max_session_slots; extern unsigned short max_session_slots;
extern unsigned short send_implementation_id; extern unsigned short send_implementation_id;
extern bool recover_lost_locks;
#define NFS4_CLIENT_ID_UNIQ_LEN (64) #define NFS4_CLIENT_ID_UNIQ_LEN (64)
extern char nfs4_client_id_uniquifier[NFS4_CLIENT_ID_UNIQ_LEN]; extern char nfs4_client_id_uniquifier[NFS4_CLIENT_ID_UNIQ_LEN];
@ -429,6 +485,8 @@ static inline bool nfs4_valid_open_stateid(const struct nfs4_state *state)
#define nfs4_close_state(a, b) do { } while (0) #define nfs4_close_state(a, b) do { } while (0)
#define nfs4_close_sync(a, b) do { } while (0) #define nfs4_close_sync(a, b) do { } while (0)
#define nfs4_state_protect(a, b, c, d) do { } while (0)
#define nfs4_state_protect_write(a, b, c, d) do { } while (0)
#endif /* CONFIG_NFS_V4 */ #endif /* CONFIG_NFS_V4 */
#endif /* __LINUX_FS_NFS_NFS4_FS.H */ #endif /* __LINUX_FS_NFS_NFS4_FS.H */

View File

@ -41,20 +41,139 @@ static int nfs_get_cb_ident_idr(struct nfs_client *clp, int minorversion)
} }
#ifdef CONFIG_NFS_V4_1 #ifdef CONFIG_NFS_V4_1
static void nfs4_shutdown_session(struct nfs_client *clp) /**
* Per auth flavor data server rpc clients
*/
struct nfs4_ds_server {
struct list_head list; /* ds_clp->cl_ds_clients */
struct rpc_clnt *rpc_clnt;
};
/**
* Common lookup case for DS I/O
*/
static struct nfs4_ds_server *
nfs4_find_ds_client(struct nfs_client *ds_clp, rpc_authflavor_t flavor)
{
struct nfs4_ds_server *dss;
rcu_read_lock();
list_for_each_entry_rcu(dss, &ds_clp->cl_ds_clients, list) {
if (dss->rpc_clnt->cl_auth->au_flavor != flavor)
continue;
goto out;
}
dss = NULL;
out:
rcu_read_unlock();
return dss;
}
static struct nfs4_ds_server *
nfs4_add_ds_client(struct nfs_client *ds_clp, rpc_authflavor_t flavor,
struct nfs4_ds_server *new)
{
struct nfs4_ds_server *dss;
spin_lock(&ds_clp->cl_lock);
list_for_each_entry(dss, &ds_clp->cl_ds_clients, list) {
if (dss->rpc_clnt->cl_auth->au_flavor != flavor)
continue;
goto out;
}
if (new)
list_add_rcu(&new->list, &ds_clp->cl_ds_clients);
dss = new;
out:
spin_unlock(&ds_clp->cl_lock); /* need some lock to protect list */
return dss;
}
static struct nfs4_ds_server *
nfs4_alloc_ds_server(struct nfs_client *ds_clp, rpc_authflavor_t flavor)
{
struct nfs4_ds_server *dss;
dss = kmalloc(sizeof(*dss), GFP_NOFS);
if (dss == NULL)
return ERR_PTR(-ENOMEM);
dss->rpc_clnt = rpc_clone_client_set_auth(ds_clp->cl_rpcclient, flavor);
if (IS_ERR(dss->rpc_clnt)) {
int err = PTR_ERR(dss->rpc_clnt);
kfree (dss);
return ERR_PTR(err);
}
INIT_LIST_HEAD(&dss->list);
return dss;
}
static void
nfs4_free_ds_server(struct nfs4_ds_server *dss)
{
rpc_release_client(dss->rpc_clnt);
kfree(dss);
}
/**
* Find or create a DS rpc client with th MDS server rpc client auth flavor
* in the nfs_client cl_ds_clients list.
*/
struct rpc_clnt *
nfs4_find_or_create_ds_client(struct nfs_client *ds_clp, struct inode *inode)
{
struct nfs4_ds_server *dss, *new;
rpc_authflavor_t flavor = NFS_SERVER(inode)->client->cl_auth->au_flavor;
dss = nfs4_find_ds_client(ds_clp, flavor);
if (dss != NULL)
goto out;
new = nfs4_alloc_ds_server(ds_clp, flavor);
if (IS_ERR(new))
return ERR_CAST(new);
dss = nfs4_add_ds_client(ds_clp, flavor, new);
if (dss != new)
nfs4_free_ds_server(new);
out:
return dss->rpc_clnt;
}
EXPORT_SYMBOL_GPL(nfs4_find_or_create_ds_client);
static void
nfs4_shutdown_ds_clients(struct nfs_client *clp)
{
struct nfs4_ds_server *dss;
LIST_HEAD(shutdown_list);
while (!list_empty(&clp->cl_ds_clients)) {
dss = list_entry(clp->cl_ds_clients.next,
struct nfs4_ds_server, list);
list_del(&dss->list);
rpc_shutdown_client(dss->rpc_clnt);
kfree (dss);
}
}
void nfs41_shutdown_client(struct nfs_client *clp)
{ {
if (nfs4_has_session(clp)) { if (nfs4_has_session(clp)) {
nfs4_shutdown_ds_clients(clp);
nfs4_destroy_session(clp->cl_session); nfs4_destroy_session(clp->cl_session);
nfs4_destroy_clientid(clp); nfs4_destroy_clientid(clp);
} }
}
#else /* CONFIG_NFS_V4_1 */
static void nfs4_shutdown_session(struct nfs_client *clp)
{
} }
#endif /* CONFIG_NFS_V4_1 */ #endif /* CONFIG_NFS_V4_1 */
void nfs40_shutdown_client(struct nfs_client *clp)
{
if (clp->cl_slot_tbl) {
nfs4_release_slot_table(clp->cl_slot_tbl);
kfree(clp->cl_slot_tbl);
}
}
struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init) struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
{ {
int err; int err;
@ -73,6 +192,7 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
spin_lock_init(&clp->cl_lock); spin_lock_init(&clp->cl_lock);
INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state);
INIT_LIST_HEAD(&clp->cl_ds_clients);
rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client");
clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED; clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
clp->cl_minorversion = cl_init->minorversion; clp->cl_minorversion = cl_init->minorversion;
@ -97,7 +217,7 @@ static void nfs4_shutdown_client(struct nfs_client *clp)
{ {
if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state)) if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state))
nfs4_kill_renewd(clp); nfs4_kill_renewd(clp);
nfs4_shutdown_session(clp); clp->cl_mvops->shutdown_client(clp);
nfs4_destroy_callback(clp); nfs4_destroy_callback(clp);
if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state))
nfs_idmap_delete(clp); nfs_idmap_delete(clp);
@ -144,14 +264,44 @@ static int nfs4_init_callback(struct nfs_client *clp)
return 0; return 0;
} }
/* /**
* Initialize the minor version specific parts of an NFS4 client record * nfs40_init_client - nfs_client initialization tasks for NFSv4.0
* @clp - nfs_client to initialize
*
* Returns zero on success, or a negative errno if some error occurred.
*/ */
static int nfs4_init_client_minor_version(struct nfs_client *clp) int nfs40_init_client(struct nfs_client *clp)
{ {
struct nfs4_slot_table *tbl;
int ret;
tbl = kzalloc(sizeof(*tbl), GFP_NOFS);
if (tbl == NULL)
return -ENOMEM;
ret = nfs4_setup_slot_table(tbl, NFS4_MAX_SLOT_TABLE,
"NFSv4.0 transport Slot table");
if (ret) {
kfree(tbl);
return ret;
}
clp->cl_slot_tbl = tbl;
return 0;
}
#if defined(CONFIG_NFS_V4_1) #if defined(CONFIG_NFS_V4_1)
if (clp->cl_mvops->minor_version) {
/**
* nfs41_init_client - nfs_client initialization tasks for NFSv4.1+
* @clp - nfs_client to initialize
*
* Returns zero on success, or a negative errno if some error occurred.
*/
int nfs41_init_client(struct nfs_client *clp)
{
struct nfs4_session *session = NULL; struct nfs4_session *session = NULL;
/* /*
* Create the session and mark it expired. * Create the session and mark it expired.
* When a SEQUENCE operation encounters the expired session * When a SEQUENCE operation encounters the expired session
@ -162,6 +312,7 @@ static int nfs4_init_client_minor_version(struct nfs_client *clp)
return -ENOMEM; return -ENOMEM;
clp->cl_session = session; clp->cl_session = session;
/* /*
* The create session reply races with the server back * The create session reply races with the server back
* channel probe. Mark the client NFS_CS_SESSION_INITING * channel probe. Mark the client NFS_CS_SESSION_INITING
@ -169,9 +320,21 @@ static int nfs4_init_client_minor_version(struct nfs_client *clp)
* nfs_client struct * nfs_client struct
*/ */
nfs_mark_client_ready(clp, NFS_CS_SESSION_INITING); nfs_mark_client_ready(clp, NFS_CS_SESSION_INITING);
return 0;
} }
#endif /* CONFIG_NFS_V4_1 */ #endif /* CONFIG_NFS_V4_1 */
/*
* Initialize the minor version specific parts of an NFS4 client record
*/
static int nfs4_init_client_minor_version(struct nfs_client *clp)
{
int ret;
ret = clp->cl_mvops->init_client(clp);
if (ret)
return ret;
return nfs4_init_callback(clp); return nfs4_init_callback(clp);
} }
@ -187,8 +350,7 @@ static int nfs4_init_client_minor_version(struct nfs_client *clp)
*/ */
struct nfs_client *nfs4_init_client(struct nfs_client *clp, struct nfs_client *nfs4_init_client(struct nfs_client *clp,
const struct rpc_timeout *timeparms, const struct rpc_timeout *timeparms,
const char *ip_addr, const char *ip_addr)
rpc_authflavor_t authflavour)
{ {
char buf[INET6_ADDRSTRLEN + 1]; char buf[INET6_ADDRSTRLEN + 1];
struct nfs_client *old; struct nfs_client *old;
@ -723,7 +885,7 @@ static void nfs4_session_set_rwsize(struct nfs_server *server)
} }
static int nfs4_server_common_setup(struct nfs_server *server, static int nfs4_server_common_setup(struct nfs_server *server,
struct nfs_fh *mntfh) struct nfs_fh *mntfh, bool auth_probe)
{ {
struct nfs_fattr *fattr; struct nfs_fattr *fattr;
int error; int error;
@ -755,7 +917,7 @@ static int nfs4_server_common_setup(struct nfs_server *server,
/* Probe the root fh to retrieve its FSID and filehandle */ /* Probe the root fh to retrieve its FSID and filehandle */
error = nfs4_get_rootfh(server, mntfh); error = nfs4_get_rootfh(server, mntfh, auth_probe);
if (error < 0) if (error < 0)
goto out; goto out;
@ -787,6 +949,7 @@ out:
static int nfs4_init_server(struct nfs_server *server, static int nfs4_init_server(struct nfs_server *server,
const struct nfs_parsed_mount_data *data) const struct nfs_parsed_mount_data *data)
{ {
rpc_authflavor_t pseudoflavor = RPC_AUTH_UNIX;
struct rpc_timeout timeparms; struct rpc_timeout timeparms;
int error; int error;
@ -799,13 +962,16 @@ static int nfs4_init_server(struct nfs_server *server,
server->flags = data->flags; server->flags = data->flags;
server->options = data->options; server->options = data->options;
if (data->auth_flavor_len >= 1)
pseudoflavor = data->auth_flavors[0];
/* Get a client record */ /* Get a client record */
error = nfs4_set_client(server, error = nfs4_set_client(server,
data->nfs_server.hostname, data->nfs_server.hostname,
(const struct sockaddr *)&data->nfs_server.address, (const struct sockaddr *)&data->nfs_server.address,
data->nfs_server.addrlen, data->nfs_server.addrlen,
data->client_address, data->client_address,
data->auth_flavors[0], pseudoflavor,
data->nfs_server.protocol, data->nfs_server.protocol,
&timeparms, &timeparms,
data->minorversion, data->minorversion,
@ -825,7 +991,7 @@ static int nfs4_init_server(struct nfs_server *server,
server->port = data->nfs_server.port; server->port = data->nfs_server.port;
error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]); error = nfs_init_server_rpcclient(server, &timeparms, pseudoflavor);
error: error:
/* Done */ /* Done */
@ -843,6 +1009,7 @@ struct nfs_server *nfs4_create_server(struct nfs_mount_info *mount_info,
struct nfs_subversion *nfs_mod) struct nfs_subversion *nfs_mod)
{ {
struct nfs_server *server; struct nfs_server *server;
bool auth_probe;
int error; int error;
dprintk("--> nfs4_create_server()\n"); dprintk("--> nfs4_create_server()\n");
@ -851,12 +1018,14 @@ struct nfs_server *nfs4_create_server(struct nfs_mount_info *mount_info,
if (!server) if (!server)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
auth_probe = mount_info->parsed->auth_flavor_len < 1;
/* set up the general RPC client */ /* set up the general RPC client */
error = nfs4_init_server(server, mount_info->parsed); error = nfs4_init_server(server, mount_info->parsed);
if (error < 0) if (error < 0)
goto error; goto error;
error = nfs4_server_common_setup(server, mount_info->mntfh); error = nfs4_server_common_setup(server, mount_info->mntfh, auth_probe);
if (error < 0) if (error < 0)
goto error; goto error;
@ -909,7 +1078,8 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
if (error < 0) if (error < 0)
goto error; goto error;
error = nfs4_server_common_setup(server, mntfh); error = nfs4_server_common_setup(server, mntfh,
!(parent_server->flags & NFS_MOUNT_SECFLAVOUR));
if (error < 0) if (error < 0)
goto error; goto error;

View File

@ -39,6 +39,7 @@
#include "internal.h" #include "internal.h"
#include "delegation.h" #include "delegation.h"
#include "nfs4filelayout.h" #include "nfs4filelayout.h"
#include "nfs4trace.h"
#define NFSDBG_FACILITY NFSDBG_PNFS_LD #define NFSDBG_FACILITY NFSDBG_PNFS_LD
@ -247,6 +248,7 @@ static int filelayout_read_done_cb(struct rpc_task *task,
struct nfs_pgio_header *hdr = data->header; struct nfs_pgio_header *hdr = data->header;
int err; int err;
trace_nfs4_pnfs_read(data, task->tk_status);
err = filelayout_async_handle_error(task, data->args.context->state, err = filelayout_async_handle_error(task, data->args.context->state,
data->ds_clp, hdr->lseg); data->ds_clp, hdr->lseg);
@ -363,6 +365,7 @@ static int filelayout_write_done_cb(struct rpc_task *task,
struct nfs_pgio_header *hdr = data->header; struct nfs_pgio_header *hdr = data->header;
int err; int err;
trace_nfs4_pnfs_write(data, task->tk_status);
err = filelayout_async_handle_error(task, data->args.context->state, err = filelayout_async_handle_error(task, data->args.context->state,
data->ds_clp, hdr->lseg); data->ds_clp, hdr->lseg);
@ -395,6 +398,7 @@ static int filelayout_commit_done_cb(struct rpc_task *task,
{ {
int err; int err;
trace_nfs4_pnfs_commit_ds(data, task->tk_status);
err = filelayout_async_handle_error(task, NULL, data->ds_clp, err = filelayout_async_handle_error(task, NULL, data->ds_clp,
data->lseg); data->lseg);
@ -524,6 +528,7 @@ filelayout_read_pagelist(struct nfs_read_data *data)
struct nfs_pgio_header *hdr = data->header; struct nfs_pgio_header *hdr = data->header;
struct pnfs_layout_segment *lseg = hdr->lseg; struct pnfs_layout_segment *lseg = hdr->lseg;
struct nfs4_pnfs_ds *ds; struct nfs4_pnfs_ds *ds;
struct rpc_clnt *ds_clnt;
loff_t offset = data->args.offset; loff_t offset = data->args.offset;
u32 j, idx; u32 j, idx;
struct nfs_fh *fh; struct nfs_fh *fh;
@ -538,6 +543,11 @@ filelayout_read_pagelist(struct nfs_read_data *data)
ds = nfs4_fl_prepare_ds(lseg, idx); ds = nfs4_fl_prepare_ds(lseg, idx);
if (!ds) if (!ds)
return PNFS_NOT_ATTEMPTED; return PNFS_NOT_ATTEMPTED;
ds_clnt = nfs4_find_or_create_ds_client(ds->ds_clp, hdr->inode);
if (IS_ERR(ds_clnt))
return PNFS_NOT_ATTEMPTED;
dprintk("%s USE DS: %s cl_count %d\n", __func__, dprintk("%s USE DS: %s cl_count %d\n", __func__,
ds->ds_remotestr, atomic_read(&ds->ds_clp->cl_count)); ds->ds_remotestr, atomic_read(&ds->ds_clp->cl_count));
@ -552,7 +562,7 @@ filelayout_read_pagelist(struct nfs_read_data *data)
data->mds_offset = offset; data->mds_offset = offset;
/* Perform an asynchronous read to ds */ /* Perform an asynchronous read to ds */
nfs_initiate_read(ds->ds_clp->cl_rpcclient, data, nfs_initiate_read(ds_clnt, data,
&filelayout_read_call_ops, RPC_TASK_SOFTCONN); &filelayout_read_call_ops, RPC_TASK_SOFTCONN);
return PNFS_ATTEMPTED; return PNFS_ATTEMPTED;
} }
@ -564,6 +574,7 @@ filelayout_write_pagelist(struct nfs_write_data *data, int sync)
struct nfs_pgio_header *hdr = data->header; struct nfs_pgio_header *hdr = data->header;
struct pnfs_layout_segment *lseg = hdr->lseg; struct pnfs_layout_segment *lseg = hdr->lseg;
struct nfs4_pnfs_ds *ds; struct nfs4_pnfs_ds *ds;
struct rpc_clnt *ds_clnt;
loff_t offset = data->args.offset; loff_t offset = data->args.offset;
u32 j, idx; u32 j, idx;
struct nfs_fh *fh; struct nfs_fh *fh;
@ -574,6 +585,11 @@ filelayout_write_pagelist(struct nfs_write_data *data, int sync)
ds = nfs4_fl_prepare_ds(lseg, idx); ds = nfs4_fl_prepare_ds(lseg, idx);
if (!ds) if (!ds)
return PNFS_NOT_ATTEMPTED; return PNFS_NOT_ATTEMPTED;
ds_clnt = nfs4_find_or_create_ds_client(ds->ds_clp, hdr->inode);
if (IS_ERR(ds_clnt))
return PNFS_NOT_ATTEMPTED;
dprintk("%s ino %lu sync %d req %Zu@%llu DS: %s cl_count %d\n", dprintk("%s ino %lu sync %d req %Zu@%llu DS: %s cl_count %d\n",
__func__, hdr->inode->i_ino, sync, (size_t) data->args.count, __func__, hdr->inode->i_ino, sync, (size_t) data->args.count,
offset, ds->ds_remotestr, atomic_read(&ds->ds_clp->cl_count)); offset, ds->ds_remotestr, atomic_read(&ds->ds_clp->cl_count));
@ -591,7 +607,7 @@ filelayout_write_pagelist(struct nfs_write_data *data, int sync)
data->args.offset = filelayout_get_dserver_offset(lseg, offset); data->args.offset = filelayout_get_dserver_offset(lseg, offset);
/* Perform an asynchronous write */ /* Perform an asynchronous write */
nfs_initiate_write(ds->ds_clp->cl_rpcclient, data, nfs_initiate_write(ds_clnt, data,
&filelayout_write_call_ops, sync, &filelayout_write_call_ops, sync,
RPC_TASK_SOFTCONN); RPC_TASK_SOFTCONN);
return PNFS_ATTEMPTED; return PNFS_ATTEMPTED;
@ -1101,16 +1117,19 @@ static int filelayout_initiate_commit(struct nfs_commit_data *data, int how)
{ {
struct pnfs_layout_segment *lseg = data->lseg; struct pnfs_layout_segment *lseg = data->lseg;
struct nfs4_pnfs_ds *ds; struct nfs4_pnfs_ds *ds;
struct rpc_clnt *ds_clnt;
u32 idx; u32 idx;
struct nfs_fh *fh; struct nfs_fh *fh;
idx = calc_ds_index_from_commit(lseg, data->ds_commit_index); idx = calc_ds_index_from_commit(lseg, data->ds_commit_index);
ds = nfs4_fl_prepare_ds(lseg, idx); ds = nfs4_fl_prepare_ds(lseg, idx);
if (!ds) { if (!ds)
prepare_to_resend_writes(data); goto out_err;
filelayout_commit_release(data);
return -EAGAIN; ds_clnt = nfs4_find_or_create_ds_client(ds->ds_clp, data->inode);
} if (IS_ERR(ds_clnt))
goto out_err;
dprintk("%s ino %lu, how %d cl_count %d\n", __func__, dprintk("%s ino %lu, how %d cl_count %d\n", __func__,
data->inode->i_ino, how, atomic_read(&ds->ds_clp->cl_count)); data->inode->i_ino, how, atomic_read(&ds->ds_clp->cl_count));
data->commit_done_cb = filelayout_commit_done_cb; data->commit_done_cb = filelayout_commit_done_cb;
@ -1119,9 +1138,13 @@ static int filelayout_initiate_commit(struct nfs_commit_data *data, int how)
fh = select_ds_fh_from_commit(lseg, data->ds_commit_index); fh = select_ds_fh_from_commit(lseg, data->ds_commit_index);
if (fh) if (fh)
data->args.fh = fh; data->args.fh = fh;
return nfs_initiate_commit(ds->ds_clp->cl_rpcclient, data, return nfs_initiate_commit(ds_clnt, data,
&filelayout_commit_call_ops, how, &filelayout_commit_call_ops, how,
RPC_TASK_SOFTCONN); RPC_TASK_SOFTCONN);
out_err:
prepare_to_resend_writes(data);
filelayout_commit_release(data);
return -EAGAIN;
} }
static int static int

View File

@ -9,7 +9,7 @@
#define NFSDBG_FACILITY NFSDBG_CLIENT #define NFSDBG_FACILITY NFSDBG_CLIENT
int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh) int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh, bool auth_probe)
{ {
struct nfs_fsinfo fsinfo; struct nfs_fsinfo fsinfo;
int ret = -ENOMEM; int ret = -ENOMEM;
@ -21,7 +21,7 @@ int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh)
goto out; goto out;
/* Start by getting the root filehandle from the server */ /* Start by getting the root filehandle from the server */
ret = nfs4_proc_get_rootfh(server, mntfh, &fsinfo); ret = nfs4_proc_get_rootfh(server, mntfh, &fsinfo, auth_probe);
if (ret < 0) { if (ret < 0) {
dprintk("nfs4_get_rootfh: getroot error = %d\n", -ret); dprintk("nfs4_get_rootfh: getroot error = %d\n", -ret);
goto out; goto out;

View File

@ -11,6 +11,7 @@
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include <linux/nfs_mount.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/sunrpc/clnt.h> #include <linux/sunrpc/clnt.h>
@ -369,21 +370,33 @@ out:
struct vfsmount *nfs4_submount(struct nfs_server *server, struct dentry *dentry, struct vfsmount *nfs4_submount(struct nfs_server *server, struct dentry *dentry,
struct nfs_fh *fh, struct nfs_fattr *fattr) struct nfs_fh *fh, struct nfs_fattr *fattr)
{ {
rpc_authflavor_t flavor = server->client->cl_auth->au_flavor;
struct dentry *parent = dget_parent(dentry); struct dentry *parent = dget_parent(dentry);
struct inode *dir = parent->d_inode;
struct qstr *name = &dentry->d_name;
struct rpc_clnt *client; struct rpc_clnt *client;
struct vfsmount *mnt; struct vfsmount *mnt;
/* Look it up again to get its attributes and sec flavor */ /* Look it up again to get its attributes and sec flavor */
client = nfs4_proc_lookup_mountpoint(parent->d_inode, &dentry->d_name, fh, fattr); client = nfs4_proc_lookup_mountpoint(dir, name, fh, fattr);
dput(parent); dput(parent);
if (IS_ERR(client)) if (IS_ERR(client))
return ERR_CAST(client); return ERR_CAST(client);
if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
mnt = nfs_do_refmount(client, dentry); mnt = nfs_do_refmount(client, dentry);
else goto out;
mnt = nfs_do_submount(dentry, fh, fattr, client->cl_auth->au_flavor); }
if (client->cl_auth->au_flavor != flavor)
flavor = client->cl_auth->au_flavor;
else if (!(server->flags & NFS_MOUNT_SECFLAVOUR)) {
rpc_authflavor_t new = nfs4_negotiate_security(dir, name);
if ((int)new >= 0)
flavor = new;
}
mnt = nfs_do_submount(dentry, fh, fattr, flavor);
out:
rpc_shutdown_client(client); rpc_shutdown_client(client);
return mnt; return mnt;
} }

File diff suppressed because it is too large Load Diff

View File

@ -23,6 +23,14 @@
#define NFSDBG_FACILITY NFSDBG_STATE #define NFSDBG_FACILITY NFSDBG_STATE
static void nfs4_init_slot_table(struct nfs4_slot_table *tbl, const char *queue)
{
tbl->highest_used_slotid = NFS4_NO_SLOT;
spin_lock_init(&tbl->slot_tbl_lock);
rpc_init_priority_wait_queue(&tbl->slot_tbl_waitq, queue);
init_completion(&tbl->complete);
}
/* /*
* nfs4_shrink_slot_table - free retired slots from the slot table * nfs4_shrink_slot_table - free retired slots from the slot table
*/ */
@ -44,6 +52,17 @@ static void nfs4_shrink_slot_table(struct nfs4_slot_table *tbl, u32 newsize)
} }
} }
/**
* nfs4_slot_tbl_drain_complete - wake waiters when drain is complete
* @tbl - controlling slot table
*
*/
void nfs4_slot_tbl_drain_complete(struct nfs4_slot_table *tbl)
{
if (nfs4_slot_tbl_draining(tbl))
complete(&tbl->complete);
}
/* /*
* nfs4_free_slot - free a slot and efficiently update slot table. * nfs4_free_slot - free a slot and efficiently update slot table.
* *
@ -76,7 +95,7 @@ void nfs4_free_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *slot)
nfs4_slot_tbl_drain_complete(tbl); nfs4_slot_tbl_drain_complete(tbl);
} }
} }
dprintk("%s: slotid %u highest_used_slotid %d\n", __func__, dprintk("%s: slotid %u highest_used_slotid %u\n", __func__,
slotid, tbl->highest_used_slotid); slotid, tbl->highest_used_slotid);
} }
@ -146,9 +165,9 @@ struct nfs4_slot *nfs4_alloc_slot(struct nfs4_slot_table *tbl)
ret->generation = tbl->generation; ret->generation = tbl->generation;
out: out:
dprintk("<-- %s used_slots=%04lx highest_used=%d slotid=%d \n", dprintk("<-- %s used_slots=%04lx highest_used=%u slotid=%u\n",
__func__, tbl->used_slots[0], tbl->highest_used_slotid, __func__, tbl->used_slots[0], tbl->highest_used_slotid,
!IS_ERR(ret) ? ret->slot_nr : -1); !IS_ERR(ret) ? ret->slot_nr : NFS4_NO_SLOT);
return ret; return ret;
} }
@ -191,7 +210,7 @@ static int nfs4_realloc_slot_table(struct nfs4_slot_table *tbl,
{ {
int ret; int ret;
dprintk("--> %s: max_reqs=%u, tbl->max_slots %d\n", __func__, dprintk("--> %s: max_reqs=%u, tbl->max_slots %u\n", __func__,
max_reqs, tbl->max_slots); max_reqs, tbl->max_slots);
if (max_reqs > NFS4_MAX_SLOT_TABLE) if (max_reqs > NFS4_MAX_SLOT_TABLE)
@ -205,18 +224,36 @@ static int nfs4_realloc_slot_table(struct nfs4_slot_table *tbl,
nfs4_reset_slot_table(tbl, max_reqs - 1, ivalue); nfs4_reset_slot_table(tbl, max_reqs - 1, ivalue);
spin_unlock(&tbl->slot_tbl_lock); spin_unlock(&tbl->slot_tbl_lock);
dprintk("%s: tbl=%p slots=%p max_slots=%d\n", __func__, dprintk("%s: tbl=%p slots=%p max_slots=%u\n", __func__,
tbl, tbl->slots, tbl->max_slots); tbl, tbl->slots, tbl->max_slots);
out: out:
dprintk("<-- %s: return %d\n", __func__, ret); dprintk("<-- %s: return %d\n", __func__, ret);
return ret; return ret;
} }
/* Destroy the slot table */ /**
static void nfs4_destroy_slot_tables(struct nfs4_session *session) * nfs4_release_slot_table - release resources attached to a slot table
* @tbl: slot table to shut down
*
*/
void nfs4_release_slot_table(struct nfs4_slot_table *tbl)
{ {
nfs4_shrink_slot_table(&session->fc_slot_table, 0); nfs4_shrink_slot_table(tbl, 0);
nfs4_shrink_slot_table(&session->bc_slot_table, 0); }
/**
* nfs4_setup_slot_table - prepare a stand-alone slot table for use
* @tbl: slot table to set up
* @max_reqs: maximum number of requests allowed
* @queue: name to give RPC wait queue
*
* Returns zero on success, or a negative errno.
*/
int nfs4_setup_slot_table(struct nfs4_slot_table *tbl, unsigned int max_reqs,
const char *queue)
{
nfs4_init_slot_table(tbl, queue);
return nfs4_realloc_slot_table(tbl, max_reqs, 0);
} }
static bool nfs41_assign_slot(struct rpc_task *task, void *pslot) static bool nfs41_assign_slot(struct rpc_task *task, void *pslot)
@ -273,6 +310,8 @@ void nfs41_wake_slot_table(struct nfs4_slot_table *tbl)
} }
} }
#if defined(CONFIG_NFS_V4_1)
static void nfs41_set_max_slotid_locked(struct nfs4_slot_table *tbl, static void nfs41_set_max_slotid_locked(struct nfs4_slot_table *tbl,
u32 target_highest_slotid) u32 target_highest_slotid)
{ {
@ -383,6 +422,12 @@ void nfs41_update_target_slotid(struct nfs4_slot_table *tbl,
spin_unlock(&tbl->slot_tbl_lock); spin_unlock(&tbl->slot_tbl_lock);
} }
static void nfs4_destroy_session_slot_tables(struct nfs4_session *session)
{
nfs4_release_slot_table(&session->fc_slot_table);
nfs4_release_slot_table(&session->bc_slot_table);
}
/* /*
* Initialize or reset the forechannel and backchannel tables * Initialize or reset the forechannel and backchannel tables
*/ */
@ -405,31 +450,20 @@ int nfs4_setup_session_slot_tables(struct nfs4_session *ses)
if (status && tbl->slots == NULL) if (status && tbl->slots == NULL)
/* Fore and back channel share a connection so get /* Fore and back channel share a connection so get
* both slot tables or neither */ * both slot tables or neither */
nfs4_destroy_slot_tables(ses); nfs4_destroy_session_slot_tables(ses);
return status; return status;
} }
struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp) struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp)
{ {
struct nfs4_session *session; struct nfs4_session *session;
struct nfs4_slot_table *tbl;
session = kzalloc(sizeof(struct nfs4_session), GFP_NOFS); session = kzalloc(sizeof(struct nfs4_session), GFP_NOFS);
if (!session) if (!session)
return NULL; return NULL;
tbl = &session->fc_slot_table; nfs4_init_slot_table(&session->fc_slot_table, "ForeChannel Slot table");
tbl->highest_used_slotid = NFS4_NO_SLOT; nfs4_init_slot_table(&session->bc_slot_table, "BackChannel Slot table");
spin_lock_init(&tbl->slot_tbl_lock);
rpc_init_priority_wait_queue(&tbl->slot_tbl_waitq, "ForeChannel Slot table");
init_completion(&tbl->complete);
tbl = &session->bc_slot_table;
tbl->highest_used_slotid = NFS4_NO_SLOT;
spin_lock_init(&tbl->slot_tbl_lock);
rpc_init_wait_queue(&tbl->slot_tbl_waitq, "BackChannel Slot table");
init_completion(&tbl->complete);
session->session_state = 1<<NFS4_SESSION_INITING; session->session_state = 1<<NFS4_SESSION_INITING;
session->clp = clp; session->clp = clp;
@ -441,7 +475,7 @@ void nfs4_destroy_session(struct nfs4_session *session)
struct rpc_xprt *xprt; struct rpc_xprt *xprt;
struct rpc_cred *cred; struct rpc_cred *cred;
cred = nfs4_get_exchange_id_cred(session->clp); cred = nfs4_get_clid_cred(session->clp);
nfs4_proc_destroy_session(session, cred); nfs4_proc_destroy_session(session, cred);
if (cred) if (cred)
put_rpccred(cred); put_rpccred(cred);
@ -452,7 +486,7 @@ void nfs4_destroy_session(struct nfs4_session *session)
dprintk("%s Destroy backchannel for xprt %p\n", dprintk("%s Destroy backchannel for xprt %p\n",
__func__, xprt); __func__, xprt);
xprt_destroy_backchannel(xprt, NFS41_BC_MIN_CALLBACKS); xprt_destroy_backchannel(xprt, NFS41_BC_MIN_CALLBACKS);
nfs4_destroy_slot_tables(session); nfs4_destroy_session_slot_tables(session);
kfree(session); kfree(session);
} }
@ -513,4 +547,4 @@ int nfs4_init_ds_session(struct nfs_client *clp, unsigned long lease_time)
} }
EXPORT_SYMBOL_GPL(nfs4_init_ds_session); EXPORT_SYMBOL_GPL(nfs4_init_ds_session);
#endif /* defined(CONFIG_NFS_V4_1) */

View File

@ -8,7 +8,7 @@
#define __LINUX_FS_NFS_NFS4SESSION_H #define __LINUX_FS_NFS_NFS4SESSION_H
/* maximum number of slots to use */ /* maximum number of slots to use */
#define NFS4_DEF_SLOT_TABLE_SIZE (16U) #define NFS4_DEF_SLOT_TABLE_SIZE (64U)
#define NFS4_MAX_SLOT_TABLE (1024U) #define NFS4_MAX_SLOT_TABLE (1024U)
#define NFS4_NO_SLOT ((u32)-1) #define NFS4_NO_SLOT ((u32)-1)
@ -72,10 +72,22 @@ enum nfs4_session_state {
NFS4_SESSION_INITING, NFS4_SESSION_INITING,
}; };
#if defined(CONFIG_NFS_V4_1) extern int nfs4_setup_slot_table(struct nfs4_slot_table *tbl,
unsigned int max_reqs, const char *queue);
extern void nfs4_release_slot_table(struct nfs4_slot_table *tbl);
extern struct nfs4_slot *nfs4_alloc_slot(struct nfs4_slot_table *tbl); extern struct nfs4_slot *nfs4_alloc_slot(struct nfs4_slot_table *tbl);
extern void nfs4_free_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *slot); extern void nfs4_free_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *slot);
extern void nfs4_slot_tbl_drain_complete(struct nfs4_slot_table *tbl);
bool nfs41_wake_and_assign_slot(struct nfs4_slot_table *tbl,
struct nfs4_slot *slot);
void nfs41_wake_slot_table(struct nfs4_slot_table *tbl);
static inline bool nfs4_slot_tbl_draining(struct nfs4_slot_table *tbl)
{
return !!test_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state);
}
#if defined(CONFIG_NFS_V4_1)
extern void nfs41_set_target_slotid(struct nfs4_slot_table *tbl, extern void nfs41_set_target_slotid(struct nfs4_slot_table *tbl,
u32 target_highest_slotid); u32 target_highest_slotid);
extern void nfs41_update_target_slotid(struct nfs4_slot_table *tbl, extern void nfs41_update_target_slotid(struct nfs4_slot_table *tbl,
@ -89,17 +101,6 @@ extern void nfs4_destroy_session(struct nfs4_session *session);
extern int nfs4_init_session(struct nfs_client *clp); extern int nfs4_init_session(struct nfs_client *clp);
extern int nfs4_init_ds_session(struct nfs_client *, unsigned long); extern int nfs4_init_ds_session(struct nfs_client *, unsigned long);
extern void nfs4_slot_tbl_drain_complete(struct nfs4_slot_table *tbl);
static inline bool nfs4_slot_tbl_draining(struct nfs4_slot_table *tbl)
{
return !!test_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state);
}
bool nfs41_wake_and_assign_slot(struct nfs4_slot_table *tbl,
struct nfs4_slot *slot);
void nfs41_wake_slot_table(struct nfs4_slot_table *tbl);
/* /*
* Determine if sessions are in use. * Determine if sessions are in use.
*/ */
@ -117,6 +118,16 @@ static inline int nfs4_has_persistent_session(const struct nfs_client *clp)
return 0; return 0;
} }
#ifdef CONFIG_CRC32
/*
* nfs_session_id_hash - calculate the crc32 hash for the session id
* @session - pointer to session
*/
#define nfs_session_id_hash(sess_id) \
(~crc32_le(0xFFFFFFFF, &(sess_id)->data[0], sizeof((sess_id)->data)))
#else
#define nfs_session_id_hash(session) (0)
#endif
#else /* defined(CONFIG_NFS_V4_1) */ #else /* defined(CONFIG_NFS_V4_1) */
static inline int nfs4_init_session(struct nfs_client *clp) static inline int nfs4_init_session(struct nfs_client *clp)

View File

@ -154,6 +154,19 @@ struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp)
return cred; return cred;
} }
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);
}
static struct rpc_cred * static struct rpc_cred *
nfs4_get_renew_cred_server_locked(struct nfs_server *server) nfs4_get_renew_cred_server_locked(struct nfs_server *server)
{ {
@ -202,8 +215,61 @@ out:
return cred; return cred;
} }
static void nfs4_end_drain_slot_table(struct nfs4_slot_table *tbl)
{
if (test_and_clear_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state)) {
spin_lock(&tbl->slot_tbl_lock);
nfs41_wake_slot_table(tbl);
spin_unlock(&tbl->slot_tbl_lock);
}
}
static void nfs4_end_drain_session(struct nfs_client *clp)
{
struct nfs4_session *ses = clp->cl_session;
if (clp->cl_slot_tbl) {
nfs4_end_drain_slot_table(clp->cl_slot_tbl);
return;
}
if (ses != NULL) {
nfs4_end_drain_slot_table(&ses->bc_slot_table);
nfs4_end_drain_slot_table(&ses->fc_slot_table);
}
}
#if defined(CONFIG_NFS_V4_1) #if defined(CONFIG_NFS_V4_1)
static int nfs4_drain_slot_tbl(struct nfs4_slot_table *tbl)
{
set_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state);
spin_lock(&tbl->slot_tbl_lock);
if (tbl->highest_used_slotid != NFS4_NO_SLOT) {
INIT_COMPLETION(tbl->complete);
spin_unlock(&tbl->slot_tbl_lock);
return wait_for_completion_interruptible(&tbl->complete);
}
spin_unlock(&tbl->slot_tbl_lock);
return 0;
}
static int nfs4_begin_drain_session(struct nfs_client *clp)
{
struct nfs4_session *ses = clp->cl_session;
int ret = 0;
if (clp->cl_slot_tbl)
return nfs4_drain_slot_tbl(clp->cl_slot_tbl);
/* back channel */
ret = nfs4_drain_slot_tbl(&ses->bc_slot_table);
if (ret)
return ret;
/* fore channel */
return nfs4_drain_slot_tbl(&ses->fc_slot_table);
}
static int nfs41_setup_state_renewal(struct nfs_client *clp) static int nfs41_setup_state_renewal(struct nfs_client *clp)
{ {
int status; int status;
@ -228,60 +294,6 @@ static int nfs41_setup_state_renewal(struct nfs_client *clp)
return status; return status;
} }
static void nfs4_end_drain_slot_table(struct nfs4_slot_table *tbl)
{
if (test_and_clear_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state)) {
spin_lock(&tbl->slot_tbl_lock);
nfs41_wake_slot_table(tbl);
spin_unlock(&tbl->slot_tbl_lock);
}
}
static void nfs4_end_drain_session(struct nfs_client *clp)
{
struct nfs4_session *ses = clp->cl_session;
if (ses != NULL) {
nfs4_end_drain_slot_table(&ses->bc_slot_table);
nfs4_end_drain_slot_table(&ses->fc_slot_table);
}
}
/*
* Signal state manager thread if session fore channel is drained
*/
void nfs4_slot_tbl_drain_complete(struct nfs4_slot_table *tbl)
{
if (nfs4_slot_tbl_draining(tbl))
complete(&tbl->complete);
}
static int nfs4_drain_slot_tbl(struct nfs4_slot_table *tbl)
{
set_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state);
spin_lock(&tbl->slot_tbl_lock);
if (tbl->highest_used_slotid != NFS4_NO_SLOT) {
INIT_COMPLETION(tbl->complete);
spin_unlock(&tbl->slot_tbl_lock);
return wait_for_completion_interruptible(&tbl->complete);
}
spin_unlock(&tbl->slot_tbl_lock);
return 0;
}
static int nfs4_begin_drain_session(struct nfs_client *clp)
{
struct nfs4_session *ses = clp->cl_session;
int ret = 0;
/* back channel */
ret = nfs4_drain_slot_tbl(&ses->bc_slot_table);
if (ret)
return ret;
/* fore channel */
return nfs4_drain_slot_tbl(&ses->fc_slot_table);
}
static void nfs41_finish_session_reset(struct nfs_client *clp) static void nfs41_finish_session_reset(struct nfs_client *clp)
{ {
clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
@ -339,62 +351,21 @@ int nfs41_discover_server_trunking(struct nfs_client *clp,
return nfs41_walk_client_list(clp, result, cred); return nfs41_walk_client_list(clp, result, cred);
} }
struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp)
{
struct rpc_cred *cred;
spin_lock(&clp->cl_lock);
cred = nfs4_get_machine_cred_locked(clp);
spin_unlock(&clp->cl_lock);
return cred;
}
#endif /* CONFIG_NFS_V4_1 */ #endif /* CONFIG_NFS_V4_1 */
static struct rpc_cred *
nfs4_get_setclientid_cred_server(struct nfs_server *server)
{
struct nfs_client *clp = server->nfs_client;
struct rpc_cred *cred = NULL;
struct nfs4_state_owner *sp;
struct rb_node *pos;
spin_lock(&clp->cl_lock);
pos = rb_first(&server->state_owners);
if (pos != NULL) {
sp = rb_entry(pos, struct nfs4_state_owner, so_server_node);
cred = get_rpccred(sp->so_cred);
}
spin_unlock(&clp->cl_lock);
return cred;
}
/** /**
* nfs4_get_setclientid_cred - Acquire credential for a setclientid operation * nfs4_get_clid_cred - Acquire credential for a setclientid operation
* @clp: client state handle * @clp: client state handle
* *
* Returns an rpc_cred with reference count bumped, or NULL. * Returns an rpc_cred with reference count bumped, or NULL.
*/ */
struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp) struct rpc_cred *nfs4_get_clid_cred(struct nfs_client *clp)
{ {
struct nfs_server *server;
struct rpc_cred *cred; struct rpc_cred *cred;
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
cred = nfs4_get_machine_cred_locked(clp); cred = nfs4_get_machine_cred_locked(clp);
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
if (cred != NULL)
goto out;
rcu_read_lock();
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
cred = nfs4_get_setclientid_cred_server(server);
if (cred != NULL)
break;
}
rcu_read_unlock();
out:
return cred; return cred;
} }
@ -998,7 +969,9 @@ static int nfs4_copy_lock_stateid(nfs4_stateid *dst,
fl_pid = lockowner->l_pid; fl_pid = lockowner->l_pid;
spin_lock(&state->state_lock); spin_lock(&state->state_lock);
lsp = __nfs4_find_lock_state(state, fl_owner, fl_pid, NFS4_ANY_LOCK_TYPE); lsp = __nfs4_find_lock_state(state, fl_owner, fl_pid, NFS4_ANY_LOCK_TYPE);
if (lsp != NULL && test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) != 0) { 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) {
nfs4_stateid_copy(dst, &lsp->ls_stateid); nfs4_stateid_copy(dst, &lsp->ls_stateid);
ret = 0; ret = 0;
smp_rmb(); smp_rmb();
@ -1038,11 +1011,17 @@ static int nfs4_copy_open_stateid(nfs4_stateid *dst, struct nfs4_state *state)
int nfs4_select_rw_stateid(nfs4_stateid *dst, struct nfs4_state *state, int nfs4_select_rw_stateid(nfs4_stateid *dst, struct nfs4_state *state,
fmode_t fmode, const struct nfs_lockowner *lockowner) fmode_t fmode, const struct nfs_lockowner *lockowner)
{ {
int ret = 0; int ret = nfs4_copy_lock_stateid(dst, state, lockowner);
if (ret == -EIO)
/* A lost lock - don't even consider delegations */
goto out;
if (nfs4_copy_delegation_stateid(dst, state->inode, fmode)) if (nfs4_copy_delegation_stateid(dst, state->inode, fmode))
goto out; goto out;
ret = nfs4_copy_lock_stateid(dst, state, lockowner);
if (ret != -ENOENT) if (ret != -ENOENT)
/* nfs4_copy_delegation_stateid() didn't over-write
* dst, so it still has the lock stateid which we now
* choose to use.
*/
goto out; goto out;
ret = nfs4_copy_open_stateid(dst, state); ret = nfs4_copy_open_stateid(dst, state);
out: out:
@ -1443,6 +1422,7 @@ restart:
if (status >= 0) { if (status >= 0) {
status = nfs4_reclaim_locks(state, ops); status = nfs4_reclaim_locks(state, ops);
if (status >= 0) { if (status >= 0) {
if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0) {
spin_lock(&state->state_lock); spin_lock(&state->state_lock);
list_for_each_entry(lock, &state->lock_states, ls_locks) { list_for_each_entry(lock, &state->lock_states, ls_locks) {
if (!test_bit(NFS_LOCK_INITIALIZED, &lock->ls_flags)) if (!test_bit(NFS_LOCK_INITIALIZED, &lock->ls_flags))
@ -1451,6 +1431,7 @@ restart:
"failed!\n", __func__); "failed!\n", __func__);
} }
spin_unlock(&state->state_lock); spin_unlock(&state->state_lock);
}
nfs4_put_open_state(state); nfs4_put_open_state(state);
spin_lock(&sp->so_lock); spin_lock(&sp->so_lock);
goto restart; goto restart;
@ -1618,7 +1599,7 @@ static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp)
if (!nfs4_state_clear_reclaim_reboot(clp)) if (!nfs4_state_clear_reclaim_reboot(clp))
return; return;
ops = clp->cl_mvops->reboot_recovery_ops; ops = clp->cl_mvops->reboot_recovery_ops;
cred = ops->get_clid_cred(clp); cred = nfs4_get_clid_cred(clp);
nfs4_reclaim_complete(clp, ops, cred); nfs4_reclaim_complete(clp, ops, cred);
put_rpccred(cred); put_rpccred(cred);
} }
@ -1732,7 +1713,7 @@ static int nfs4_check_lease(struct nfs_client *clp)
cred = ops->get_state_renewal_cred_locked(clp); cred = ops->get_state_renewal_cred_locked(clp);
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
if (cred == NULL) { if (cred == NULL) {
cred = nfs4_get_setclientid_cred(clp); cred = nfs4_get_clid_cred(clp);
status = -ENOKEY; status = -ENOKEY;
if (cred == NULL) if (cred == NULL)
goto out; goto out;
@ -1804,7 +1785,7 @@ static int nfs4_establish_lease(struct nfs_client *clp)
clp->cl_mvops->reboot_recovery_ops; clp->cl_mvops->reboot_recovery_ops;
int status; int status;
cred = ops->get_clid_cred(clp); cred = nfs4_get_clid_cred(clp);
if (cred == NULL) if (cred == NULL)
return -ENOENT; return -ENOENT;
status = ops->establish_clid(clp, cred); status = ops->establish_clid(clp, cred);
@ -1878,7 +1859,7 @@ int nfs4_discover_server_trunking(struct nfs_client *clp,
mutex_lock(&nfs_clid_init_mutex); mutex_lock(&nfs_clid_init_mutex);
again: again:
status = -ENOENT; status = -ENOENT;
cred = ops->get_clid_cred(clp); cred = nfs4_get_clid_cred(clp);
if (cred == NULL) if (cred == NULL)
goto out_unlock; goto out_unlock;
@ -1896,7 +1877,11 @@ again:
__func__, status); __func__, status);
goto again; goto again;
case -EACCES: case -EACCES:
if (i++) if (i++ == 0) {
nfs4_root_machine_cred(clp);
goto again;
}
if (i > 2)
break; break;
case -NFS4ERR_CLID_INUSE: case -NFS4ERR_CLID_INUSE:
case -NFS4ERR_WRONGSEC: case -NFS4ERR_WRONGSEC:
@ -2052,7 +2037,7 @@ static int nfs4_reset_session(struct nfs_client *clp)
if (!nfs4_has_session(clp)) if (!nfs4_has_session(clp))
return 0; return 0;
nfs4_begin_drain_session(clp); nfs4_begin_drain_session(clp);
cred = nfs4_get_exchange_id_cred(clp); cred = nfs4_get_clid_cred(clp);
status = nfs4_proc_destroy_session(clp->cl_session, cred); status = nfs4_proc_destroy_session(clp->cl_session, cred);
switch (status) { switch (status) {
case 0: case 0:
@ -2095,7 +2080,7 @@ static int nfs4_bind_conn_to_session(struct nfs_client *clp)
if (!nfs4_has_session(clp)) if (!nfs4_has_session(clp))
return 0; return 0;
nfs4_begin_drain_session(clp); nfs4_begin_drain_session(clp);
cred = nfs4_get_exchange_id_cred(clp); cred = nfs4_get_clid_cred(clp);
ret = nfs4_proc_bind_conn_to_session(clp, cred); ret = nfs4_proc_bind_conn_to_session(clp, cred);
if (cred) if (cred)
put_rpccred(cred); put_rpccred(cred);
@ -2116,7 +2101,6 @@ static int nfs4_bind_conn_to_session(struct nfs_client *clp)
} }
#else /* CONFIG_NFS_V4_1 */ #else /* CONFIG_NFS_V4_1 */
static int nfs4_reset_session(struct nfs_client *clp) { return 0; } static int nfs4_reset_session(struct nfs_client *clp) { return 0; }
static int nfs4_end_drain_session(struct nfs_client *clp) { return 0; }
static int nfs4_bind_conn_to_session(struct nfs_client *clp) static int nfs4_bind_conn_to_session(struct nfs_client *clp)
{ {

View File

@ -253,8 +253,6 @@ struct dentry *nfs4_try_mount(int flags, const char *dev_name,
dfprintk(MOUNT, "--> nfs4_try_mount()\n"); dfprintk(MOUNT, "--> nfs4_try_mount()\n");
if (data->auth_flavors[0] == RPC_AUTH_MAXFLAVOR)
data->auth_flavors[0] = RPC_AUTH_UNIX;
export_path = data->nfs_server.export_path; export_path = data->nfs_server.export_path;
data->nfs_server.export_path = "/"; data->nfs_server.export_path = "/";
root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, mount_info, root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, mount_info,

17
fs/nfs/nfs4trace.c Normal file
View File

@ -0,0 +1,17 @@
/*
* Copyright (c) 2013 Trond Myklebust <Trond.Myklebust@netapp.com>
*/
#include <linux/nfs_fs.h>
#include "nfs4_fs.h"
#include "internal.h"
#include "nfs4session.h"
#include "callback.h"
#define CREATE_TRACE_POINTS
#include "nfs4trace.h"
#ifdef CONFIG_NFS_V4_1
EXPORT_TRACEPOINT_SYMBOL_GPL(nfs4_pnfs_read);
EXPORT_TRACEPOINT_SYMBOL_GPL(nfs4_pnfs_write);
EXPORT_TRACEPOINT_SYMBOL_GPL(nfs4_pnfs_commit_ds);
#endif

1148
fs/nfs/nfs4trace.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -294,7 +294,9 @@ static int nfs4_stat_to_errno(int);
XDR_QUADLEN(NFS4_EXCHANGE_ID_LEN) + \ XDR_QUADLEN(NFS4_EXCHANGE_ID_LEN) + \
1 /* flags */ + \ 1 /* flags */ + \
1 /* spa_how */ + \ 1 /* spa_how */ + \
0 /* SP4_NONE (for now) */ + \ /* max is SP4_MACH_CRED (for now) */ + \
1 + NFS4_OP_MAP_NUM_WORDS + \
1 + NFS4_OP_MAP_NUM_WORDS + \
1 /* implementation id array of size 1 */ + \ 1 /* implementation id array of size 1 */ + \
1 /* nii_domain */ + \ 1 /* nii_domain */ + \
XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + \ XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + \
@ -306,7 +308,9 @@ static int nfs4_stat_to_errno(int);
1 /* eir_sequenceid */ + \ 1 /* eir_sequenceid */ + \
1 /* eir_flags */ + \ 1 /* eir_flags */ + \
1 /* spr_how */ + \ 1 /* spr_how */ + \
0 /* SP4_NONE (for now) */ + \ /* max is SP4_MACH_CRED (for now) */ + \
1 + NFS4_OP_MAP_NUM_WORDS + \
1 + NFS4_OP_MAP_NUM_WORDS + \
2 /* eir_server_owner.so_minor_id */ + \ 2 /* eir_server_owner.so_minor_id */ + \
/* eir_server_owner.so_major_id<> */ \ /* eir_server_owner.so_major_id<> */ \
XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1 + \ XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1 + \
@ -997,12 +1001,10 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
int owner_namelen = 0; int owner_namelen = 0;
int owner_grouplen = 0; int owner_grouplen = 0;
__be32 *p; __be32 *p;
__be32 *q; unsigned i;
int len; uint32_t len = 0;
uint32_t bmval_len = 2; uint32_t bmval_len;
uint32_t bmval0 = 0; uint32_t bmval[3] = { 0 };
uint32_t bmval1 = 0;
uint32_t bmval2 = 0;
/* /*
* We reserve enough space to write the entire attribute buffer at once. * We reserve enough space to write the entire attribute buffer at once.
@ -1011,13 +1013,14 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
* = 40 bytes, plus any contribution from variable-length fields * = 40 bytes, plus any contribution from variable-length fields
* such as owner/group. * such as owner/group.
*/ */
len = 8; if (iap->ia_valid & ATTR_SIZE) {
bmval[0] |= FATTR4_WORD0_SIZE;
/* Sigh */
if (iap->ia_valid & ATTR_SIZE)
len += 8; len += 8;
if (iap->ia_valid & ATTR_MODE) }
if (iap->ia_valid & ATTR_MODE) {
bmval[1] |= FATTR4_WORD1_MODE;
len += 4; len += 4;
}
if (iap->ia_valid & ATTR_UID) { if (iap->ia_valid & ATTR_UID) {
owner_namelen = nfs_map_uid_to_name(server, iap->ia_uid, owner_name, IDMAP_NAMESZ); owner_namelen = nfs_map_uid_to_name(server, iap->ia_uid, owner_name, IDMAP_NAMESZ);
if (owner_namelen < 0) { if (owner_namelen < 0) {
@ -1028,6 +1031,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
owner_namelen = sizeof("nobody") - 1; owner_namelen = sizeof("nobody") - 1;
/* goto out; */ /* goto out; */
} }
bmval[1] |= FATTR4_WORD1_OWNER;
len += 4 + (XDR_QUADLEN(owner_namelen) << 2); len += 4 + (XDR_QUADLEN(owner_namelen) << 2);
} }
if (iap->ia_valid & ATTR_GID) { if (iap->ia_valid & ATTR_GID) {
@ -1039,92 +1043,73 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
owner_grouplen = sizeof("nobody") - 1; owner_grouplen = sizeof("nobody") - 1;
/* goto out; */ /* goto out; */
} }
bmval[1] |= FATTR4_WORD1_OWNER_GROUP;
len += 4 + (XDR_QUADLEN(owner_grouplen) << 2); len += 4 + (XDR_QUADLEN(owner_grouplen) << 2);
} }
if (iap->ia_valid & ATTR_ATIME_SET) if (iap->ia_valid & ATTR_ATIME_SET) {
bmval[1] |= FATTR4_WORD1_TIME_ACCESS_SET;
len += 16; len += 16;
else if (iap->ia_valid & ATTR_ATIME) } else if (iap->ia_valid & ATTR_ATIME) {
bmval[1] |= FATTR4_WORD1_TIME_ACCESS_SET;
len += 4; len += 4;
if (iap->ia_valid & ATTR_MTIME_SET) }
if (iap->ia_valid & ATTR_MTIME_SET) {
bmval[1] |= FATTR4_WORD1_TIME_MODIFY_SET;
len += 16; len += 16;
else if (iap->ia_valid & ATTR_MTIME) } else if (iap->ia_valid & ATTR_MTIME) {
bmval[1] |= FATTR4_WORD1_TIME_MODIFY_SET;
len += 4; len += 4;
}
if (label) { if (label) {
len += 4 + 4 + 4 + (XDR_QUADLEN(label->len) << 2); len += 4 + 4 + 4 + (XDR_QUADLEN(label->len) << 2);
bmval[2] |= FATTR4_WORD2_SECURITY_LABEL;
}
if (bmval[2] != 0)
bmval_len = 3; bmval_len = 3;
} else if (bmval[1] != 0)
bmval_len = 2;
else
bmval_len = 1;
len += bmval_len << 2; p = reserve_space(xdr, 4 + (bmval_len << 2) + 4 + len);
p = reserve_space(xdr, len);
/*
* We write the bitmap length now, but leave the bitmap and the attribute
* buffer length to be backfilled at the end of this routine.
*/
*p++ = cpu_to_be32(bmval_len); *p++ = cpu_to_be32(bmval_len);
q = p; for (i = 0; i < bmval_len; i++)
/* Skip bitmap entries + attrlen */ *p++ = cpu_to_be32(bmval[i]);
p += bmval_len + 1; *p++ = cpu_to_be32(len);
if (iap->ia_valid & ATTR_SIZE) { if (bmval[0] & FATTR4_WORD0_SIZE)
bmval0 |= FATTR4_WORD0_SIZE;
p = xdr_encode_hyper(p, iap->ia_size); p = xdr_encode_hyper(p, iap->ia_size);
} if (bmval[1] & FATTR4_WORD1_MODE)
if (iap->ia_valid & ATTR_MODE) {
bmval1 |= FATTR4_WORD1_MODE;
*p++ = cpu_to_be32(iap->ia_mode & S_IALLUGO); *p++ = cpu_to_be32(iap->ia_mode & S_IALLUGO);
} if (bmval[1] & FATTR4_WORD1_OWNER)
if (iap->ia_valid & ATTR_UID) {
bmval1 |= FATTR4_WORD1_OWNER;
p = xdr_encode_opaque(p, owner_name, owner_namelen); p = xdr_encode_opaque(p, owner_name, owner_namelen);
} if (bmval[1] & FATTR4_WORD1_OWNER_GROUP)
if (iap->ia_valid & ATTR_GID) {
bmval1 |= FATTR4_WORD1_OWNER_GROUP;
p = xdr_encode_opaque(p, owner_group, owner_grouplen); p = xdr_encode_opaque(p, owner_group, owner_grouplen);
} if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
if (iap->ia_valid & ATTR_ATIME_SET) { if (iap->ia_valid & ATTR_ATIME_SET) {
bmval1 |= FATTR4_WORD1_TIME_ACCESS_SET;
*p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME); *p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
p = xdr_encode_hyper(p, (s64)iap->ia_atime.tv_sec); p = xdr_encode_hyper(p, (s64)iap->ia_atime.tv_sec);
*p++ = cpu_to_be32(iap->ia_atime.tv_nsec); *p++ = cpu_to_be32(iap->ia_atime.tv_nsec);
} } else
else if (iap->ia_valid & ATTR_ATIME) {
bmval1 |= FATTR4_WORD1_TIME_ACCESS_SET;
*p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME); *p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME);
} }
if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
if (iap->ia_valid & ATTR_MTIME_SET) { if (iap->ia_valid & ATTR_MTIME_SET) {
bmval1 |= FATTR4_WORD1_TIME_MODIFY_SET;
*p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME); *p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
p = xdr_encode_hyper(p, (s64)iap->ia_mtime.tv_sec); p = xdr_encode_hyper(p, (s64)iap->ia_mtime.tv_sec);
*p++ = cpu_to_be32(iap->ia_mtime.tv_nsec); *p++ = cpu_to_be32(iap->ia_mtime.tv_nsec);
} } else
else if (iap->ia_valid & ATTR_MTIME) {
bmval1 |= FATTR4_WORD1_TIME_MODIFY_SET;
*p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME); *p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME);
} }
if (label) { if (bmval[2] & FATTR4_WORD2_SECURITY_LABEL) {
bmval2 |= FATTR4_WORD2_SECURITY_LABEL;
*p++ = cpu_to_be32(label->lfs); *p++ = cpu_to_be32(label->lfs);
*p++ = cpu_to_be32(label->pi); *p++ = cpu_to_be32(label->pi);
*p++ = cpu_to_be32(label->len); *p++ = cpu_to_be32(label->len);
p = xdr_encode_opaque_fixed(p, label->label, label->len); p = xdr_encode_opaque_fixed(p, label->label, label->len);
} }
/*
* Now we backfill the bitmap and the attribute buffer length.
*/
if (len != ((char *)p - (char *)q) + 4) {
printk(KERN_ERR "NFS: Attr length error, %u != %Zu\n",
len, ((char *)p - (char *)q) + 4);
BUG();
}
*q++ = htonl(bmval0);
*q++ = htonl(bmval1);
if (bmval_len == 3)
*q++ = htonl(bmval2);
len = (char *)p - (char *)(q + 1);
*q = htonl(len);
/* out: */ /* out: */
} }
@ -1745,6 +1730,14 @@ static void encode_bind_conn_to_session(struct xdr_stream *xdr,
*p = 0; /* use_conn_in_rdma_mode = False */ *p = 0; /* use_conn_in_rdma_mode = False */
} }
static void encode_op_map(struct xdr_stream *xdr, struct nfs4_op_map *op_map)
{
unsigned int i;
encode_uint32(xdr, NFS4_OP_MAP_NUM_WORDS);
for (i = 0; i < NFS4_OP_MAP_NUM_WORDS; i++)
encode_uint32(xdr, op_map->u.words[i]);
}
static void encode_exchange_id(struct xdr_stream *xdr, static void encode_exchange_id(struct xdr_stream *xdr,
struct nfs41_exchange_id_args *args, struct nfs41_exchange_id_args *args,
struct compound_hdr *hdr) struct compound_hdr *hdr)
@ -1758,9 +1751,20 @@ static void encode_exchange_id(struct xdr_stream *xdr,
encode_string(xdr, args->id_len, args->id); encode_string(xdr, args->id_len, args->id);
p = reserve_space(xdr, 12); encode_uint32(xdr, args->flags);
*p++ = cpu_to_be32(args->flags); encode_uint32(xdr, args->state_protect.how);
*p++ = cpu_to_be32(0); /* zero length state_protect4_a */
switch (args->state_protect.how) {
case SP4_NONE:
break;
case SP4_MACH_CRED:
encode_op_map(xdr, &args->state_protect.enforce);
encode_op_map(xdr, &args->state_protect.allow);
break;
default:
WARN_ON_ONCE(1);
break;
}
if (send_implementation_id && if (send_implementation_id &&
sizeof(CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN) > 1 && sizeof(CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN) > 1 &&
@ -1771,7 +1775,7 @@ static void encode_exchange_id(struct xdr_stream *xdr,
utsname()->version, utsname()->machine); utsname()->version, utsname()->machine);
if (len > 0) { if (len > 0) {
*p = cpu_to_be32(1); /* implementation id array length=1 */ encode_uint32(xdr, 1); /* implementation id array length=1 */
encode_string(xdr, encode_string(xdr,
sizeof(CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN) - 1, sizeof(CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN) - 1,
@ -1782,7 +1786,7 @@ static void encode_exchange_id(struct xdr_stream *xdr,
p = xdr_encode_hyper(p, 0); p = xdr_encode_hyper(p, 0);
*p = cpu_to_be32(0); *p = cpu_to_be32(0);
} else } else
*p = cpu_to_be32(0); /* implementation id array length=0 */ encode_uint32(xdr, 0); /* implementation id array length=0 */
} }
static void encode_create_session(struct xdr_stream *xdr, static void encode_create_session(struct xdr_stream *xdr,
@ -1835,7 +1839,7 @@ static void encode_create_session(struct xdr_stream *xdr,
*p++ = cpu_to_be32(RPC_AUTH_UNIX); /* auth_sys */ *p++ = cpu_to_be32(RPC_AUTH_UNIX); /* auth_sys */
/* authsys_parms rfc1831 */ /* authsys_parms rfc1831 */
*p++ = (__be32)nn->boot_time.tv_nsec; /* stamp */ *p++ = cpu_to_be32(nn->boot_time.tv_nsec); /* stamp */
p = xdr_encode_opaque(p, machine_name, len); p = xdr_encode_opaque(p, machine_name, len);
*p++ = cpu_to_be32(0); /* UID */ *p++ = cpu_to_be32(0); /* UID */
*p++ = cpu_to_be32(0); /* GID */ *p++ = cpu_to_be32(0); /* GID */
@ -1877,11 +1881,10 @@ static void encode_sequence(struct xdr_stream *xdr,
struct nfs4_slot *slot = args->sa_slot; struct nfs4_slot *slot = args->sa_slot;
__be32 *p; __be32 *p;
if (slot == NULL)
return;
tp = slot->table; tp = slot->table;
session = tp->session; session = tp->session;
if (!session)
return;
encode_op_hdr(xdr, OP_SEQUENCE, decode_sequence_maxsz, hdr); encode_op_hdr(xdr, OP_SEQUENCE, decode_sequence_maxsz, hdr);
@ -2062,9 +2065,9 @@ static void encode_free_stateid(struct xdr_stream *xdr,
static u32 nfs4_xdr_minorversion(const struct nfs4_sequence_args *args) static u32 nfs4_xdr_minorversion(const struct nfs4_sequence_args *args)
{ {
#if defined(CONFIG_NFS_V4_1) #if defined(CONFIG_NFS_V4_1)
struct nfs4_session *session = args->sa_slot->table->session;
if (args->sa_slot) if (session)
return args->sa_slot->table->session->clp->cl_mvops->minor_version; return session->clp->cl_mvops->minor_version;
#endif /* CONFIG_NFS_V4_1 */ #endif /* CONFIG_NFS_V4_1 */
return 0; return 0;
} }
@ -4649,7 +4652,7 @@ static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr,
static int decode_first_pnfs_layout_type(struct xdr_stream *xdr, static int decode_first_pnfs_layout_type(struct xdr_stream *xdr,
uint32_t *layouttype) uint32_t *layouttype)
{ {
uint32_t *p; __be32 *p;
int num; int num;
p = xdr_inline_decode(xdr, 4); p = xdr_inline_decode(xdr, 4);
@ -5394,6 +5397,23 @@ static int decode_secinfo_no_name(struct xdr_stream *xdr, struct nfs4_secinfo_re
return decode_secinfo_common(xdr, res); return decode_secinfo_common(xdr, res);
} }
static int decode_op_map(struct xdr_stream *xdr, struct nfs4_op_map *op_map)
{
__be32 *p;
uint32_t bitmap_words;
unsigned int i;
p = xdr_inline_decode(xdr, 4);
bitmap_words = be32_to_cpup(p++);
if (bitmap_words > NFS4_OP_MAP_NUM_WORDS)
return -EIO;
p = xdr_inline_decode(xdr, 4 * bitmap_words);
for (i = 0; i < bitmap_words; i++)
op_map->u.words[i] = be32_to_cpup(p++);
return 0;
}
static int decode_exchange_id(struct xdr_stream *xdr, static int decode_exchange_id(struct xdr_stream *xdr,
struct nfs41_exchange_id_res *res) struct nfs41_exchange_id_res *res)
{ {
@ -5417,10 +5437,22 @@ static int decode_exchange_id(struct xdr_stream *xdr,
res->seqid = be32_to_cpup(p++); res->seqid = be32_to_cpup(p++);
res->flags = be32_to_cpup(p++); res->flags = be32_to_cpup(p++);
/* We ask for SP4_NONE */ res->state_protect.how = be32_to_cpup(p);
dummy = be32_to_cpup(p); switch (res->state_protect.how) {
if (dummy != SP4_NONE) case SP4_NONE:
break;
case SP4_MACH_CRED:
status = decode_op_map(xdr, &res->state_protect.enforce);
if (status)
return status;
status = decode_op_map(xdr, &res->state_protect.allow);
if (status)
return status;
break;
default:
WARN_ON_ONCE(1);
return -EIO; return -EIO;
}
/* server_owner4.so_minor_id */ /* server_owner4.so_minor_id */
p = xdr_inline_decode(xdr, 8); p = xdr_inline_decode(xdr, 8);
@ -5614,6 +5646,8 @@ static int decode_sequence(struct xdr_stream *xdr,
if (res->sr_slot == NULL) if (res->sr_slot == NULL)
return 0; return 0;
if (!res->sr_slot->table->session)
return 0;
status = decode_op_hdr(xdr, OP_SEQUENCE); status = decode_op_hdr(xdr, OP_SEQUENCE);
if (!status) if (!status)

9
fs/nfs/nfstrace.c Normal file
View File

@ -0,0 +1,9 @@
/*
* Copyright (c) 2013 Trond Myklebust <Trond.Myklebust@netapp.com>
*/
#include <linux/nfs_fs.h>
#include <linux/namei.h>
#include "internal.h"
#define CREATE_TRACE_POINTS
#include "nfstrace.h"

729
fs/nfs/nfstrace.h Normal file
View File

@ -0,0 +1,729 @@
/*
* Copyright (c) 2013 Trond Myklebust <Trond.Myklebust@netapp.com>
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM nfs
#if !defined(_TRACE_NFS_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_NFS_H
#include <linux/tracepoint.h>
#define nfs_show_file_type(ftype) \
__print_symbolic(ftype, \
{ DT_UNKNOWN, "UNKNOWN" }, \
{ DT_FIFO, "FIFO" }, \
{ DT_CHR, "CHR" }, \
{ DT_DIR, "DIR" }, \
{ DT_BLK, "BLK" }, \
{ DT_REG, "REG" }, \
{ DT_LNK, "LNK" }, \
{ DT_SOCK, "SOCK" }, \
{ DT_WHT, "WHT" })
#define nfs_show_cache_validity(v) \
__print_flags(v, "|", \
{ NFS_INO_INVALID_ATTR, "INVALID_ATTR" }, \
{ NFS_INO_INVALID_DATA, "INVALID_DATA" }, \
{ NFS_INO_INVALID_ATIME, "INVALID_ATIME" }, \
{ NFS_INO_INVALID_ACCESS, "INVALID_ACCESS" }, \
{ NFS_INO_INVALID_ACL, "INVALID_ACL" }, \
{ NFS_INO_REVAL_PAGECACHE, "REVAL_PAGECACHE" }, \
{ NFS_INO_REVAL_FORCED, "REVAL_FORCED" }, \
{ NFS_INO_INVALID_LABEL, "INVALID_LABEL" })
#define nfs_show_nfsi_flags(v) \
__print_flags(v, "|", \
{ 1 << NFS_INO_ADVISE_RDPLUS, "ADVISE_RDPLUS" }, \
{ 1 << NFS_INO_STALE, "STALE" }, \
{ 1 << NFS_INO_FLUSHING, "FLUSHING" }, \
{ 1 << NFS_INO_FSCACHE, "FSCACHE" }, \
{ 1 << NFS_INO_COMMIT, "COMMIT" }, \
{ 1 << NFS_INO_LAYOUTCOMMIT, "NEED_LAYOUTCOMMIT" }, \
{ 1 << NFS_INO_LAYOUTCOMMITTING, "LAYOUTCOMMIT" })
DECLARE_EVENT_CLASS(nfs_inode_event,
TP_PROTO(
const struct inode *inode
),
TP_ARGS(inode),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(u32, fhandle)
__field(u64, fileid)
__field(u64, version)
),
TP_fast_assign(
const struct nfs_inode *nfsi = NFS_I(inode);
__entry->dev = inode->i_sb->s_dev;
__entry->fileid = nfsi->fileid;
__entry->fhandle = nfs_fhandle_hash(&nfsi->fh);
__entry->version = inode->i_version;
),
TP_printk(
"fileid=%02x:%02x:%llu fhandle=0x%08x version=%llu ",
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid,
__entry->fhandle,
(unsigned long long)__entry->version
)
);
DECLARE_EVENT_CLASS(nfs_inode_event_done,
TP_PROTO(
const struct inode *inode,
int error
),
TP_ARGS(inode, error),
TP_STRUCT__entry(
__field(int, error)
__field(dev_t, dev)
__field(u32, fhandle)
__field(unsigned char, type)
__field(u64, fileid)
__field(u64, version)
__field(loff_t, size)
__field(unsigned long, nfsi_flags)
__field(unsigned long, cache_validity)
),
TP_fast_assign(
const struct nfs_inode *nfsi = NFS_I(inode);
__entry->error = error;
__entry->dev = inode->i_sb->s_dev;
__entry->fileid = nfsi->fileid;
__entry->fhandle = nfs_fhandle_hash(&nfsi->fh);
__entry->type = nfs_umode_to_dtype(inode->i_mode);
__entry->version = inode->i_version;
__entry->size = i_size_read(inode);
__entry->nfsi_flags = nfsi->flags;
__entry->cache_validity = nfsi->cache_validity;
),
TP_printk(
"error=%d fileid=%02x:%02x:%llu fhandle=0x%08x "
"type=%u (%s) version=%llu size=%lld "
"cache_validity=%lu (%s) nfs_flags=%ld (%s)",
__entry->error,
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid,
__entry->fhandle,
__entry->type,
nfs_show_file_type(__entry->type),
(unsigned long long)__entry->version,
(long long)__entry->size,
__entry->cache_validity,
nfs_show_cache_validity(__entry->cache_validity),
__entry->nfsi_flags,
nfs_show_nfsi_flags(__entry->nfsi_flags)
)
);
#define DEFINE_NFS_INODE_EVENT(name) \
DEFINE_EVENT(nfs_inode_event, name, \
TP_PROTO( \
const struct inode *inode \
), \
TP_ARGS(inode))
#define DEFINE_NFS_INODE_EVENT_DONE(name) \
DEFINE_EVENT(nfs_inode_event_done, name, \
TP_PROTO( \
const struct inode *inode, \
int error \
), \
TP_ARGS(inode, error))
DEFINE_NFS_INODE_EVENT(nfs_refresh_inode_enter);
DEFINE_NFS_INODE_EVENT_DONE(nfs_refresh_inode_exit);
DEFINE_NFS_INODE_EVENT(nfs_revalidate_inode_enter);
DEFINE_NFS_INODE_EVENT_DONE(nfs_revalidate_inode_exit);
DEFINE_NFS_INODE_EVENT(nfs_invalidate_mapping_enter);
DEFINE_NFS_INODE_EVENT_DONE(nfs_invalidate_mapping_exit);
DEFINE_NFS_INODE_EVENT(nfs_getattr_enter);
DEFINE_NFS_INODE_EVENT_DONE(nfs_getattr_exit);
DEFINE_NFS_INODE_EVENT(nfs_setattr_enter);
DEFINE_NFS_INODE_EVENT_DONE(nfs_setattr_exit);
DEFINE_NFS_INODE_EVENT(nfs_writeback_page_enter);
DEFINE_NFS_INODE_EVENT_DONE(nfs_writeback_page_exit);
DEFINE_NFS_INODE_EVENT(nfs_writeback_inode_enter);
DEFINE_NFS_INODE_EVENT_DONE(nfs_writeback_inode_exit);
DEFINE_NFS_INODE_EVENT(nfs_fsync_enter);
DEFINE_NFS_INODE_EVENT_DONE(nfs_fsync_exit);
DEFINE_NFS_INODE_EVENT(nfs_access_enter);
DEFINE_NFS_INODE_EVENT_DONE(nfs_access_exit);
#define show_lookup_flags(flags) \
__print_flags((unsigned long)flags, "|", \
{ LOOKUP_AUTOMOUNT, "AUTOMOUNT" }, \
{ LOOKUP_DIRECTORY, "DIRECTORY" }, \
{ LOOKUP_OPEN, "OPEN" }, \
{ LOOKUP_CREATE, "CREATE" }, \
{ LOOKUP_EXCL, "EXCL" })
DECLARE_EVENT_CLASS(nfs_lookup_event,
TP_PROTO(
const struct inode *dir,
const struct dentry *dentry,
unsigned int flags
),
TP_ARGS(dir, dentry, flags),
TP_STRUCT__entry(
__field(unsigned int, flags)
__field(dev_t, dev)
__field(u64, dir)
__string(name, dentry->d_name.name)
),
TP_fast_assign(
__entry->dev = dir->i_sb->s_dev;
__entry->dir = NFS_FILEID(dir);
__entry->flags = flags;
__assign_str(name, dentry->d_name.name);
),
TP_printk(
"flags=%u (%s) name=%02x:%02x:%llu/%s",
__entry->flags,
show_lookup_flags(__entry->flags),
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->dir,
__get_str(name)
)
);
#define DEFINE_NFS_LOOKUP_EVENT(name) \
DEFINE_EVENT(nfs_lookup_event, name, \
TP_PROTO( \
const struct inode *dir, \
const struct dentry *dentry, \
unsigned int flags \
), \
TP_ARGS(dir, dentry, flags))
DECLARE_EVENT_CLASS(nfs_lookup_event_done,
TP_PROTO(
const struct inode *dir,
const struct dentry *dentry,
unsigned int flags,
int error
),
TP_ARGS(dir, dentry, flags, error),
TP_STRUCT__entry(
__field(int, error)
__field(unsigned int, flags)
__field(dev_t, dev)
__field(u64, dir)
__string(name, dentry->d_name.name)
),
TP_fast_assign(
__entry->dev = dir->i_sb->s_dev;
__entry->dir = NFS_FILEID(dir);
__entry->error = error;
__entry->flags = flags;
__assign_str(name, dentry->d_name.name);
),
TP_printk(
"error=%d flags=%u (%s) name=%02x:%02x:%llu/%s",
__entry->error,
__entry->flags,
show_lookup_flags(__entry->flags),
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->dir,
__get_str(name)
)
);
#define DEFINE_NFS_LOOKUP_EVENT_DONE(name) \
DEFINE_EVENT(nfs_lookup_event_done, name, \
TP_PROTO( \
const struct inode *dir, \
const struct dentry *dentry, \
unsigned int flags, \
int error \
), \
TP_ARGS(dir, dentry, flags, error))
DEFINE_NFS_LOOKUP_EVENT(nfs_lookup_enter);
DEFINE_NFS_LOOKUP_EVENT_DONE(nfs_lookup_exit);
DEFINE_NFS_LOOKUP_EVENT(nfs_lookup_revalidate_enter);
DEFINE_NFS_LOOKUP_EVENT_DONE(nfs_lookup_revalidate_exit);
#define show_open_flags(flags) \
__print_flags((unsigned long)flags, "|", \
{ O_CREAT, "O_CREAT" }, \
{ O_EXCL, "O_EXCL" }, \
{ O_TRUNC, "O_TRUNC" }, \
{ O_APPEND, "O_APPEND" }, \
{ O_DSYNC, "O_DSYNC" }, \
{ O_DIRECT, "O_DIRECT" }, \
{ O_DIRECTORY, "O_DIRECTORY" })
#define show_fmode_flags(mode) \
__print_flags(mode, "|", \
{ ((__force unsigned long)FMODE_READ), "READ" }, \
{ ((__force unsigned long)FMODE_WRITE), "WRITE" }, \
{ ((__force unsigned long)FMODE_EXEC), "EXEC" })
TRACE_EVENT(nfs_atomic_open_enter,
TP_PROTO(
const struct inode *dir,
const struct nfs_open_context *ctx,
unsigned int flags
),
TP_ARGS(dir, ctx, flags),
TP_STRUCT__entry(
__field(unsigned int, flags)
__field(unsigned int, fmode)
__field(dev_t, dev)
__field(u64, dir)
__string(name, ctx->dentry->d_name.name)
),
TP_fast_assign(
__entry->dev = dir->i_sb->s_dev;
__entry->dir = NFS_FILEID(dir);
__entry->flags = flags;
__entry->fmode = (__force unsigned int)ctx->mode;
__assign_str(name, ctx->dentry->d_name.name);
),
TP_printk(
"flags=%u (%s) fmode=%s name=%02x:%02x:%llu/%s",
__entry->flags,
show_open_flags(__entry->flags),
show_fmode_flags(__entry->fmode),
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->dir,
__get_str(name)
)
);
TRACE_EVENT(nfs_atomic_open_exit,
TP_PROTO(
const struct inode *dir,
const struct nfs_open_context *ctx,
unsigned int flags,
int error
),
TP_ARGS(dir, ctx, flags, error),
TP_STRUCT__entry(
__field(int, error)
__field(unsigned int, flags)
__field(unsigned int, fmode)
__field(dev_t, dev)
__field(u64, dir)
__string(name, ctx->dentry->d_name.name)
),
TP_fast_assign(
__entry->error = error;
__entry->dev = dir->i_sb->s_dev;
__entry->dir = NFS_FILEID(dir);
__entry->flags = flags;
__entry->fmode = (__force unsigned int)ctx->mode;
__assign_str(name, ctx->dentry->d_name.name);
),
TP_printk(
"error=%d flags=%u (%s) fmode=%s "
"name=%02x:%02x:%llu/%s",
__entry->error,
__entry->flags,
show_open_flags(__entry->flags),
show_fmode_flags(__entry->fmode),
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->dir,
__get_str(name)
)
);
TRACE_EVENT(nfs_create_enter,
TP_PROTO(
const struct inode *dir,
const struct dentry *dentry,
unsigned int flags
),
TP_ARGS(dir, dentry, flags),
TP_STRUCT__entry(
__field(unsigned int, flags)
__field(dev_t, dev)
__field(u64, dir)
__string(name, dentry->d_name.name)
),
TP_fast_assign(
__entry->dev = dir->i_sb->s_dev;
__entry->dir = NFS_FILEID(dir);
__entry->flags = flags;
__assign_str(name, dentry->d_name.name);
),
TP_printk(
"flags=%u (%s) name=%02x:%02x:%llu/%s",
__entry->flags,
show_open_flags(__entry->flags),
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->dir,
__get_str(name)
)
);
TRACE_EVENT(nfs_create_exit,
TP_PROTO(
const struct inode *dir,
const struct dentry *dentry,
unsigned int flags,
int error
),
TP_ARGS(dir, dentry, flags, error),
TP_STRUCT__entry(
__field(int, error)
__field(unsigned int, flags)
__field(dev_t, dev)
__field(u64, dir)
__string(name, dentry->d_name.name)
),
TP_fast_assign(
__entry->error = error;
__entry->dev = dir->i_sb->s_dev;
__entry->dir = NFS_FILEID(dir);
__entry->flags = flags;
__assign_str(name, dentry->d_name.name);
),
TP_printk(
"error=%d flags=%u (%s) name=%02x:%02x:%llu/%s",
__entry->error,
__entry->flags,
show_open_flags(__entry->flags),
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->dir,
__get_str(name)
)
);
DECLARE_EVENT_CLASS(nfs_directory_event,
TP_PROTO(
const struct inode *dir,
const struct dentry *dentry
),
TP_ARGS(dir, dentry),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(u64, dir)
__string(name, dentry->d_name.name)
),
TP_fast_assign(
__entry->dev = dir->i_sb->s_dev;
__entry->dir = NFS_FILEID(dir);
__assign_str(name, dentry->d_name.name);
),
TP_printk(
"name=%02x:%02x:%llu/%s",
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->dir,
__get_str(name)
)
);
#define DEFINE_NFS_DIRECTORY_EVENT(name) \
DEFINE_EVENT(nfs_directory_event, name, \
TP_PROTO( \
const struct inode *dir, \
const struct dentry *dentry \
), \
TP_ARGS(dir, dentry))
DECLARE_EVENT_CLASS(nfs_directory_event_done,
TP_PROTO(
const struct inode *dir,
const struct dentry *dentry,
int error
),
TP_ARGS(dir, dentry, error),
TP_STRUCT__entry(
__field(int, error)
__field(dev_t, dev)
__field(u64, dir)
__string(name, dentry->d_name.name)
),
TP_fast_assign(
__entry->dev = dir->i_sb->s_dev;
__entry->dir = NFS_FILEID(dir);
__entry->error = error;
__assign_str(name, dentry->d_name.name);
),
TP_printk(
"error=%d name=%02x:%02x:%llu/%s",
__entry->error,
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->dir,
__get_str(name)
)
);
#define DEFINE_NFS_DIRECTORY_EVENT_DONE(name) \
DEFINE_EVENT(nfs_directory_event_done, name, \
TP_PROTO( \
const struct inode *dir, \
const struct dentry *dentry, \
int error \
), \
TP_ARGS(dir, dentry, error))
DEFINE_NFS_DIRECTORY_EVENT(nfs_mknod_enter);
DEFINE_NFS_DIRECTORY_EVENT_DONE(nfs_mknod_exit);
DEFINE_NFS_DIRECTORY_EVENT(nfs_mkdir_enter);
DEFINE_NFS_DIRECTORY_EVENT_DONE(nfs_mkdir_exit);
DEFINE_NFS_DIRECTORY_EVENT(nfs_rmdir_enter);
DEFINE_NFS_DIRECTORY_EVENT_DONE(nfs_rmdir_exit);
DEFINE_NFS_DIRECTORY_EVENT(nfs_remove_enter);
DEFINE_NFS_DIRECTORY_EVENT_DONE(nfs_remove_exit);
DEFINE_NFS_DIRECTORY_EVENT(nfs_unlink_enter);
DEFINE_NFS_DIRECTORY_EVENT_DONE(nfs_unlink_exit);
DEFINE_NFS_DIRECTORY_EVENT(nfs_symlink_enter);
DEFINE_NFS_DIRECTORY_EVENT_DONE(nfs_symlink_exit);
TRACE_EVENT(nfs_link_enter,
TP_PROTO(
const struct inode *inode,
const struct inode *dir,
const struct dentry *dentry
),
TP_ARGS(inode, dir, dentry),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(u64, fileid)
__field(u64, dir)
__string(name, dentry->d_name.name)
),
TP_fast_assign(
__entry->dev = inode->i_sb->s_dev;
__entry->fileid = NFS_FILEID(inode);
__entry->dir = NFS_FILEID(dir);
__assign_str(name, dentry->d_name.name);
),
TP_printk(
"fileid=%02x:%02x:%llu name=%02x:%02x:%llu/%s",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->fileid,
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->dir,
__get_str(name)
)
);
TRACE_EVENT(nfs_link_exit,
TP_PROTO(
const struct inode *inode,
const struct inode *dir,
const struct dentry *dentry,
int error
),
TP_ARGS(inode, dir, dentry, error),
TP_STRUCT__entry(
__field(int, error)
__field(dev_t, dev)
__field(u64, fileid)
__field(u64, dir)
__string(name, dentry->d_name.name)
),
TP_fast_assign(
__entry->dev = inode->i_sb->s_dev;
__entry->fileid = NFS_FILEID(inode);
__entry->dir = NFS_FILEID(dir);
__entry->error = error;
__assign_str(name, dentry->d_name.name);
),
TP_printk(
"error=%d fileid=%02x:%02x:%llu name=%02x:%02x:%llu/%s",
__entry->error,
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->fileid,
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->dir,
__get_str(name)
)
);
DECLARE_EVENT_CLASS(nfs_rename_event,
TP_PROTO(
const struct inode *old_dir,
const struct dentry *old_dentry,
const struct inode *new_dir,
const struct dentry *new_dentry
),
TP_ARGS(old_dir, old_dentry, new_dir, new_dentry),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(u64, old_dir)
__field(u64, new_dir)
__string(old_name, old_dentry->d_name.name)
__string(new_name, new_dentry->d_name.name)
),
TP_fast_assign(
__entry->dev = old_dir->i_sb->s_dev;
__entry->old_dir = NFS_FILEID(old_dir);
__entry->new_dir = NFS_FILEID(new_dir);
__assign_str(old_name, old_dentry->d_name.name);
__assign_str(new_name, new_dentry->d_name.name);
),
TP_printk(
"old_name=%02x:%02x:%llu/%s new_name=%02x:%02x:%llu/%s",
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->old_dir,
__get_str(old_name),
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->new_dir,
__get_str(new_name)
)
);
#define DEFINE_NFS_RENAME_EVENT(name) \
DEFINE_EVENT(nfs_rename_event, name, \
TP_PROTO( \
const struct inode *old_dir, \
const struct dentry *old_dentry, \
const struct inode *new_dir, \
const struct dentry *new_dentry \
), \
TP_ARGS(old_dir, old_dentry, new_dir, new_dentry))
DECLARE_EVENT_CLASS(nfs_rename_event_done,
TP_PROTO(
const struct inode *old_dir,
const struct dentry *old_dentry,
const struct inode *new_dir,
const struct dentry *new_dentry,
int error
),
TP_ARGS(old_dir, old_dentry, new_dir, new_dentry, error),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(int, error)
__field(u64, old_dir)
__string(old_name, old_dentry->d_name.name)
__field(u64, new_dir)
__string(new_name, new_dentry->d_name.name)
),
TP_fast_assign(
__entry->dev = old_dir->i_sb->s_dev;
__entry->old_dir = NFS_FILEID(old_dir);
__entry->new_dir = NFS_FILEID(new_dir);
__entry->error = error;
__assign_str(old_name, old_dentry->d_name.name);
__assign_str(new_name, new_dentry->d_name.name);
),
TP_printk(
"error=%d old_name=%02x:%02x:%llu/%s "
"new_name=%02x:%02x:%llu/%s",
__entry->error,
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->old_dir,
__get_str(old_name),
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->new_dir,
__get_str(new_name)
)
);
#define DEFINE_NFS_RENAME_EVENT_DONE(name) \
DEFINE_EVENT(nfs_rename_event_done, name, \
TP_PROTO( \
const struct inode *old_dir, \
const struct dentry *old_dentry, \
const struct inode *new_dir, \
const struct dentry *new_dentry, \
int error \
), \
TP_ARGS(old_dir, old_dentry, new_dir, \
new_dentry, error))
DEFINE_NFS_RENAME_EVENT(nfs_rename_enter);
DEFINE_NFS_RENAME_EVENT_DONE(nfs_rename_exit);
DEFINE_NFS_RENAME_EVENT_DONE(nfs_sillyrename_rename);
TRACE_EVENT(nfs_sillyrename_unlink,
TP_PROTO(
const struct nfs_unlinkdata *data,
int error
),
TP_ARGS(data, error),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(int, error)
__field(u64, dir)
__dynamic_array(char, name, data->args.name.len + 1)
),
TP_fast_assign(
struct inode *dir = data->dir;
size_t len = data->args.name.len;
__entry->dev = dir->i_sb->s_dev;
__entry->dir = NFS_FILEID(dir);
__entry->error = error;
memcpy(__get_dynamic_array(name),
data->args.name.name, len);
((char *)__get_dynamic_array(name))[len] = 0;
),
TP_printk(
"error=%d name=%02x:%02x:%llu/%s",
__entry->error,
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->dir,
__get_str(name)
)
);
#endif /* _TRACE_NFS_H */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#define TRACE_INCLUDE_FILE nfstrace
/* This part must be outside protection */
#include <trace/define_trace.h>

View File

@ -328,6 +328,19 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
} }
EXPORT_SYMBOL_GPL(nfs_pageio_init); EXPORT_SYMBOL_GPL(nfs_pageio_init);
static bool nfs_match_open_context(const struct nfs_open_context *ctx1,
const struct nfs_open_context *ctx2)
{
return ctx1->cred == ctx2->cred && ctx1->state == ctx2->state;
}
static bool nfs_match_lock_context(const struct nfs_lock_context *l1,
const struct nfs_lock_context *l2)
{
return l1->lockowner.l_owner == l2->lockowner.l_owner
&& l1->lockowner.l_pid == l2->lockowner.l_pid;
}
/** /**
* nfs_can_coalesce_requests - test two requests for compatibility * nfs_can_coalesce_requests - test two requests for compatibility
* @prev: pointer to nfs_page * @prev: pointer to nfs_page
@ -343,13 +356,10 @@ static bool nfs_can_coalesce_requests(struct nfs_page *prev,
struct nfs_page *req, struct nfs_page *req,
struct nfs_pageio_descriptor *pgio) struct nfs_pageio_descriptor *pgio)
{ {
if (req->wb_context->cred != prev->wb_context->cred) if (!nfs_match_open_context(req->wb_context, prev->wb_context))
return false; return false;
if (req->wb_lock_context->lockowner.l_owner != prev->wb_lock_context->lockowner.l_owner) if (req->wb_context->dentry->d_inode->i_flock != NULL &&
return false; !nfs_match_lock_context(req->wb_lock_context, prev->wb_lock_context))
if (req->wb_lock_context->lockowner.l_pid != prev->wb_lock_context->lockowner.l_pid)
return false;
if (req->wb_context->state != prev->wb_context->state)
return false; return false;
if (req->wb_pgbase != 0) if (req->wb_pgbase != 0)
return false; return false;

View File

@ -33,6 +33,7 @@
#include "internal.h" #include "internal.h"
#include "pnfs.h" #include "pnfs.h"
#include "iostat.h" #include "iostat.h"
#include "nfs4trace.h"
#define NFSDBG_FACILITY NFSDBG_PNFS #define NFSDBG_FACILITY NFSDBG_PNFS
#define PNFS_LAYOUTGET_RETRY_TIMEOUT (120*HZ) #define PNFS_LAYOUTGET_RETRY_TIMEOUT (120*HZ)
@ -1526,6 +1527,7 @@ void pnfs_ld_write_done(struct nfs_write_data *data)
{ {
struct nfs_pgio_header *hdr = data->header; struct nfs_pgio_header *hdr = data->header;
trace_nfs4_pnfs_write(data, hdr->pnfs_error);
if (!hdr->pnfs_error) { if (!hdr->pnfs_error) {
pnfs_set_layoutcommit(data); pnfs_set_layoutcommit(data);
hdr->mds_ops->rpc_call_done(&data->task, data); hdr->mds_ops->rpc_call_done(&data->task, data);
@ -1680,6 +1682,7 @@ void pnfs_ld_read_done(struct nfs_read_data *data)
{ {
struct nfs_pgio_header *hdr = data->header; struct nfs_pgio_header *hdr = data->header;
trace_nfs4_pnfs_read(data, hdr->pnfs_error);
if (likely(!hdr->pnfs_error)) { if (likely(!hdr->pnfs_error)) {
__nfs4_read_done_cb(data); __nfs4_read_done_cb(data);
hdr->mds_ops->rpc_call_done(&data->task, data); hdr->mds_ops->rpc_call_done(&data->task, data);

View File

@ -623,9 +623,10 @@ static void nfs_proc_read_setup(struct nfs_read_data *data, struct rpc_message *
msg->rpc_proc = &nfs_procedures[NFSPROC_READ]; msg->rpc_proc = &nfs_procedures[NFSPROC_READ];
} }
static void nfs_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data) static int nfs_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data)
{ {
rpc_call_start(task); rpc_call_start(task);
return 0;
} }
static int nfs_write_done(struct rpc_task *task, struct nfs_write_data *data) static int nfs_write_done(struct rpc_task *task, struct nfs_write_data *data)
@ -644,9 +645,10 @@ static void nfs_proc_write_setup(struct nfs_write_data *data, struct rpc_message
msg->rpc_proc = &nfs_procedures[NFSPROC_WRITE]; msg->rpc_proc = &nfs_procedures[NFSPROC_WRITE];
} }
static void nfs_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data) static int nfs_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data)
{ {
rpc_call_start(task); rpc_call_start(task);
return 0;
} }
static void nfs_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data) static void nfs_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data)

View File

@ -513,9 +513,10 @@ static void nfs_readpage_release_common(void *calldata)
void nfs_read_prepare(struct rpc_task *task, void *calldata) void nfs_read_prepare(struct rpc_task *task, void *calldata)
{ {
struct nfs_read_data *data = calldata; struct nfs_read_data *data = calldata;
NFS_PROTO(data->header->inode)->read_rpc_prepare(task, data); int err;
if (unlikely(test_bit(NFS_CONTEXT_BAD, &data->args.context->flags))) err = NFS_PROTO(data->header->inode)->read_rpc_prepare(task, data);
rpc_exit(task, -EIO); if (err)
rpc_exit(task, err);
} }
static const struct rpc_call_ops nfs_read_common_ops = { static const struct rpc_call_ops nfs_read_common_ops = {

View File

@ -923,7 +923,7 @@ static struct nfs_parsed_mount_data *nfs_alloc_parsed_mount_data(void)
data->nfs_server.port = NFS_UNSPEC_PORT; data->nfs_server.port = NFS_UNSPEC_PORT;
data->nfs_server.protocol = XPRT_TRANSPORT_TCP; data->nfs_server.protocol = XPRT_TRANSPORT_TCP;
data->auth_flavors[0] = RPC_AUTH_MAXFLAVOR; data->auth_flavors[0] = RPC_AUTH_MAXFLAVOR;
data->auth_flavor_len = 1; data->auth_flavor_len = 0;
data->minorversion = 0; data->minorversion = 0;
data->need_mount = true; data->need_mount = true;
data->net = current->nsproxy->net_ns; data->net = current->nsproxy->net_ns;
@ -1018,6 +1018,13 @@ static void nfs_set_mount_transport_protocol(struct nfs_parsed_mount_data *mnt)
} }
} }
static void nfs_set_auth_parsed_mount_data(struct nfs_parsed_mount_data *data,
rpc_authflavor_t pseudoflavor)
{
data->auth_flavors[0] = pseudoflavor;
data->auth_flavor_len = 1;
}
/* /*
* Parse the value of the 'sec=' option. * Parse the value of the 'sec=' option.
*/ */
@ -1025,49 +1032,50 @@ static int nfs_parse_security_flavors(char *value,
struct nfs_parsed_mount_data *mnt) struct nfs_parsed_mount_data *mnt)
{ {
substring_t args[MAX_OPT_ARGS]; substring_t args[MAX_OPT_ARGS];
rpc_authflavor_t pseudoflavor;
dfprintk(MOUNT, "NFS: parsing sec=%s option\n", value); dfprintk(MOUNT, "NFS: parsing sec=%s option\n", value);
switch (match_token(value, nfs_secflavor_tokens, args)) { switch (match_token(value, nfs_secflavor_tokens, args)) {
case Opt_sec_none: case Opt_sec_none:
mnt->auth_flavors[0] = RPC_AUTH_NULL; pseudoflavor = RPC_AUTH_NULL;
break; break;
case Opt_sec_sys: case Opt_sec_sys:
mnt->auth_flavors[0] = RPC_AUTH_UNIX; pseudoflavor = RPC_AUTH_UNIX;
break; break;
case Opt_sec_krb5: case Opt_sec_krb5:
mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5; pseudoflavor = RPC_AUTH_GSS_KRB5;
break; break;
case Opt_sec_krb5i: case Opt_sec_krb5i:
mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5I; pseudoflavor = RPC_AUTH_GSS_KRB5I;
break; break;
case Opt_sec_krb5p: case Opt_sec_krb5p:
mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5P; pseudoflavor = RPC_AUTH_GSS_KRB5P;
break; break;
case Opt_sec_lkey: case Opt_sec_lkey:
mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEY; pseudoflavor = RPC_AUTH_GSS_LKEY;
break; break;
case Opt_sec_lkeyi: case Opt_sec_lkeyi:
mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYI; pseudoflavor = RPC_AUTH_GSS_LKEYI;
break; break;
case Opt_sec_lkeyp: case Opt_sec_lkeyp:
mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYP; pseudoflavor = RPC_AUTH_GSS_LKEYP;
break; break;
case Opt_sec_spkm: case Opt_sec_spkm:
mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKM; pseudoflavor = RPC_AUTH_GSS_SPKM;
break; break;
case Opt_sec_spkmi: case Opt_sec_spkmi:
mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMI; pseudoflavor = RPC_AUTH_GSS_SPKMI;
break; break;
case Opt_sec_spkmp: case Opt_sec_spkmp:
mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMP; pseudoflavor = RPC_AUTH_GSS_SPKMP;
break; break;
default: default:
return 0; return 0;
} }
mnt->flags |= NFS_MOUNT_SECFLAVOUR; mnt->flags |= NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 1; nfs_set_auth_parsed_mount_data(mnt, pseudoflavor);
return 1; return 1;
} }
@ -1729,7 +1737,7 @@ static struct nfs_server *nfs_try_mount_request(struct nfs_mount_info *mount_inf
* Was a sec= authflavor specified in the options? First, verify * Was a sec= authflavor specified in the options? First, verify
* whether the server supports it, and then just try to use it if so. * whether the server supports it, and then just try to use it if so.
*/ */
if (args->auth_flavors[0] != RPC_AUTH_MAXFLAVOR) { if (args->auth_flavor_len > 0) {
status = nfs_verify_authflavor(args, authlist, authlist_len); status = nfs_verify_authflavor(args, authlist, authlist_len);
dfprintk(MOUNT, "NFS: using auth flavor %u\n", args->auth_flavors[0]); dfprintk(MOUNT, "NFS: using auth flavor %u\n", args->auth_flavors[0]);
if (status) if (status)
@ -1760,7 +1768,7 @@ static struct nfs_server *nfs_try_mount_request(struct nfs_mount_info *mount_inf
/* Fallthrough */ /* Fallthrough */
} }
dfprintk(MOUNT, "NFS: attempting to use auth flavor %u\n", flavor); dfprintk(MOUNT, "NFS: attempting to use auth flavor %u\n", flavor);
args->auth_flavors[0] = flavor; nfs_set_auth_parsed_mount_data(args, flavor);
server = nfs_mod->rpc_ops->create_server(mount_info, nfs_mod); server = nfs_mod->rpc_ops->create_server(mount_info, nfs_mod);
if (!IS_ERR(server)) if (!IS_ERR(server))
return server; return server;
@ -1776,7 +1784,7 @@ static struct nfs_server *nfs_try_mount_request(struct nfs_mount_info *mount_inf
/* Last chance! Try AUTH_UNIX */ /* Last chance! Try AUTH_UNIX */
dfprintk(MOUNT, "NFS: attempting to use auth flavor %u\n", RPC_AUTH_UNIX); dfprintk(MOUNT, "NFS: attempting to use auth flavor %u\n", RPC_AUTH_UNIX);
args->auth_flavors[0] = RPC_AUTH_UNIX; nfs_set_auth_parsed_mount_data(args, RPC_AUTH_UNIX);
return nfs_mod->rpc_ops->create_server(mount_info, nfs_mod); return nfs_mod->rpc_ops->create_server(mount_info, nfs_mod);
} }
@ -1893,6 +1901,7 @@ static int nfs23_validate_mount_data(void *options,
{ {
struct nfs_mount_data *data = (struct nfs_mount_data *)options; struct nfs_mount_data *data = (struct nfs_mount_data *)options;
struct sockaddr *sap = (struct sockaddr *)&args->nfs_server.address; struct sockaddr *sap = (struct sockaddr *)&args->nfs_server.address;
int extra_flags = NFS_MOUNT_LEGACY_INTERFACE;
if (data == NULL) if (data == NULL)
goto out_no_data; goto out_no_data;
@ -1908,6 +1917,8 @@ static int nfs23_validate_mount_data(void *options,
goto out_no_v3; goto out_no_v3;
data->root.size = NFS2_FHSIZE; data->root.size = NFS2_FHSIZE;
memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE); memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE);
/* Turn off security negotiation */
extra_flags |= NFS_MOUNT_SECFLAVOUR;
case 4: case 4:
if (data->flags & NFS_MOUNT_SECFLAVOUR) if (data->flags & NFS_MOUNT_SECFLAVOUR)
goto out_no_sec; goto out_no_sec;
@ -1935,7 +1946,7 @@ static int nfs23_validate_mount_data(void *options,
* can deal with. * can deal with.
*/ */
args->flags = data->flags & NFS_MOUNT_FLAGMASK; args->flags = data->flags & NFS_MOUNT_FLAGMASK;
args->flags |= NFS_MOUNT_LEGACY_INTERFACE; args->flags |= extra_flags;
args->rsize = data->rsize; args->rsize = data->rsize;
args->wsize = data->wsize; args->wsize = data->wsize;
args->timeo = data->timeo; args->timeo = data->timeo;
@ -1959,9 +1970,10 @@ static int nfs23_validate_mount_data(void *options,
args->namlen = data->namlen; args->namlen = data->namlen;
args->bsize = data->bsize; args->bsize = data->bsize;
args->auth_flavors[0] = RPC_AUTH_UNIX;
if (data->flags & NFS_MOUNT_SECFLAVOUR) if (data->flags & NFS_MOUNT_SECFLAVOUR)
args->auth_flavors[0] = data->pseudoflavor; nfs_set_auth_parsed_mount_data(args, data->pseudoflavor);
else
nfs_set_auth_parsed_mount_data(args, RPC_AUTH_UNIX);
if (!args->nfs_server.hostname) if (!args->nfs_server.hostname)
goto out_nomem; goto out_nomem;
@ -2084,6 +2096,8 @@ static int nfs_validate_text_mount_data(void *options,
max_namelen = NFS4_MAXNAMLEN; max_namelen = NFS4_MAXNAMLEN;
max_pathlen = NFS4_MAXPATHLEN; max_pathlen = NFS4_MAXPATHLEN;
nfs_validate_transport_protocol(args); nfs_validate_transport_protocol(args);
if (args->nfs_server.protocol == XPRT_TRANSPORT_UDP)
goto out_invalid_transport_udp;
nfs4_validate_mount_flags(args); nfs4_validate_mount_flags(args);
#else #else
goto out_v4_not_compiled; goto out_v4_not_compiled;
@ -2106,6 +2120,10 @@ static int nfs_validate_text_mount_data(void *options,
out_v4_not_compiled: out_v4_not_compiled:
dfprintk(MOUNT, "NFS: NFSv4 is not compiled into kernel\n"); dfprintk(MOUNT, "NFS: NFSv4 is not compiled into kernel\n");
return -EPROTONOSUPPORT; return -EPROTONOSUPPORT;
#else
out_invalid_transport_udp:
dfprintk(MOUNT, "NFSv4: Unsupported transport protocol udp\n");
return -EINVAL;
#endif /* !CONFIG_NFS_V4 */ #endif /* !CONFIG_NFS_V4 */
out_no_address: out_no_address:
@ -2170,7 +2188,7 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data)
data->rsize = nfss->rsize; data->rsize = nfss->rsize;
data->wsize = nfss->wsize; data->wsize = nfss->wsize;
data->retrans = nfss->client->cl_timeout->to_retries; data->retrans = nfss->client->cl_timeout->to_retries;
data->auth_flavors[0] = nfss->client->cl_auth->au_flavor; nfs_set_auth_parsed_mount_data(data, nfss->client->cl_auth->au_flavor);
data->acregmin = nfss->acregmin / HZ; data->acregmin = nfss->acregmin / HZ;
data->acregmax = nfss->acregmax / HZ; data->acregmax = nfss->acregmax / HZ;
data->acdirmin = nfss->acdirmin / HZ; data->acdirmin = nfss->acdirmin / HZ;
@ -2277,6 +2295,18 @@ void nfs_clone_super(struct super_block *sb, struct nfs_mount_info *mount_info)
nfs_initialise_sb(sb); nfs_initialise_sb(sb);
} }
#define NFS_MOUNT_CMP_FLAGMASK ~(NFS_MOUNT_INTR \
| NFS_MOUNT_SECURE \
| NFS_MOUNT_TCP \
| NFS_MOUNT_VER3 \
| NFS_MOUNT_KERBEROS \
| NFS_MOUNT_NONLM \
| NFS_MOUNT_BROKEN_SUID \
| NFS_MOUNT_STRICTLOCK \
| NFS_MOUNT_UNSHARED \
| NFS_MOUNT_NORESVPORT \
| NFS_MOUNT_LEGACY_INTERFACE)
static int nfs_compare_mount_options(const struct super_block *s, const struct nfs_server *b, int flags) static int nfs_compare_mount_options(const struct super_block *s, const struct nfs_server *b, int flags)
{ {
const struct nfs_server *a = s->s_fs_info; const struct nfs_server *a = s->s_fs_info;
@ -2287,7 +2317,7 @@ static int nfs_compare_mount_options(const struct super_block *s, const struct n
goto Ebusy; goto Ebusy;
if (a->nfs_client != b->nfs_client) if (a->nfs_client != b->nfs_client)
goto Ebusy; goto Ebusy;
if (a->flags != b->flags) if ((a->flags ^ b->flags) & NFS_MOUNT_CMP_FLAGMASK)
goto Ebusy; goto Ebusy;
if (a->wsize != b->wsize) if (a->wsize != b->wsize)
goto Ebusy; goto Ebusy;
@ -2301,7 +2331,8 @@ static int nfs_compare_mount_options(const struct super_block *s, const struct n
goto Ebusy; goto Ebusy;
if (a->acdirmax != b->acdirmax) if (a->acdirmax != b->acdirmax)
goto Ebusy; goto Ebusy;
if (clnt_a->cl_auth->au_flavor != clnt_b->cl_auth->au_flavor) if (b->flags & NFS_MOUNT_SECFLAVOUR &&
clnt_a->cl_auth->au_flavor != clnt_b->cl_auth->au_flavor)
goto Ebusy; goto Ebusy;
return 1; return 1;
Ebusy: Ebusy:
@ -2673,15 +2704,17 @@ static int nfs4_validate_mount_data(void *options,
goto out_no_address; goto out_no_address;
args->nfs_server.port = ntohs(((struct sockaddr_in *)sap)->sin_port); args->nfs_server.port = ntohs(((struct sockaddr_in *)sap)->sin_port);
args->auth_flavors[0] = RPC_AUTH_UNIX;
if (data->auth_flavourlen) { if (data->auth_flavourlen) {
rpc_authflavor_t pseudoflavor;
if (data->auth_flavourlen > 1) if (data->auth_flavourlen > 1)
goto out_inval_auth; goto out_inval_auth;
if (copy_from_user(&args->auth_flavors[0], if (copy_from_user(&pseudoflavor,
data->auth_flavours, data->auth_flavours,
sizeof(args->auth_flavors[0]))) sizeof(pseudoflavor)))
return -EFAULT; return -EFAULT;
} nfs_set_auth_parsed_mount_data(args, pseudoflavor);
} else
nfs_set_auth_parsed_mount_data(args, RPC_AUTH_UNIX);
c = strndup_user(data->hostname.data, NFS4_MAXNAMLEN); c = strndup_user(data->hostname.data, NFS4_MAXNAMLEN);
if (IS_ERR(c)) if (IS_ERR(c))
@ -2715,6 +2748,8 @@ static int nfs4_validate_mount_data(void *options,
args->acdirmax = data->acdirmax; args->acdirmax = data->acdirmax;
args->nfs_server.protocol = data->proto; args->nfs_server.protocol = data->proto;
nfs_validate_transport_protocol(args); nfs_validate_transport_protocol(args);
if (args->nfs_server.protocol == XPRT_TRANSPORT_UDP)
goto out_invalid_transport_udp;
break; break;
default: default:
@ -2735,6 +2770,10 @@ out_inval_auth:
out_no_address: out_no_address:
dfprintk(MOUNT, "NFS4: mount program didn't pass remote address\n"); dfprintk(MOUNT, "NFS4: mount program didn't pass remote address\n");
return -EINVAL; return -EINVAL;
out_invalid_transport_udp:
dfprintk(MOUNT, "NFSv4: Unsupported transport protocol udp\n");
return -EINVAL;
} }
/* /*
@ -2750,6 +2789,7 @@ bool nfs4_disable_idmapping = true;
unsigned short max_session_slots = NFS4_DEF_SLOT_TABLE_SIZE; unsigned short max_session_slots = NFS4_DEF_SLOT_TABLE_SIZE;
unsigned short send_implementation_id = 1; unsigned short send_implementation_id = 1;
char nfs4_client_id_uniquifier[NFS4_CLIENT_ID_UNIQ_LEN] = ""; char nfs4_client_id_uniquifier[NFS4_CLIENT_ID_UNIQ_LEN] = "";
bool recover_lost_locks = false;
EXPORT_SYMBOL_GPL(nfs_callback_set_tcpport); EXPORT_SYMBOL_GPL(nfs_callback_set_tcpport);
EXPORT_SYMBOL_GPL(nfs_callback_tcpport); EXPORT_SYMBOL_GPL(nfs_callback_tcpport);
@ -2758,6 +2798,7 @@ EXPORT_SYMBOL_GPL(nfs4_disable_idmapping);
EXPORT_SYMBOL_GPL(max_session_slots); EXPORT_SYMBOL_GPL(max_session_slots);
EXPORT_SYMBOL_GPL(send_implementation_id); EXPORT_SYMBOL_GPL(send_implementation_id);
EXPORT_SYMBOL_GPL(nfs4_client_id_uniquifier); EXPORT_SYMBOL_GPL(nfs4_client_id_uniquifier);
EXPORT_SYMBOL_GPL(recover_lost_locks);
#define NFS_CALLBACK_MAXPORTNR (65535U) #define NFS_CALLBACK_MAXPORTNR (65535U)
@ -2795,4 +2836,10 @@ MODULE_PARM_DESC(send_implementation_id,
"Send implementation ID with NFSv4.1 exchange_id"); "Send implementation ID with NFSv4.1 exchange_id");
MODULE_PARM_DESC(nfs4_unique_id, "nfs_client_id4 uniquifier string"); MODULE_PARM_DESC(nfs4_unique_id, "nfs_client_id4 uniquifier string");
module_param(recover_lost_locks, bool, 0644);
MODULE_PARM_DESC(recover_lost_locks,
"If the server reports that a lock might be lost, "
"try to recover it risking data corruption.");
#endif /* CONFIG_NFS_V4 */ #endif /* CONFIG_NFS_V4 */

View File

@ -20,6 +20,8 @@
#include "iostat.h" #include "iostat.h"
#include "delegation.h" #include "delegation.h"
#include "nfstrace.h"
/** /**
* nfs_free_unlinkdata - release data from a sillydelete operation. * nfs_free_unlinkdata - release data from a sillydelete operation.
* @data: pointer to unlink structure. * @data: pointer to unlink structure.
@ -77,6 +79,7 @@ static void nfs_async_unlink_done(struct rpc_task *task, void *calldata)
struct nfs_unlinkdata *data = calldata; struct nfs_unlinkdata *data = calldata;
struct inode *dir = data->dir; struct inode *dir = data->dir;
trace_nfs_sillyrename_unlink(data, task->tk_status);
if (!NFS_PROTO(dir)->unlink_done(task, dir)) if (!NFS_PROTO(dir)->unlink_done(task, dir))
rpc_restart_call_prepare(task); rpc_restart_call_prepare(task);
} }
@ -204,6 +207,13 @@ out_free:
return ret; return ret;
} }
void nfs_wait_on_sillyrename(struct dentry *dentry)
{
struct nfs_inode *nfsi = NFS_I(dentry->d_inode);
wait_event(nfsi->waitqueue, atomic_read(&nfsi->silly_count) <= 1);
}
void nfs_block_sillyrename(struct dentry *dentry) void nfs_block_sillyrename(struct dentry *dentry)
{ {
struct nfs_inode *nfsi = NFS_I(dentry->d_inode); struct nfs_inode *nfsi = NFS_I(dentry->d_inode);
@ -336,6 +346,8 @@ static void nfs_async_rename_done(struct rpc_task *task, void *calldata)
struct inode *new_dir = data->new_dir; struct inode *new_dir = data->new_dir;
struct dentry *old_dentry = data->old_dentry; struct dentry *old_dentry = data->old_dentry;
trace_nfs_sillyrename_rename(old_dir, old_dentry,
new_dir, data->new_dentry, task->tk_status);
if (!NFS_PROTO(old_dir)->rename_done(task, old_dir, new_dir)) { if (!NFS_PROTO(old_dir)->rename_done(task, old_dir, new_dir)) {
rpc_restart_call_prepare(task); rpc_restart_call_prepare(task);
return; return;
@ -444,6 +456,14 @@ nfs_async_rename(struct inode *old_dir, struct inode *new_dir,
return rpc_run_task(&task_setup_data); return rpc_run_task(&task_setup_data);
} }
#define SILLYNAME_PREFIX ".nfs"
#define SILLYNAME_PREFIX_LEN ((unsigned)sizeof(SILLYNAME_PREFIX) - 1)
#define SILLYNAME_FILEID_LEN ((unsigned)sizeof(u64) << 1)
#define SILLYNAME_COUNTER_LEN ((unsigned)sizeof(unsigned int) << 1)
#define SILLYNAME_LEN (SILLYNAME_PREFIX_LEN + \
SILLYNAME_FILEID_LEN + \
SILLYNAME_COUNTER_LEN)
/** /**
* nfs_sillyrename - Perform a silly-rename of a dentry * nfs_sillyrename - Perform a silly-rename of a dentry
* @dir: inode of directory that contains dentry * @dir: inode of directory that contains dentry
@ -469,10 +489,8 @@ int
nfs_sillyrename(struct inode *dir, struct dentry *dentry) nfs_sillyrename(struct inode *dir, struct dentry *dentry)
{ {
static unsigned int sillycounter; static unsigned int sillycounter;
const int fileidsize = sizeof(NFS_FILEID(dentry->d_inode))*2; unsigned char silly[SILLYNAME_LEN + 1];
const int countersize = sizeof(sillycounter)*2; unsigned long long fileid;
const int slen = sizeof(".nfs")+fileidsize+countersize-1;
char silly[slen+1];
struct dentry *sdentry; struct dentry *sdentry;
struct rpc_task *task; struct rpc_task *task;
int error = -EIO; int error = -EIO;
@ -489,20 +507,20 @@ nfs_sillyrename(struct inode *dir, struct dentry *dentry)
if (dentry->d_flags & DCACHE_NFSFS_RENAMED) if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
goto out; goto out;
sprintf(silly, ".nfs%*.*Lx", fileid = NFS_FILEID(dentry->d_inode);
fileidsize, fileidsize,
(unsigned long long)NFS_FILEID(dentry->d_inode));
/* Return delegation in anticipation of the rename */ /* Return delegation in anticipation of the rename */
NFS_PROTO(dentry->d_inode)->return_delegation(dentry->d_inode); NFS_PROTO(dentry->d_inode)->return_delegation(dentry->d_inode);
sdentry = NULL; sdentry = NULL;
do { do {
char *suffix = silly + slen - countersize; int slen;
dput(sdentry); dput(sdentry);
sillycounter++; sillycounter++;
sprintf(suffix, "%*.*x", countersize, countersize, sillycounter); slen = scnprintf(silly, sizeof(silly),
SILLYNAME_PREFIX "%0*llx%0*x",
SILLYNAME_FILEID_LEN, fileid,
SILLYNAME_COUNTER_LEN, sillycounter);
dfprintk(VFS, "NFS: trying to rename %s to %s\n", dfprintk(VFS, "NFS: trying to rename %s to %s\n",
dentry->d_name.name, silly); dentry->d_name.name, silly);

View File

@ -31,6 +31,8 @@
#include "fscache.h" #include "fscache.h"
#include "pnfs.h" #include "pnfs.h"
#include "nfstrace.h"
#define NFSDBG_FACILITY NFSDBG_PAGECACHE #define NFSDBG_FACILITY NFSDBG_PAGECACHE
#define MIN_POOL_WRITE (32) #define MIN_POOL_WRITE (32)
@ -861,7 +863,7 @@ int nfs_flush_incompatible(struct file *file, struct page *page)
return 0; return 0;
l_ctx = req->wb_lock_context; l_ctx = req->wb_lock_context;
do_flush = req->wb_page != page || req->wb_context != ctx; do_flush = req->wb_page != page || req->wb_context != ctx;
if (l_ctx) { if (l_ctx && ctx->dentry->d_inode->i_flock != NULL) {
do_flush |= l_ctx->lockowner.l_owner != current->files do_flush |= l_ctx->lockowner.l_owner != current->files
|| l_ctx->lockowner.l_pid != current->tgid; || l_ctx->lockowner.l_pid != current->tgid;
} }
@ -873,6 +875,33 @@ int nfs_flush_incompatible(struct file *file, struct page *page)
return status; return status;
} }
/*
* Avoid buffered writes when a open context credential's key would
* expire soon.
*
* Returns -EACCES if the key will expire within RPC_KEY_EXPIRE_FAIL.
*
* Return 0 and set a credential flag which triggers the inode to flush
* and performs NFS_FILE_SYNC writes if the key will expired within
* RPC_KEY_EXPIRE_TIMEO.
*/
int
nfs_key_timeout_notify(struct file *filp, struct inode *inode)
{
struct nfs_open_context *ctx = nfs_file_open_context(filp);
struct rpc_auth *auth = NFS_SERVER(inode)->client->cl_auth;
return rpcauth_key_timeout_notify(auth, ctx->cred);
}
/*
* Test if the open context credential key is marked to expire soon.
*/
bool nfs_ctx_key_to_expire(struct nfs_open_context *ctx)
{
return rpcauth_cred_key_to_expire(ctx->cred);
}
/* /*
* If the page cache is marked as unsafe or invalid, then we can't rely on * If the page cache is marked as unsafe or invalid, then we can't rely on
* the PageUptodate() flag. In this case, we will need to turn off * the PageUptodate() flag. In this case, we will need to turn off
@ -993,6 +1022,9 @@ int nfs_initiate_write(struct rpc_clnt *clnt,
data->args.count, data->args.count,
(unsigned long long)data->args.offset); (unsigned long long)data->args.offset);
nfs4_state_protect_write(NFS_SERVER(inode)->nfs_client,
&task_setup_data.rpc_client, &msg, data);
task = rpc_run_task(&task_setup_data); task = rpc_run_task(&task_setup_data);
if (IS_ERR(task)) { if (IS_ERR(task)) {
ret = PTR_ERR(task); ret = PTR_ERR(task);
@ -1265,9 +1297,10 @@ EXPORT_SYMBOL_GPL(nfs_pageio_reset_write_mds);
void nfs_write_prepare(struct rpc_task *task, void *calldata) void nfs_write_prepare(struct rpc_task *task, void *calldata)
{ {
struct nfs_write_data *data = calldata; struct nfs_write_data *data = calldata;
NFS_PROTO(data->header->inode)->write_rpc_prepare(task, data); int err;
if (unlikely(test_bit(NFS_CONTEXT_BAD, &data->args.context->flags))) err = NFS_PROTO(data->header->inode)->write_rpc_prepare(task, data);
rpc_exit(task, -EIO); if (err)
rpc_exit(task, err);
} }
void nfs_commit_prepare(struct rpc_task *task, void *calldata) void nfs_commit_prepare(struct rpc_task *task, void *calldata)
@ -1458,6 +1491,9 @@ int nfs_initiate_commit(struct rpc_clnt *clnt, struct nfs_commit_data *data,
dprintk("NFS: %5u initiated commit call\n", data->task.tk_pid); dprintk("NFS: %5u initiated commit call\n", data->task.tk_pid);
nfs4_state_protect(NFS_SERVER(data->inode)->nfs_client,
NFS_SP4_MACH_CRED_COMMIT, &task_setup_data.rpc_client, &msg);
task = rpc_run_task(&task_setup_data); task = rpc_run_task(&task_setup_data);
if (IS_ERR(task)) if (IS_ERR(task))
return PTR_ERR(task); return PTR_ERR(task);
@ -1732,8 +1768,14 @@ int nfs_wb_all(struct inode *inode)
.range_start = 0, .range_start = 0,
.range_end = LLONG_MAX, .range_end = LLONG_MAX,
}; };
int ret;
return sync_inode(inode, &wbc); trace_nfs_writeback_inode_enter(inode);
ret = sync_inode(inode, &wbc);
trace_nfs_writeback_inode_exit(inode, ret);
return ret;
} }
EXPORT_SYMBOL_GPL(nfs_wb_all); EXPORT_SYMBOL_GPL(nfs_wb_all);
@ -1781,6 +1823,8 @@ int nfs_wb_page(struct inode *inode, struct page *page)
}; };
int ret; int ret;
trace_nfs_writeback_page_enter(inode);
for (;;) { for (;;) {
wait_on_page_writeback(page); wait_on_page_writeback(page);
if (clear_page_dirty_for_io(page)) { if (clear_page_dirty_for_io(page)) {
@ -1789,14 +1833,15 @@ int nfs_wb_page(struct inode *inode, struct page *page)
goto out_error; goto out_error;
continue; continue;
} }
ret = 0;
if (!PagePrivate(page)) if (!PagePrivate(page))
break; break;
ret = nfs_commit_inode(inode, FLUSH_SYNC); ret = nfs_commit_inode(inode, FLUSH_SYNC);
if (ret < 0) if (ret < 0)
goto out_error; goto out_error;
} }
return 0;
out_error: out_error:
trace_nfs_writeback_page_exit(inode, ret);
return ret; return ret;
} }

View File

@ -524,6 +524,7 @@ static inline void nfs4_label_free(void *label) {}
* linux/fs/nfs/unlink.c * linux/fs/nfs/unlink.c
*/ */
extern void nfs_complete_unlink(struct dentry *dentry, struct inode *); extern void nfs_complete_unlink(struct dentry *dentry, struct inode *);
extern void nfs_wait_on_sillyrename(struct dentry *dentry);
extern void nfs_block_sillyrename(struct dentry *dentry); extern void nfs_block_sillyrename(struct dentry *dentry);
extern void nfs_unblock_sillyrename(struct dentry *dentry); extern void nfs_unblock_sillyrename(struct dentry *dentry);
extern int nfs_sillyrename(struct inode *dir, struct dentry *dentry); extern int nfs_sillyrename(struct inode *dir, struct dentry *dentry);

View File

@ -56,6 +56,7 @@ struct nfs_client {
struct rpc_cred *cl_machine_cred; struct rpc_cred *cl_machine_cred;
#if IS_ENABLED(CONFIG_NFS_V4) #if IS_ENABLED(CONFIG_NFS_V4)
struct list_head cl_ds_clients; /* auth flavor data servers */
u64 cl_clientid; /* constant */ u64 cl_clientid; /* constant */
nfs4_verifier cl_confirm; /* Clientid verifier */ nfs4_verifier cl_confirm; /* Clientid verifier */
unsigned long cl_state; unsigned long cl_state;
@ -78,6 +79,9 @@ struct nfs_client {
u32 cl_cb_ident; /* v4.0 callback identifier */ u32 cl_cb_ident; /* v4.0 callback identifier */
const struct nfs4_minor_version_ops *cl_mvops; const struct nfs4_minor_version_ops *cl_mvops;
/* NFSv4.0 transport blocking */
struct nfs4_slot_table *cl_slot_tbl;
/* The sequence id to use for the next CREATE_SESSION */ /* The sequence id to use for the next CREATE_SESSION */
u32 cl_seqid; u32 cl_seqid;
/* The flags used for obtaining the clientid during EXCHANGE_ID */ /* The flags used for obtaining the clientid during EXCHANGE_ID */
@ -87,6 +91,15 @@ struct nfs_client {
struct nfs41_server_owner *cl_serverowner; struct nfs41_server_owner *cl_serverowner;
struct nfs41_server_scope *cl_serverscope; struct nfs41_server_scope *cl_serverscope;
struct nfs41_impl_id *cl_implid; struct nfs41_impl_id *cl_implid;
/* nfs 4.1+ state protection modes: */
unsigned long cl_sp4_flags;
#define NFS_SP4_MACH_CRED_MINIMAL 1 /* Minimal sp4_mach_cred - state ops
* must use machine cred */
#define NFS_SP4_MACH_CRED_CLEANUP 2 /* CLOSE and LOCKU */
#define NFS_SP4_MACH_CRED_SECINFO 3 /* SECINFO and SECINFO_NO_NAME */
#define NFS_SP4_MACH_CRED_STATEID 4 /* TEST_STATEID and FREE_STATEID */
#define NFS_SP4_MACH_CRED_WRITE 5 /* WRITE */
#define NFS_SP4_MACH_CRED_COMMIT 6 /* COMMIT */
#endif /* CONFIG_NFS_V4 */ #endif /* CONFIG_NFS_V4 */
#ifdef CONFIG_NFS_FSCACHE #ifdef CONFIG_NFS_FSCACHE

View File

@ -1107,6 +1107,23 @@ struct pnfs_ds_commit_info {
struct pnfs_commit_bucket *buckets; struct pnfs_commit_bucket *buckets;
}; };
#define NFS4_OP_MAP_NUM_LONGS \
DIV_ROUND_UP(LAST_NFS4_OP, 8 * sizeof(unsigned long))
#define NFS4_OP_MAP_NUM_WORDS \
(NFS4_OP_MAP_NUM_LONGS * sizeof(unsigned long) / sizeof(u32))
struct nfs4_op_map {
union {
unsigned long longs[NFS4_OP_MAP_NUM_LONGS];
u32 words[NFS4_OP_MAP_NUM_WORDS];
} u;
};
struct nfs41_state_protection {
u32 how;
struct nfs4_op_map enforce;
struct nfs4_op_map allow;
};
#define NFS4_EXCHANGE_ID_LEN (48) #define NFS4_EXCHANGE_ID_LEN (48)
struct nfs41_exchange_id_args { struct nfs41_exchange_id_args {
struct nfs_client *client; struct nfs_client *client;
@ -1114,6 +1131,7 @@ struct nfs41_exchange_id_args {
unsigned int id_len; unsigned int id_len;
char id[NFS4_EXCHANGE_ID_LEN]; char id[NFS4_EXCHANGE_ID_LEN];
u32 flags; u32 flags;
struct nfs41_state_protection state_protect;
}; };
struct nfs41_server_owner { struct nfs41_server_owner {
@ -1146,6 +1164,7 @@ struct nfs41_exchange_id_res {
struct nfs41_server_owner *server_owner; struct nfs41_server_owner *server_owner;
struct nfs41_server_scope *server_scope; struct nfs41_server_scope *server_scope;
struct nfs41_impl_id *impl_id; struct nfs41_impl_id *impl_id;
struct nfs41_state_protection state_protect;
}; };
struct nfs41_create_session_args { struct nfs41_create_session_args {
@ -1419,12 +1438,12 @@ struct nfs_rpc_ops {
void (*read_setup) (struct nfs_read_data *, struct rpc_message *); void (*read_setup) (struct nfs_read_data *, struct rpc_message *);
void (*read_pageio_init)(struct nfs_pageio_descriptor *, struct inode *, void (*read_pageio_init)(struct nfs_pageio_descriptor *, struct inode *,
const struct nfs_pgio_completion_ops *); const struct nfs_pgio_completion_ops *);
void (*read_rpc_prepare)(struct rpc_task *, struct nfs_read_data *); int (*read_rpc_prepare)(struct rpc_task *, struct nfs_read_data *);
int (*read_done) (struct rpc_task *, struct nfs_read_data *); int (*read_done) (struct rpc_task *, struct nfs_read_data *);
void (*write_setup) (struct nfs_write_data *, struct rpc_message *); void (*write_setup) (struct nfs_write_data *, struct rpc_message *);
void (*write_pageio_init)(struct nfs_pageio_descriptor *, struct inode *, int, void (*write_pageio_init)(struct nfs_pageio_descriptor *, struct inode *, int,
const struct nfs_pgio_completion_ops *); const struct nfs_pgio_completion_ops *);
void (*write_rpc_prepare)(struct rpc_task *, struct nfs_write_data *); int (*write_rpc_prepare)(struct rpc_task *, struct nfs_write_data *);
int (*write_done) (struct rpc_task *, struct nfs_write_data *); int (*write_done) (struct rpc_task *, struct nfs_write_data *);
void (*commit_setup) (struct nfs_commit_data *, struct rpc_message *); void (*commit_setup) (struct nfs_commit_data *, struct rpc_message *);
void (*commit_rpc_prepare)(struct rpc_task *, struct nfs_commit_data *); void (*commit_rpc_prepare)(struct rpc_task *, struct nfs_commit_data *);
@ -1442,7 +1461,7 @@ struct nfs_rpc_ops {
struct nfs_client *(*alloc_client) (const struct nfs_client_initdata *); struct nfs_client *(*alloc_client) (const struct nfs_client_initdata *);
struct nfs_client * struct nfs_client *
(*init_client) (struct nfs_client *, const struct rpc_timeout *, (*init_client) (struct nfs_client *, const struct rpc_timeout *,
const char *, rpc_authflavor_t); const char *);
void (*free_client) (struct nfs_client *); void (*free_client) (struct nfs_client *);
struct nfs_server *(*create_server)(struct nfs_mount_info *, struct nfs_subversion *); struct nfs_server *(*create_server)(struct nfs_mount_info *, struct nfs_subversion *);
struct nfs_server *(*clone_server)(struct nfs_server *, struct nfs_fh *, struct nfs_server *(*clone_server)(struct nfs_server *, struct nfs_fh *,

View File

@ -24,12 +24,21 @@
struct rpcsec_gss_info; struct rpcsec_gss_info;
/* auth_cred ac_flags bits */
enum {
RPC_CRED_NO_CRKEY_TIMEOUT = 0, /* underlying cred has no key timeout */
RPC_CRED_KEY_EXPIRE_SOON = 1, /* underlying cred key will expire soon */
RPC_CRED_NOTIFY_TIMEOUT = 2, /* nofity generic cred when underlying
key will expire soon */
};
/* Work around the lack of a VFS credential */ /* Work around the lack of a VFS credential */
struct auth_cred { struct auth_cred {
kuid_t uid; kuid_t uid;
kgid_t gid; kgid_t gid;
struct group_info *group_info; struct group_info *group_info;
const char *principal; const char *principal;
unsigned long ac_flags;
unsigned char machine_cred : 1; unsigned char machine_cred : 1;
}; };
@ -87,6 +96,11 @@ struct rpc_auth {
/* per-flavor data */ /* per-flavor data */
}; };
struct rpc_auth_create_args {
rpc_authflavor_t pseudoflavor;
const char *target_name;
};
/* Flags for rpcauth_lookupcred() */ /* Flags for rpcauth_lookupcred() */
#define RPCAUTH_LOOKUP_NEW 0x01 /* Accept an uninitialised cred */ #define RPCAUTH_LOOKUP_NEW 0x01 /* Accept an uninitialised cred */
@ -97,17 +111,17 @@ struct rpc_authops {
struct module *owner; struct module *owner;
rpc_authflavor_t au_flavor; /* flavor (RPC_AUTH_*) */ rpc_authflavor_t au_flavor; /* flavor (RPC_AUTH_*) */
char * au_name; char * au_name;
struct rpc_auth * (*create)(struct rpc_clnt *, rpc_authflavor_t); struct rpc_auth * (*create)(struct rpc_auth_create_args *, struct rpc_clnt *);
void (*destroy)(struct rpc_auth *); void (*destroy)(struct rpc_auth *);
struct rpc_cred * (*lookup_cred)(struct rpc_auth *, struct auth_cred *, int); struct rpc_cred * (*lookup_cred)(struct rpc_auth *, struct auth_cred *, int);
struct rpc_cred * (*crcreate)(struct rpc_auth*, struct auth_cred *, int); struct rpc_cred * (*crcreate)(struct rpc_auth*, struct auth_cred *, int);
int (*pipes_create)(struct rpc_auth *);
void (*pipes_destroy)(struct rpc_auth *);
int (*list_pseudoflavors)(rpc_authflavor_t *, int); int (*list_pseudoflavors)(rpc_authflavor_t *, int);
rpc_authflavor_t (*info2flavor)(struct rpcsec_gss_info *); rpc_authflavor_t (*info2flavor)(struct rpcsec_gss_info *);
int (*flavor2info)(rpc_authflavor_t, int (*flavor2info)(rpc_authflavor_t,
struct rpcsec_gss_info *); struct rpcsec_gss_info *);
int (*key_timeout)(struct rpc_auth *,
struct rpc_cred *);
}; };
struct rpc_credops { struct rpc_credops {
@ -124,6 +138,8 @@ struct rpc_credops {
void *, __be32 *, void *); void *, __be32 *, void *);
int (*crunwrap_resp)(struct rpc_task *, kxdrdproc_t, int (*crunwrap_resp)(struct rpc_task *, kxdrdproc_t,
void *, __be32 *, void *); void *, __be32 *, void *);
int (*crkey_timeout)(struct rpc_cred *);
bool (*crkey_to_expire)(struct rpc_cred *);
}; };
extern const struct rpc_authops authunix_ops; extern const struct rpc_authops authunix_ops;
@ -140,7 +156,8 @@ struct rpc_cred * rpc_lookup_cred(void);
struct rpc_cred * rpc_lookup_machine_cred(const char *service_name); struct rpc_cred * rpc_lookup_machine_cred(const char *service_name);
int rpcauth_register(const struct rpc_authops *); int rpcauth_register(const struct rpc_authops *);
int rpcauth_unregister(const struct rpc_authops *); int rpcauth_unregister(const struct rpc_authops *);
struct rpc_auth * rpcauth_create(rpc_authflavor_t, struct rpc_clnt *); struct rpc_auth * rpcauth_create(struct rpc_auth_create_args *,
struct rpc_clnt *);
void rpcauth_release(struct rpc_auth *); void rpcauth_release(struct rpc_auth *);
rpc_authflavor_t rpcauth_get_pseudoflavor(rpc_authflavor_t, rpc_authflavor_t rpcauth_get_pseudoflavor(rpc_authflavor_t,
struct rpcsec_gss_info *); struct rpcsec_gss_info *);
@ -162,6 +179,9 @@ int rpcauth_uptodatecred(struct rpc_task *);
int rpcauth_init_credcache(struct rpc_auth *); int rpcauth_init_credcache(struct rpc_auth *);
void rpcauth_destroy_credcache(struct rpc_auth *); void rpcauth_destroy_credcache(struct rpc_auth *);
void rpcauth_clear_credcache(struct rpc_cred_cache *); void rpcauth_clear_credcache(struct rpc_cred_cache *);
int rpcauth_key_timeout_notify(struct rpc_auth *,
struct rpc_cred *);
bool rpcauth_cred_key_to_expire(struct rpc_cred *);
static inline static inline
struct rpc_cred * get_rpccred(struct rpc_cred *cred) struct rpc_cred * get_rpccred(struct rpc_cred *cred)

View File

@ -21,6 +21,7 @@
#include <linux/sunrpc/stats.h> #include <linux/sunrpc/stats.h>
#include <linux/sunrpc/xdr.h> #include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/timer.h> #include <linux/sunrpc/timer.h>
#include <linux/sunrpc/rpc_pipe_fs.h>
#include <asm/signal.h> #include <asm/signal.h>
#include <linux/path.h> #include <linux/path.h>
#include <net/ipv6.h> #include <net/ipv6.h>
@ -32,6 +33,7 @@ struct rpc_inode;
*/ */
struct rpc_clnt { struct rpc_clnt {
atomic_t cl_count; /* Number of references */ atomic_t cl_count; /* Number of references */
unsigned int cl_clid; /* client id */
struct list_head cl_clients; /* Global list of clients */ struct list_head cl_clients; /* Global list of clients */
struct list_head cl_tasks; /* List of tasks */ struct list_head cl_tasks; /* List of tasks */
spinlock_t cl_lock; /* spinlock */ spinlock_t cl_lock; /* spinlock */
@ -41,7 +43,6 @@ struct rpc_clnt {
cl_vers, /* RPC version number */ cl_vers, /* RPC version number */
cl_maxproc; /* max procedure number */ cl_maxproc; /* max procedure number */
const char * cl_protname; /* protocol name */
struct rpc_auth * cl_auth; /* authenticator */ struct rpc_auth * cl_auth; /* authenticator */
struct rpc_stat * cl_stats; /* per-program statistics */ struct rpc_stat * cl_stats; /* per-program statistics */
struct rpc_iostats * cl_metrics; /* per-client statistics */ struct rpc_iostats * cl_metrics; /* per-client statistics */
@ -56,12 +57,11 @@ struct rpc_clnt {
int cl_nodelen; /* nodename length */ int cl_nodelen; /* nodename length */
char cl_nodename[UNX_MAXNODENAME]; char cl_nodename[UNX_MAXNODENAME];
struct dentry * cl_dentry; struct rpc_pipe_dir_head cl_pipedir_objects;
struct rpc_clnt * cl_parent; /* Points to parent of clones */ struct rpc_clnt * cl_parent; /* Points to parent of clones */
struct rpc_rtt cl_rtt_default; struct rpc_rtt cl_rtt_default;
struct rpc_timeout cl_timeout_default; struct rpc_timeout cl_timeout_default;
const struct rpc_program *cl_program; const struct rpc_program *cl_program;
char *cl_principal; /* target to authenticate to */
}; };
/* /*

View File

@ -5,6 +5,26 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
struct rpc_pipe_dir_head {
struct list_head pdh_entries;
struct dentry *pdh_dentry;
};
struct rpc_pipe_dir_object_ops;
struct rpc_pipe_dir_object {
struct list_head pdo_head;
const struct rpc_pipe_dir_object_ops *pdo_ops;
void *pdo_data;
};
struct rpc_pipe_dir_object_ops {
int (*create)(struct dentry *dir,
struct rpc_pipe_dir_object *pdo);
void (*destroy)(struct dentry *dir,
struct rpc_pipe_dir_object *pdo);
};
struct rpc_pipe_msg { struct rpc_pipe_msg {
struct list_head list; struct list_head list;
void *data; void *data;
@ -74,7 +94,24 @@ extern int rpc_queue_upcall(struct rpc_pipe *, struct rpc_pipe_msg *);
struct rpc_clnt; struct rpc_clnt;
extern struct dentry *rpc_create_client_dir(struct dentry *, const char *, struct rpc_clnt *); extern struct dentry *rpc_create_client_dir(struct dentry *, const char *, struct rpc_clnt *);
extern int rpc_remove_client_dir(struct dentry *); extern int rpc_remove_client_dir(struct rpc_clnt *);
extern void rpc_init_pipe_dir_head(struct rpc_pipe_dir_head *pdh);
extern void rpc_init_pipe_dir_object(struct rpc_pipe_dir_object *pdo,
const struct rpc_pipe_dir_object_ops *pdo_ops,
void *pdo_data);
extern int rpc_add_pipe_dir_object(struct net *net,
struct rpc_pipe_dir_head *pdh,
struct rpc_pipe_dir_object *pdo);
extern void rpc_remove_pipe_dir_object(struct net *net,
struct rpc_pipe_dir_head *pdh,
struct rpc_pipe_dir_object *pdo);
extern struct rpc_pipe_dir_object *rpc_find_or_alloc_pipe_dir_object(
struct net *net,
struct rpc_pipe_dir_head *pdh,
int (*match)(struct rpc_pipe_dir_object *, void *),
struct rpc_pipe_dir_object *(*alloc)(void *),
void *data);
struct cache_detail; struct cache_detail;
extern struct dentry *rpc_create_cache_dir(struct dentry *, extern struct dentry *rpc_create_cache_dir(struct dentry *,

View File

@ -79,7 +79,7 @@ struct rpc_task {
unsigned short tk_flags; /* misc flags */ unsigned short tk_flags; /* misc flags */
unsigned short tk_timeouts; /* maj timeouts */ unsigned short tk_timeouts; /* maj timeouts */
#ifdef RPC_DEBUG #if defined(RPC_DEBUG) || defined(RPC_TRACEPOINTS)
unsigned short tk_pid; /* debugging aid */ unsigned short tk_pid; /* debugging aid */
#endif #endif
unsigned char tk_priority : 2,/* Task priority */ unsigned char tk_priority : 2,/* Task priority */

View File

@ -6,6 +6,8 @@
#include <linux/sunrpc/sched.h> #include <linux/sunrpc/sched.h>
#include <linux/sunrpc/clnt.h> #include <linux/sunrpc/clnt.h>
#include <net/tcp_states.h>
#include <linux/net.h>
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
DECLARE_EVENT_CLASS(rpc_task_status, DECLARE_EVENT_CLASS(rpc_task_status,
@ -15,18 +17,20 @@ DECLARE_EVENT_CLASS(rpc_task_status,
TP_ARGS(task), TP_ARGS(task),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(const struct rpc_task *, task) __field(unsigned int, task_id)
__field(const struct rpc_clnt *, clnt) __field(unsigned int, client_id)
__field(int, status) __field(int, status)
), ),
TP_fast_assign( TP_fast_assign(
__entry->task = task; __entry->task_id = task->tk_pid;
__entry->clnt = task->tk_client; __entry->client_id = task->tk_client->cl_clid;
__entry->status = task->tk_status; __entry->status = task->tk_status;
), ),
TP_printk("task:%p@%p, status %d",__entry->task, __entry->clnt, __entry->status) TP_printk("task:%u@%u, status %d",
__entry->task_id, __entry->client_id,
__entry->status)
); );
DEFINE_EVENT(rpc_task_status, rpc_call_status, DEFINE_EVENT(rpc_task_status, rpc_call_status,
@ -47,18 +51,20 @@ TRACE_EVENT(rpc_connect_status,
TP_ARGS(task, status), TP_ARGS(task, status),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(const struct rpc_task *, task) __field(unsigned int, task_id)
__field(const struct rpc_clnt *, clnt) __field(unsigned int, client_id)
__field(int, status) __field(int, status)
), ),
TP_fast_assign( TP_fast_assign(
__entry->task = task; __entry->task_id = task->tk_pid;
__entry->clnt = task->tk_client; __entry->client_id = task->tk_client->cl_clid;
__entry->status = status; __entry->status = status;
), ),
TP_printk("task:%p@%p, status %d",__entry->task, __entry->clnt, __entry->status) TP_printk("task:%u@%u, status %d",
__entry->task_id, __entry->client_id,
__entry->status)
); );
DECLARE_EVENT_CLASS(rpc_task_running, DECLARE_EVENT_CLASS(rpc_task_running,
@ -68,8 +74,8 @@ DECLARE_EVENT_CLASS(rpc_task_running,
TP_ARGS(clnt, task, action), TP_ARGS(clnt, task, action),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(const struct rpc_clnt *, clnt) __field(unsigned int, task_id)
__field(const struct rpc_task *, task) __field(unsigned int, client_id)
__field(const void *, action) __field(const void *, action)
__field(unsigned long, runstate) __field(unsigned long, runstate)
__field(int, status) __field(int, status)
@ -77,17 +83,16 @@ DECLARE_EVENT_CLASS(rpc_task_running,
), ),
TP_fast_assign( TP_fast_assign(
__entry->clnt = clnt; __entry->client_id = clnt->cl_clid;
__entry->task = task; __entry->task_id = task->tk_pid;
__entry->action = action; __entry->action = action;
__entry->runstate = task->tk_runstate; __entry->runstate = task->tk_runstate;
__entry->status = task->tk_status; __entry->status = task->tk_status;
__entry->flags = task->tk_flags; __entry->flags = task->tk_flags;
), ),
TP_printk("task:%p@%p flags=%4.4x state=%4.4lx status=%d action=%pf", TP_printk("task:%u@%u flags=%4.4x state=%4.4lx status=%d action=%pf",
__entry->task, __entry->task_id, __entry->client_id,
__entry->clnt,
__entry->flags, __entry->flags,
__entry->runstate, __entry->runstate,
__entry->status, __entry->status,
@ -126,8 +131,8 @@ DECLARE_EVENT_CLASS(rpc_task_queued,
TP_ARGS(clnt, task, q), TP_ARGS(clnt, task, q),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(const struct rpc_clnt *, clnt) __field(unsigned int, task_id)
__field(const struct rpc_task *, task) __field(unsigned int, client_id)
__field(unsigned long, timeout) __field(unsigned long, timeout)
__field(unsigned long, runstate) __field(unsigned long, runstate)
__field(int, status) __field(int, status)
@ -136,8 +141,8 @@ DECLARE_EVENT_CLASS(rpc_task_queued,
), ),
TP_fast_assign( TP_fast_assign(
__entry->clnt = clnt; __entry->client_id = clnt->cl_clid;
__entry->task = task; __entry->task_id = task->tk_pid;
__entry->timeout = task->tk_timeout; __entry->timeout = task->tk_timeout;
__entry->runstate = task->tk_runstate; __entry->runstate = task->tk_runstate;
__entry->status = task->tk_status; __entry->status = task->tk_status;
@ -145,9 +150,8 @@ DECLARE_EVENT_CLASS(rpc_task_queued,
__assign_str(q_name, rpc_qname(q)); __assign_str(q_name, rpc_qname(q));
), ),
TP_printk("task:%p@%p flags=%4.4x state=%4.4lx status=%d timeout=%lu queue=%s", TP_printk("task:%u@%u flags=%4.4x state=%4.4lx status=%d timeout=%lu queue=%s",
__entry->task, __entry->task_id, __entry->client_id,
__entry->clnt,
__entry->flags, __entry->flags,
__entry->runstate, __entry->runstate,
__entry->status, __entry->status,
@ -172,6 +176,135 @@ DEFINE_EVENT(rpc_task_queued, rpc_task_wakeup,
); );
#define rpc_show_socket_state(state) \
__print_symbolic(state, \
{ SS_FREE, "FREE" }, \
{ SS_UNCONNECTED, "UNCONNECTED" }, \
{ SS_CONNECTING, "CONNECTING," }, \
{ SS_CONNECTED, "CONNECTED," }, \
{ SS_DISCONNECTING, "DISCONNECTING" })
#define rpc_show_sock_state(state) \
__print_symbolic(state, \
{ TCP_ESTABLISHED, "ESTABLISHED" }, \
{ TCP_SYN_SENT, "SYN_SENT" }, \
{ TCP_SYN_RECV, "SYN_RECV" }, \
{ TCP_FIN_WAIT1, "FIN_WAIT1" }, \
{ TCP_FIN_WAIT2, "FIN_WAIT2" }, \
{ TCP_TIME_WAIT, "TIME_WAIT" }, \
{ TCP_CLOSE, "CLOSE" }, \
{ TCP_CLOSE_WAIT, "CLOSE_WAIT" }, \
{ TCP_LAST_ACK, "LAST_ACK" }, \
{ TCP_LISTEN, "LISTEN" }, \
{ TCP_CLOSING, "CLOSING" })
DECLARE_EVENT_CLASS(xs_socket_event,
TP_PROTO(
struct rpc_xprt *xprt,
struct socket *socket
),
TP_ARGS(xprt, socket),
TP_STRUCT__entry(
__field(unsigned int, socket_state)
__field(unsigned int, sock_state)
__field(unsigned long long, ino)
__string(dstaddr,
xprt->address_strings[RPC_DISPLAY_ADDR])
__string(dstport,
xprt->address_strings[RPC_DISPLAY_PORT])
),
TP_fast_assign(
struct inode *inode = SOCK_INODE(socket);
__entry->socket_state = socket->state;
__entry->sock_state = socket->sk->sk_state;
__entry->ino = (unsigned long long)inode->i_ino;
__assign_str(dstaddr,
xprt->address_strings[RPC_DISPLAY_ADDR]);
__assign_str(dstport,
xprt->address_strings[RPC_DISPLAY_PORT]);
),
TP_printk(
"socket:[%llu] dstaddr=%s/%s "
"state=%u (%s) sk_state=%u (%s)",
__entry->ino, __get_str(dstaddr), __get_str(dstport),
__entry->socket_state,
rpc_show_socket_state(__entry->socket_state),
__entry->sock_state,
rpc_show_sock_state(__entry->sock_state)
)
);
#define DEFINE_RPC_SOCKET_EVENT(name) \
DEFINE_EVENT(xs_socket_event, name, \
TP_PROTO( \
struct rpc_xprt *xprt, \
struct socket *socket \
), \
TP_ARGS(xprt, socket))
DECLARE_EVENT_CLASS(xs_socket_event_done,
TP_PROTO(
struct rpc_xprt *xprt,
struct socket *socket,
int error
),
TP_ARGS(xprt, socket, error),
TP_STRUCT__entry(
__field(int, error)
__field(unsigned int, socket_state)
__field(unsigned int, sock_state)
__field(unsigned long long, ino)
__string(dstaddr,
xprt->address_strings[RPC_DISPLAY_ADDR])
__string(dstport,
xprt->address_strings[RPC_DISPLAY_PORT])
),
TP_fast_assign(
struct inode *inode = SOCK_INODE(socket);
__entry->socket_state = socket->state;
__entry->sock_state = socket->sk->sk_state;
__entry->ino = (unsigned long long)inode->i_ino;
__entry->error = error;
__assign_str(dstaddr,
xprt->address_strings[RPC_DISPLAY_ADDR]);
__assign_str(dstport,
xprt->address_strings[RPC_DISPLAY_PORT]);
),
TP_printk(
"error=%d socket:[%llu] dstaddr=%s/%s "
"state=%u (%s) sk_state=%u (%s)",
__entry->error,
__entry->ino, __get_str(dstaddr), __get_str(dstport),
__entry->socket_state,
rpc_show_socket_state(__entry->socket_state),
__entry->sock_state,
rpc_show_sock_state(__entry->sock_state)
)
);
#define DEFINE_RPC_SOCKET_EVENT_DONE(name) \
DEFINE_EVENT(xs_socket_event_done, name, \
TP_PROTO( \
struct rpc_xprt *xprt, \
struct socket *socket, \
int error \
), \
TP_ARGS(xprt, socket, error))
DEFINE_RPC_SOCKET_EVENT(rpc_socket_state_change);
DEFINE_RPC_SOCKET_EVENT_DONE(rpc_socket_connect);
DEFINE_RPC_SOCKET_EVENT_DONE(rpc_socket_reset_connection);
DEFINE_RPC_SOCKET_EVENT(rpc_socket_close);
DEFINE_RPC_SOCKET_EVENT(rpc_socket_shutdown);
#endif /* _TRACE_SUNRPC_H */ #endif /* _TRACE_SUNRPC_H */
#include <trace/define_trace.h> #include <trace/define_trace.h>

View File

@ -250,11 +250,11 @@ rpcauth_list_flavors(rpc_authflavor_t *array, int size)
EXPORT_SYMBOL_GPL(rpcauth_list_flavors); EXPORT_SYMBOL_GPL(rpcauth_list_flavors);
struct rpc_auth * struct rpc_auth *
rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt) rpcauth_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
{ {
struct rpc_auth *auth; struct rpc_auth *auth;
const struct rpc_authops *ops; const struct rpc_authops *ops;
u32 flavor = pseudoflavor_to_flavor(pseudoflavor); u32 flavor = pseudoflavor_to_flavor(args->pseudoflavor);
auth = ERR_PTR(-EINVAL); auth = ERR_PTR(-EINVAL);
if (flavor >= RPC_AUTH_MAXFLAVOR) if (flavor >= RPC_AUTH_MAXFLAVOR)
@ -269,7 +269,7 @@ rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt)
goto out; goto out;
} }
spin_unlock(&rpc_authflavor_lock); spin_unlock(&rpc_authflavor_lock);
auth = ops->create(clnt, pseudoflavor); auth = ops->create(args, clnt);
module_put(ops->owner); module_put(ops->owner);
if (IS_ERR(auth)) if (IS_ERR(auth))
return auth; return auth;
@ -342,6 +342,27 @@ out_nocache:
} }
EXPORT_SYMBOL_GPL(rpcauth_init_credcache); EXPORT_SYMBOL_GPL(rpcauth_init_credcache);
/*
* Setup a credential key lifetime timeout notification
*/
int
rpcauth_key_timeout_notify(struct rpc_auth *auth, struct rpc_cred *cred)
{
if (!cred->cr_auth->au_ops->key_timeout)
return 0;
return cred->cr_auth->au_ops->key_timeout(auth, cred);
}
EXPORT_SYMBOL_GPL(rpcauth_key_timeout_notify);
bool
rpcauth_cred_key_to_expire(struct rpc_cred *cred)
{
if (!cred->cr_ops->crkey_to_expire)
return false;
return cred->cr_ops->crkey_to_expire(cred);
}
EXPORT_SYMBOL_GPL(rpcauth_cred_key_to_expire);
/* /*
* Destroy a list of credentials * Destroy a list of credentials
*/ */

View File

@ -89,6 +89,7 @@ generic_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
gcred->acred.uid = acred->uid; gcred->acred.uid = acred->uid;
gcred->acred.gid = acred->gid; gcred->acred.gid = acred->gid;
gcred->acred.group_info = acred->group_info; gcred->acred.group_info = acred->group_info;
gcred->acred.ac_flags = 0;
if (gcred->acred.group_info != NULL) if (gcred->acred.group_info != NULL)
get_group_info(gcred->acred.group_info); get_group_info(gcred->acred.group_info);
gcred->acred.machine_cred = acred->machine_cred; gcred->acred.machine_cred = acred->machine_cred;
@ -182,11 +183,78 @@ void rpc_destroy_generic_auth(void)
rpcauth_destroy_credcache(&generic_auth); rpcauth_destroy_credcache(&generic_auth);
} }
/*
* Test the the current time (now) against the underlying credential key expiry
* minus a timeout and setup notification.
*
* The normal case:
* If 'now' is before the key expiry minus RPC_KEY_EXPIRE_TIMEO, set
* the RPC_CRED_NOTIFY_TIMEOUT flag to setup the underlying credential
* rpc_credops crmatch routine to notify this generic cred when it's key
* expiration is within RPC_KEY_EXPIRE_TIMEO, and return 0.
*
* The error case:
* If the underlying cred lookup fails, return -EACCES.
*
* The 'almost' error case:
* If 'now' is within key expiry minus RPC_KEY_EXPIRE_TIMEO, but not within
* key expiry minus RPC_KEY_EXPIRE_FAIL, set the RPC_CRED_EXPIRE_SOON bit
* on the acred ac_flags and return 0.
*/
static int
generic_key_timeout(struct rpc_auth *auth, struct rpc_cred *cred)
{
struct auth_cred *acred = &container_of(cred, struct generic_cred,
gc_base)->acred;
struct rpc_cred *tcred;
int ret = 0;
/* Fast track for non crkey_timeout (no key) underlying credentials */
if (test_bit(RPC_CRED_NO_CRKEY_TIMEOUT, &acred->ac_flags))
return 0;
/* Fast track for the normal case */
if (test_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags))
return 0;
/* lookup_cred either returns a valid referenced rpc_cred, or PTR_ERR */
tcred = auth->au_ops->lookup_cred(auth, acred, 0);
if (IS_ERR(tcred))
return -EACCES;
if (!tcred->cr_ops->crkey_timeout) {
set_bit(RPC_CRED_NO_CRKEY_TIMEOUT, &acred->ac_flags);
ret = 0;
goto out_put;
}
/* Test for the almost error case */
ret = tcred->cr_ops->crkey_timeout(tcred);
if (ret != 0) {
set_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags);
ret = 0;
} else {
/* In case underlying cred key has been reset */
if (test_and_clear_bit(RPC_CRED_KEY_EXPIRE_SOON,
&acred->ac_flags))
dprintk("RPC: UID %d Credential key reset\n",
tcred->cr_uid);
/* set up fasttrack for the normal case */
set_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags);
}
out_put:
put_rpccred(tcred);
return ret;
}
static const struct rpc_authops generic_auth_ops = { static const struct rpc_authops generic_auth_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.au_name = "Generic", .au_name = "Generic",
.lookup_cred = generic_lookup_cred, .lookup_cred = generic_lookup_cred,
.crcreate = generic_create_cred, .crcreate = generic_create_cred,
.key_timeout = generic_key_timeout,
}; };
static struct rpc_auth generic_auth = { static struct rpc_auth generic_auth = {
@ -194,9 +262,23 @@ static struct rpc_auth generic_auth = {
.au_count = ATOMIC_INIT(0), .au_count = ATOMIC_INIT(0),
}; };
static bool generic_key_to_expire(struct rpc_cred *cred)
{
struct auth_cred *acred = &container_of(cred, struct generic_cred,
gc_base)->acred;
bool ret;
get_rpccred(cred);
ret = test_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags);
put_rpccred(cred);
return ret;
}
static const struct rpc_credops generic_credops = { static const struct rpc_credops generic_credops = {
.cr_name = "Generic cred", .cr_name = "Generic cred",
.crdestroy = generic_destroy_cred, .crdestroy = generic_destroy_cred,
.crbind = generic_bind_cred, .crbind = generic_bind_cred,
.crmatch = generic_match, .crmatch = generic_match,
.crkey_to_expire = generic_key_to_expire,
}; };

View File

@ -51,6 +51,7 @@
#include <linux/sunrpc/rpc_pipe_fs.h> #include <linux/sunrpc/rpc_pipe_fs.h>
#include <linux/sunrpc/gss_api.h> #include <linux/sunrpc/gss_api.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <linux/hashtable.h>
#include "../netns.h" #include "../netns.h"
@ -62,6 +63,9 @@ static const struct rpc_credops gss_nullops;
#define GSS_RETRY_EXPIRED 5 #define GSS_RETRY_EXPIRED 5
static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED; static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED;
#define GSS_KEY_EXPIRE_TIMEO 240
static unsigned int gss_key_expire_timeo = GSS_KEY_EXPIRE_TIMEO;
#ifdef RPC_DEBUG #ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH # define RPCDBG_FACILITY RPCDBG_AUTH
#endif #endif
@ -71,19 +75,33 @@ static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED;
* using integrity (two 4-byte integers): */ * using integrity (two 4-byte integers): */
#define GSS_VERF_SLACK 100 #define GSS_VERF_SLACK 100
static DEFINE_HASHTABLE(gss_auth_hash_table, 16);
static DEFINE_SPINLOCK(gss_auth_hash_lock);
struct gss_pipe {
struct rpc_pipe_dir_object pdo;
struct rpc_pipe *pipe;
struct rpc_clnt *clnt;
const char *name;
struct kref kref;
};
struct gss_auth { struct gss_auth {
struct kref kref; struct kref kref;
struct hlist_node hash;
struct rpc_auth rpc_auth; struct rpc_auth rpc_auth;
struct gss_api_mech *mech; struct gss_api_mech *mech;
enum rpc_gss_svc service; enum rpc_gss_svc service;
struct rpc_clnt *client; struct rpc_clnt *client;
struct net *net;
/* /*
* There are two upcall pipes; dentry[1], named "gssd", is used * There are two upcall pipes; dentry[1], named "gssd", is used
* for the new text-based upcall; dentry[0] is named after the * for the new text-based upcall; dentry[0] is named after the
* mechanism (for example, "krb5") and exists for * mechanism (for example, "krb5") and exists for
* backwards-compatibility with older gssd's. * backwards-compatibility with older gssd's.
*/ */
struct rpc_pipe *pipe[2]; struct gss_pipe *gss_pipe[2];
const char *target_name;
}; };
/* pipe_version >= 0 if and only if someone has a pipe open. */ /* pipe_version >= 0 if and only if someone has a pipe open. */
@ -294,7 +312,7 @@ static void put_pipe_version(struct net *net)
static void static void
gss_release_msg(struct gss_upcall_msg *gss_msg) gss_release_msg(struct gss_upcall_msg *gss_msg)
{ {
struct net *net = rpc_net_ns(gss_msg->auth->client); struct net *net = gss_msg->auth->net;
if (!atomic_dec_and_test(&gss_msg->count)) if (!atomic_dec_and_test(&gss_msg->count))
return; return;
put_pipe_version(net); put_pipe_version(net);
@ -406,8 +424,8 @@ static void gss_encode_v0_msg(struct gss_upcall_msg *gss_msg)
} }
static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg, static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg,
struct rpc_clnt *clnt, const char *service_name,
const char *service_name) const char *target_name)
{ {
struct gss_api_mech *mech = gss_msg->auth->mech; struct gss_api_mech *mech = gss_msg->auth->mech;
char *p = gss_msg->databuf; char *p = gss_msg->databuf;
@ -417,8 +435,8 @@ static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg,
mech->gm_name, mech->gm_name,
from_kuid(&init_user_ns, gss_msg->uid)); from_kuid(&init_user_ns, gss_msg->uid));
p += gss_msg->msg.len; p += gss_msg->msg.len;
if (clnt->cl_principal) { if (target_name) {
len = sprintf(p, "target=%s ", clnt->cl_principal); len = sprintf(p, "target=%s ", target_name);
p += len; p += len;
gss_msg->msg.len += len; gss_msg->msg.len += len;
} }
@ -439,21 +457,8 @@ static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg,
BUG_ON(gss_msg->msg.len > UPCALL_BUF_LEN); BUG_ON(gss_msg->msg.len > UPCALL_BUF_LEN);
} }
static void gss_encode_msg(struct gss_upcall_msg *gss_msg,
struct rpc_clnt *clnt,
const char *service_name)
{
struct net *net = rpc_net_ns(clnt);
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
if (sn->pipe_version == 0)
gss_encode_v0_msg(gss_msg);
else /* pipe_version == 1 */
gss_encode_v1_msg(gss_msg, clnt, service_name);
}
static struct gss_upcall_msg * static struct gss_upcall_msg *
gss_alloc_msg(struct gss_auth *gss_auth, struct rpc_clnt *clnt, gss_alloc_msg(struct gss_auth *gss_auth,
kuid_t uid, const char *service_name) kuid_t uid, const char *service_name)
{ {
struct gss_upcall_msg *gss_msg; struct gss_upcall_msg *gss_msg;
@ -462,31 +467,36 @@ gss_alloc_msg(struct gss_auth *gss_auth, struct rpc_clnt *clnt,
gss_msg = kzalloc(sizeof(*gss_msg), GFP_NOFS); gss_msg = kzalloc(sizeof(*gss_msg), GFP_NOFS);
if (gss_msg == NULL) if (gss_msg == NULL)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
vers = get_pipe_version(rpc_net_ns(clnt)); vers = get_pipe_version(gss_auth->net);
if (vers < 0) { if (vers < 0) {
kfree(gss_msg); kfree(gss_msg);
return ERR_PTR(vers); return ERR_PTR(vers);
} }
gss_msg->pipe = gss_auth->pipe[vers]; gss_msg->pipe = gss_auth->gss_pipe[vers]->pipe;
INIT_LIST_HEAD(&gss_msg->list); INIT_LIST_HEAD(&gss_msg->list);
rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq"); rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq");
init_waitqueue_head(&gss_msg->waitqueue); init_waitqueue_head(&gss_msg->waitqueue);
atomic_set(&gss_msg->count, 1); atomic_set(&gss_msg->count, 1);
gss_msg->uid = uid; gss_msg->uid = uid;
gss_msg->auth = gss_auth; gss_msg->auth = gss_auth;
gss_encode_msg(gss_msg, clnt, service_name); switch (vers) {
case 0:
gss_encode_v0_msg(gss_msg);
default:
gss_encode_v1_msg(gss_msg, service_name, gss_auth->target_name);
};
return gss_msg; return gss_msg;
} }
static struct gss_upcall_msg * static struct gss_upcall_msg *
gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cred *cred) gss_setup_upcall(struct gss_auth *gss_auth, struct rpc_cred *cred)
{ {
struct gss_cred *gss_cred = container_of(cred, struct gss_cred *gss_cred = container_of(cred,
struct gss_cred, gc_base); struct gss_cred, gc_base);
struct gss_upcall_msg *gss_new, *gss_msg; struct gss_upcall_msg *gss_new, *gss_msg;
kuid_t uid = cred->cr_uid; kuid_t uid = cred->cr_uid;
gss_new = gss_alloc_msg(gss_auth, clnt, uid, gss_cred->gc_principal); gss_new = gss_alloc_msg(gss_auth, uid, gss_cred->gc_principal);
if (IS_ERR(gss_new)) if (IS_ERR(gss_new))
return gss_new; return gss_new;
gss_msg = gss_add_msg(gss_new); gss_msg = gss_add_msg(gss_new);
@ -527,7 +537,7 @@ gss_refresh_upcall(struct rpc_task *task)
dprintk("RPC: %5u %s for uid %u\n", dprintk("RPC: %5u %s for uid %u\n",
task->tk_pid, __func__, from_kuid(&init_user_ns, cred->cr_uid)); task->tk_pid, __func__, from_kuid(&init_user_ns, cred->cr_uid));
gss_msg = gss_setup_upcall(task->tk_client, gss_auth, cred); gss_msg = gss_setup_upcall(gss_auth, cred);
if (PTR_ERR(gss_msg) == -EAGAIN) { if (PTR_ERR(gss_msg) == -EAGAIN) {
/* XXX: warning on the first, under the assumption we /* XXX: warning on the first, under the assumption we
* shouldn't normally hit this case on a refresh. */ * shouldn't normally hit this case on a refresh. */
@ -566,7 +576,7 @@ out:
static inline int static inline int
gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred) gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred)
{ {
struct net *net = rpc_net_ns(gss_auth->client); struct net *net = gss_auth->net;
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
struct rpc_pipe *pipe; struct rpc_pipe *pipe;
struct rpc_cred *cred = &gss_cred->gc_base; struct rpc_cred *cred = &gss_cred->gc_base;
@ -583,7 +593,7 @@ retry:
timeout = 15 * HZ; timeout = 15 * HZ;
if (!sn->gssd_running) if (!sn->gssd_running)
timeout = HZ >> 2; timeout = HZ >> 2;
gss_msg = gss_setup_upcall(gss_auth->client, gss_auth, cred); gss_msg = gss_setup_upcall(gss_auth, cred);
if (PTR_ERR(gss_msg) == -EAGAIN) { if (PTR_ERR(gss_msg) == -EAGAIN) {
err = wait_event_interruptible_timeout(pipe_version_waitqueue, err = wait_event_interruptible_timeout(pipe_version_waitqueue,
sn->pipe_version >= 0, timeout); sn->pipe_version >= 0, timeout);
@ -797,83 +807,153 @@ gss_pipe_destroy_msg(struct rpc_pipe_msg *msg)
} }
} }
static void gss_pipes_dentries_destroy(struct rpc_auth *auth) static void gss_pipe_dentry_destroy(struct dentry *dir,
struct rpc_pipe_dir_object *pdo)
{ {
struct gss_auth *gss_auth; struct gss_pipe *gss_pipe = pdo->pdo_data;
struct rpc_pipe *pipe = gss_pipe->pipe;
gss_auth = container_of(auth, struct gss_auth, rpc_auth); if (pipe->dentry != NULL) {
if (gss_auth->pipe[0]->dentry) rpc_unlink(pipe->dentry);
rpc_unlink(gss_auth->pipe[0]->dentry); pipe->dentry = NULL;
if (gss_auth->pipe[1]->dentry) }
rpc_unlink(gss_auth->pipe[1]->dentry);
} }
static int gss_pipes_dentries_create(struct rpc_auth *auth) static int gss_pipe_dentry_create(struct dentry *dir,
struct rpc_pipe_dir_object *pdo)
{ {
int err; struct gss_pipe *p = pdo->pdo_data;
struct gss_auth *gss_auth; struct dentry *dentry;
struct rpc_clnt *clnt;
gss_auth = container_of(auth, struct gss_auth, rpc_auth); dentry = rpc_mkpipe_dentry(dir, p->name, p->clnt, p->pipe);
clnt = gss_auth->client; if (IS_ERR(dentry))
return PTR_ERR(dentry);
gss_auth->pipe[1]->dentry = rpc_mkpipe_dentry(clnt->cl_dentry, p->pipe->dentry = dentry;
"gssd",
clnt, gss_auth->pipe[1]);
if (IS_ERR(gss_auth->pipe[1]->dentry))
return PTR_ERR(gss_auth->pipe[1]->dentry);
gss_auth->pipe[0]->dentry = rpc_mkpipe_dentry(clnt->cl_dentry,
gss_auth->mech->gm_name,
clnt, gss_auth->pipe[0]);
if (IS_ERR(gss_auth->pipe[0]->dentry)) {
err = PTR_ERR(gss_auth->pipe[0]->dentry);
goto err_unlink_pipe_1;
}
return 0; return 0;
err_unlink_pipe_1:
rpc_unlink(gss_auth->pipe[1]->dentry);
return err;
} }
static void gss_pipes_dentries_destroy_net(struct rpc_clnt *clnt, static const struct rpc_pipe_dir_object_ops gss_pipe_dir_object_ops = {
struct rpc_auth *auth) .create = gss_pipe_dentry_create,
.destroy = gss_pipe_dentry_destroy,
};
static struct gss_pipe *gss_pipe_alloc(struct rpc_clnt *clnt,
const char *name,
const struct rpc_pipe_ops *upcall_ops)
{
struct gss_pipe *p;
int err = -ENOMEM;
p = kmalloc(sizeof(*p), GFP_KERNEL);
if (p == NULL)
goto err;
p->pipe = rpc_mkpipe_data(upcall_ops, RPC_PIPE_WAIT_FOR_OPEN);
if (IS_ERR(p->pipe)) {
err = PTR_ERR(p->pipe);
goto err_free_gss_pipe;
}
p->name = name;
p->clnt = clnt;
kref_init(&p->kref);
rpc_init_pipe_dir_object(&p->pdo,
&gss_pipe_dir_object_ops,
p);
return p;
err_free_gss_pipe:
kfree(p);
err:
return ERR_PTR(err);
}
struct gss_alloc_pdo {
struct rpc_clnt *clnt;
const char *name;
const struct rpc_pipe_ops *upcall_ops;
};
static int gss_pipe_match_pdo(struct rpc_pipe_dir_object *pdo, void *data)
{
struct gss_pipe *gss_pipe;
struct gss_alloc_pdo *args = data;
if (pdo->pdo_ops != &gss_pipe_dir_object_ops)
return 0;
gss_pipe = container_of(pdo, struct gss_pipe, pdo);
if (strcmp(gss_pipe->name, args->name) != 0)
return 0;
if (!kref_get_unless_zero(&gss_pipe->kref))
return 0;
return 1;
}
static struct rpc_pipe_dir_object *gss_pipe_alloc_pdo(void *data)
{
struct gss_pipe *gss_pipe;
struct gss_alloc_pdo *args = data;
gss_pipe = gss_pipe_alloc(args->clnt, args->name, args->upcall_ops);
if (!IS_ERR(gss_pipe))
return &gss_pipe->pdo;
return NULL;
}
static struct gss_pipe *gss_pipe_get(struct rpc_clnt *clnt,
const char *name,
const struct rpc_pipe_ops *upcall_ops)
{ {
struct net *net = rpc_net_ns(clnt); struct net *net = rpc_net_ns(clnt);
struct super_block *sb; struct rpc_pipe_dir_object *pdo;
struct gss_alloc_pdo args = {
.clnt = clnt,
.name = name,
.upcall_ops = upcall_ops,
};
sb = rpc_get_sb_net(net); pdo = rpc_find_or_alloc_pipe_dir_object(net,
if (sb) { &clnt->cl_pipedir_objects,
if (clnt->cl_dentry) gss_pipe_match_pdo,
gss_pipes_dentries_destroy(auth); gss_pipe_alloc_pdo,
rpc_put_sb_net(net); &args);
} if (pdo != NULL)
return container_of(pdo, struct gss_pipe, pdo);
return ERR_PTR(-ENOMEM);
} }
static int gss_pipes_dentries_create_net(struct rpc_clnt *clnt, static void __gss_pipe_free(struct gss_pipe *p)
struct rpc_auth *auth)
{ {
struct rpc_clnt *clnt = p->clnt;
struct net *net = rpc_net_ns(clnt); struct net *net = rpc_net_ns(clnt);
struct super_block *sb;
int err = 0;
sb = rpc_get_sb_net(net); rpc_remove_pipe_dir_object(net,
if (sb) { &clnt->cl_pipedir_objects,
if (clnt->cl_dentry) &p->pdo);
err = gss_pipes_dentries_create(auth); rpc_destroy_pipe_data(p->pipe);
rpc_put_sb_net(net); kfree(p);
} }
return err;
static void __gss_pipe_release(struct kref *kref)
{
struct gss_pipe *p = container_of(kref, struct gss_pipe, kref);
__gss_pipe_free(p);
}
static void gss_pipe_free(struct gss_pipe *p)
{
if (p != NULL)
kref_put(&p->kref, __gss_pipe_release);
} }
/* /*
* NOTE: we have the opportunity to use different * NOTE: we have the opportunity to use different
* parameters based on the input flavor (which must be a pseudoflavor) * parameters based on the input flavor (which must be a pseudoflavor)
*/ */
static struct rpc_auth * static struct gss_auth *
gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) gss_create_new(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
{ {
rpc_authflavor_t flavor = args->pseudoflavor;
struct gss_auth *gss_auth; struct gss_auth *gss_auth;
struct gss_pipe *gss_pipe;
struct rpc_auth * auth; struct rpc_auth * auth;
int err = -ENOMEM; /* XXX? */ int err = -ENOMEM; /* XXX? */
@ -883,12 +963,20 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
return ERR_PTR(err); return ERR_PTR(err);
if (!(gss_auth = kmalloc(sizeof(*gss_auth), GFP_KERNEL))) if (!(gss_auth = kmalloc(sizeof(*gss_auth), GFP_KERNEL)))
goto out_dec; goto out_dec;
INIT_HLIST_NODE(&gss_auth->hash);
gss_auth->target_name = NULL;
if (args->target_name) {
gss_auth->target_name = kstrdup(args->target_name, GFP_KERNEL);
if (gss_auth->target_name == NULL)
goto err_free;
}
gss_auth->client = clnt; gss_auth->client = clnt;
gss_auth->net = get_net(rpc_net_ns(clnt));
err = -EINVAL; err = -EINVAL;
gss_auth->mech = gss_mech_get_by_pseudoflavor(flavor); gss_auth->mech = gss_mech_get_by_pseudoflavor(flavor);
if (!gss_auth->mech) { if (!gss_auth->mech) {
dprintk("RPC: Pseudoflavor %d not found!\n", flavor); dprintk("RPC: Pseudoflavor %d not found!\n", flavor);
goto err_free; goto err_put_net;
} }
gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor); gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor);
if (gss_auth->service == 0) if (gss_auth->service == 0)
@ -901,42 +989,41 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
atomic_set(&auth->au_count, 1); atomic_set(&auth->au_count, 1);
kref_init(&gss_auth->kref); kref_init(&gss_auth->kref);
err = rpcauth_init_credcache(auth);
if (err)
goto err_put_mech;
/* /*
* Note: if we created the old pipe first, then someone who * Note: if we created the old pipe first, then someone who
* examined the directory at the right moment might conclude * examined the directory at the right moment might conclude
* that we supported only the old pipe. So we instead create * that we supported only the old pipe. So we instead create
* the new pipe first. * the new pipe first.
*/ */
gss_auth->pipe[1] = rpc_mkpipe_data(&gss_upcall_ops_v1, gss_pipe = gss_pipe_get(clnt, "gssd", &gss_upcall_ops_v1);
RPC_PIPE_WAIT_FOR_OPEN); if (IS_ERR(gss_pipe)) {
if (IS_ERR(gss_auth->pipe[1])) { err = PTR_ERR(gss_pipe);
err = PTR_ERR(gss_auth->pipe[1]); goto err_destroy_credcache;
goto err_put_mech;
} }
gss_auth->gss_pipe[1] = gss_pipe;
gss_auth->pipe[0] = rpc_mkpipe_data(&gss_upcall_ops_v0, gss_pipe = gss_pipe_get(clnt, gss_auth->mech->gm_name,
RPC_PIPE_WAIT_FOR_OPEN); &gss_upcall_ops_v0);
if (IS_ERR(gss_auth->pipe[0])) { if (IS_ERR(gss_pipe)) {
err = PTR_ERR(gss_auth->pipe[0]); err = PTR_ERR(gss_pipe);
goto err_destroy_pipe_1; goto err_destroy_pipe_1;
} }
err = gss_pipes_dentries_create_net(clnt, auth); gss_auth->gss_pipe[0] = gss_pipe;
if (err)
goto err_destroy_pipe_0;
err = rpcauth_init_credcache(auth);
if (err)
goto err_unlink_pipes;
return auth; return gss_auth;
err_unlink_pipes:
gss_pipes_dentries_destroy_net(clnt, auth);
err_destroy_pipe_0:
rpc_destroy_pipe_data(gss_auth->pipe[0]);
err_destroy_pipe_1: err_destroy_pipe_1:
rpc_destroy_pipe_data(gss_auth->pipe[1]); gss_pipe_free(gss_auth->gss_pipe[1]);
err_destroy_credcache:
rpcauth_destroy_credcache(auth);
err_put_mech: err_put_mech:
gss_mech_put(gss_auth->mech); gss_mech_put(gss_auth->mech);
err_put_net:
put_net(gss_auth->net);
err_free: err_free:
kfree(gss_auth->target_name);
kfree(gss_auth); kfree(gss_auth);
out_dec: out_dec:
module_put(THIS_MODULE); module_put(THIS_MODULE);
@ -946,10 +1033,11 @@ out_dec:
static void static void
gss_free(struct gss_auth *gss_auth) gss_free(struct gss_auth *gss_auth)
{ {
gss_pipes_dentries_destroy_net(gss_auth->client, &gss_auth->rpc_auth); gss_pipe_free(gss_auth->gss_pipe[0]);
rpc_destroy_pipe_data(gss_auth->pipe[0]); gss_pipe_free(gss_auth->gss_pipe[1]);
rpc_destroy_pipe_data(gss_auth->pipe[1]);
gss_mech_put(gss_auth->mech); gss_mech_put(gss_auth->mech);
put_net(gss_auth->net);
kfree(gss_auth->target_name);
kfree(gss_auth); kfree(gss_auth);
module_put(THIS_MODULE); module_put(THIS_MODULE);
@ -966,17 +1054,101 @@ gss_free_callback(struct kref *kref)
static void static void
gss_destroy(struct rpc_auth *auth) gss_destroy(struct rpc_auth *auth)
{ {
struct gss_auth *gss_auth; struct gss_auth *gss_auth = container_of(auth,
struct gss_auth, rpc_auth);
dprintk("RPC: destroying GSS authenticator %p flavor %d\n", dprintk("RPC: destroying GSS authenticator %p flavor %d\n",
auth, auth->au_flavor); auth, auth->au_flavor);
if (hash_hashed(&gss_auth->hash)) {
spin_lock(&gss_auth_hash_lock);
hash_del(&gss_auth->hash);
spin_unlock(&gss_auth_hash_lock);
}
gss_pipe_free(gss_auth->gss_pipe[0]);
gss_auth->gss_pipe[0] = NULL;
gss_pipe_free(gss_auth->gss_pipe[1]);
gss_auth->gss_pipe[1] = NULL;
rpcauth_destroy_credcache(auth); rpcauth_destroy_credcache(auth);
gss_auth = container_of(auth, struct gss_auth, rpc_auth);
kref_put(&gss_auth->kref, gss_free_callback); kref_put(&gss_auth->kref, gss_free_callback);
} }
static struct gss_auth *
gss_auth_find_or_add_hashed(struct rpc_auth_create_args *args,
struct rpc_clnt *clnt,
struct gss_auth *new)
{
struct gss_auth *gss_auth;
unsigned long hashval = (unsigned long)clnt;
spin_lock(&gss_auth_hash_lock);
hash_for_each_possible(gss_auth_hash_table,
gss_auth,
hash,
hashval) {
if (gss_auth->rpc_auth.au_flavor != args->pseudoflavor)
continue;
if (gss_auth->target_name != args->target_name) {
if (gss_auth->target_name == NULL)
continue;
if (args->target_name == NULL)
continue;
if (strcmp(gss_auth->target_name, args->target_name))
continue;
}
if (!atomic_inc_not_zero(&gss_auth->rpc_auth.au_count))
continue;
goto out;
}
if (new)
hash_add(gss_auth_hash_table, &new->hash, hashval);
gss_auth = new;
out:
spin_unlock(&gss_auth_hash_lock);
return gss_auth;
}
static struct gss_auth *
gss_create_hashed(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
{
struct gss_auth *gss_auth;
struct gss_auth *new;
gss_auth = gss_auth_find_or_add_hashed(args, clnt, NULL);
if (gss_auth != NULL)
goto out;
new = gss_create_new(args, clnt);
if (IS_ERR(new))
return new;
gss_auth = gss_auth_find_or_add_hashed(args, clnt, new);
if (gss_auth != new)
gss_destroy(&new->rpc_auth);
out:
return gss_auth;
}
static struct rpc_auth *
gss_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
{
struct gss_auth *gss_auth;
struct rpc_xprt *xprt = rcu_access_pointer(clnt->cl_xprt);
while (clnt != clnt->cl_parent) {
struct rpc_clnt *parent = clnt->cl_parent;
/* Find the original parent for this transport */
if (rcu_access_pointer(parent->cl_xprt) != xprt)
break;
clnt = parent;
}
gss_auth = gss_create_hashed(args, clnt);
if (IS_ERR(gss_auth))
return ERR_CAST(gss_auth);
return &gss_auth->rpc_auth;
}
/* /*
* gss_destroying_context will cause the RPCSEC_GSS to send a NULL RPC call * gss_destroying_context will cause the RPCSEC_GSS to send a NULL RPC call
* to the server with the GSS control procedure field set to * to the server with the GSS control procedure field set to
@ -1126,10 +1298,32 @@ gss_cred_init(struct rpc_auth *auth, struct rpc_cred *cred)
return err; return err;
} }
/*
* Returns -EACCES if GSS context is NULL or will expire within the
* timeout (miliseconds)
*/
static int
gss_key_timeout(struct rpc_cred *rc)
{
struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base);
unsigned long now = jiffies;
unsigned long expire;
if (gss_cred->gc_ctx == NULL)
return -EACCES;
expire = gss_cred->gc_ctx->gc_expiry - (gss_key_expire_timeo * HZ);
if (time_after(now, expire))
return -EACCES;
return 0;
}
static int static int
gss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags) gss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags)
{ {
struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base); struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base);
int ret;
if (test_bit(RPCAUTH_CRED_NEW, &rc->cr_flags)) if (test_bit(RPCAUTH_CRED_NEW, &rc->cr_flags))
goto out; goto out;
@ -1142,11 +1336,26 @@ out:
if (acred->principal != NULL) { if (acred->principal != NULL) {
if (gss_cred->gc_principal == NULL) if (gss_cred->gc_principal == NULL)
return 0; return 0;
return strcmp(acred->principal, gss_cred->gc_principal) == 0; ret = strcmp(acred->principal, gss_cred->gc_principal) == 0;
goto check_expire;
} }
if (gss_cred->gc_principal != NULL) if (gss_cred->gc_principal != NULL)
return 0; return 0;
return uid_eq(rc->cr_uid, acred->uid); ret = uid_eq(rc->cr_uid, acred->uid);
check_expire:
if (ret == 0)
return ret;
/* Notify acred users of GSS context expiration timeout */
if (test_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags) &&
(gss_key_timeout(rc) != 0)) {
/* test will now be done from generic cred */
test_and_clear_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags);
/* tell NFS layer that key will expire soon */
set_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags);
}
return ret;
} }
/* /*
@ -1292,6 +1501,7 @@ gss_validate(struct rpc_task *task, __be32 *p)
struct xdr_netobj mic; struct xdr_netobj mic;
u32 flav,len; u32 flav,len;
u32 maj_stat; u32 maj_stat;
__be32 *ret = ERR_PTR(-EIO);
dprintk("RPC: %5u %s\n", task->tk_pid, __func__); dprintk("RPC: %5u %s\n", task->tk_pid, __func__);
@ -1307,6 +1517,7 @@ gss_validate(struct rpc_task *task, __be32 *p)
mic.data = (u8 *)p; mic.data = (u8 *)p;
mic.len = len; mic.len = len;
ret = ERR_PTR(-EACCES);
maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic); maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic);
if (maj_stat == GSS_S_CONTEXT_EXPIRED) if (maj_stat == GSS_S_CONTEXT_EXPIRED)
clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
@ -1324,8 +1535,9 @@ gss_validate(struct rpc_task *task, __be32 *p)
return p + XDR_QUADLEN(len); return p + XDR_QUADLEN(len);
out_bad: out_bad:
gss_put_ctx(ctx); gss_put_ctx(ctx);
dprintk("RPC: %5u %s failed.\n", task->tk_pid, __func__); dprintk("RPC: %5u %s failed ret %ld.\n", task->tk_pid, __func__,
return NULL; PTR_ERR(ret));
return ret;
} }
static void gss_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp, static void gss_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp,
@ -1657,8 +1869,6 @@ static const struct rpc_authops authgss_ops = {
.destroy = gss_destroy, .destroy = gss_destroy,
.lookup_cred = gss_lookup_cred, .lookup_cred = gss_lookup_cred,
.crcreate = gss_create_cred, .crcreate = gss_create_cred,
.pipes_create = gss_pipes_dentries_create,
.pipes_destroy = gss_pipes_dentries_destroy,
.list_pseudoflavors = gss_mech_list_pseudoflavors, .list_pseudoflavors = gss_mech_list_pseudoflavors,
.info2flavor = gss_mech_info2flavor, .info2flavor = gss_mech_info2flavor,
.flavor2info = gss_mech_flavor2info, .flavor2info = gss_mech_flavor2info,
@ -1675,6 +1885,7 @@ static const struct rpc_credops gss_credops = {
.crvalidate = gss_validate, .crvalidate = gss_validate,
.crwrap_req = gss_wrap_req, .crwrap_req = gss_wrap_req,
.crunwrap_resp = gss_unwrap_resp, .crunwrap_resp = gss_unwrap_resp,
.crkey_timeout = gss_key_timeout,
}; };
static const struct rpc_credops gss_nullops = { static const struct rpc_credops gss_nullops = {
@ -1762,5 +1973,12 @@ module_param_named(expired_cred_retry_delay,
MODULE_PARM_DESC(expired_cred_retry_delay, "Timeout (in seconds) until " MODULE_PARM_DESC(expired_cred_retry_delay, "Timeout (in seconds) until "
"the RPC engine retries an expired credential"); "the RPC engine retries an expired credential");
module_param_named(key_expire_timeo,
gss_key_expire_timeo,
uint, 0644);
MODULE_PARM_DESC(key_expire_timeo, "Time (in seconds) at the end of a "
"credential keys lifetime where the NFS layer cleans up "
"prior to key expiration");
module_init(init_rpcsec_gss) module_init(init_rpcsec_gss)
module_exit(exit_rpcsec_gss) module_exit(exit_rpcsec_gss)

View File

@ -18,7 +18,7 @@ static struct rpc_auth null_auth;
static struct rpc_cred null_cred; static struct rpc_cred null_cred;
static struct rpc_auth * static struct rpc_auth *
nul_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) nul_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
{ {
atomic_inc(&null_auth.au_count); atomic_inc(&null_auth.au_count);
return &null_auth; return &null_auth;
@ -88,13 +88,13 @@ nul_validate(struct rpc_task *task, __be32 *p)
flavor = ntohl(*p++); flavor = ntohl(*p++);
if (flavor != RPC_AUTH_NULL) { if (flavor != RPC_AUTH_NULL) {
printk("RPC: bad verf flavor: %u\n", flavor); printk("RPC: bad verf flavor: %u\n", flavor);
return NULL; return ERR_PTR(-EIO);
} }
size = ntohl(*p++); size = ntohl(*p++);
if (size != 0) { if (size != 0) {
printk("RPC: bad verf size: %u\n", size); printk("RPC: bad verf size: %u\n", size);
return NULL; return ERR_PTR(-EIO);
} }
return p; return p;

View File

@ -33,7 +33,7 @@ static struct rpc_auth unix_auth;
static const struct rpc_credops unix_credops; static const struct rpc_credops unix_credops;
static struct rpc_auth * static struct rpc_auth *
unx_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) unx_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
{ {
dprintk("RPC: creating UNIX authenticator for client %p\n", dprintk("RPC: creating UNIX authenticator for client %p\n",
clnt); clnt);
@ -192,13 +192,13 @@ unx_validate(struct rpc_task *task, __be32 *p)
flavor != RPC_AUTH_UNIX && flavor != RPC_AUTH_UNIX &&
flavor != RPC_AUTH_SHORT) { flavor != RPC_AUTH_SHORT) {
printk("RPC: bad verf flavor: %u\n", flavor); printk("RPC: bad verf flavor: %u\n", flavor);
return NULL; return ERR_PTR(-EIO);
} }
size = ntohl(*p++); size = ntohl(*p++);
if (size > RPC_MAX_AUTH_SIZE) { if (size > RPC_MAX_AUTH_SIZE) {
printk("RPC: giant verf size: %u\n", size); printk("RPC: giant verf size: %u\n", size);
return NULL; return ERR_PTR(-EIO);
} }
task->tk_rqstp->rq_cred->cr_auth->au_rslack = (size >> 2) + 2; task->tk_rqstp->rq_cred->cr_auth->au_rslack = (size >> 2) + 2;
p += (size >> 2); p += (size >> 2);

View File

@ -102,12 +102,7 @@ static void rpc_unregister_client(struct rpc_clnt *clnt)
static void __rpc_clnt_remove_pipedir(struct rpc_clnt *clnt) static void __rpc_clnt_remove_pipedir(struct rpc_clnt *clnt)
{ {
if (clnt->cl_dentry) { rpc_remove_client_dir(clnt);
if (clnt->cl_auth && clnt->cl_auth->au_ops->pipes_destroy)
clnt->cl_auth->au_ops->pipes_destroy(clnt->cl_auth);
rpc_remove_client_dir(clnt->cl_dentry);
}
clnt->cl_dentry = NULL;
} }
static void rpc_clnt_remove_pipedir(struct rpc_clnt *clnt) static void rpc_clnt_remove_pipedir(struct rpc_clnt *clnt)
@ -123,10 +118,10 @@ static void rpc_clnt_remove_pipedir(struct rpc_clnt *clnt)
} }
static struct dentry *rpc_setup_pipedir_sb(struct super_block *sb, static struct dentry *rpc_setup_pipedir_sb(struct super_block *sb,
struct rpc_clnt *clnt, struct rpc_clnt *clnt)
const char *dir_name)
{ {
static uint32_t clntid; static uint32_t clntid;
const char *dir_name = clnt->cl_program->pipe_dir_name;
char name[15]; char name[15];
struct dentry *dir, *dentry; struct dentry *dir, *dentry;
@ -153,28 +148,35 @@ static struct dentry *rpc_setup_pipedir_sb(struct super_block *sb,
} }
static int static int
rpc_setup_pipedir(struct rpc_clnt *clnt, const char *dir_name, rpc_setup_pipedir(struct super_block *pipefs_sb, struct rpc_clnt *clnt)
struct super_block *pipefs_sb)
{ {
struct dentry *dentry; struct dentry *dentry;
clnt->cl_dentry = NULL; if (clnt->cl_program->pipe_dir_name != NULL) {
if (dir_name == NULL) dentry = rpc_setup_pipedir_sb(pipefs_sb, clnt);
return 0;
dentry = rpc_setup_pipedir_sb(pipefs_sb, clnt, dir_name);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
return PTR_ERR(dentry); return PTR_ERR(dentry);
clnt->cl_dentry = dentry; }
return 0; return 0;
} }
static inline int rpc_clnt_skip_event(struct rpc_clnt *clnt, unsigned long event) static int rpc_clnt_skip_event(struct rpc_clnt *clnt, unsigned long event)
{ {
if (((event == RPC_PIPEFS_MOUNT) && clnt->cl_dentry) || if (clnt->cl_program->pipe_dir_name == NULL)
((event == RPC_PIPEFS_UMOUNT) && !clnt->cl_dentry))
return 1; return 1;
if ((event == RPC_PIPEFS_MOUNT) && atomic_read(&clnt->cl_count) == 0)
switch (event) {
case RPC_PIPEFS_MOUNT:
if (clnt->cl_pipedir_objects.pdh_dentry != NULL)
return 1; return 1;
if (atomic_read(&clnt->cl_count) == 0)
return 1;
break;
case RPC_PIPEFS_UMOUNT:
if (clnt->cl_pipedir_objects.pdh_dentry == NULL)
return 1;
break;
}
return 0; return 0;
} }
@ -186,18 +188,11 @@ static int __rpc_clnt_handle_event(struct rpc_clnt *clnt, unsigned long event,
switch (event) { switch (event) {
case RPC_PIPEFS_MOUNT: case RPC_PIPEFS_MOUNT:
dentry = rpc_setup_pipedir_sb(sb, clnt, dentry = rpc_setup_pipedir_sb(sb, clnt);
clnt->cl_program->pipe_dir_name);
if (!dentry) if (!dentry)
return -ENOENT; return -ENOENT;
if (IS_ERR(dentry)) if (IS_ERR(dentry))
return PTR_ERR(dentry); return PTR_ERR(dentry);
clnt->cl_dentry = dentry;
if (clnt->cl_auth->au_ops->pipes_create) {
err = clnt->cl_auth->au_ops->pipes_create(clnt->cl_auth);
if (err)
__rpc_clnt_remove_pipedir(clnt);
}
break; break;
case RPC_PIPEFS_UMOUNT: case RPC_PIPEFS_UMOUNT:
__rpc_clnt_remove_pipedir(clnt); __rpc_clnt_remove_pipedir(clnt);
@ -230,8 +225,6 @@ static struct rpc_clnt *rpc_get_client_for_event(struct net *net, int event)
spin_lock(&sn->rpc_client_lock); spin_lock(&sn->rpc_client_lock);
list_for_each_entry(clnt, &sn->all_clients, cl_clients) { list_for_each_entry(clnt, &sn->all_clients, cl_clients) {
if (clnt->cl_program->pipe_dir_name == NULL)
continue;
if (rpc_clnt_skip_event(clnt, event)) if (rpc_clnt_skip_event(clnt, event))
continue; continue;
spin_unlock(&sn->rpc_client_lock); spin_unlock(&sn->rpc_client_lock);
@ -282,7 +275,10 @@ static void rpc_clnt_set_nodename(struct rpc_clnt *clnt, const char *nodename)
static int rpc_client_register(const struct rpc_create_args *args, static int rpc_client_register(const struct rpc_create_args *args,
struct rpc_clnt *clnt) struct rpc_clnt *clnt)
{ {
const struct rpc_program *program = args->program; struct rpc_auth_create_args auth_args = {
.pseudoflavor = args->authflavor,
.target_name = args->client_name,
};
struct rpc_auth *auth; struct rpc_auth *auth;
struct net *net = rpc_net_ns(clnt); struct net *net = rpc_net_ns(clnt);
struct super_block *pipefs_sb; struct super_block *pipefs_sb;
@ -290,7 +286,7 @@ static int rpc_client_register(const struct rpc_create_args *args,
pipefs_sb = rpc_get_sb_net(net); pipefs_sb = rpc_get_sb_net(net);
if (pipefs_sb) { if (pipefs_sb) {
err = rpc_setup_pipedir(clnt, program->pipe_dir_name, pipefs_sb); err = rpc_setup_pipedir(pipefs_sb, clnt);
if (err) if (err)
goto out; goto out;
} }
@ -299,7 +295,7 @@ static int rpc_client_register(const struct rpc_create_args *args,
if (pipefs_sb) if (pipefs_sb)
rpc_put_sb_net(net); rpc_put_sb_net(net);
auth = rpcauth_create(args->authflavor, clnt); auth = rpcauth_create(&auth_args, clnt);
if (IS_ERR(auth)) { if (IS_ERR(auth)) {
dprintk("RPC: Couldn't create auth handle (flavor %u)\n", dprintk("RPC: Couldn't create auth handle (flavor %u)\n",
args->authflavor); args->authflavor);
@ -317,7 +313,27 @@ out:
return err; return err;
} }
static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, struct rpc_xprt *xprt) static DEFINE_IDA(rpc_clids);
static int rpc_alloc_clid(struct rpc_clnt *clnt)
{
int clid;
clid = ida_simple_get(&rpc_clids, 0, 0, GFP_KERNEL);
if (clid < 0)
return clid;
clnt->cl_clid = clid;
return 0;
}
static void rpc_free_clid(struct rpc_clnt *clnt)
{
ida_simple_remove(&rpc_clids, clnt->cl_clid);
}
static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args,
struct rpc_xprt *xprt,
struct rpc_clnt *parent)
{ {
const struct rpc_program *program = args->program; const struct rpc_program *program = args->program;
const struct rpc_version *version; const struct rpc_version *version;
@ -343,16 +359,20 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
clnt = kzalloc(sizeof(*clnt), GFP_KERNEL); clnt = kzalloc(sizeof(*clnt), GFP_KERNEL);
if (!clnt) if (!clnt)
goto out_err; goto out_err;
clnt->cl_parent = clnt; clnt->cl_parent = parent ? : clnt;
err = rpc_alloc_clid(clnt);
if (err)
goto out_no_clid;
rcu_assign_pointer(clnt->cl_xprt, xprt); rcu_assign_pointer(clnt->cl_xprt, xprt);
clnt->cl_procinfo = version->procs; clnt->cl_procinfo = version->procs;
clnt->cl_maxproc = version->nrprocs; clnt->cl_maxproc = version->nrprocs;
clnt->cl_protname = program->name;
clnt->cl_prog = args->prognumber ? : program->number; clnt->cl_prog = args->prognumber ? : program->number;
clnt->cl_vers = version->number; clnt->cl_vers = version->number;
clnt->cl_stats = program->stats; clnt->cl_stats = program->stats;
clnt->cl_metrics = rpc_alloc_iostats(clnt); clnt->cl_metrics = rpc_alloc_iostats(clnt);
rpc_init_pipe_dir_head(&clnt->cl_pipedir_objects);
err = -ENOMEM; err = -ENOMEM;
if (clnt->cl_metrics == NULL) if (clnt->cl_metrics == NULL)
goto out_no_stats; goto out_no_stats;
@ -372,12 +392,6 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
clnt->cl_rtt = &clnt->cl_rtt_default; clnt->cl_rtt = &clnt->cl_rtt_default;
rpc_init_rtt(&clnt->cl_rtt_default, clnt->cl_timeout->to_initval); rpc_init_rtt(&clnt->cl_rtt_default, clnt->cl_timeout->to_initval);
clnt->cl_principal = NULL;
if (args->client_name) {
clnt->cl_principal = kstrdup(args->client_name, GFP_KERNEL);
if (!clnt->cl_principal)
goto out_no_principal;
}
atomic_set(&clnt->cl_count, 1); atomic_set(&clnt->cl_count, 1);
@ -387,13 +401,15 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
err = rpc_client_register(args, clnt); err = rpc_client_register(args, clnt);
if (err) if (err)
goto out_no_path; goto out_no_path;
if (parent)
atomic_inc(&parent->cl_count);
return clnt; return clnt;
out_no_path: out_no_path:
kfree(clnt->cl_principal);
out_no_principal:
rpc_free_iostats(clnt->cl_metrics); rpc_free_iostats(clnt->cl_metrics);
out_no_stats: out_no_stats:
rpc_free_clid(clnt);
out_no_clid:
kfree(clnt); kfree(clnt);
out_err: out_err:
rpciod_down(); rpciod_down();
@ -479,7 +495,7 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
if (args->flags & RPC_CLNT_CREATE_NONPRIVPORT) if (args->flags & RPC_CLNT_CREATE_NONPRIVPORT)
xprt->resvport = 0; xprt->resvport = 0;
clnt = rpc_new_client(args, xprt); clnt = rpc_new_client(args, xprt, NULL);
if (IS_ERR(clnt)) if (IS_ERR(clnt))
return clnt; return clnt;
@ -526,15 +542,12 @@ static struct rpc_clnt *__rpc_clone_client(struct rpc_create_args *args,
goto out_err; goto out_err;
args->servername = xprt->servername; args->servername = xprt->servername;
new = rpc_new_client(args, xprt); new = rpc_new_client(args, xprt, clnt);
if (IS_ERR(new)) { if (IS_ERR(new)) {
err = PTR_ERR(new); err = PTR_ERR(new);
goto out_err; goto out_err;
} }
atomic_inc(&clnt->cl_count);
new->cl_parent = clnt;
/* Turn off autobind on clones */ /* Turn off autobind on clones */
new->cl_autobind = 0; new->cl_autobind = 0;
new->cl_softrtry = clnt->cl_softrtry; new->cl_softrtry = clnt->cl_softrtry;
@ -561,7 +574,6 @@ struct rpc_clnt *rpc_clone_client(struct rpc_clnt *clnt)
.prognumber = clnt->cl_prog, .prognumber = clnt->cl_prog,
.version = clnt->cl_vers, .version = clnt->cl_vers,
.authflavor = clnt->cl_auth->au_flavor, .authflavor = clnt->cl_auth->au_flavor,
.client_name = clnt->cl_principal,
}; };
return __rpc_clone_client(&args, clnt); return __rpc_clone_client(&args, clnt);
} }
@ -583,7 +595,6 @@ rpc_clone_client_set_auth(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
.prognumber = clnt->cl_prog, .prognumber = clnt->cl_prog,
.version = clnt->cl_vers, .version = clnt->cl_vers,
.authflavor = flavor, .authflavor = flavor,
.client_name = clnt->cl_principal,
}; };
return __rpc_clone_client(&args, clnt); return __rpc_clone_client(&args, clnt);
} }
@ -629,7 +640,7 @@ void rpc_shutdown_client(struct rpc_clnt *clnt)
might_sleep(); might_sleep();
dprintk_rcu("RPC: shutting down %s client for %s\n", dprintk_rcu("RPC: shutting down %s client for %s\n",
clnt->cl_protname, clnt->cl_program->name,
rcu_dereference(clnt->cl_xprt)->servername); rcu_dereference(clnt->cl_xprt)->servername);
while (!list_empty(&clnt->cl_tasks)) { while (!list_empty(&clnt->cl_tasks)) {
@ -649,17 +660,17 @@ static void
rpc_free_client(struct rpc_clnt *clnt) rpc_free_client(struct rpc_clnt *clnt)
{ {
dprintk_rcu("RPC: destroying %s client for %s\n", dprintk_rcu("RPC: destroying %s client for %s\n",
clnt->cl_protname, clnt->cl_program->name,
rcu_dereference(clnt->cl_xprt)->servername); rcu_dereference(clnt->cl_xprt)->servername);
if (clnt->cl_parent != clnt) if (clnt->cl_parent != clnt)
rpc_release_client(clnt->cl_parent); rpc_release_client(clnt->cl_parent);
rpc_clnt_remove_pipedir(clnt); rpc_clnt_remove_pipedir(clnt);
rpc_unregister_client(clnt); rpc_unregister_client(clnt);
rpc_free_iostats(clnt->cl_metrics); rpc_free_iostats(clnt->cl_metrics);
kfree(clnt->cl_principal);
clnt->cl_metrics = NULL; clnt->cl_metrics = NULL;
xprt_put(rcu_dereference_raw(clnt->cl_xprt)); xprt_put(rcu_dereference_raw(clnt->cl_xprt));
rpciod_down(); rpciod_down();
rpc_free_clid(clnt);
kfree(clnt); kfree(clnt);
} }
@ -720,7 +731,6 @@ struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *old,
.prognumber = program->number, .prognumber = program->number,
.version = vers, .version = vers,
.authflavor = old->cl_auth->au_flavor, .authflavor = old->cl_auth->au_flavor,
.client_name = old->cl_principal,
}; };
struct rpc_clnt *clnt; struct rpc_clnt *clnt;
int err; int err;
@ -1299,7 +1309,7 @@ call_start(struct rpc_task *task)
struct rpc_clnt *clnt = task->tk_client; struct rpc_clnt *clnt = task->tk_client;
dprintk("RPC: %5u call_start %s%d proc %s (%s)\n", task->tk_pid, dprintk("RPC: %5u call_start %s%d proc %s (%s)\n", task->tk_pid,
clnt->cl_protname, clnt->cl_vers, clnt->cl_program->name, clnt->cl_vers,
rpc_proc_name(task), rpc_proc_name(task),
(RPC_IS_ASYNC(task) ? "async" : "sync")); (RPC_IS_ASYNC(task) ? "async" : "sync"));
@ -1423,9 +1433,9 @@ call_refreshresult(struct rpc_task *task)
return; return;
case -ETIMEDOUT: case -ETIMEDOUT:
rpc_delay(task, 3*HZ); rpc_delay(task, 3*HZ);
case -EKEYEXPIRED:
case -EAGAIN: case -EAGAIN:
status = -EACCES; status = -EACCES;
case -EKEYEXPIRED:
if (!task->tk_cred_retry) if (!task->tk_cred_retry)
break; break;
task->tk_cred_retry--; task->tk_cred_retry--;
@ -1912,7 +1922,7 @@ call_status(struct rpc_task *task)
default: default:
if (clnt->cl_chatty) if (clnt->cl_chatty)
printk("%s: RPC call returned error %d\n", printk("%s: RPC call returned error %d\n",
clnt->cl_protname, -status); clnt->cl_program->name, -status);
rpc_exit(task, status); rpc_exit(task, status);
} }
} }
@ -1943,7 +1953,7 @@ call_timeout(struct rpc_task *task)
if (clnt->cl_chatty) { if (clnt->cl_chatty) {
rcu_read_lock(); rcu_read_lock();
printk(KERN_NOTICE "%s: server %s not responding, timed out\n", printk(KERN_NOTICE "%s: server %s not responding, timed out\n",
clnt->cl_protname, clnt->cl_program->name,
rcu_dereference(clnt->cl_xprt)->servername); rcu_dereference(clnt->cl_xprt)->servername);
rcu_read_unlock(); rcu_read_unlock();
} }
@ -1959,7 +1969,7 @@ call_timeout(struct rpc_task *task)
if (clnt->cl_chatty) { if (clnt->cl_chatty) {
rcu_read_lock(); rcu_read_lock();
printk(KERN_NOTICE "%s: server %s not responding, still trying\n", printk(KERN_NOTICE "%s: server %s not responding, still trying\n",
clnt->cl_protname, clnt->cl_program->name,
rcu_dereference(clnt->cl_xprt)->servername); rcu_dereference(clnt->cl_xprt)->servername);
rcu_read_unlock(); rcu_read_unlock();
} }
@ -1994,7 +2004,7 @@ call_decode(struct rpc_task *task)
if (clnt->cl_chatty) { if (clnt->cl_chatty) {
rcu_read_lock(); rcu_read_lock();
printk(KERN_NOTICE "%s: server %s OK\n", printk(KERN_NOTICE "%s: server %s OK\n",
clnt->cl_protname, clnt->cl_program->name,
rcu_dereference(clnt->cl_xprt)->servername); rcu_dereference(clnt->cl_xprt)->servername);
rcu_read_unlock(); rcu_read_unlock();
} }
@ -2019,7 +2029,7 @@ call_decode(struct rpc_task *task)
goto out_retry; goto out_retry;
} }
dprintk("RPC: %s: too small RPC reply size (%d bytes)\n", dprintk("RPC: %s: too small RPC reply size (%d bytes)\n",
clnt->cl_protname, task->tk_status); clnt->cl_program->name, task->tk_status);
task->tk_action = call_timeout; task->tk_action = call_timeout;
goto out_retry; goto out_retry;
} }
@ -2091,7 +2101,8 @@ rpc_verify_header(struct rpc_task *task)
dprintk("RPC: %5u %s: XDR representation not a multiple of" dprintk("RPC: %5u %s: XDR representation not a multiple of"
" 4 bytes: 0x%x\n", task->tk_pid, __func__, " 4 bytes: 0x%x\n", task->tk_pid, __func__,
task->tk_rqstp->rq_rcv_buf.len); task->tk_rqstp->rq_rcv_buf.len);
goto out_eio; error = -EIO;
goto out_err;
} }
if ((len -= 3) < 0) if ((len -= 3) < 0)
goto out_overflow; goto out_overflow;
@ -2100,6 +2111,7 @@ rpc_verify_header(struct rpc_task *task)
if ((n = ntohl(*p++)) != RPC_REPLY) { if ((n = ntohl(*p++)) != RPC_REPLY) {
dprintk("RPC: %5u %s: not an RPC reply: %x\n", dprintk("RPC: %5u %s: not an RPC reply: %x\n",
task->tk_pid, __func__, n); task->tk_pid, __func__, n);
error = -EIO;
goto out_garbage; goto out_garbage;
} }
@ -2118,7 +2130,8 @@ rpc_verify_header(struct rpc_task *task)
dprintk("RPC: %5u %s: RPC call rejected, " dprintk("RPC: %5u %s: RPC call rejected, "
"unknown error: %x\n", "unknown error: %x\n",
task->tk_pid, __func__, n); task->tk_pid, __func__, n);
goto out_eio; error = -EIO;
goto out_err;
} }
if (--len < 0) if (--len < 0)
goto out_overflow; goto out_overflow;
@ -2163,9 +2176,11 @@ rpc_verify_header(struct rpc_task *task)
task->tk_pid, __func__, n); task->tk_pid, __func__, n);
goto out_err; goto out_err;
} }
if (!(p = rpcauth_checkverf(task, p))) { p = rpcauth_checkverf(task, p);
dprintk("RPC: %5u %s: auth check failed\n", if (IS_ERR(p)) {
task->tk_pid, __func__); error = PTR_ERR(p);
dprintk("RPC: %5u %s: auth check failed with %d\n",
task->tk_pid, __func__, error);
goto out_garbage; /* bad verifier, retry */ goto out_garbage; /* bad verifier, retry */
} }
len = p - (__be32 *)iov->iov_base - 1; len = p - (__be32 *)iov->iov_base - 1;
@ -2218,8 +2233,6 @@ out_garbage:
out_retry: out_retry:
return ERR_PTR(-EAGAIN); return ERR_PTR(-EAGAIN);
} }
out_eio:
error = -EIO;
out_err: out_err:
rpc_exit(task, error); rpc_exit(task, error);
dprintk("RPC: %5u %s: call failed with error %d\n", task->tk_pid, dprintk("RPC: %5u %s: call failed with error %d\n", task->tk_pid,
@ -2291,7 +2304,7 @@ static void rpc_show_task(const struct rpc_clnt *clnt,
printk(KERN_INFO "%5u %04x %6d %8p %8p %8ld %8p %sv%u %s a:%ps q:%s\n", printk(KERN_INFO "%5u %04x %6d %8p %8p %8ld %8p %sv%u %s a:%ps q:%s\n",
task->tk_pid, task->tk_flags, task->tk_status, task->tk_pid, task->tk_flags, task->tk_status,
clnt, task->tk_rqstp, task->tk_timeout, task->tk_ops, clnt, task->tk_rqstp, task->tk_timeout, task->tk_ops,
clnt->cl_protname, clnt->cl_vers, rpc_proc_name(task), clnt->cl_program->name, clnt->cl_vers, rpc_proc_name(task),
task->tk_action, rpc_waitq); task->tk_action, rpc_waitq);
} }

View File

@ -409,7 +409,7 @@ rpc_show_info(struct seq_file *m, void *v)
rcu_read_lock(); rcu_read_lock();
seq_printf(m, "RPC server: %s\n", seq_printf(m, "RPC server: %s\n",
rcu_dereference(clnt->cl_xprt)->servername); rcu_dereference(clnt->cl_xprt)->servername);
seq_printf(m, "service: %s (%d) version %d\n", clnt->cl_protname, seq_printf(m, "service: %s (%d) version %d\n", clnt->cl_program->name,
clnt->cl_prog, clnt->cl_vers); clnt->cl_prog, clnt->cl_vers);
seq_printf(m, "address: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_ADDR)); seq_printf(m, "address: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_ADDR));
seq_printf(m, "protocol: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_PROTO)); seq_printf(m, "protocol: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_PROTO));
@ -480,23 +480,6 @@ static const struct dentry_operations rpc_dentry_operations = {
.d_delete = rpc_delete_dentry, .d_delete = rpc_delete_dentry,
}; };
/*
* Lookup the data. This is trivial - if the dentry didn't already
* exist, we know it is negative.
*/
static struct dentry *
rpc_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
{
if (dentry->d_name.len > NAME_MAX)
return ERR_PTR(-ENAMETOOLONG);
d_add(dentry, NULL);
return NULL;
}
static const struct inode_operations rpc_dir_inode_operations = {
.lookup = rpc_lookup,
};
static struct inode * static struct inode *
rpc_get_inode(struct super_block *sb, umode_t mode) rpc_get_inode(struct super_block *sb, umode_t mode)
{ {
@ -509,7 +492,7 @@ rpc_get_inode(struct super_block *sb, umode_t mode)
switch (mode & S_IFMT) { switch (mode & S_IFMT) {
case S_IFDIR: case S_IFDIR:
inode->i_fop = &simple_dir_operations; inode->i_fop = &simple_dir_operations;
inode->i_op = &rpc_dir_inode_operations; inode->i_op = &simple_dir_inode_operations;
inc_nlink(inode); inc_nlink(inode);
default: default:
break; break;
@ -901,6 +884,159 @@ rpc_unlink(struct dentry *dentry)
} }
EXPORT_SYMBOL_GPL(rpc_unlink); EXPORT_SYMBOL_GPL(rpc_unlink);
/**
* rpc_init_pipe_dir_head - initialise a struct rpc_pipe_dir_head
* @pdh: pointer to struct rpc_pipe_dir_head
*/
void rpc_init_pipe_dir_head(struct rpc_pipe_dir_head *pdh)
{
INIT_LIST_HEAD(&pdh->pdh_entries);
pdh->pdh_dentry = NULL;
}
EXPORT_SYMBOL_GPL(rpc_init_pipe_dir_head);
/**
* rpc_init_pipe_dir_object - initialise a struct rpc_pipe_dir_object
* @pdo: pointer to struct rpc_pipe_dir_object
* @pdo_ops: pointer to const struct rpc_pipe_dir_object_ops
* @pdo_data: pointer to caller-defined data
*/
void rpc_init_pipe_dir_object(struct rpc_pipe_dir_object *pdo,
const struct rpc_pipe_dir_object_ops *pdo_ops,
void *pdo_data)
{
INIT_LIST_HEAD(&pdo->pdo_head);
pdo->pdo_ops = pdo_ops;
pdo->pdo_data = pdo_data;
}
EXPORT_SYMBOL_GPL(rpc_init_pipe_dir_object);
static int
rpc_add_pipe_dir_object_locked(struct net *net,
struct rpc_pipe_dir_head *pdh,
struct rpc_pipe_dir_object *pdo)
{
int ret = 0;
if (pdh->pdh_dentry)
ret = pdo->pdo_ops->create(pdh->pdh_dentry, pdo);
if (ret == 0)
list_add_tail(&pdo->pdo_head, &pdh->pdh_entries);
return ret;
}
static void
rpc_remove_pipe_dir_object_locked(struct net *net,
struct rpc_pipe_dir_head *pdh,
struct rpc_pipe_dir_object *pdo)
{
if (pdh->pdh_dentry)
pdo->pdo_ops->destroy(pdh->pdh_dentry, pdo);
list_del_init(&pdo->pdo_head);
}
/**
* rpc_add_pipe_dir_object - associate a rpc_pipe_dir_object to a directory
* @net: pointer to struct net
* @pdh: pointer to struct rpc_pipe_dir_head
* @pdo: pointer to struct rpc_pipe_dir_object
*
*/
int
rpc_add_pipe_dir_object(struct net *net,
struct rpc_pipe_dir_head *pdh,
struct rpc_pipe_dir_object *pdo)
{
int ret = 0;
if (list_empty(&pdo->pdo_head)) {
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
mutex_lock(&sn->pipefs_sb_lock);
ret = rpc_add_pipe_dir_object_locked(net, pdh, pdo);
mutex_unlock(&sn->pipefs_sb_lock);
}
return ret;
}
EXPORT_SYMBOL_GPL(rpc_add_pipe_dir_object);
/**
* rpc_remove_pipe_dir_object - remove a rpc_pipe_dir_object from a directory
* @net: pointer to struct net
* @pdh: pointer to struct rpc_pipe_dir_head
* @pdo: pointer to struct rpc_pipe_dir_object
*
*/
void
rpc_remove_pipe_dir_object(struct net *net,
struct rpc_pipe_dir_head *pdh,
struct rpc_pipe_dir_object *pdo)
{
if (!list_empty(&pdo->pdo_head)) {
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
mutex_lock(&sn->pipefs_sb_lock);
rpc_remove_pipe_dir_object_locked(net, pdh, pdo);
mutex_unlock(&sn->pipefs_sb_lock);
}
}
EXPORT_SYMBOL_GPL(rpc_remove_pipe_dir_object);
/**
* rpc_find_or_alloc_pipe_dir_object
* @net: pointer to struct net
* @pdh: pointer to struct rpc_pipe_dir_head
* @match: match struct rpc_pipe_dir_object to data
* @alloc: allocate a new struct rpc_pipe_dir_object
* @data: user defined data for match() and alloc()
*
*/
struct rpc_pipe_dir_object *
rpc_find_or_alloc_pipe_dir_object(struct net *net,
struct rpc_pipe_dir_head *pdh,
int (*match)(struct rpc_pipe_dir_object *, void *),
struct rpc_pipe_dir_object *(*alloc)(void *),
void *data)
{
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
struct rpc_pipe_dir_object *pdo;
mutex_lock(&sn->pipefs_sb_lock);
list_for_each_entry(pdo, &pdh->pdh_entries, pdo_head) {
if (!match(pdo, data))
continue;
goto out;
}
pdo = alloc(data);
if (!pdo)
goto out;
rpc_add_pipe_dir_object_locked(net, pdh, pdo);
out:
mutex_unlock(&sn->pipefs_sb_lock);
return pdo;
}
EXPORT_SYMBOL_GPL(rpc_find_or_alloc_pipe_dir_object);
static void
rpc_create_pipe_dir_objects(struct rpc_pipe_dir_head *pdh)
{
struct rpc_pipe_dir_object *pdo;
struct dentry *dir = pdh->pdh_dentry;
list_for_each_entry(pdo, &pdh->pdh_entries, pdo_head)
pdo->pdo_ops->create(dir, pdo);
}
static void
rpc_destroy_pipe_dir_objects(struct rpc_pipe_dir_head *pdh)
{
struct rpc_pipe_dir_object *pdo;
struct dentry *dir = pdh->pdh_dentry;
list_for_each_entry(pdo, &pdh->pdh_entries, pdo_head)
pdo->pdo_ops->destroy(dir, pdo);
}
enum { enum {
RPCAUTH_info, RPCAUTH_info,
RPCAUTH_EOF RPCAUTH_EOF
@ -941,16 +1077,29 @@ struct dentry *rpc_create_client_dir(struct dentry *dentry,
const char *name, const char *name,
struct rpc_clnt *rpc_client) struct rpc_clnt *rpc_client)
{ {
return rpc_mkdir_populate(dentry, name, S_IRUGO | S_IXUGO, NULL, struct dentry *ret;
ret = rpc_mkdir_populate(dentry, name, S_IRUGO | S_IXUGO, NULL,
rpc_clntdir_populate, rpc_client); rpc_clntdir_populate, rpc_client);
if (!IS_ERR(ret)) {
rpc_client->cl_pipedir_objects.pdh_dentry = ret;
rpc_create_pipe_dir_objects(&rpc_client->cl_pipedir_objects);
}
return ret;
} }
/** /**
* rpc_remove_client_dir - Remove a directory created with rpc_create_client_dir() * rpc_remove_client_dir - Remove a directory created with rpc_create_client_dir()
* @dentry: dentry for the pipe * @rpc_client: rpc_client for the pipe
*/ */
int rpc_remove_client_dir(struct dentry *dentry) int rpc_remove_client_dir(struct rpc_clnt *rpc_client)
{ {
struct dentry *dentry = rpc_client->cl_pipedir_objects.pdh_dentry;
if (dentry == NULL)
return 0;
rpc_destroy_pipe_dir_objects(&rpc_client->cl_pipedir_objects);
rpc_client->cl_pipedir_objects.pdh_dentry = NULL;
return rpc_rmdir_depopulate(dentry, rpc_clntdir_depopulate); return rpc_rmdir_depopulate(dentry, rpc_clntdir_depopulate);
} }

View File

@ -258,7 +258,7 @@ static int rpc_wait_bit_killable(void *word)
return 0; return 0;
} }
#ifdef RPC_DEBUG #if defined(RPC_DEBUG) || defined(RPC_TRACEPOINTS)
static void rpc_task_set_debuginfo(struct rpc_task *task) static void rpc_task_set_debuginfo(struct rpc_task *task)
{ {
static atomic_t rpc_pid; static atomic_t rpc_pid;

View File

@ -188,7 +188,7 @@ void rpc_print_iostats(struct seq_file *seq, struct rpc_clnt *clnt)
seq_printf(seq, "\tRPC iostats version: %s ", RPC_IOSTATS_VERS); seq_printf(seq, "\tRPC iostats version: %s ", RPC_IOSTATS_VERS);
seq_printf(seq, "p/v: %u/%u (%s)\n", seq_printf(seq, "p/v: %u/%u (%s)\n",
clnt->cl_prog, clnt->cl_vers, clnt->cl_protname); clnt->cl_prog, clnt->cl_vers, clnt->cl_program->name);
rcu_read_lock(); rcu_read_lock();
xprt = rcu_dereference(clnt->cl_xprt); xprt = rcu_dereference(clnt->cl_xprt);

View File

@ -47,6 +47,8 @@
#include <net/udp.h> #include <net/udp.h>
#include <net/tcp.h> #include <net/tcp.h>
#include <trace/events/sunrpc.h>
#include "sunrpc.h" #include "sunrpc.h"
static void xs_close(struct rpc_xprt *xprt); static void xs_close(struct rpc_xprt *xprt);
@ -665,8 +667,10 @@ static void xs_tcp_shutdown(struct rpc_xprt *xprt)
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt); struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
struct socket *sock = transport->sock; struct socket *sock = transport->sock;
if (sock != NULL) if (sock != NULL) {
kernel_sock_shutdown(sock, SHUT_WR); kernel_sock_shutdown(sock, SHUT_WR);
trace_rpc_socket_shutdown(xprt, sock);
}
} }
/** /**
@ -811,6 +815,7 @@ static void xs_reset_transport(struct sock_xprt *transport)
sk->sk_no_check = 0; sk->sk_no_check = 0;
trace_rpc_socket_close(&transport->xprt, sock);
sock_release(sock); sock_release(sock);
} }
@ -1492,6 +1497,7 @@ static void xs_tcp_state_change(struct sock *sk)
sock_flag(sk, SOCK_ZAPPED), sock_flag(sk, SOCK_ZAPPED),
sk->sk_shutdown); sk->sk_shutdown);
trace_rpc_socket_state_change(xprt, sk->sk_socket);
switch (sk->sk_state) { switch (sk->sk_state) {
case TCP_ESTABLISHED: case TCP_ESTABLISHED:
spin_lock(&xprt->transport_lock); spin_lock(&xprt->transport_lock);
@ -1896,6 +1902,7 @@ static int xs_local_setup_socket(struct sock_xprt *transport)
xprt, xprt->address_strings[RPC_DISPLAY_ADDR]); xprt, xprt->address_strings[RPC_DISPLAY_ADDR]);
status = xs_local_finish_connecting(xprt, sock); status = xs_local_finish_connecting(xprt, sock);
trace_rpc_socket_connect(xprt, sock, status);
switch (status) { switch (status) {
case 0: case 0:
dprintk("RPC: xprt %p connected to %s\n", dprintk("RPC: xprt %p connected to %s\n",
@ -2039,6 +2046,7 @@ static void xs_udp_setup_socket(struct work_struct *work)
xprt->address_strings[RPC_DISPLAY_PORT]); xprt->address_strings[RPC_DISPLAY_PORT]);
xs_udp_finish_connecting(xprt, sock); xs_udp_finish_connecting(xprt, sock);
trace_rpc_socket_connect(xprt, sock, 0);
status = 0; status = 0;
out: out:
xprt_clear_connecting(xprt); xprt_clear_connecting(xprt);
@ -2064,6 +2072,8 @@ static void xs_abort_connection(struct sock_xprt *transport)
memset(&any, 0, sizeof(any)); memset(&any, 0, sizeof(any));
any.sa_family = AF_UNSPEC; any.sa_family = AF_UNSPEC;
result = kernel_connect(transport->sock, &any, sizeof(any), 0); result = kernel_connect(transport->sock, &any, sizeof(any), 0);
trace_rpc_socket_reset_connection(&transport->xprt,
transport->sock, result);
if (!result) if (!result)
xs_sock_reset_connection_flags(&transport->xprt); xs_sock_reset_connection_flags(&transport->xprt);
dprintk("RPC: AF_UNSPEC connect return code %d\n", result); dprintk("RPC: AF_UNSPEC connect return code %d\n", result);
@ -2194,6 +2204,7 @@ static void xs_tcp_setup_socket(struct work_struct *work)
xprt->address_strings[RPC_DISPLAY_PORT]); xprt->address_strings[RPC_DISPLAY_PORT]);
status = xs_tcp_finish_connecting(xprt, sock); status = xs_tcp_finish_connecting(xprt, sock);
trace_rpc_socket_connect(xprt, sock, status);
dprintk("RPC: %p connect status %d connected %d sock state %d\n", dprintk("RPC: %p connect status %d connected %d sock state %d\n",
xprt, -status, xprt_connected(xprt), xprt, -status, xprt_connected(xprt),
sock->sk->sk_state); sock->sk->sk_state);