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:
commit
bf97293eb8
|
@ -1899,6 +1899,18 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
|
|||
The default is to send the implementation identification
|
||||
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=
|
||||
[NFSv4] When set to the default of '1', the NFSv4
|
||||
server will return only numeric uids and gids to
|
||||
|
|
|
@ -4,9 +4,10 @@
|
|||
|
||||
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 \
|
||||
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_SYSCTL) += sysctl.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
|
||||
|
||||
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 \
|
||||
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_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
|
||||
nfs_layout_nfsv41_files-y := nfs4filelayout.o nfs4filelayoutdev.o
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "internal.h"
|
||||
#include "pnfs.h"
|
||||
#include "nfs4session.h"
|
||||
#include "nfs4trace.h"
|
||||
|
||||
#ifdef NFS_DEBUG
|
||||
#define NFSDBG_FACILITY NFSDBG_CALLBACK
|
||||
|
@ -93,6 +94,7 @@ __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy,
|
|||
default:
|
||||
res = htonl(NFS4ERR_RESOURCE);
|
||||
}
|
||||
trace_nfs4_recall_delegation(inode, -ntohl(res));
|
||||
iput(inode);
|
||||
out:
|
||||
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;
|
||||
|
||||
dprintk("%s enter. slotid %d seqid %d\n",
|
||||
dprintk("%s enter. slotid %u seqid %u\n",
|
||||
__func__, args->csa_slotid, args->csa_sequenceid);
|
||||
|
||||
if (args->csa_slotid >= NFS41_BC_MAX_CALLBACKS)
|
||||
return htonl(NFS4ERR_BADSLOT);
|
||||
|
||||
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 */
|
||||
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 */
|
||||
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);
|
||||
/* Signal process_op to set this error on next op */
|
||||
if (args->csa_cachethis == 0)
|
||||
|
@ -462,6 +464,7 @@ out:
|
|||
} else
|
||||
res->csr_status = status;
|
||||
|
||||
trace_nfs4_cb_sequence(args, res, status);
|
||||
dprintk("%s: exit with status = %d res->csr_status %d\n", __func__,
|
||||
ntohl(status), ntohl(res->csr_status));
|
||||
return status;
|
||||
|
@ -518,7 +521,7 @@ __be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy,
|
|||
if (!cps->clp) /* set in cb_sequence */
|
||||
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),
|
||||
args->crsa_target_highest_slotid);
|
||||
|
||||
|
|
|
@ -501,8 +501,7 @@ nfs_get_client(const struct nfs_client_initdata *cl_init,
|
|||
&nn->nfs_client_list);
|
||||
spin_unlock(&nn->nfs_client_lock);
|
||||
new->cl_flags = cl_init->init_flags;
|
||||
return rpc_ops->init_client(new, timeparms, ip_addr,
|
||||
authflavour);
|
||||
return rpc_ops->init_client(new, timeparms, ip_addr);
|
||||
}
|
||||
|
||||
spin_unlock(&nn->nfs_client_lock);
|
||||
|
@ -694,13 +693,12 @@ EXPORT_SYMBOL_GPL(nfs_init_server_rpcclient);
|
|||
* @clp: nfs_client to initialise
|
||||
* @timeparms: timeout parameters for underlying RPC transport
|
||||
* @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.
|
||||
*/
|
||||
struct nfs_client *nfs_init_client(struct nfs_client *clp,
|
||||
const struct rpc_timeout *timeparms,
|
||||
const char *ip_addr, rpc_authflavor_t authflavour)
|
||||
const char *ip_addr)
|
||||
{
|
||||
int error;
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "nfs4_fs.h"
|
||||
#include "delegation.h"
|
||||
#include "internal.h"
|
||||
#include "nfs4trace.h"
|
||||
|
||||
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);
|
||||
put_rpccred(oldcred);
|
||||
rcu_read_unlock();
|
||||
trace_nfs4_reclaim_delegation(inode, res->delegation_type);
|
||||
} else {
|
||||
/* We appear to have raced with a delegation return. */
|
||||
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);
|
||||
nfsi->cache_validity |= NFS_INO_REVAL_FORCED;
|
||||
spin_unlock(&inode->i_lock);
|
||||
trace_nfs4_set_delegation(inode, res->delegation_type);
|
||||
|
||||
out:
|
||||
spin_unlock(&clp->cl_lock);
|
||||
|
|
81
fs/nfs/dir.c
81
fs/nfs/dir.c
|
@ -43,6 +43,8 @@
|
|||
#include "internal.h"
|
||||
#include "fscache.h"
|
||||
|
||||
#include "nfstrace.h"
|
||||
|
||||
/* #define NFS_DEBUG_VERBOSE 1 */
|
||||
|
||||
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))
|
||||
goto out_error;
|
||||
|
||||
trace_nfs_lookup_revalidate_enter(dir, dentry, flags);
|
||||
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label);
|
||||
trace_nfs_lookup_revalidate_exit(dir, dentry, flags, error);
|
||||
if (error)
|
||||
goto out_bad;
|
||||
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;
|
||||
/* Protect against concurrent sillydeletes */
|
||||
trace_nfs_lookup_enter(dir, dentry, flags);
|
||||
nfs_block_sillyrename(parent);
|
||||
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label);
|
||||
if (error == -ENOENT)
|
||||
|
@ -1338,6 +1343,7 @@ no_entry:
|
|||
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
|
||||
out_unblock_sillyrename:
|
||||
nfs_unblock_sillyrename(parent);
|
||||
trace_nfs_lookup_exit(dir, dentry, flags, error);
|
||||
nfs4_label_free(label);
|
||||
out:
|
||||
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);
|
||||
|
||||
out:
|
||||
put_nfs_open_context(ctx);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -1404,6 +1409,7 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
|
|||
struct dentry *res;
|
||||
struct iattr attr = { .ia_valid = ATTR_OPEN };
|
||||
struct inode *inode;
|
||||
unsigned int lookup_flags = 0;
|
||||
int err;
|
||||
|
||||
/* 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",
|
||||
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 */
|
||||
if ((open_flags & O_DIRECTORY)) {
|
||||
if (!d_unhashed(dentry)) {
|
||||
|
@ -1422,6 +1432,7 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
|
|||
*/
|
||||
return -ENOENT;
|
||||
}
|
||||
lookup_flags = LOOKUP_OPEN|LOOKUP_DIRECTORY;
|
||||
goto no_open;
|
||||
}
|
||||
|
||||
|
@ -1442,12 +1453,14 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
|
|||
if (IS_ERR(ctx))
|
||||
goto out;
|
||||
|
||||
trace_nfs_atomic_open_enter(dir, ctx, open_flags);
|
||||
nfs_block_sillyrename(dentry->d_parent);
|
||||
inode = NFS_PROTO(dir)->open_context(dir, ctx, open_flags, &attr);
|
||||
nfs_unblock_sillyrename(dentry->d_parent);
|
||||
if (IS_ERR(inode)) {
|
||||
put_nfs_open_context(ctx);
|
||||
err = PTR_ERR(inode);
|
||||
trace_nfs_atomic_open_exit(dir, ctx, open_flags, err);
|
||||
put_nfs_open_context(ctx);
|
||||
switch (err) {
|
||||
case -ENOENT:
|
||||
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);
|
||||
trace_nfs_atomic_open_exit(dir, ctx, open_flags, err);
|
||||
put_nfs_open_context(ctx);
|
||||
out:
|
||||
return err;
|
||||
|
||||
no_open:
|
||||
res = nfs_lookup(dir, dentry, 0);
|
||||
res = nfs_lookup(dir, dentry, lookup_flags);
|
||||
err = PTR_ERR(res);
|
||||
if (IS_ERR(res))
|
||||
goto out;
|
||||
|
@ -1596,7 +1611,9 @@ int nfs_create(struct inode *dir, struct dentry *dentry,
|
|||
attr.ia_mode = mode;
|
||||
attr.ia_valid = ATTR_MODE;
|
||||
|
||||
trace_nfs_create_enter(dir, dentry, open_flags);
|
||||
error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags);
|
||||
trace_nfs_create_exit(dir, dentry, open_flags, error);
|
||||
if (error != 0)
|
||||
goto out_err;
|
||||
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_valid = ATTR_MODE;
|
||||
|
||||
trace_nfs_mknod_enter(dir, dentry);
|
||||
status = NFS_PROTO(dir)->mknod(dir, dentry, &attr, rdev);
|
||||
trace_nfs_mknod_exit(dir, dentry, status);
|
||||
if (status != 0)
|
||||
goto out_err;
|
||||
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_mode = mode | S_IFDIR;
|
||||
|
||||
trace_nfs_mkdir_enter(dir, dentry);
|
||||
error = NFS_PROTO(dir)->mkdir(dir, dentry, &attr);
|
||||
trace_nfs_mkdir_exit(dir, dentry, error);
|
||||
if (error != 0)
|
||||
goto out_err;
|
||||
return 0;
|
||||
|
@ -1671,12 +1692,21 @@ int nfs_rmdir(struct inode *dir, struct dentry *dentry)
|
|||
dfprintk(VFS, "NFS: rmdir(%s/%ld), %s\n",
|
||||
dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
|
||||
|
||||
error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
|
||||
/* Ensure the VFS deletes this inode */
|
||||
if (error == 0 && dentry->d_inode != NULL)
|
||||
clear_nlink(dentry->d_inode);
|
||||
else if (error == -ENOENT)
|
||||
nfs_dentry_handle_enoent(dentry);
|
||||
trace_nfs_rmdir_enter(dir, dentry);
|
||||
if (dentry->d_inode) {
|
||||
nfs_wait_on_sillyrename(dentry);
|
||||
error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
|
||||
/* Ensure the VFS deletes this inode */
|
||||
switch (error) {
|
||||
case 0:
|
||||
clear_nlink(dentry->d_inode);
|
||||
break;
|
||||
case -ENOENT:
|
||||
nfs_dentry_handle_enoent(dentry);
|
||||
}
|
||||
} else
|
||||
error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
|
||||
trace_nfs_rmdir_exit(dir, dentry, error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
@ -1704,6 +1734,7 @@ static int nfs_safe_remove(struct dentry *dentry)
|
|||
goto out;
|
||||
}
|
||||
|
||||
trace_nfs_remove_enter(dir, dentry);
|
||||
if (inode != NULL) {
|
||||
NFS_PROTO(inode)->return_delegation(inode);
|
||||
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);
|
||||
if (error == -ENOENT)
|
||||
nfs_dentry_handle_enoent(dentry);
|
||||
trace_nfs_remove_exit(dir, dentry, error);
|
||||
out:
|
||||
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,
|
||||
dir->i_ino, dentry->d_name.name);
|
||||
|
||||
trace_nfs_unlink_enter(dir, dentry);
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (d_count(dentry) > 1) {
|
||||
spin_unlock(&dentry->d_lock);
|
||||
/* Start asynchronous writeout of the inode */
|
||||
write_inode_now(dentry->d_inode, 0);
|
||||
error = nfs_sillyrename(dir, dentry);
|
||||
return error;
|
||||
goto out;
|
||||
}
|
||||
if (!d_unhashed(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));
|
||||
} else if (need_rehash)
|
||||
d_rehash(dentry);
|
||||
out:
|
||||
trace_nfs_unlink_exit(dir, dentry, error);
|
||||
return error;
|
||||
}
|
||||
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);
|
||||
kunmap_atomic(kaddr);
|
||||
|
||||
trace_nfs_symlink_enter(dir, dentry);
|
||||
error = NFS_PROTO(dir)->symlink(dir, dentry, page, pathlen, &attr);
|
||||
trace_nfs_symlink_exit(dir, dentry, error);
|
||||
if (error != 0) {
|
||||
dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s) error %d\n",
|
||||
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,
|
||||
dentry->d_parent->d_name.name, dentry->d_name.name);
|
||||
|
||||
trace_nfs_link_enter(inode, dir, dentry);
|
||||
NFS_PROTO(inode)->return_delegation(inode);
|
||||
|
||||
d_drop(dentry);
|
||||
|
@ -1837,6 +1875,7 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
|
|||
ihold(inode);
|
||||
d_add(dentry, inode);
|
||||
}
|
||||
trace_nfs_link_exit(inode, dir, dentry, error);
|
||||
return error;
|
||||
}
|
||||
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,
|
||||
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,
|
||||
* 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:
|
||||
if (rehash)
|
||||
d_rehash(rehash);
|
||||
trace_nfs_rename_exit(old_dir, old_dentry,
|
||||
new_dir, new_dentry, error);
|
||||
if (!error) {
|
||||
if (new_inode != NULL)
|
||||
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;
|
||||
int status;
|
||||
|
||||
trace_nfs_access_enter(inode);
|
||||
|
||||
status = nfs_access_get_cached(inode, cred, &cache);
|
||||
if (status == 0)
|
||||
goto out;
|
||||
goto out_cached;
|
||||
|
||||
/* Be clever: ask server to check for all possible rights */
|
||||
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))
|
||||
set_bit(NFS_INO_STALE, &NFS_I(inode)->flags);
|
||||
}
|
||||
return status;
|
||||
goto out;
|
||||
}
|
||||
nfs_access_add_cache(inode, &cache);
|
||||
out_cached:
|
||||
if ((mask & ~cache.mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) != 0)
|
||||
status = -EACCES;
|
||||
out:
|
||||
if ((mask & ~cache.mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
|
||||
return 0;
|
||||
return -EACCES;
|
||||
trace_nfs_access_exit(inode, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int nfs_open_permission_mask(int openflags)
|
||||
|
@ -2240,11 +2286,6 @@ int nfs_permission(struct inode *inode, int mask)
|
|||
case S_IFLNK:
|
||||
goto out;
|
||||
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;
|
||||
case S_IFDIR:
|
||||
/*
|
||||
|
|
|
@ -37,6 +37,8 @@
|
|||
#include "iostat.h"
|
||||
#include "fscache.h"
|
||||
|
||||
#include "nfstrace.h"
|
||||
|
||||
#define NFSDBG_FACILITY NFSDBG_FILE
|
||||
|
||||
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;
|
||||
struct inode *inode = file_inode(file);
|
||||
|
||||
trace_nfs_fsync_enter(inode);
|
||||
|
||||
do {
|
||||
ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
||||
if (ret != 0)
|
||||
|
@ -310,6 +314,7 @@ nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
|||
end = LLONG_MAX;
|
||||
} while (ret == -EAGAIN);
|
||||
|
||||
trace_nfs_fsync_exit(inode, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -406,6 +411,7 @@ static int nfs_write_end(struct file *file, struct address_space *mapping,
|
|||
struct page *page, void *fsdata)
|
||||
{
|
||||
unsigned offset = pos & (PAGE_CACHE_SIZE - 1);
|
||||
struct nfs_open_context *ctx = nfs_file_open_context(file);
|
||||
int status;
|
||||
|
||||
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)
|
||||
return status;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
return 1;
|
||||
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 0;
|
||||
}
|
||||
|
@ -651,6 +665,10 @@ ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
|
|||
ssize_t result;
|
||||
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)
|
||||
return nfs_file_direct_write(iocb, iov, nr_segs, pos, true);
|
||||
|
||||
|
|
195
fs/nfs/idmap.c
195
fs/nfs/idmap.c
|
@ -49,6 +49,7 @@
|
|||
|
||||
#include "internal.h"
|
||||
#include "netns.h"
|
||||
#include "nfs4trace.h"
|
||||
|
||||
#define NFS_UINT_MAXLEN 11
|
||||
|
||||
|
@ -63,6 +64,7 @@ struct idmap_legacy_upcalldata {
|
|||
};
|
||||
|
||||
struct idmap {
|
||||
struct rpc_pipe_dir_object idmap_pdo;
|
||||
struct rpc_pipe *idmap_pipe;
|
||||
struct idmap_legacy_upcalldata *idmap_upcall_data;
|
||||
struct mutex idmap_mutex;
|
||||
|
@ -310,7 +312,7 @@ static ssize_t nfs_idmap_get_key(const char *name, size_t namelen,
|
|||
if (ret < 0)
|
||||
goto out_up;
|
||||
|
||||
payload = rcu_dereference(rkey->payload.data);
|
||||
payload = rcu_dereference(rkey->payload.rcudata);
|
||||
if (IS_ERR_OR_NULL(payload)) {
|
||||
ret = PTR_ERR(payload);
|
||||
goto out_up;
|
||||
|
@ -401,16 +403,23 @@ static struct key_type key_type_id_resolver_legacy = {
|
|||
.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);
|
||||
pipe->dentry = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int __nfs_idmap_register(struct dentry *dir,
|
||||
struct idmap *idmap,
|
||||
struct rpc_pipe *pipe)
|
||||
static int nfs_idmap_pipe_create(struct dentry *dir,
|
||||
struct rpc_pipe_dir_object *pdo)
|
||||
{
|
||||
struct idmap *idmap = pdo->pdo_data;
|
||||
struct rpc_pipe *pipe = idmap->idmap_pipe;
|
||||
struct dentry *dentry;
|
||||
|
||||
dentry = rpc_mkpipe_dentry(dir, "idmap", idmap, pipe);
|
||||
|
@ -420,36 +429,10 @@ static int __nfs_idmap_register(struct dentry *dir,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void nfs_idmap_unregister(struct nfs_client *clp,
|
||||
struct rpc_pipe *pipe)
|
||||
{
|
||||
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;
|
||||
}
|
||||
static const struct rpc_pipe_dir_object_ops nfs_idmap_pipe_dir_object_ops = {
|
||||
.create = nfs_idmap_pipe_create,
|
||||
.destroy = nfs_idmap_pipe_destroy,
|
||||
};
|
||||
|
||||
int
|
||||
nfs_idmap_new(struct nfs_client *clp)
|
||||
|
@ -462,23 +445,31 @@ nfs_idmap_new(struct nfs_client *clp)
|
|||
if (idmap == NULL)
|
||||
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);
|
||||
if (IS_ERR(pipe)) {
|
||||
error = PTR_ERR(pipe);
|
||||
kfree(idmap);
|
||||
return error;
|
||||
}
|
||||
error = nfs_idmap_register(clp, idmap, pipe);
|
||||
if (error) {
|
||||
rpc_destroy_pipe_data(pipe);
|
||||
kfree(idmap);
|
||||
return error;
|
||||
goto err;
|
||||
}
|
||||
idmap->idmap_pipe = pipe;
|
||||
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;
|
||||
return 0;
|
||||
err_destroy_pipe:
|
||||
rpc_destroy_pipe_data(idmap->idmap_pipe);
|
||||
err:
|
||||
kfree(idmap);
|
||||
return error;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -488,130 +479,26 @@ nfs_idmap_delete(struct nfs_client *clp)
|
|||
|
||||
if (!idmap)
|
||||
return;
|
||||
nfs_idmap_unregister(clp, idmap->idmap_pipe);
|
||||
rpc_destroy_pipe_data(idmap->idmap_pipe);
|
||||
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);
|
||||
}
|
||||
|
||||
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 ret;
|
||||
ret = nfs_idmap_init_keyring();
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
ret = rpc_pipefs_notifier_register(&nfs_idmap_block);
|
||||
if (ret != 0)
|
||||
nfs_idmap_quit_keyring();
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void nfs_idmap_quit(void)
|
||||
{
|
||||
rpc_pipefs_notifier_unregister(&nfs_idmap_block);
|
||||
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))
|
||||
ret = -ERANGE;
|
||||
}
|
||||
trace_nfs4_map_name_to_uid(name, namelen, id, 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))
|
||||
ret = -ERANGE;
|
||||
}
|
||||
trace_nfs4_map_group_to_gid(name, namelen, id, 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);
|
||||
if (ret < 0)
|
||||
ret = nfs_map_numeric_to_string(id, buf, buflen);
|
||||
trace_nfs4_map_uid_to_name(buf, ret, id, ret);
|
||||
return ret;
|
||||
}
|
||||
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);
|
||||
if (ret < 0)
|
||||
ret = nfs_map_numeric_to_string(id, buf, buflen);
|
||||
trace_nfs4_map_gid_to_group(buf, ret, id, ret);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/crc32.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
|
@ -52,6 +51,8 @@
|
|||
#include "nfs.h"
|
||||
#include "netns.h"
|
||||
|
||||
#include "nfstrace.h"
|
||||
|
||||
#define NFSDBG_FACILITY NFSDBG_VFS
|
||||
|
||||
#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)
|
||||
return 0;
|
||||
|
||||
trace_nfs_setattr_enter(inode);
|
||||
|
||||
/* Write all dirty data */
|
||||
if (S_ISREG(inode->i_mode)) {
|
||||
nfs_inode_dio_wait(inode);
|
||||
|
@ -522,6 +525,7 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
|
|||
error = nfs_refresh_inode(inode, fattr);
|
||||
nfs_free_fattr(fattr);
|
||||
out:
|
||||
trace_nfs_setattr_exit(inode, error);
|
||||
return error;
|
||||
}
|
||||
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 err;
|
||||
|
||||
trace_nfs_getattr_enter(inode);
|
||||
/* Flush out writes to the server in order to update c/mtime. */
|
||||
if (S_ISREG(inode->i_mode)) {
|
||||
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));
|
||||
}
|
||||
out:
|
||||
trace_nfs_getattr_exit(inode, err);
|
||||
return err;
|
||||
}
|
||||
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",
|
||||
inode->i_sb->s_id, (long long)NFS_FILEID(inode));
|
||||
|
||||
trace_nfs_revalidate_inode_enter(inode);
|
||||
|
||||
if (is_bad_inode(inode))
|
||||
goto out;
|
||||
if (NFS_STALE(inode))
|
||||
|
@ -925,6 +933,7 @@ err_out:
|
|||
nfs4_label_free(label);
|
||||
out:
|
||||
nfs_free_fattr(fattr);
|
||||
trace_nfs_revalidate_inode_exit(inode, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -981,6 +990,7 @@ static int nfs_invalidate_mapping(struct inode *inode, struct address_space *map
|
|||
spin_unlock(&inode->i_lock);
|
||||
nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE);
|
||||
nfs_fscache_wait_on_invalidate(inode);
|
||||
|
||||
dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n",
|
||||
inode->i_sb->s_id, (long long)NFS_FILEID(inode));
|
||||
return 0;
|
||||
|
@ -1014,8 +1024,12 @@ int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
|
|||
if (ret < 0)
|
||||
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);
|
||||
trace_nfs_invalidate_mapping_exit(inode, ret);
|
||||
}
|
||||
|
||||
out:
|
||||
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
|
||||
* 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)
|
||||
{
|
||||
int ret;
|
||||
|
||||
trace_nfs_refresh_inode_enter(inode);
|
||||
|
||||
if (nfs_inode_attrs_need_update(inode, fattr))
|
||||
return nfs_update_inode(inode, fattr);
|
||||
return nfs_check_inode_attributes(inode, fattr);
|
||||
ret = nfs_update_inode(inode, fattr);
|
||||
else
|
||||
ret = nfs_check_inode_attributes(inode, fattr);
|
||||
|
||||
trace_nfs_refresh_inode_exit(inode, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "nfs4_fs.h"
|
||||
#include <linux/mount.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/crc32.h>
|
||||
|
||||
#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,
|
||||
unsigned int ds_timeo,
|
||||
unsigned int ds_retrans);
|
||||
extern struct rpc_clnt *nfs4_find_or_create_ds_client(struct nfs_client *,
|
||||
struct inode *);
|
||||
#ifdef CONFIG_PROC_FS
|
||||
extern int __init nfs_fs_proc_init(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);
|
||||
extern struct nfs_client *nfs_init_client(struct nfs_client *clp,
|
||||
const struct rpc_timeout *timeparms,
|
||||
const char *ip_addr, rpc_authflavor_t authflavour);
|
||||
const char *ip_addr);
|
||||
|
||||
/* dir.c */
|
||||
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 *,
|
||||
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
|
||||
|
||||
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,
|
||||
struct inode *inode,
|
||||
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
|
||||
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 struct nfs_client *nfs4_init_client(struct nfs_client *clp,
|
||||
const struct rpc_timeout *timeparms,
|
||||
const char *ip_addr,
|
||||
rpc_authflavor_t authflavour);
|
||||
const char *ip_addr);
|
||||
extern int nfs40_walk_client_list(struct nfs_client *clp,
|
||||
struct nfs_client **result,
|
||||
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;
|
||||
}
|
||||
|
||||
#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
|
||||
|
|
|
@ -336,8 +336,8 @@ nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
|
|||
data->arg.create.createmode = NFS3_CREATE_UNCHECKED;
|
||||
if (flags & O_EXCL) {
|
||||
data->arg.create.createmode = NFS3_CREATE_EXCLUSIVE;
|
||||
data->arg.create.verifier[0] = jiffies;
|
||||
data->arg.create.verifier[1] = current->pid;
|
||||
data->arg.create.verifier[0] = cpu_to_be32(jiffies);
|
||||
data->arg.create.verifier[1] = cpu_to_be32(current->pid);
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nfs3_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data)
|
||||
|
|
|
@ -38,17 +38,15 @@ struct nfs4_minor_version_ops {
|
|||
u32 minor_version;
|
||||
unsigned init_caps;
|
||||
|
||||
int (*call_sync)(struct rpc_clnt *clnt,
|
||||
struct nfs_server *server,
|
||||
struct rpc_message *msg,
|
||||
struct nfs4_sequence_args *args,
|
||||
struct nfs4_sequence_res *res);
|
||||
int (*init_client)(struct nfs_client *);
|
||||
void (*shutdown_client)(struct nfs_client *);
|
||||
bool (*match_stateid)(const nfs4_stateid *,
|
||||
const nfs4_stateid *);
|
||||
int (*find_root_sec)(struct nfs_server *, struct nfs_fh *,
|
||||
struct nfs_fsinfo *);
|
||||
int (*free_lock_state)(struct nfs_server *,
|
||||
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 *nograce_recovery_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 nfs4_state * ls_state; /* Pointer to open state */
|
||||
#define NFS_LOCK_INITIALIZED 0
|
||||
#define NFS_LOCK_LOST 1
|
||||
unsigned long ls_flags;
|
||||
struct nfs_seqid_counter ls_seqid;
|
||||
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_lock)(struct nfs4_state *, struct file_lock *);
|
||||
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 (*detect_trunking)(struct nfs_client *, struct nfs_client **,
|
||||
struct rpc_cred *);
|
||||
|
@ -223,7 +221,7 @@ struct vfsmount *nfs4_submount(struct nfs_server *, struct dentry *,
|
|||
/* 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_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_exchange_id(struct nfs_client *clp, struct rpc_cred *cred);
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
struct nfs4_sequence_args *args, struct nfs4_sequence_res *res,
|
||||
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;
|
||||
}
|
||||
|
||||
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 */
|
||||
static inline struct nfs4_session *nfs4_get_session(const struct nfs_server *server)
|
||||
{
|
||||
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
|
||||
is_ds_only_client(struct nfs_client *clp)
|
||||
{
|
||||
|
@ -298,6 +338,18 @@ is_ds_client(struct nfs_client *clp)
|
|||
{
|
||||
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 */
|
||||
|
||||
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_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 *);
|
||||
|
||||
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 *);
|
||||
|
||||
/* 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_renew_cred_locked(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,
|
||||
struct nfs_client **, struct rpc_cred *);
|
||||
#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,
|
||||
struct nfs_client **, struct rpc_cred *);
|
||||
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 unsigned short max_session_slots;
|
||||
extern unsigned short send_implementation_id;
|
||||
extern bool recover_lost_locks;
|
||||
|
||||
#define NFS4_CLIENT_ID_UNIQ_LEN (64)
|
||||
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_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 /* __LINUX_FS_NFS_NFS4_FS.H */
|
||||
|
|
|
@ -41,19 +41,138 @@ static int nfs_get_cb_ident_idr(struct nfs_client *clp, int minorversion)
|
|||
}
|
||||
|
||||
#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)) {
|
||||
nfs4_shutdown_ds_clients(clp);
|
||||
nfs4_destroy_session(clp->cl_session);
|
||||
nfs4_destroy_clientid(clp);
|
||||
}
|
||||
|
||||
}
|
||||
#else /* CONFIG_NFS_V4_1 */
|
||||
static void nfs4_shutdown_session(struct nfs_client *clp)
|
||||
#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);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_NFS_V4_1 */
|
||||
|
||||
struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
|
||||
{
|
||||
|
@ -73,6 +192,7 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
|
|||
|
||||
spin_lock_init(&clp->cl_lock);
|
||||
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");
|
||||
clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
|
||||
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))
|
||||
nfs4_kill_renewd(clp);
|
||||
nfs4_shutdown_session(clp);
|
||||
clp->cl_mvops->shutdown_client(clp);
|
||||
nfs4_destroy_callback(clp);
|
||||
if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state))
|
||||
nfs_idmap_delete(clp);
|
||||
|
@ -144,34 +264,77 @@ static int nfs4_init_callback(struct nfs_client *clp)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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)
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* Create the session and mark it expired.
|
||||
* When a SEQUENCE operation encounters the expired session
|
||||
* it will do session recovery to initialize it.
|
||||
*/
|
||||
session = nfs4_alloc_session(clp);
|
||||
if (!session)
|
||||
return -ENOMEM;
|
||||
|
||||
clp->cl_session = session;
|
||||
|
||||
/*
|
||||
* The create session reply races with the server back
|
||||
* channel probe. Mark the client NFS_CS_SESSION_INITING
|
||||
* so that the client back channel can find the
|
||||
* nfs_client struct
|
||||
*/
|
||||
nfs_mark_client_ready(clp, NFS_CS_SESSION_INITING);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
#if defined(CONFIG_NFS_V4_1)
|
||||
if (clp->cl_mvops->minor_version) {
|
||||
struct nfs4_session *session = NULL;
|
||||
/*
|
||||
* Create the session and mark it expired.
|
||||
* When a SEQUENCE operation encounters the expired session
|
||||
* it will do session recovery to initialize it.
|
||||
*/
|
||||
session = nfs4_alloc_session(clp);
|
||||
if (!session)
|
||||
return -ENOMEM;
|
||||
|
||||
clp->cl_session = session;
|
||||
/*
|
||||
* The create session reply races with the server back
|
||||
* channel probe. Mark the client NFS_CS_SESSION_INITING
|
||||
* so that the client back channel can find the
|
||||
* nfs_client struct
|
||||
*/
|
||||
nfs_mark_client_ready(clp, NFS_CS_SESSION_INITING);
|
||||
}
|
||||
#endif /* CONFIG_NFS_V4_1 */
|
||||
int ret;
|
||||
|
||||
ret = clp->cl_mvops->init_client(clp);
|
||||
if (ret)
|
||||
return ret;
|
||||
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,
|
||||
const struct rpc_timeout *timeparms,
|
||||
const char *ip_addr,
|
||||
rpc_authflavor_t authflavour)
|
||||
const char *ip_addr)
|
||||
{
|
||||
char buf[INET6_ADDRSTRLEN + 1];
|
||||
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,
|
||||
struct nfs_fh *mntfh)
|
||||
struct nfs_fh *mntfh, bool auth_probe)
|
||||
{
|
||||
struct nfs_fattr *fattr;
|
||||
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 */
|
||||
error = nfs4_get_rootfh(server, mntfh);
|
||||
error = nfs4_get_rootfh(server, mntfh, auth_probe);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
|
||||
|
@ -787,6 +949,7 @@ out:
|
|||
static int nfs4_init_server(struct nfs_server *server,
|
||||
const struct nfs_parsed_mount_data *data)
|
||||
{
|
||||
rpc_authflavor_t pseudoflavor = RPC_AUTH_UNIX;
|
||||
struct rpc_timeout timeparms;
|
||||
int error;
|
||||
|
||||
|
@ -799,13 +962,16 @@ static int nfs4_init_server(struct nfs_server *server,
|
|||
server->flags = data->flags;
|
||||
server->options = data->options;
|
||||
|
||||
if (data->auth_flavor_len >= 1)
|
||||
pseudoflavor = data->auth_flavors[0];
|
||||
|
||||
/* Get a client record */
|
||||
error = nfs4_set_client(server,
|
||||
data->nfs_server.hostname,
|
||||
(const struct sockaddr *)&data->nfs_server.address,
|
||||
data->nfs_server.addrlen,
|
||||
data->client_address,
|
||||
data->auth_flavors[0],
|
||||
pseudoflavor,
|
||||
data->nfs_server.protocol,
|
||||
&timeparms,
|
||||
data->minorversion,
|
||||
|
@ -825,7 +991,7 @@ static int nfs4_init_server(struct nfs_server *server,
|
|||
|
||||
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:
|
||||
/* Done */
|
||||
|
@ -843,6 +1009,7 @@ struct nfs_server *nfs4_create_server(struct nfs_mount_info *mount_info,
|
|||
struct nfs_subversion *nfs_mod)
|
||||
{
|
||||
struct nfs_server *server;
|
||||
bool auth_probe;
|
||||
int error;
|
||||
|
||||
dprintk("--> nfs4_create_server()\n");
|
||||
|
@ -851,12 +1018,14 @@ struct nfs_server *nfs4_create_server(struct nfs_mount_info *mount_info,
|
|||
if (!server)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
auth_probe = mount_info->parsed->auth_flavor_len < 1;
|
||||
|
||||
/* set up the general RPC client */
|
||||
error = nfs4_init_server(server, mount_info->parsed);
|
||||
if (error < 0)
|
||||
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)
|
||||
goto error;
|
||||
|
||||
|
@ -909,7 +1078,8 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
|
|||
if (error < 0)
|
||||
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)
|
||||
goto error;
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "internal.h"
|
||||
#include "delegation.h"
|
||||
#include "nfs4filelayout.h"
|
||||
#include "nfs4trace.h"
|
||||
|
||||
#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;
|
||||
int err;
|
||||
|
||||
trace_nfs4_pnfs_read(data, task->tk_status);
|
||||
err = filelayout_async_handle_error(task, data->args.context->state,
|
||||
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;
|
||||
int err;
|
||||
|
||||
trace_nfs4_pnfs_write(data, task->tk_status);
|
||||
err = filelayout_async_handle_error(task, data->args.context->state,
|
||||
data->ds_clp, hdr->lseg);
|
||||
|
||||
|
@ -395,6 +398,7 @@ static int filelayout_commit_done_cb(struct rpc_task *task,
|
|||
{
|
||||
int err;
|
||||
|
||||
trace_nfs4_pnfs_commit_ds(data, task->tk_status);
|
||||
err = filelayout_async_handle_error(task, NULL, data->ds_clp,
|
||||
data->lseg);
|
||||
|
||||
|
@ -524,6 +528,7 @@ filelayout_read_pagelist(struct nfs_read_data *data)
|
|||
struct nfs_pgio_header *hdr = data->header;
|
||||
struct pnfs_layout_segment *lseg = hdr->lseg;
|
||||
struct nfs4_pnfs_ds *ds;
|
||||
struct rpc_clnt *ds_clnt;
|
||||
loff_t offset = data->args.offset;
|
||||
u32 j, idx;
|
||||
struct nfs_fh *fh;
|
||||
|
@ -538,6 +543,11 @@ filelayout_read_pagelist(struct nfs_read_data *data)
|
|||
ds = nfs4_fl_prepare_ds(lseg, idx);
|
||||
if (!ds)
|
||||
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__,
|
||||
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;
|
||||
|
||||
/* 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);
|
||||
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 pnfs_layout_segment *lseg = hdr->lseg;
|
||||
struct nfs4_pnfs_ds *ds;
|
||||
struct rpc_clnt *ds_clnt;
|
||||
loff_t offset = data->args.offset;
|
||||
u32 j, idx;
|
||||
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);
|
||||
if (!ds)
|
||||
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",
|
||||
__func__, hdr->inode->i_ino, sync, (size_t) data->args.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);
|
||||
|
||||
/* Perform an asynchronous write */
|
||||
nfs_initiate_write(ds->ds_clp->cl_rpcclient, data,
|
||||
nfs_initiate_write(ds_clnt, data,
|
||||
&filelayout_write_call_ops, sync,
|
||||
RPC_TASK_SOFTCONN);
|
||||
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 nfs4_pnfs_ds *ds;
|
||||
struct rpc_clnt *ds_clnt;
|
||||
u32 idx;
|
||||
struct nfs_fh *fh;
|
||||
|
||||
idx = calc_ds_index_from_commit(lseg, data->ds_commit_index);
|
||||
ds = nfs4_fl_prepare_ds(lseg, idx);
|
||||
if (!ds) {
|
||||
prepare_to_resend_writes(data);
|
||||
filelayout_commit_release(data);
|
||||
return -EAGAIN;
|
||||
}
|
||||
if (!ds)
|
||||
goto out_err;
|
||||
|
||||
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__,
|
||||
data->inode->i_ino, how, atomic_read(&ds->ds_clp->cl_count));
|
||||
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);
|
||||
if (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,
|
||||
RPC_TASK_SOFTCONN);
|
||||
out_err:
|
||||
prepare_to_resend_writes(data);
|
||||
filelayout_commit_release(data);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
#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;
|
||||
int ret = -ENOMEM;
|
||||
|
@ -21,7 +21,7 @@ int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh)
|
|||
goto out;
|
||||
|
||||
/* 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) {
|
||||
dprintk("nfs4_get_rootfh: getroot error = %d\n", -ret);
|
||||
goto out;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/mount.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/nfs_fs.h>
|
||||
#include <linux/nfs_mount.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
|
@ -369,21 +370,33 @@ out:
|
|||
struct vfsmount *nfs4_submount(struct nfs_server *server, struct dentry *dentry,
|
||||
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 inode *dir = parent->d_inode;
|
||||
struct qstr *name = &dentry->d_name;
|
||||
struct rpc_clnt *client;
|
||||
struct vfsmount *mnt;
|
||||
|
||||
/* 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);
|
||||
if (IS_ERR(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);
|
||||
else
|
||||
mnt = nfs_do_submount(dentry, fh, fattr, client->cl_auth->au_flavor);
|
||||
goto out;
|
||||
}
|
||||
|
||||
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);
|
||||
return mnt;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -23,6 +23,14 @@
|
|||
|
||||
#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
|
||||
*/
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -76,7 +95,7 @@ void nfs4_free_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *slot)
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -146,9 +165,9 @@ struct nfs4_slot *nfs4_alloc_slot(struct nfs4_slot_table *tbl)
|
|||
ret->generation = tbl->generation;
|
||||
|
||||
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,
|
||||
!IS_ERR(ret) ? ret->slot_nr : -1);
|
||||
!IS_ERR(ret) ? ret->slot_nr : NFS4_NO_SLOT);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -191,7 +210,7 @@ static int nfs4_realloc_slot_table(struct nfs4_slot_table *tbl,
|
|||
{
|
||||
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);
|
||||
|
||||
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);
|
||||
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);
|
||||
out:
|
||||
dprintk("<-- %s: return %d\n", __func__, 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(&session->bc_slot_table, 0);
|
||||
nfs4_shrink_slot_table(tbl, 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)
|
||||
|
@ -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,
|
||||
u32 target_highest_slotid)
|
||||
{
|
||||
|
@ -383,6 +422,12 @@ void nfs41_update_target_slotid(struct nfs4_slot_table *tbl,
|
|||
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
|
||||
*/
|
||||
|
@ -405,31 +450,20 @@ int nfs4_setup_session_slot_tables(struct nfs4_session *ses)
|
|||
if (status && tbl->slots == NULL)
|
||||
/* Fore and back channel share a connection so get
|
||||
* both slot tables or neither */
|
||||
nfs4_destroy_slot_tables(ses);
|
||||
nfs4_destroy_session_slot_tables(ses);
|
||||
return status;
|
||||
}
|
||||
|
||||
struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp)
|
||||
{
|
||||
struct nfs4_session *session;
|
||||
struct nfs4_slot_table *tbl;
|
||||
|
||||
session = kzalloc(sizeof(struct nfs4_session), GFP_NOFS);
|
||||
if (!session)
|
||||
return NULL;
|
||||
|
||||
tbl = &session->fc_slot_table;
|
||||
tbl->highest_used_slotid = NFS4_NO_SLOT;
|
||||
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);
|
||||
|
||||
nfs4_init_slot_table(&session->fc_slot_table, "ForeChannel Slot table");
|
||||
nfs4_init_slot_table(&session->bc_slot_table, "BackChannel Slot table");
|
||||
session->session_state = 1<<NFS4_SESSION_INITING;
|
||||
|
||||
session->clp = clp;
|
||||
|
@ -441,7 +475,7 @@ void nfs4_destroy_session(struct nfs4_session *session)
|
|||
struct rpc_xprt *xprt;
|
||||
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);
|
||||
if (cred)
|
||||
put_rpccred(cred);
|
||||
|
@ -452,7 +486,7 @@ void nfs4_destroy_session(struct nfs4_session *session)
|
|||
dprintk("%s Destroy backchannel for xprt %p\n",
|
||||
__func__, xprt);
|
||||
xprt_destroy_backchannel(xprt, NFS41_BC_MIN_CALLBACKS);
|
||||
nfs4_destroy_slot_tables(session);
|
||||
nfs4_destroy_session_slot_tables(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);
|
||||
|
||||
|
||||
#endif /* defined(CONFIG_NFS_V4_1) */
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#define __LINUX_FS_NFS_NFS4SESSION_H
|
||||
|
||||
/* 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_NO_SLOT ((u32)-1)
|
||||
|
||||
|
@ -72,10 +72,22 @@ enum nfs4_session_state {
|
|||
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 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,
|
||||
u32 target_highest_slotid);
|
||||
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_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.
|
||||
*/
|
||||
|
@ -117,6 +118,16 @@ static inline int nfs4_has_persistent_session(const struct nfs_client *clp)
|
|||
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) */
|
||||
|
||||
static inline int nfs4_init_session(struct nfs_client *clp)
|
||||
|
|
|
@ -154,6 +154,19 @@ struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp)
|
|||
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 *
|
||||
nfs4_get_renew_cred_server_locked(struct nfs_server *server)
|
||||
{
|
||||
|
@ -202,8 +215,61 @@ out:
|
|||
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)
|
||||
|
||||
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)
|
||||
{
|
||||
int status;
|
||||
|
@ -228,60 +294,6 @@ static int nfs41_setup_state_renewal(struct nfs_client *clp)
|
|||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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 */
|
||||
|
||||
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
|
||||
*
|
||||
* 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;
|
||||
|
||||
spin_lock(&clp->cl_lock);
|
||||
cred = nfs4_get_machine_cred_locked(clp);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -998,7 +969,9 @@ static int nfs4_copy_lock_stateid(nfs4_stateid *dst,
|
|||
fl_pid = lockowner->l_pid;
|
||||
spin_lock(&state->state_lock);
|
||||
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);
|
||||
ret = 0;
|
||||
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,
|
||||
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))
|
||||
goto out;
|
||||
ret = nfs4_copy_lock_stateid(dst, state, lockowner);
|
||||
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;
|
||||
ret = nfs4_copy_open_stateid(dst, state);
|
||||
out:
|
||||
|
@ -1443,14 +1422,16 @@ restart:
|
|||
if (status >= 0) {
|
||||
status = nfs4_reclaim_locks(state, ops);
|
||||
if (status >= 0) {
|
||||
spin_lock(&state->state_lock);
|
||||
list_for_each_entry(lock, &state->lock_states, ls_locks) {
|
||||
if (!test_bit(NFS_LOCK_INITIALIZED, &lock->ls_flags))
|
||||
pr_warn_ratelimited("NFS: "
|
||||
"%s: Lock reclaim "
|
||||
"failed!\n", __func__);
|
||||
if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0) {
|
||||
spin_lock(&state->state_lock);
|
||||
list_for_each_entry(lock, &state->lock_states, ls_locks) {
|
||||
if (!test_bit(NFS_LOCK_INITIALIZED, &lock->ls_flags))
|
||||
pr_warn_ratelimited("NFS: "
|
||||
"%s: Lock reclaim "
|
||||
"failed!\n", __func__);
|
||||
}
|
||||
spin_unlock(&state->state_lock);
|
||||
}
|
||||
spin_unlock(&state->state_lock);
|
||||
nfs4_put_open_state(state);
|
||||
spin_lock(&sp->so_lock);
|
||||
goto restart;
|
||||
|
@ -1618,7 +1599,7 @@ static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp)
|
|||
if (!nfs4_state_clear_reclaim_reboot(clp))
|
||||
return;
|
||||
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);
|
||||
put_rpccred(cred);
|
||||
}
|
||||
|
@ -1732,7 +1713,7 @@ static int nfs4_check_lease(struct nfs_client *clp)
|
|||
cred = ops->get_state_renewal_cred_locked(clp);
|
||||
spin_unlock(&clp->cl_lock);
|
||||
if (cred == NULL) {
|
||||
cred = nfs4_get_setclientid_cred(clp);
|
||||
cred = nfs4_get_clid_cred(clp);
|
||||
status = -ENOKEY;
|
||||
if (cred == NULL)
|
||||
goto out;
|
||||
|
@ -1804,7 +1785,7 @@ static int nfs4_establish_lease(struct nfs_client *clp)
|
|||
clp->cl_mvops->reboot_recovery_ops;
|
||||
int status;
|
||||
|
||||
cred = ops->get_clid_cred(clp);
|
||||
cred = nfs4_get_clid_cred(clp);
|
||||
if (cred == NULL)
|
||||
return -ENOENT;
|
||||
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);
|
||||
again:
|
||||
status = -ENOENT;
|
||||
cred = ops->get_clid_cred(clp);
|
||||
cred = nfs4_get_clid_cred(clp);
|
||||
if (cred == NULL)
|
||||
goto out_unlock;
|
||||
|
||||
|
@ -1896,7 +1877,11 @@ again:
|
|||
__func__, status);
|
||||
goto again;
|
||||
case -EACCES:
|
||||
if (i++)
|
||||
if (i++ == 0) {
|
||||
nfs4_root_machine_cred(clp);
|
||||
goto again;
|
||||
}
|
||||
if (i > 2)
|
||||
break;
|
||||
case -NFS4ERR_CLID_INUSE:
|
||||
case -NFS4ERR_WRONGSEC:
|
||||
|
@ -2052,7 +2037,7 @@ static int nfs4_reset_session(struct nfs_client *clp)
|
|||
if (!nfs4_has_session(clp))
|
||||
return 0;
|
||||
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);
|
||||
switch (status) {
|
||||
case 0:
|
||||
|
@ -2095,7 +2080,7 @@ static int nfs4_bind_conn_to_session(struct nfs_client *clp)
|
|||
if (!nfs4_has_session(clp))
|
||||
return 0;
|
||||
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);
|
||||
if (cred)
|
||||
put_rpccred(cred);
|
||||
|
@ -2116,7 +2101,6 @@ static int nfs4_bind_conn_to_session(struct nfs_client *clp)
|
|||
}
|
||||
#else /* CONFIG_NFS_V4_1 */
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -253,8 +253,6 @@ struct dentry *nfs4_try_mount(int flags, const char *dev_name,
|
|||
|
||||
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;
|
||||
data->nfs_server.export_path = "/";
|
||||
root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, mount_info,
|
||||
|
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
238
fs/nfs/nfs4xdr.c
238
fs/nfs/nfs4xdr.c
|
@ -294,7 +294,9 @@ static int nfs4_stat_to_errno(int);
|
|||
XDR_QUADLEN(NFS4_EXCHANGE_ID_LEN) + \
|
||||
1 /* flags */ + \
|
||||
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 /* nii_domain */ + \
|
||||
XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + \
|
||||
|
@ -306,7 +308,9 @@ static int nfs4_stat_to_errno(int);
|
|||
1 /* eir_sequenceid */ + \
|
||||
1 /* eir_flags */ + \
|
||||
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 */ + \
|
||||
/* eir_server_owner.so_major_id<> */ \
|
||||
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_grouplen = 0;
|
||||
__be32 *p;
|
||||
__be32 *q;
|
||||
int len;
|
||||
uint32_t bmval_len = 2;
|
||||
uint32_t bmval0 = 0;
|
||||
uint32_t bmval1 = 0;
|
||||
uint32_t bmval2 = 0;
|
||||
unsigned i;
|
||||
uint32_t len = 0;
|
||||
uint32_t bmval_len;
|
||||
uint32_t bmval[3] = { 0 };
|
||||
|
||||
/*
|
||||
* 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
|
||||
* such as owner/group.
|
||||
*/
|
||||
len = 8;
|
||||
|
||||
/* Sigh */
|
||||
if (iap->ia_valid & ATTR_SIZE)
|
||||
if (iap->ia_valid & ATTR_SIZE) {
|
||||
bmval[0] |= FATTR4_WORD0_SIZE;
|
||||
len += 8;
|
||||
if (iap->ia_valid & ATTR_MODE)
|
||||
}
|
||||
if (iap->ia_valid & ATTR_MODE) {
|
||||
bmval[1] |= FATTR4_WORD1_MODE;
|
||||
len += 4;
|
||||
}
|
||||
if (iap->ia_valid & ATTR_UID) {
|
||||
owner_namelen = nfs_map_uid_to_name(server, iap->ia_uid, owner_name, IDMAP_NAMESZ);
|
||||
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;
|
||||
/* goto out; */
|
||||
}
|
||||
bmval[1] |= FATTR4_WORD1_OWNER;
|
||||
len += 4 + (XDR_QUADLEN(owner_namelen) << 2);
|
||||
}
|
||||
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;
|
||||
/* goto out; */
|
||||
}
|
||||
bmval[1] |= FATTR4_WORD1_OWNER_GROUP;
|
||||
len += 4 + (XDR_QUADLEN(owner_grouplen) << 2);
|
||||
}
|
||||
if (iap->ia_valid & ATTR_ATIME_SET)
|
||||
len += 16;
|
||||
else if (iap->ia_valid & ATTR_ATIME)
|
||||
len += 4;
|
||||
if (iap->ia_valid & ATTR_MTIME_SET)
|
||||
len += 16;
|
||||
else if (iap->ia_valid & ATTR_MTIME)
|
||||
len += 4;
|
||||
if (label) {
|
||||
len += 4 + 4 + 4 + (XDR_QUADLEN(label->len) << 2);
|
||||
bmval_len = 3;
|
||||
}
|
||||
|
||||
len += bmval_len << 2;
|
||||
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);
|
||||
q = p;
|
||||
/* Skip bitmap entries + attrlen */
|
||||
p += bmval_len + 1;
|
||||
|
||||
if (iap->ia_valid & ATTR_SIZE) {
|
||||
bmval0 |= FATTR4_WORD0_SIZE;
|
||||
p = xdr_encode_hyper(p, iap->ia_size);
|
||||
}
|
||||
if (iap->ia_valid & ATTR_MODE) {
|
||||
bmval1 |= FATTR4_WORD1_MODE;
|
||||
*p++ = cpu_to_be32(iap->ia_mode & S_IALLUGO);
|
||||
}
|
||||
if (iap->ia_valid & ATTR_UID) {
|
||||
bmval1 |= FATTR4_WORD1_OWNER;
|
||||
p = xdr_encode_opaque(p, owner_name, owner_namelen);
|
||||
}
|
||||
if (iap->ia_valid & ATTR_GID) {
|
||||
bmval1 |= FATTR4_WORD1_OWNER_GROUP;
|
||||
p = xdr_encode_opaque(p, owner_group, owner_grouplen);
|
||||
}
|
||||
if (iap->ia_valid & ATTR_ATIME_SET) {
|
||||
bmval1 |= FATTR4_WORD1_TIME_ACCESS_SET;
|
||||
*p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
|
||||
p = xdr_encode_hyper(p, (s64)iap->ia_atime.tv_sec);
|
||||
*p++ = cpu_to_be32(iap->ia_atime.tv_nsec);
|
||||
}
|
||||
else if (iap->ia_valid & ATTR_ATIME) {
|
||||
bmval1 |= FATTR4_WORD1_TIME_ACCESS_SET;
|
||||
*p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME);
|
||||
bmval[1] |= FATTR4_WORD1_TIME_ACCESS_SET;
|
||||
len += 16;
|
||||
} else if (iap->ia_valid & ATTR_ATIME) {
|
||||
bmval[1] |= FATTR4_WORD1_TIME_ACCESS_SET;
|
||||
len += 4;
|
||||
}
|
||||
if (iap->ia_valid & ATTR_MTIME_SET) {
|
||||
bmval1 |= FATTR4_WORD1_TIME_MODIFY_SET;
|
||||
*p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
|
||||
p = xdr_encode_hyper(p, (s64)iap->ia_mtime.tv_sec);
|
||||
*p++ = cpu_to_be32(iap->ia_mtime.tv_nsec);
|
||||
}
|
||||
else if (iap->ia_valid & ATTR_MTIME) {
|
||||
bmval1 |= FATTR4_WORD1_TIME_MODIFY_SET;
|
||||
*p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME);
|
||||
bmval[1] |= FATTR4_WORD1_TIME_MODIFY_SET;
|
||||
len += 16;
|
||||
} else if (iap->ia_valid & ATTR_MTIME) {
|
||||
bmval[1] |= FATTR4_WORD1_TIME_MODIFY_SET;
|
||||
len += 4;
|
||||
}
|
||||
if (label) {
|
||||
bmval2 |= FATTR4_WORD2_SECURITY_LABEL;
|
||||
len += 4 + 4 + 4 + (XDR_QUADLEN(label->len) << 2);
|
||||
bmval[2] |= FATTR4_WORD2_SECURITY_LABEL;
|
||||
}
|
||||
|
||||
if (bmval[2] != 0)
|
||||
bmval_len = 3;
|
||||
else if (bmval[1] != 0)
|
||||
bmval_len = 2;
|
||||
else
|
||||
bmval_len = 1;
|
||||
|
||||
p = reserve_space(xdr, 4 + (bmval_len << 2) + 4 + len);
|
||||
|
||||
*p++ = cpu_to_be32(bmval_len);
|
||||
for (i = 0; i < bmval_len; i++)
|
||||
*p++ = cpu_to_be32(bmval[i]);
|
||||
*p++ = cpu_to_be32(len);
|
||||
|
||||
if (bmval[0] & FATTR4_WORD0_SIZE)
|
||||
p = xdr_encode_hyper(p, iap->ia_size);
|
||||
if (bmval[1] & FATTR4_WORD1_MODE)
|
||||
*p++ = cpu_to_be32(iap->ia_mode & S_IALLUGO);
|
||||
if (bmval[1] & FATTR4_WORD1_OWNER)
|
||||
p = xdr_encode_opaque(p, owner_name, owner_namelen);
|
||||
if (bmval[1] & FATTR4_WORD1_OWNER_GROUP)
|
||||
p = xdr_encode_opaque(p, owner_group, owner_grouplen);
|
||||
if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
|
||||
if (iap->ia_valid & ATTR_ATIME_SET) {
|
||||
*p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
|
||||
p = xdr_encode_hyper(p, (s64)iap->ia_atime.tv_sec);
|
||||
*p++ = cpu_to_be32(iap->ia_atime.tv_nsec);
|
||||
} else
|
||||
*p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME);
|
||||
}
|
||||
if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
|
||||
if (iap->ia_valid & ATTR_MTIME_SET) {
|
||||
*p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
|
||||
p = xdr_encode_hyper(p, (s64)iap->ia_mtime.tv_sec);
|
||||
*p++ = cpu_to_be32(iap->ia_mtime.tv_nsec);
|
||||
} else
|
||||
*p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME);
|
||||
}
|
||||
if (bmval[2] & FATTR4_WORD2_SECURITY_LABEL) {
|
||||
*p++ = cpu_to_be32(label->lfs);
|
||||
*p++ = cpu_to_be32(label->pi);
|
||||
*p++ = cpu_to_be32(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: */
|
||||
}
|
||||
|
||||
|
@ -1745,6 +1730,14 @@ static void encode_bind_conn_to_session(struct xdr_stream *xdr,
|
|||
*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,
|
||||
struct nfs41_exchange_id_args *args,
|
||||
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);
|
||||
|
||||
p = reserve_space(xdr, 12);
|
||||
*p++ = cpu_to_be32(args->flags);
|
||||
*p++ = cpu_to_be32(0); /* zero length state_protect4_a */
|
||||
encode_uint32(xdr, args->flags);
|
||||
encode_uint32(xdr, args->state_protect.how);
|
||||
|
||||
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 &&
|
||||
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);
|
||||
|
||||
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,
|
||||
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 = cpu_to_be32(0);
|
||||
} 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,
|
||||
|
@ -1835,7 +1839,7 @@ static void encode_create_session(struct xdr_stream *xdr,
|
|||
*p++ = cpu_to_be32(RPC_AUTH_UNIX); /* auth_sys */
|
||||
|
||||
/* 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++ = cpu_to_be32(0); /* UID */
|
||||
*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;
|
||||
__be32 *p;
|
||||
|
||||
if (slot == NULL)
|
||||
return;
|
||||
|
||||
tp = slot->table;
|
||||
session = tp->session;
|
||||
if (!session)
|
||||
return;
|
||||
|
||||
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)
|
||||
{
|
||||
#if defined(CONFIG_NFS_V4_1)
|
||||
|
||||
if (args->sa_slot)
|
||||
return args->sa_slot->table->session->clp->cl_mvops->minor_version;
|
||||
struct nfs4_session *session = args->sa_slot->table->session;
|
||||
if (session)
|
||||
return session->clp->cl_mvops->minor_version;
|
||||
#endif /* CONFIG_NFS_V4_1 */
|
||||
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,
|
||||
uint32_t *layouttype)
|
||||
{
|
||||
uint32_t *p;
|
||||
__be32 *p;
|
||||
int num;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
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->flags = be32_to_cpup(p++);
|
||||
|
||||
/* We ask for SP4_NONE */
|
||||
dummy = be32_to_cpup(p);
|
||||
if (dummy != SP4_NONE)
|
||||
res->state_protect.how = be32_to_cpup(p);
|
||||
switch (res->state_protect.how) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* server_owner4.so_minor_id */
|
||||
p = xdr_inline_decode(xdr, 8);
|
||||
|
@ -5614,6 +5646,8 @@ static int decode_sequence(struct xdr_stream *xdr,
|
|||
|
||||
if (res->sr_slot == NULL)
|
||||
return 0;
|
||||
if (!res->sr_slot->table->session)
|
||||
return 0;
|
||||
|
||||
status = decode_op_hdr(xdr, OP_SEQUENCE);
|
||||
if (!status)
|
||||
|
|
|
@ -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"
|
|
@ -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>
|
|
@ -328,6 +328,19 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
|
|||
}
|
||||
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
|
||||
* @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_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;
|
||||
if (req->wb_lock_context->lockowner.l_owner != prev->wb_lock_context->lockowner.l_owner)
|
||||
return false;
|
||||
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)
|
||||
if (req->wb_context->dentry->d_inode->i_flock != NULL &&
|
||||
!nfs_match_lock_context(req->wb_lock_context, prev->wb_lock_context))
|
||||
return false;
|
||||
if (req->wb_pgbase != 0)
|
||||
return false;
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "internal.h"
|
||||
#include "pnfs.h"
|
||||
#include "iostat.h"
|
||||
#include "nfs4trace.h"
|
||||
|
||||
#define NFSDBG_FACILITY NFSDBG_PNFS
|
||||
#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;
|
||||
|
||||
trace_nfs4_pnfs_write(data, hdr->pnfs_error);
|
||||
if (!hdr->pnfs_error) {
|
||||
pnfs_set_layoutcommit(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;
|
||||
|
||||
trace_nfs4_pnfs_read(data, hdr->pnfs_error);
|
||||
if (likely(!hdr->pnfs_error)) {
|
||||
__nfs4_read_done_cb(data);
|
||||
hdr->mds_ops->rpc_call_done(&data->task, data);
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nfs_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data)
|
||||
|
|
|
@ -513,9 +513,10 @@ static void nfs_readpage_release_common(void *calldata)
|
|||
void nfs_read_prepare(struct rpc_task *task, void *calldata)
|
||||
{
|
||||
struct nfs_read_data *data = calldata;
|
||||
NFS_PROTO(data->header->inode)->read_rpc_prepare(task, data);
|
||||
if (unlikely(test_bit(NFS_CONTEXT_BAD, &data->args.context->flags)))
|
||||
rpc_exit(task, -EIO);
|
||||
int err;
|
||||
err = NFS_PROTO(data->header->inode)->read_rpc_prepare(task, data);
|
||||
if (err)
|
||||
rpc_exit(task, err);
|
||||
}
|
||||
|
||||
static const struct rpc_call_ops nfs_read_common_ops = {
|
||||
|
|
|
@ -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.protocol = XPRT_TRANSPORT_TCP;
|
||||
data->auth_flavors[0] = RPC_AUTH_MAXFLAVOR;
|
||||
data->auth_flavor_len = 1;
|
||||
data->auth_flavor_len = 0;
|
||||
data->minorversion = 0;
|
||||
data->need_mount = true;
|
||||
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.
|
||||
*/
|
||||
|
@ -1025,49 +1032,50 @@ static int nfs_parse_security_flavors(char *value,
|
|||
struct nfs_parsed_mount_data *mnt)
|
||||
{
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
rpc_authflavor_t pseudoflavor;
|
||||
|
||||
dfprintk(MOUNT, "NFS: parsing sec=%s option\n", value);
|
||||
|
||||
switch (match_token(value, nfs_secflavor_tokens, args)) {
|
||||
case Opt_sec_none:
|
||||
mnt->auth_flavors[0] = RPC_AUTH_NULL;
|
||||
pseudoflavor = RPC_AUTH_NULL;
|
||||
break;
|
||||
case Opt_sec_sys:
|
||||
mnt->auth_flavors[0] = RPC_AUTH_UNIX;
|
||||
pseudoflavor = RPC_AUTH_UNIX;
|
||||
break;
|
||||
case Opt_sec_krb5:
|
||||
mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5;
|
||||
pseudoflavor = RPC_AUTH_GSS_KRB5;
|
||||
break;
|
||||
case Opt_sec_krb5i:
|
||||
mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5I;
|
||||
pseudoflavor = RPC_AUTH_GSS_KRB5I;
|
||||
break;
|
||||
case Opt_sec_krb5p:
|
||||
mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5P;
|
||||
pseudoflavor = RPC_AUTH_GSS_KRB5P;
|
||||
break;
|
||||
case Opt_sec_lkey:
|
||||
mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEY;
|
||||
pseudoflavor = RPC_AUTH_GSS_LKEY;
|
||||
break;
|
||||
case Opt_sec_lkeyi:
|
||||
mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYI;
|
||||
pseudoflavor = RPC_AUTH_GSS_LKEYI;
|
||||
break;
|
||||
case Opt_sec_lkeyp:
|
||||
mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYP;
|
||||
pseudoflavor = RPC_AUTH_GSS_LKEYP;
|
||||
break;
|
||||
case Opt_sec_spkm:
|
||||
mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKM;
|
||||
pseudoflavor = RPC_AUTH_GSS_SPKM;
|
||||
break;
|
||||
case Opt_sec_spkmi:
|
||||
mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMI;
|
||||
pseudoflavor = RPC_AUTH_GSS_SPKMI;
|
||||
break;
|
||||
case Opt_sec_spkmp:
|
||||
mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMP;
|
||||
pseudoflavor = RPC_AUTH_GSS_SPKMP;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
mnt->flags |= NFS_MOUNT_SECFLAVOUR;
|
||||
mnt->auth_flavor_len = 1;
|
||||
nfs_set_auth_parsed_mount_data(mnt, pseudoflavor);
|
||||
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
|
||||
* 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);
|
||||
dfprintk(MOUNT, "NFS: using auth flavor %u\n", args->auth_flavors[0]);
|
||||
if (status)
|
||||
|
@ -1760,7 +1768,7 @@ static struct nfs_server *nfs_try_mount_request(struct nfs_mount_info *mount_inf
|
|||
/* Fallthrough */
|
||||
}
|
||||
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);
|
||||
if (!IS_ERR(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 */
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1893,6 +1901,7 @@ static int nfs23_validate_mount_data(void *options,
|
|||
{
|
||||
struct nfs_mount_data *data = (struct nfs_mount_data *)options;
|
||||
struct sockaddr *sap = (struct sockaddr *)&args->nfs_server.address;
|
||||
int extra_flags = NFS_MOUNT_LEGACY_INTERFACE;
|
||||
|
||||
if (data == NULL)
|
||||
goto out_no_data;
|
||||
|
@ -1908,6 +1917,8 @@ static int nfs23_validate_mount_data(void *options,
|
|||
goto out_no_v3;
|
||||
data->root.size = NFS2_FHSIZE;
|
||||
memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE);
|
||||
/* Turn off security negotiation */
|
||||
extra_flags |= NFS_MOUNT_SECFLAVOUR;
|
||||
case 4:
|
||||
if (data->flags & NFS_MOUNT_SECFLAVOUR)
|
||||
goto out_no_sec;
|
||||
|
@ -1935,7 +1946,7 @@ static int nfs23_validate_mount_data(void *options,
|
|||
* can deal with.
|
||||
*/
|
||||
args->flags = data->flags & NFS_MOUNT_FLAGMASK;
|
||||
args->flags |= NFS_MOUNT_LEGACY_INTERFACE;
|
||||
args->flags |= extra_flags;
|
||||
args->rsize = data->rsize;
|
||||
args->wsize = data->wsize;
|
||||
args->timeo = data->timeo;
|
||||
|
@ -1959,9 +1970,10 @@ static int nfs23_validate_mount_data(void *options,
|
|||
args->namlen = data->namlen;
|
||||
args->bsize = data->bsize;
|
||||
|
||||
args->auth_flavors[0] = RPC_AUTH_UNIX;
|
||||
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)
|
||||
goto out_nomem;
|
||||
|
||||
|
@ -2084,6 +2096,8 @@ static int nfs_validate_text_mount_data(void *options,
|
|||
max_namelen = NFS4_MAXNAMLEN;
|
||||
max_pathlen = NFS4_MAXPATHLEN;
|
||||
nfs_validate_transport_protocol(args);
|
||||
if (args->nfs_server.protocol == XPRT_TRANSPORT_UDP)
|
||||
goto out_invalid_transport_udp;
|
||||
nfs4_validate_mount_flags(args);
|
||||
#else
|
||||
goto out_v4_not_compiled;
|
||||
|
@ -2106,6 +2120,10 @@ static int nfs_validate_text_mount_data(void *options,
|
|||
out_v4_not_compiled:
|
||||
dfprintk(MOUNT, "NFS: NFSv4 is not compiled into kernel\n");
|
||||
return -EPROTONOSUPPORT;
|
||||
#else
|
||||
out_invalid_transport_udp:
|
||||
dfprintk(MOUNT, "NFSv4: Unsupported transport protocol udp\n");
|
||||
return -EINVAL;
|
||||
#endif /* !CONFIG_NFS_V4 */
|
||||
|
||||
out_no_address:
|
||||
|
@ -2170,7 +2188,7 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data)
|
|||
data->rsize = nfss->rsize;
|
||||
data->wsize = nfss->wsize;
|
||||
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->acregmax = nfss->acregmax / 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);
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
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;
|
||||
if (a->nfs_client != b->nfs_client)
|
||||
goto Ebusy;
|
||||
if (a->flags != b->flags)
|
||||
if ((a->flags ^ b->flags) & NFS_MOUNT_CMP_FLAGMASK)
|
||||
goto Ebusy;
|
||||
if (a->wsize != b->wsize)
|
||||
goto Ebusy;
|
||||
|
@ -2301,7 +2331,8 @@ static int nfs_compare_mount_options(const struct super_block *s, const struct n
|
|||
goto Ebusy;
|
||||
if (a->acdirmax != b->acdirmax)
|
||||
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;
|
||||
return 1;
|
||||
Ebusy:
|
||||
|
@ -2673,15 +2704,17 @@ static int nfs4_validate_mount_data(void *options,
|
|||
goto out_no_address;
|
||||
args->nfs_server.port = ntohs(((struct sockaddr_in *)sap)->sin_port);
|
||||
|
||||
args->auth_flavors[0] = RPC_AUTH_UNIX;
|
||||
if (data->auth_flavourlen) {
|
||||
rpc_authflavor_t pseudoflavor;
|
||||
if (data->auth_flavourlen > 1)
|
||||
goto out_inval_auth;
|
||||
if (copy_from_user(&args->auth_flavors[0],
|
||||
if (copy_from_user(&pseudoflavor,
|
||||
data->auth_flavours,
|
||||
sizeof(args->auth_flavors[0])))
|
||||
sizeof(pseudoflavor)))
|
||||
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);
|
||||
if (IS_ERR(c))
|
||||
|
@ -2715,6 +2748,8 @@ static int nfs4_validate_mount_data(void *options,
|
|||
args->acdirmax = data->acdirmax;
|
||||
args->nfs_server.protocol = data->proto;
|
||||
nfs_validate_transport_protocol(args);
|
||||
if (args->nfs_server.protocol == XPRT_TRANSPORT_UDP)
|
||||
goto out_invalid_transport_udp;
|
||||
|
||||
break;
|
||||
default:
|
||||
|
@ -2735,6 +2770,10 @@ out_inval_auth:
|
|||
out_no_address:
|
||||
dfprintk(MOUNT, "NFS4: mount program didn't pass remote address\n");
|
||||
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 send_implementation_id = 1;
|
||||
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_tcpport);
|
||||
|
@ -2758,6 +2798,7 @@ EXPORT_SYMBOL_GPL(nfs4_disable_idmapping);
|
|||
EXPORT_SYMBOL_GPL(max_session_slots);
|
||||
EXPORT_SYMBOL_GPL(send_implementation_id);
|
||||
EXPORT_SYMBOL_GPL(nfs4_client_id_uniquifier);
|
||||
EXPORT_SYMBOL_GPL(recover_lost_locks);
|
||||
|
||||
#define NFS_CALLBACK_MAXPORTNR (65535U)
|
||||
|
||||
|
@ -2795,4 +2836,10 @@ MODULE_PARM_DESC(send_implementation_id,
|
|||
"Send implementation ID with NFSv4.1 exchange_id");
|
||||
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 */
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include "iostat.h"
|
||||
#include "delegation.h"
|
||||
|
||||
#include "nfstrace.h"
|
||||
|
||||
/**
|
||||
* nfs_free_unlinkdata - release data from a sillydelete operation.
|
||||
* @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 inode *dir = data->dir;
|
||||
|
||||
trace_nfs_sillyrename_unlink(data, task->tk_status);
|
||||
if (!NFS_PROTO(dir)->unlink_done(task, dir))
|
||||
rpc_restart_call_prepare(task);
|
||||
}
|
||||
|
@ -204,6 +207,13 @@ out_free:
|
|||
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)
|
||||
{
|
||||
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 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)) {
|
||||
rpc_restart_call_prepare(task);
|
||||
return;
|
||||
|
@ -444,6 +456,14 @@ nfs_async_rename(struct inode *old_dir, struct inode *new_dir,
|
|||
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
|
||||
* @dir: inode of directory that contains dentry
|
||||
|
@ -469,10 +489,8 @@ int
|
|||
nfs_sillyrename(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
static unsigned int sillycounter;
|
||||
const int fileidsize = sizeof(NFS_FILEID(dentry->d_inode))*2;
|
||||
const int countersize = sizeof(sillycounter)*2;
|
||||
const int slen = sizeof(".nfs")+fileidsize+countersize-1;
|
||||
char silly[slen+1];
|
||||
unsigned char silly[SILLYNAME_LEN + 1];
|
||||
unsigned long long fileid;
|
||||
struct dentry *sdentry;
|
||||
struct rpc_task *task;
|
||||
int error = -EIO;
|
||||
|
@ -489,20 +507,20 @@ nfs_sillyrename(struct inode *dir, struct dentry *dentry)
|
|||
if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
|
||||
goto out;
|
||||
|
||||
sprintf(silly, ".nfs%*.*Lx",
|
||||
fileidsize, fileidsize,
|
||||
(unsigned long long)NFS_FILEID(dentry->d_inode));
|
||||
fileid = NFS_FILEID(dentry->d_inode);
|
||||
|
||||
/* Return delegation in anticipation of the rename */
|
||||
NFS_PROTO(dentry->d_inode)->return_delegation(dentry->d_inode);
|
||||
|
||||
sdentry = NULL;
|
||||
do {
|
||||
char *suffix = silly + slen - countersize;
|
||||
|
||||
int slen;
|
||||
dput(sdentry);
|
||||
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",
|
||||
dentry->d_name.name, silly);
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include "fscache.h"
|
||||
#include "pnfs.h"
|
||||
|
||||
#include "nfstrace.h"
|
||||
|
||||
#define NFSDBG_FACILITY NFSDBG_PAGECACHE
|
||||
|
||||
#define MIN_POOL_WRITE (32)
|
||||
|
@ -861,7 +863,7 @@ int nfs_flush_incompatible(struct file *file, struct page *page)
|
|||
return 0;
|
||||
l_ctx = req->wb_lock_context;
|
||||
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
|
||||
|| l_ctx->lockowner.l_pid != current->tgid;
|
||||
}
|
||||
|
@ -873,6 +875,33 @@ int nfs_flush_incompatible(struct file *file, struct page *page)
|
|||
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
|
||||
* 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,
|
||||
(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);
|
||||
if (IS_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)
|
||||
{
|
||||
struct nfs_write_data *data = calldata;
|
||||
NFS_PROTO(data->header->inode)->write_rpc_prepare(task, data);
|
||||
if (unlikely(test_bit(NFS_CONTEXT_BAD, &data->args.context->flags)))
|
||||
rpc_exit(task, -EIO);
|
||||
int err;
|
||||
err = NFS_PROTO(data->header->inode)->write_rpc_prepare(task, data);
|
||||
if (err)
|
||||
rpc_exit(task, err);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
if (IS_ERR(task))
|
||||
return PTR_ERR(task);
|
||||
|
@ -1732,8 +1768,14 @@ int nfs_wb_all(struct inode *inode)
|
|||
.range_start = 0,
|
||||
.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);
|
||||
|
||||
|
@ -1781,6 +1823,8 @@ int nfs_wb_page(struct inode *inode, struct page *page)
|
|||
};
|
||||
int ret;
|
||||
|
||||
trace_nfs_writeback_page_enter(inode);
|
||||
|
||||
for (;;) {
|
||||
wait_on_page_writeback(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;
|
||||
continue;
|
||||
}
|
||||
ret = 0;
|
||||
if (!PagePrivate(page))
|
||||
break;
|
||||
ret = nfs_commit_inode(inode, FLUSH_SYNC);
|
||||
if (ret < 0)
|
||||
goto out_error;
|
||||
}
|
||||
return 0;
|
||||
out_error:
|
||||
trace_nfs_writeback_page_exit(inode, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -524,6 +524,7 @@ static inline void nfs4_label_free(void *label) {}
|
|||
* linux/fs/nfs/unlink.c
|
||||
*/
|
||||
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_unblock_sillyrename(struct dentry *dentry);
|
||||
extern int nfs_sillyrename(struct inode *dir, struct dentry *dentry);
|
||||
|
|
|
@ -56,6 +56,7 @@ struct nfs_client {
|
|||
struct rpc_cred *cl_machine_cred;
|
||||
|
||||
#if IS_ENABLED(CONFIG_NFS_V4)
|
||||
struct list_head cl_ds_clients; /* auth flavor data servers */
|
||||
u64 cl_clientid; /* constant */
|
||||
nfs4_verifier cl_confirm; /* Clientid verifier */
|
||||
unsigned long cl_state;
|
||||
|
@ -78,6 +79,9 @@ struct nfs_client {
|
|||
u32 cl_cb_ident; /* v4.0 callback identifier */
|
||||
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 */
|
||||
u32 cl_seqid;
|
||||
/* 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_scope *cl_serverscope;
|
||||
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 */
|
||||
|
||||
#ifdef CONFIG_NFS_FSCACHE
|
||||
|
|
|
@ -1107,6 +1107,23 @@ struct pnfs_ds_commit_info {
|
|||
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)
|
||||
struct nfs41_exchange_id_args {
|
||||
struct nfs_client *client;
|
||||
|
@ -1114,6 +1131,7 @@ struct nfs41_exchange_id_args {
|
|||
unsigned int id_len;
|
||||
char id[NFS4_EXCHANGE_ID_LEN];
|
||||
u32 flags;
|
||||
struct nfs41_state_protection state_protect;
|
||||
};
|
||||
|
||||
struct nfs41_server_owner {
|
||||
|
@ -1146,6 +1164,7 @@ struct nfs41_exchange_id_res {
|
|||
struct nfs41_server_owner *server_owner;
|
||||
struct nfs41_server_scope *server_scope;
|
||||
struct nfs41_impl_id *impl_id;
|
||||
struct nfs41_state_protection state_protect;
|
||||
};
|
||||
|
||||
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_pageio_init)(struct nfs_pageio_descriptor *, struct inode *,
|
||||
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 *);
|
||||
void (*write_setup) (struct nfs_write_data *, struct rpc_message *);
|
||||
void (*write_pageio_init)(struct nfs_pageio_descriptor *, struct inode *, int,
|
||||
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 *);
|
||||
void (*commit_setup) (struct nfs_commit_data *, struct rpc_message *);
|
||||
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 *
|
||||
(*init_client) (struct nfs_client *, const struct rpc_timeout *,
|
||||
const char *, rpc_authflavor_t);
|
||||
const char *);
|
||||
void (*free_client) (struct nfs_client *);
|
||||
struct nfs_server *(*create_server)(struct nfs_mount_info *, struct nfs_subversion *);
|
||||
struct nfs_server *(*clone_server)(struct nfs_server *, struct nfs_fh *,
|
||||
|
|
|
@ -24,12 +24,21 @@
|
|||
|
||||
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 */
|
||||
struct auth_cred {
|
||||
kuid_t uid;
|
||||
kgid_t gid;
|
||||
struct group_info *group_info;
|
||||
const char *principal;
|
||||
unsigned long ac_flags;
|
||||
unsigned char machine_cred : 1;
|
||||
};
|
||||
|
||||
|
@ -87,6 +96,11 @@ struct rpc_auth {
|
|||
/* per-flavor data */
|
||||
};
|
||||
|
||||
struct rpc_auth_create_args {
|
||||
rpc_authflavor_t pseudoflavor;
|
||||
const char *target_name;
|
||||
};
|
||||
|
||||
/* Flags for rpcauth_lookupcred() */
|
||||
#define RPCAUTH_LOOKUP_NEW 0x01 /* Accept an uninitialised cred */
|
||||
|
||||
|
@ -97,17 +111,17 @@ struct rpc_authops {
|
|||
struct module *owner;
|
||||
rpc_authflavor_t au_flavor; /* flavor (RPC_AUTH_*) */
|
||||
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 *);
|
||||
|
||||
struct rpc_cred * (*lookup_cred)(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);
|
||||
rpc_authflavor_t (*info2flavor)(struct rpcsec_gss_info *);
|
||||
int (*flavor2info)(rpc_authflavor_t,
|
||||
struct rpcsec_gss_info *);
|
||||
int (*key_timeout)(struct rpc_auth *,
|
||||
struct rpc_cred *);
|
||||
};
|
||||
|
||||
struct rpc_credops {
|
||||
|
@ -124,6 +138,8 @@ struct rpc_credops {
|
|||
void *, __be32 *, void *);
|
||||
int (*crunwrap_resp)(struct rpc_task *, kxdrdproc_t,
|
||||
void *, __be32 *, void *);
|
||||
int (*crkey_timeout)(struct rpc_cred *);
|
||||
bool (*crkey_to_expire)(struct rpc_cred *);
|
||||
};
|
||||
|
||||
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);
|
||||
int rpcauth_register(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 *);
|
||||
rpc_authflavor_t rpcauth_get_pseudoflavor(rpc_authflavor_t,
|
||||
struct rpcsec_gss_info *);
|
||||
|
@ -162,6 +179,9 @@ int rpcauth_uptodatecred(struct rpc_task *);
|
|||
int rpcauth_init_credcache(struct rpc_auth *);
|
||||
void rpcauth_destroy_credcache(struct rpc_auth *);
|
||||
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
|
||||
struct rpc_cred * get_rpccred(struct rpc_cred *cred)
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/sunrpc/stats.h>
|
||||
#include <linux/sunrpc/xdr.h>
|
||||
#include <linux/sunrpc/timer.h>
|
||||
#include <linux/sunrpc/rpc_pipe_fs.h>
|
||||
#include <asm/signal.h>
|
||||
#include <linux/path.h>
|
||||
#include <net/ipv6.h>
|
||||
|
@ -32,6 +33,7 @@ struct rpc_inode;
|
|||
*/
|
||||
struct rpc_clnt {
|
||||
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_tasks; /* List of tasks */
|
||||
spinlock_t cl_lock; /* spinlock */
|
||||
|
@ -41,7 +43,6 @@ struct rpc_clnt {
|
|||
cl_vers, /* RPC version number */
|
||||
cl_maxproc; /* max procedure number */
|
||||
|
||||
const char * cl_protname; /* protocol name */
|
||||
struct rpc_auth * cl_auth; /* authenticator */
|
||||
struct rpc_stat * cl_stats; /* per-program statistics */
|
||||
struct rpc_iostats * cl_metrics; /* per-client statistics */
|
||||
|
@ -56,12 +57,11 @@ struct rpc_clnt {
|
|||
|
||||
int cl_nodelen; /* nodename length */
|
||||
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_rtt cl_rtt_default;
|
||||
struct rpc_timeout cl_timeout_default;
|
||||
const struct rpc_program *cl_program;
|
||||
char *cl_principal; /* target to authenticate to */
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -5,6 +5,26 @@
|
|||
|
||||
#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 list_head list;
|
||||
void *data;
|
||||
|
@ -74,7 +94,24 @@ extern int rpc_queue_upcall(struct rpc_pipe *, struct rpc_pipe_msg *);
|
|||
|
||||
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;
|
||||
extern struct dentry *rpc_create_cache_dir(struct dentry *,
|
||||
|
|
|
@ -79,7 +79,7 @@ struct rpc_task {
|
|||
unsigned short tk_flags; /* misc flags */
|
||||
unsigned short tk_timeouts; /* maj timeouts */
|
||||
|
||||
#ifdef RPC_DEBUG
|
||||
#if defined(RPC_DEBUG) || defined(RPC_TRACEPOINTS)
|
||||
unsigned short tk_pid; /* debugging aid */
|
||||
#endif
|
||||
unsigned char tk_priority : 2,/* Task priority */
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include <linux/sunrpc/sched.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <net/tcp_states.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
DECLARE_EVENT_CLASS(rpc_task_status,
|
||||
|
@ -15,18 +17,20 @@ DECLARE_EVENT_CLASS(rpc_task_status,
|
|||
TP_ARGS(task),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(const struct rpc_task *, task)
|
||||
__field(const struct rpc_clnt *, clnt)
|
||||
__field(unsigned int, task_id)
|
||||
__field(unsigned int, client_id)
|
||||
__field(int, status)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->task = task;
|
||||
__entry->clnt = task->tk_client;
|
||||
__entry->task_id = task->tk_pid;
|
||||
__entry->client_id = task->tk_client->cl_clid;
|
||||
__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,
|
||||
|
@ -47,18 +51,20 @@ TRACE_EVENT(rpc_connect_status,
|
|||
TP_ARGS(task, status),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(const struct rpc_task *, task)
|
||||
__field(const struct rpc_clnt *, clnt)
|
||||
__field(unsigned int, task_id)
|
||||
__field(unsigned int, client_id)
|
||||
__field(int, status)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->task = task;
|
||||
__entry->clnt = task->tk_client;
|
||||
__entry->task_id = task->tk_pid;
|
||||
__entry->client_id = task->tk_client->cl_clid;
|
||||
__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,
|
||||
|
@ -68,8 +74,8 @@ DECLARE_EVENT_CLASS(rpc_task_running,
|
|||
TP_ARGS(clnt, task, action),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(const struct rpc_clnt *, clnt)
|
||||
__field(const struct rpc_task *, task)
|
||||
__field(unsigned int, task_id)
|
||||
__field(unsigned int, client_id)
|
||||
__field(const void *, action)
|
||||
__field(unsigned long, runstate)
|
||||
__field(int, status)
|
||||
|
@ -77,17 +83,16 @@ DECLARE_EVENT_CLASS(rpc_task_running,
|
|||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->clnt = clnt;
|
||||
__entry->task = task;
|
||||
__entry->client_id = clnt->cl_clid;
|
||||
__entry->task_id = task->tk_pid;
|
||||
__entry->action = action;
|
||||
__entry->runstate = task->tk_runstate;
|
||||
__entry->status = task->tk_status;
|
||||
__entry->flags = task->tk_flags;
|
||||
),
|
||||
|
||||
TP_printk("task:%p@%p flags=%4.4x state=%4.4lx status=%d action=%pf",
|
||||
__entry->task,
|
||||
__entry->clnt,
|
||||
TP_printk("task:%u@%u flags=%4.4x state=%4.4lx status=%d action=%pf",
|
||||
__entry->task_id, __entry->client_id,
|
||||
__entry->flags,
|
||||
__entry->runstate,
|
||||
__entry->status,
|
||||
|
@ -126,8 +131,8 @@ DECLARE_EVENT_CLASS(rpc_task_queued,
|
|||
TP_ARGS(clnt, task, q),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(const struct rpc_clnt *, clnt)
|
||||
__field(const struct rpc_task *, task)
|
||||
__field(unsigned int, task_id)
|
||||
__field(unsigned int, client_id)
|
||||
__field(unsigned long, timeout)
|
||||
__field(unsigned long, runstate)
|
||||
__field(int, status)
|
||||
|
@ -136,8 +141,8 @@ DECLARE_EVENT_CLASS(rpc_task_queued,
|
|||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->clnt = clnt;
|
||||
__entry->task = task;
|
||||
__entry->client_id = clnt->cl_clid;
|
||||
__entry->task_id = task->tk_pid;
|
||||
__entry->timeout = task->tk_timeout;
|
||||
__entry->runstate = task->tk_runstate;
|
||||
__entry->status = task->tk_status;
|
||||
|
@ -145,9 +150,8 @@ DECLARE_EVENT_CLASS(rpc_task_queued,
|
|||
__assign_str(q_name, rpc_qname(q));
|
||||
),
|
||||
|
||||
TP_printk("task:%p@%p flags=%4.4x state=%4.4lx status=%d timeout=%lu queue=%s",
|
||||
__entry->task,
|
||||
__entry->clnt,
|
||||
TP_printk("task:%u@%u flags=%4.4x state=%4.4lx status=%d timeout=%lu queue=%s",
|
||||
__entry->task_id, __entry->client_id,
|
||||
__entry->flags,
|
||||
__entry->runstate,
|
||||
__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 */
|
||||
|
||||
#include <trace/define_trace.h>
|
||||
|
|
|
@ -250,11 +250,11 @@ rpcauth_list_flavors(rpc_authflavor_t *array, int size)
|
|||
EXPORT_SYMBOL_GPL(rpcauth_list_flavors);
|
||||
|
||||
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;
|
||||
const struct rpc_authops *ops;
|
||||
u32 flavor = pseudoflavor_to_flavor(pseudoflavor);
|
||||
u32 flavor = pseudoflavor_to_flavor(args->pseudoflavor);
|
||||
|
||||
auth = ERR_PTR(-EINVAL);
|
||||
if (flavor >= RPC_AUTH_MAXFLAVOR)
|
||||
|
@ -269,7 +269,7 @@ rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt)
|
|||
goto out;
|
||||
}
|
||||
spin_unlock(&rpc_authflavor_lock);
|
||||
auth = ops->create(clnt, pseudoflavor);
|
||||
auth = ops->create(args, clnt);
|
||||
module_put(ops->owner);
|
||||
if (IS_ERR(auth))
|
||||
return auth;
|
||||
|
@ -342,6 +342,27 @@ out_nocache:
|
|||
}
|
||||
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
|
||||
*/
|
||||
|
|
|
@ -89,6 +89,7 @@ generic_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
|
|||
gcred->acred.uid = acred->uid;
|
||||
gcred->acred.gid = acred->gid;
|
||||
gcred->acred.group_info = acred->group_info;
|
||||
gcred->acred.ac_flags = 0;
|
||||
if (gcred->acred.group_info != NULL)
|
||||
get_group_info(gcred->acred.group_info);
|
||||
gcred->acred.machine_cred = acred->machine_cred;
|
||||
|
@ -182,11 +183,78 @@ void rpc_destroy_generic_auth(void)
|
|||
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 = {
|
||||
.owner = THIS_MODULE,
|
||||
.au_name = "Generic",
|
||||
.lookup_cred = generic_lookup_cred,
|
||||
.crcreate = generic_create_cred,
|
||||
.key_timeout = generic_key_timeout,
|
||||
};
|
||||
|
||||
static struct rpc_auth generic_auth = {
|
||||
|
@ -194,9 +262,23 @@ static struct rpc_auth generic_auth = {
|
|||
.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 = {
|
||||
.cr_name = "Generic cred",
|
||||
.crdestroy = generic_destroy_cred,
|
||||
.crbind = generic_bind_cred,
|
||||
.crmatch = generic_match,
|
||||
.crkey_to_expire = generic_key_to_expire,
|
||||
};
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include <linux/sunrpc/rpc_pipe_fs.h>
|
||||
#include <linux/sunrpc/gss_api.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/hashtable.h>
|
||||
|
||||
#include "../netns.h"
|
||||
|
||||
|
@ -62,6 +63,9 @@ static const struct rpc_credops gss_nullops;
|
|||
#define GSS_RETRY_EXPIRED 5
|
||||
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
|
||||
# define RPCDBG_FACILITY RPCDBG_AUTH
|
||||
#endif
|
||||
|
@ -71,19 +75,33 @@ static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED;
|
|||
* using integrity (two 4-byte integers): */
|
||||
#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 kref kref;
|
||||
struct hlist_node hash;
|
||||
struct rpc_auth rpc_auth;
|
||||
struct gss_api_mech *mech;
|
||||
enum rpc_gss_svc service;
|
||||
struct rpc_clnt *client;
|
||||
struct net *net;
|
||||
/*
|
||||
* There are two upcall pipes; dentry[1], named "gssd", is used
|
||||
* for the new text-based upcall; dentry[0] is named after the
|
||||
* mechanism (for example, "krb5") and exists for
|
||||
* 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. */
|
||||
|
@ -294,7 +312,7 @@ static void put_pipe_version(struct net *net)
|
|||
static void
|
||||
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))
|
||||
return;
|
||||
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,
|
||||
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;
|
||||
char *p = gss_msg->databuf;
|
||||
|
@ -417,8 +435,8 @@ static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg,
|
|||
mech->gm_name,
|
||||
from_kuid(&init_user_ns, gss_msg->uid));
|
||||
p += gss_msg->msg.len;
|
||||
if (clnt->cl_principal) {
|
||||
len = sprintf(p, "target=%s ", clnt->cl_principal);
|
||||
if (target_name) {
|
||||
len = sprintf(p, "target=%s ", target_name);
|
||||
p += 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);
|
||||
}
|
||||
|
||||
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 *
|
||||
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)
|
||||
{
|
||||
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);
|
||||
if (gss_msg == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
vers = get_pipe_version(rpc_net_ns(clnt));
|
||||
vers = get_pipe_version(gss_auth->net);
|
||||
if (vers < 0) {
|
||||
kfree(gss_msg);
|
||||
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);
|
||||
rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq");
|
||||
init_waitqueue_head(&gss_msg->waitqueue);
|
||||
atomic_set(&gss_msg->count, 1);
|
||||
gss_msg->uid = uid;
|
||||
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;
|
||||
}
|
||||
|
||||
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, gc_base);
|
||||
struct gss_upcall_msg *gss_new, *gss_msg;
|
||||
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))
|
||||
return 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",
|
||||
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) {
|
||||
/* XXX: warning on the first, under the assumption we
|
||||
* shouldn't normally hit this case on a refresh. */
|
||||
|
@ -566,7 +576,7 @@ out:
|
|||
static inline int
|
||||
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 rpc_pipe *pipe;
|
||||
struct rpc_cred *cred = &gss_cred->gc_base;
|
||||
|
@ -583,7 +593,7 @@ retry:
|
|||
timeout = 15 * HZ;
|
||||
if (!sn->gssd_running)
|
||||
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) {
|
||||
err = wait_event_interruptible_timeout(pipe_version_waitqueue,
|
||||
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 (gss_auth->pipe[0]->dentry)
|
||||
rpc_unlink(gss_auth->pipe[0]->dentry);
|
||||
if (gss_auth->pipe[1]->dentry)
|
||||
rpc_unlink(gss_auth->pipe[1]->dentry);
|
||||
if (pipe->dentry != NULL) {
|
||||
rpc_unlink(pipe->dentry);
|
||||
pipe->dentry = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
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_auth *gss_auth;
|
||||
struct rpc_clnt *clnt;
|
||||
struct gss_pipe *p = pdo->pdo_data;
|
||||
struct dentry *dentry;
|
||||
|
||||
gss_auth = container_of(auth, struct gss_auth, rpc_auth);
|
||||
clnt = gss_auth->client;
|
||||
|
||||
gss_auth->pipe[1]->dentry = rpc_mkpipe_dentry(clnt->cl_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;
|
||||
}
|
||||
dentry = rpc_mkpipe_dentry(dir, p->name, p->clnt, p->pipe);
|
||||
if (IS_ERR(dentry))
|
||||
return PTR_ERR(dentry);
|
||||
p->pipe->dentry = dentry;
|
||||
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,
|
||||
struct rpc_auth *auth)
|
||||
{
|
||||
struct net *net = rpc_net_ns(clnt);
|
||||
struct super_block *sb;
|
||||
static const struct rpc_pipe_dir_object_ops gss_pipe_dir_object_ops = {
|
||||
.create = gss_pipe_dentry_create,
|
||||
.destroy = gss_pipe_dentry_destroy,
|
||||
};
|
||||
|
||||
sb = rpc_get_sb_net(net);
|
||||
if (sb) {
|
||||
if (clnt->cl_dentry)
|
||||
gss_pipes_dentries_destroy(auth);
|
||||
rpc_put_sb_net(net);
|
||||
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);
|
||||
}
|
||||
|
||||
static int gss_pipes_dentries_create_net(struct rpc_clnt *clnt,
|
||||
struct rpc_auth *auth)
|
||||
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 super_block *sb;
|
||||
int err = 0;
|
||||
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);
|
||||
if (sb) {
|
||||
if (clnt->cl_dentry)
|
||||
err = gss_pipes_dentries_create(auth);
|
||||
rpc_put_sb_net(net);
|
||||
}
|
||||
return err;
|
||||
pdo = rpc_find_or_alloc_pipe_dir_object(net,
|
||||
&clnt->cl_pipedir_objects,
|
||||
gss_pipe_match_pdo,
|
||||
gss_pipe_alloc_pdo,
|
||||
&args);
|
||||
if (pdo != NULL)
|
||||
return container_of(pdo, struct gss_pipe, pdo);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
static void __gss_pipe_free(struct gss_pipe *p)
|
||||
{
|
||||
struct rpc_clnt *clnt = p->clnt;
|
||||
struct net *net = rpc_net_ns(clnt);
|
||||
|
||||
rpc_remove_pipe_dir_object(net,
|
||||
&clnt->cl_pipedir_objects,
|
||||
&p->pdo);
|
||||
rpc_destroy_pipe_data(p->pipe);
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
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
|
||||
* parameters based on the input flavor (which must be a pseudoflavor)
|
||||
*/
|
||||
static struct rpc_auth *
|
||||
gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
|
||||
static struct gss_auth *
|
||||
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_pipe *gss_pipe;
|
||||
struct rpc_auth * auth;
|
||||
int err = -ENOMEM; /* XXX? */
|
||||
|
||||
|
@ -883,12 +963,20 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
|
|||
return ERR_PTR(err);
|
||||
if (!(gss_auth = kmalloc(sizeof(*gss_auth), GFP_KERNEL)))
|
||||
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->net = get_net(rpc_net_ns(clnt));
|
||||
err = -EINVAL;
|
||||
gss_auth->mech = gss_mech_get_by_pseudoflavor(flavor);
|
||||
if (!gss_auth->mech) {
|
||||
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);
|
||||
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);
|
||||
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
|
||||
* examined the directory at the right moment might conclude
|
||||
* that we supported only the old pipe. So we instead create
|
||||
* the new pipe first.
|
||||
*/
|
||||
gss_auth->pipe[1] = rpc_mkpipe_data(&gss_upcall_ops_v1,
|
||||
RPC_PIPE_WAIT_FOR_OPEN);
|
||||
if (IS_ERR(gss_auth->pipe[1])) {
|
||||
err = PTR_ERR(gss_auth->pipe[1]);
|
||||
goto err_put_mech;
|
||||
gss_pipe = gss_pipe_get(clnt, "gssd", &gss_upcall_ops_v1);
|
||||
if (IS_ERR(gss_pipe)) {
|
||||
err = PTR_ERR(gss_pipe);
|
||||
goto err_destroy_credcache;
|
||||
}
|
||||
gss_auth->gss_pipe[1] = gss_pipe;
|
||||
|
||||
gss_auth->pipe[0] = rpc_mkpipe_data(&gss_upcall_ops_v0,
|
||||
RPC_PIPE_WAIT_FOR_OPEN);
|
||||
if (IS_ERR(gss_auth->pipe[0])) {
|
||||
err = PTR_ERR(gss_auth->pipe[0]);
|
||||
gss_pipe = gss_pipe_get(clnt, gss_auth->mech->gm_name,
|
||||
&gss_upcall_ops_v0);
|
||||
if (IS_ERR(gss_pipe)) {
|
||||
err = PTR_ERR(gss_pipe);
|
||||
goto err_destroy_pipe_1;
|
||||
}
|
||||
err = gss_pipes_dentries_create_net(clnt, auth);
|
||||
if (err)
|
||||
goto err_destroy_pipe_0;
|
||||
err = rpcauth_init_credcache(auth);
|
||||
if (err)
|
||||
goto err_unlink_pipes;
|
||||
gss_auth->gss_pipe[0] = gss_pipe;
|
||||
|
||||
return auth;
|
||||
err_unlink_pipes:
|
||||
gss_pipes_dentries_destroy_net(clnt, auth);
|
||||
err_destroy_pipe_0:
|
||||
rpc_destroy_pipe_data(gss_auth->pipe[0]);
|
||||
return gss_auth;
|
||||
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:
|
||||
gss_mech_put(gss_auth->mech);
|
||||
err_put_net:
|
||||
put_net(gss_auth->net);
|
||||
err_free:
|
||||
kfree(gss_auth->target_name);
|
||||
kfree(gss_auth);
|
||||
out_dec:
|
||||
module_put(THIS_MODULE);
|
||||
|
@ -946,10 +1033,11 @@ out_dec:
|
|||
static void
|
||||
gss_free(struct gss_auth *gss_auth)
|
||||
{
|
||||
gss_pipes_dentries_destroy_net(gss_auth->client, &gss_auth->rpc_auth);
|
||||
rpc_destroy_pipe_data(gss_auth->pipe[0]);
|
||||
rpc_destroy_pipe_data(gss_auth->pipe[1]);
|
||||
gss_pipe_free(gss_auth->gss_pipe[0]);
|
||||
gss_pipe_free(gss_auth->gss_pipe[1]);
|
||||
gss_mech_put(gss_auth->mech);
|
||||
put_net(gss_auth->net);
|
||||
kfree(gss_auth->target_name);
|
||||
|
||||
kfree(gss_auth);
|
||||
module_put(THIS_MODULE);
|
||||
|
@ -966,17 +1054,101 @@ gss_free_callback(struct kref *kref)
|
|||
static void
|
||||
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",
|
||||
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);
|
||||
|
||||
gss_auth = container_of(auth, struct gss_auth, rpc_auth);
|
||||
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
|
||||
* 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
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);
|
||||
int ret;
|
||||
|
||||
if (test_bit(RPCAUTH_CRED_NEW, &rc->cr_flags))
|
||||
goto out;
|
||||
|
@ -1142,11 +1336,26 @@ out:
|
|||
if (acred->principal != NULL) {
|
||||
if (gss_cred->gc_principal == NULL)
|
||||
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)
|
||||
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;
|
||||
u32 flav,len;
|
||||
u32 maj_stat;
|
||||
__be32 *ret = ERR_PTR(-EIO);
|
||||
|
||||
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.len = len;
|
||||
|
||||
ret = ERR_PTR(-EACCES);
|
||||
maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic);
|
||||
if (maj_stat == GSS_S_CONTEXT_EXPIRED)
|
||||
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);
|
||||
out_bad:
|
||||
gss_put_ctx(ctx);
|
||||
dprintk("RPC: %5u %s failed.\n", task->tk_pid, __func__);
|
||||
return NULL;
|
||||
dprintk("RPC: %5u %s failed ret %ld.\n", task->tk_pid, __func__,
|
||||
PTR_ERR(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
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,
|
||||
.lookup_cred = gss_lookup_cred,
|
||||
.crcreate = gss_create_cred,
|
||||
.pipes_create = gss_pipes_dentries_create,
|
||||
.pipes_destroy = gss_pipes_dentries_destroy,
|
||||
.list_pseudoflavors = gss_mech_list_pseudoflavors,
|
||||
.info2flavor = gss_mech_info2flavor,
|
||||
.flavor2info = gss_mech_flavor2info,
|
||||
|
@ -1675,6 +1885,7 @@ static const struct rpc_credops gss_credops = {
|
|||
.crvalidate = gss_validate,
|
||||
.crwrap_req = gss_wrap_req,
|
||||
.crunwrap_resp = gss_unwrap_resp,
|
||||
.crkey_timeout = gss_key_timeout,
|
||||
};
|
||||
|
||||
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 "
|
||||
"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_exit(exit_rpcsec_gss)
|
||||
|
|
|
@ -18,7 +18,7 @@ static struct rpc_auth null_auth;
|
|||
static struct rpc_cred null_cred;
|
||||
|
||||
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);
|
||||
return &null_auth;
|
||||
|
@ -88,13 +88,13 @@ nul_validate(struct rpc_task *task, __be32 *p)
|
|||
flavor = ntohl(*p++);
|
||||
if (flavor != RPC_AUTH_NULL) {
|
||||
printk("RPC: bad verf flavor: %u\n", flavor);
|
||||
return NULL;
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
size = ntohl(*p++);
|
||||
if (size != 0) {
|
||||
printk("RPC: bad verf size: %u\n", size);
|
||||
return NULL;
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
return p;
|
||||
|
|
|
@ -33,7 +33,7 @@ static struct rpc_auth unix_auth;
|
|||
static const struct rpc_credops unix_credops;
|
||||
|
||||
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",
|
||||
clnt);
|
||||
|
@ -192,13 +192,13 @@ unx_validate(struct rpc_task *task, __be32 *p)
|
|||
flavor != RPC_AUTH_UNIX &&
|
||||
flavor != RPC_AUTH_SHORT) {
|
||||
printk("RPC: bad verf flavor: %u\n", flavor);
|
||||
return NULL;
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
size = ntohl(*p++);
|
||||
if (size > RPC_MAX_AUTH_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;
|
||||
p += (size >> 2);
|
||||
|
|
|
@ -102,12 +102,7 @@ static void rpc_unregister_client(struct rpc_clnt *clnt)
|
|||
|
||||
static void __rpc_clnt_remove_pipedir(struct rpc_clnt *clnt)
|
||||
{
|
||||
if (clnt->cl_dentry) {
|
||||
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;
|
||||
rpc_remove_client_dir(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,
|
||||
struct rpc_clnt *clnt,
|
||||
const char *dir_name)
|
||||
struct rpc_clnt *clnt)
|
||||
{
|
||||
static uint32_t clntid;
|
||||
const char *dir_name = clnt->cl_program->pipe_dir_name;
|
||||
char name[15];
|
||||
struct dentry *dir, *dentry;
|
||||
|
||||
|
@ -153,28 +148,35 @@ static struct dentry *rpc_setup_pipedir_sb(struct super_block *sb,
|
|||
}
|
||||
|
||||
static int
|
||||
rpc_setup_pipedir(struct rpc_clnt *clnt, const char *dir_name,
|
||||
struct super_block *pipefs_sb)
|
||||
rpc_setup_pipedir(struct super_block *pipefs_sb, struct rpc_clnt *clnt)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
|
||||
clnt->cl_dentry = NULL;
|
||||
if (dir_name == NULL)
|
||||
return 0;
|
||||
dentry = rpc_setup_pipedir_sb(pipefs_sb, clnt, dir_name);
|
||||
if (IS_ERR(dentry))
|
||||
return PTR_ERR(dentry);
|
||||
clnt->cl_dentry = dentry;
|
||||
if (clnt->cl_program->pipe_dir_name != NULL) {
|
||||
dentry = rpc_setup_pipedir_sb(pipefs_sb, clnt);
|
||||
if (IS_ERR(dentry))
|
||||
return PTR_ERR(dentry);
|
||||
}
|
||||
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) ||
|
||||
((event == RPC_PIPEFS_UMOUNT) && !clnt->cl_dentry))
|
||||
return 1;
|
||||
if ((event == RPC_PIPEFS_MOUNT) && atomic_read(&clnt->cl_count) == 0)
|
||||
if (clnt->cl_program->pipe_dir_name == NULL)
|
||||
return 1;
|
||||
|
||||
switch (event) {
|
||||
case RPC_PIPEFS_MOUNT:
|
||||
if (clnt->cl_pipedir_objects.pdh_dentry != NULL)
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -186,18 +188,11 @@ static int __rpc_clnt_handle_event(struct rpc_clnt *clnt, unsigned long event,
|
|||
|
||||
switch (event) {
|
||||
case RPC_PIPEFS_MOUNT:
|
||||
dentry = rpc_setup_pipedir_sb(sb, clnt,
|
||||
clnt->cl_program->pipe_dir_name);
|
||||
dentry = rpc_setup_pipedir_sb(sb, clnt);
|
||||
if (!dentry)
|
||||
return -ENOENT;
|
||||
if (IS_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;
|
||||
case RPC_PIPEFS_UMOUNT:
|
||||
__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);
|
||||
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))
|
||||
continue;
|
||||
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,
|
||||
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 net *net = rpc_net_ns(clnt);
|
||||
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);
|
||||
if (pipefs_sb) {
|
||||
err = rpc_setup_pipedir(clnt, program->pipe_dir_name, pipefs_sb);
|
||||
err = rpc_setup_pipedir(pipefs_sb, clnt);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
@ -299,7 +295,7 @@ static int rpc_client_register(const struct rpc_create_args *args,
|
|||
if (pipefs_sb)
|
||||
rpc_put_sb_net(net);
|
||||
|
||||
auth = rpcauth_create(args->authflavor, clnt);
|
||||
auth = rpcauth_create(&auth_args, clnt);
|
||||
if (IS_ERR(auth)) {
|
||||
dprintk("RPC: Couldn't create auth handle (flavor %u)\n",
|
||||
args->authflavor);
|
||||
|
@ -317,7 +313,27 @@ out:
|
|||
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_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);
|
||||
if (!clnt)
|
||||
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);
|
||||
clnt->cl_procinfo = version->procs;
|
||||
clnt->cl_maxproc = version->nrprocs;
|
||||
clnt->cl_protname = program->name;
|
||||
clnt->cl_prog = args->prognumber ? : program->number;
|
||||
clnt->cl_vers = version->number;
|
||||
clnt->cl_stats = program->stats;
|
||||
clnt->cl_metrics = rpc_alloc_iostats(clnt);
|
||||
rpc_init_pipe_dir_head(&clnt->cl_pipedir_objects);
|
||||
err = -ENOMEM;
|
||||
if (clnt->cl_metrics == NULL)
|
||||
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;
|
||||
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);
|
||||
|
||||
|
@ -387,13 +401,15 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
|
|||
err = rpc_client_register(args, clnt);
|
||||
if (err)
|
||||
goto out_no_path;
|
||||
if (parent)
|
||||
atomic_inc(&parent->cl_count);
|
||||
return clnt;
|
||||
|
||||
out_no_path:
|
||||
kfree(clnt->cl_principal);
|
||||
out_no_principal:
|
||||
rpc_free_iostats(clnt->cl_metrics);
|
||||
out_no_stats:
|
||||
rpc_free_clid(clnt);
|
||||
out_no_clid:
|
||||
kfree(clnt);
|
||||
out_err:
|
||||
rpciod_down();
|
||||
|
@ -479,7 +495,7 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
|
|||
if (args->flags & RPC_CLNT_CREATE_NONPRIVPORT)
|
||||
xprt->resvport = 0;
|
||||
|
||||
clnt = rpc_new_client(args, xprt);
|
||||
clnt = rpc_new_client(args, xprt, NULL);
|
||||
if (IS_ERR(clnt))
|
||||
return clnt;
|
||||
|
||||
|
@ -526,15 +542,12 @@ static struct rpc_clnt *__rpc_clone_client(struct rpc_create_args *args,
|
|||
goto out_err;
|
||||
args->servername = xprt->servername;
|
||||
|
||||
new = rpc_new_client(args, xprt);
|
||||
new = rpc_new_client(args, xprt, clnt);
|
||||
if (IS_ERR(new)) {
|
||||
err = PTR_ERR(new);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
atomic_inc(&clnt->cl_count);
|
||||
new->cl_parent = clnt;
|
||||
|
||||
/* Turn off autobind on clones */
|
||||
new->cl_autobind = 0;
|
||||
new->cl_softrtry = clnt->cl_softrtry;
|
||||
|
@ -561,7 +574,6 @@ struct rpc_clnt *rpc_clone_client(struct rpc_clnt *clnt)
|
|||
.prognumber = clnt->cl_prog,
|
||||
.version = clnt->cl_vers,
|
||||
.authflavor = clnt->cl_auth->au_flavor,
|
||||
.client_name = clnt->cl_principal,
|
||||
};
|
||||
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,
|
||||
.version = clnt->cl_vers,
|
||||
.authflavor = flavor,
|
||||
.client_name = clnt->cl_principal,
|
||||
};
|
||||
return __rpc_clone_client(&args, clnt);
|
||||
}
|
||||
|
@ -629,7 +640,7 @@ void rpc_shutdown_client(struct rpc_clnt *clnt)
|
|||
might_sleep();
|
||||
|
||||
dprintk_rcu("RPC: shutting down %s client for %s\n",
|
||||
clnt->cl_protname,
|
||||
clnt->cl_program->name,
|
||||
rcu_dereference(clnt->cl_xprt)->servername);
|
||||
|
||||
while (!list_empty(&clnt->cl_tasks)) {
|
||||
|
@ -649,17 +660,17 @@ static void
|
|||
rpc_free_client(struct rpc_clnt *clnt)
|
||||
{
|
||||
dprintk_rcu("RPC: destroying %s client for %s\n",
|
||||
clnt->cl_protname,
|
||||
clnt->cl_program->name,
|
||||
rcu_dereference(clnt->cl_xprt)->servername);
|
||||
if (clnt->cl_parent != clnt)
|
||||
rpc_release_client(clnt->cl_parent);
|
||||
rpc_clnt_remove_pipedir(clnt);
|
||||
rpc_unregister_client(clnt);
|
||||
rpc_free_iostats(clnt->cl_metrics);
|
||||
kfree(clnt->cl_principal);
|
||||
clnt->cl_metrics = NULL;
|
||||
xprt_put(rcu_dereference_raw(clnt->cl_xprt));
|
||||
rpciod_down();
|
||||
rpc_free_clid(clnt);
|
||||
kfree(clnt);
|
||||
}
|
||||
|
||||
|
@ -720,7 +731,6 @@ struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *old,
|
|||
.prognumber = program->number,
|
||||
.version = vers,
|
||||
.authflavor = old->cl_auth->au_flavor,
|
||||
.client_name = old->cl_principal,
|
||||
};
|
||||
struct rpc_clnt *clnt;
|
||||
int err;
|
||||
|
@ -1299,7 +1309,7 @@ call_start(struct rpc_task *task)
|
|||
struct rpc_clnt *clnt = task->tk_client;
|
||||
|
||||
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_IS_ASYNC(task) ? "async" : "sync"));
|
||||
|
||||
|
@ -1423,9 +1433,9 @@ call_refreshresult(struct rpc_task *task)
|
|||
return;
|
||||
case -ETIMEDOUT:
|
||||
rpc_delay(task, 3*HZ);
|
||||
case -EKEYEXPIRED:
|
||||
case -EAGAIN:
|
||||
status = -EACCES;
|
||||
case -EKEYEXPIRED:
|
||||
if (!task->tk_cred_retry)
|
||||
break;
|
||||
task->tk_cred_retry--;
|
||||
|
@ -1912,7 +1922,7 @@ call_status(struct rpc_task *task)
|
|||
default:
|
||||
if (clnt->cl_chatty)
|
||||
printk("%s: RPC call returned error %d\n",
|
||||
clnt->cl_protname, -status);
|
||||
clnt->cl_program->name, -status);
|
||||
rpc_exit(task, status);
|
||||
}
|
||||
}
|
||||
|
@ -1943,7 +1953,7 @@ call_timeout(struct rpc_task *task)
|
|||
if (clnt->cl_chatty) {
|
||||
rcu_read_lock();
|
||||
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_read_unlock();
|
||||
}
|
||||
|
@ -1959,7 +1969,7 @@ call_timeout(struct rpc_task *task)
|
|||
if (clnt->cl_chatty) {
|
||||
rcu_read_lock();
|
||||
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_read_unlock();
|
||||
}
|
||||
|
@ -1994,7 +2004,7 @@ call_decode(struct rpc_task *task)
|
|||
if (clnt->cl_chatty) {
|
||||
rcu_read_lock();
|
||||
printk(KERN_NOTICE "%s: server %s OK\n",
|
||||
clnt->cl_protname,
|
||||
clnt->cl_program->name,
|
||||
rcu_dereference(clnt->cl_xprt)->servername);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
@ -2019,7 +2029,7 @@ call_decode(struct rpc_task *task)
|
|||
goto out_retry;
|
||||
}
|
||||
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;
|
||||
goto out_retry;
|
||||
}
|
||||
|
@ -2091,7 +2101,8 @@ rpc_verify_header(struct rpc_task *task)
|
|||
dprintk("RPC: %5u %s: XDR representation not a multiple of"
|
||||
" 4 bytes: 0x%x\n", task->tk_pid, __func__,
|
||||
task->tk_rqstp->rq_rcv_buf.len);
|
||||
goto out_eio;
|
||||
error = -EIO;
|
||||
goto out_err;
|
||||
}
|
||||
if ((len -= 3) < 0)
|
||||
goto out_overflow;
|
||||
|
@ -2100,6 +2111,7 @@ rpc_verify_header(struct rpc_task *task)
|
|||
if ((n = ntohl(*p++)) != RPC_REPLY) {
|
||||
dprintk("RPC: %5u %s: not an RPC reply: %x\n",
|
||||
task->tk_pid, __func__, n);
|
||||
error = -EIO;
|
||||
goto out_garbage;
|
||||
}
|
||||
|
||||
|
@ -2118,7 +2130,8 @@ rpc_verify_header(struct rpc_task *task)
|
|||
dprintk("RPC: %5u %s: RPC call rejected, "
|
||||
"unknown error: %x\n",
|
||||
task->tk_pid, __func__, n);
|
||||
goto out_eio;
|
||||
error = -EIO;
|
||||
goto out_err;
|
||||
}
|
||||
if (--len < 0)
|
||||
goto out_overflow;
|
||||
|
@ -2163,9 +2176,11 @@ rpc_verify_header(struct rpc_task *task)
|
|||
task->tk_pid, __func__, n);
|
||||
goto out_err;
|
||||
}
|
||||
if (!(p = rpcauth_checkverf(task, p))) {
|
||||
dprintk("RPC: %5u %s: auth check failed\n",
|
||||
task->tk_pid, __func__);
|
||||
p = rpcauth_checkverf(task, p);
|
||||
if (IS_ERR(p)) {
|
||||
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 */
|
||||
}
|
||||
len = p - (__be32 *)iov->iov_base - 1;
|
||||
|
@ -2218,8 +2233,6 @@ out_garbage:
|
|||
out_retry:
|
||||
return ERR_PTR(-EAGAIN);
|
||||
}
|
||||
out_eio:
|
||||
error = -EIO;
|
||||
out_err:
|
||||
rpc_exit(task, error);
|
||||
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",
|
||||
task->tk_pid, task->tk_flags, task->tk_status,
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -409,7 +409,7 @@ rpc_show_info(struct seq_file *m, void *v)
|
|||
rcu_read_lock();
|
||||
seq_printf(m, "RPC server: %s\n",
|
||||
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);
|
||||
seq_printf(m, "address: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_ADDR));
|
||||
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,
|
||||
};
|
||||
|
||||
/*
|
||||
* 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 *
|
||||
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) {
|
||||
case S_IFDIR:
|
||||
inode->i_fop = &simple_dir_operations;
|
||||
inode->i_op = &rpc_dir_inode_operations;
|
||||
inode->i_op = &simple_dir_inode_operations;
|
||||
inc_nlink(inode);
|
||||
default:
|
||||
break;
|
||||
|
@ -901,6 +884,159 @@ rpc_unlink(struct dentry *dentry)
|
|||
}
|
||||
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 {
|
||||
RPCAUTH_info,
|
||||
RPCAUTH_EOF
|
||||
|
@ -941,16 +1077,29 @@ struct dentry *rpc_create_client_dir(struct dentry *dentry,
|
|||
const char *name,
|
||||
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);
|
||||
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()
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
|
|
@ -258,7 +258,7 @@ static int rpc_wait_bit_killable(void *word)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef RPC_DEBUG
|
||||
#if defined(RPC_DEBUG) || defined(RPC_TRACEPOINTS)
|
||||
static void rpc_task_set_debuginfo(struct rpc_task *task)
|
||||
{
|
||||
static atomic_t rpc_pid;
|
||||
|
|
|
@ -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, "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();
|
||||
xprt = rcu_dereference(clnt->cl_xprt);
|
||||
|
|
|
@ -47,6 +47,8 @@
|
|||
#include <net/udp.h>
|
||||
#include <net/tcp.h>
|
||||
|
||||
#include <trace/events/sunrpc.h>
|
||||
|
||||
#include "sunrpc.h"
|
||||
|
||||
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 socket *sock = transport->sock;
|
||||
|
||||
if (sock != NULL)
|
||||
if (sock != NULL) {
|
||||
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;
|
||||
|
||||
trace_rpc_socket_close(&transport->xprt, sock);
|
||||
sock_release(sock);
|
||||
}
|
||||
|
||||
|
@ -1492,6 +1497,7 @@ static void xs_tcp_state_change(struct sock *sk)
|
|||
sock_flag(sk, SOCK_ZAPPED),
|
||||
sk->sk_shutdown);
|
||||
|
||||
trace_rpc_socket_state_change(xprt, sk->sk_socket);
|
||||
switch (sk->sk_state) {
|
||||
case TCP_ESTABLISHED:
|
||||
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]);
|
||||
|
||||
status = xs_local_finish_connecting(xprt, sock);
|
||||
trace_rpc_socket_connect(xprt, sock, status);
|
||||
switch (status) {
|
||||
case 0:
|
||||
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]);
|
||||
|
||||
xs_udp_finish_connecting(xprt, sock);
|
||||
trace_rpc_socket_connect(xprt, sock, 0);
|
||||
status = 0;
|
||||
out:
|
||||
xprt_clear_connecting(xprt);
|
||||
|
@ -2064,6 +2072,8 @@ static void xs_abort_connection(struct sock_xprt *transport)
|
|||
memset(&any, 0, sizeof(any));
|
||||
any.sa_family = AF_UNSPEC;
|
||||
result = kernel_connect(transport->sock, &any, sizeof(any), 0);
|
||||
trace_rpc_socket_reset_connection(&transport->xprt,
|
||||
transport->sock, result);
|
||||
if (!result)
|
||||
xs_sock_reset_connection_flags(&transport->xprt);
|
||||
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]);
|
||||
|
||||
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",
|
||||
xprt, -status, xprt_connected(xprt),
|
||||
sock->sk->sk_state);
|
||||
|
|
Loading…
Reference in New Issue