NFS Client Updates for Linux 5.6

Stable bugfixes:
 - Fix memory leaks and corruption in readdir # v2.6.37+
 - Directory page cache needs to be locked when read # v2.6.37+
 
 New features:
 - Convert NFS to use the new mount API
 - Add "softreval" mount option to let clients use cache if server goes down
 - Add a config option to compile without UDP support
 - Limit the number of inactive delegations the client can cache at once
 - Improved readdir concurrency using iterate_shared()
 
 Other bugfixes and cleanups:
 - More 64-bit time conversions
 - Add additional diagnostic tracepoints
 - Check for holes in swapfiles, and add dependency on CONFIG_SWAP
 - Various xprtrdma cleanups to prepare for 5.7's changes
 - Several fixes for NFS writeback and commit handling
 - Fix acls over krb5i/krb5p mounts
 - Recover from premature loss of openstateids
 - Fix NFS v3 chacl and chmod bug
 - Compare creds using cred_fscmp()
 - Use kmemdup_nul() in more places
 - Optimize readdir cache page invalidation
 - Lease renewal and recovery fixes
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEnZ5MQTpR7cLU7KEp18tUv7ClQOsFAl48kMUACgkQ18tUv7Cl
 QOs/bA/+KAHaee+1jWdgRS88CnNDfeokU2sGWuyXWrVTmiKZ+IjnIUIWqmeKhVyg
 RTbaG4PGTIwiLDFibgzdnc3cTOQEgLnVGWWZ50Xh3b7ubock7+/4JHxqZS+/f3vf
 yqwM0dZaXi5Kcx1kEJ+niBxuzkc9mFI+nHh+wLIlin/kaaUdLKu7mP3NXj2cmWxN
 NoRaKc2gEvkPHhPSH4Z1DVXTHxvH2REFvt9APPUgfLfqcUVHV9b7V/wI/roiGWMn
 53h6f38IdqoNQIpzMog/k/va67NLmEvUZOlpCYPyanPOjuxTrmi8iC2S6gLEOjtc
 GGnQnc5skVL31seFR1NbOJiiN3hTLTncnoXza0cKtYxmo7a/FjXApw4jCu3Rkrav
 UXpCI4O6+2AVVG+pEPbjQy3/GEImeoGvp+xr57jBSZBHoDZU9LDwag65qvZ1btIq
 KOBx2gweQz0aB2heXmfee7qzxFdftHmtMWhIMnJASKNuAWGL23Scqem+d97i2T6H
 7y9OJ3aOXiYxFMLYJCsLWjUJxYiaIANNBmHMjf27mZzcdDuxGFms277CMpNPr3SU
 WZk6/oKw9jaRSzHzaKgVDXiULLXQE1/xZ/mvgR/zk1QAusyeXPvVnMdxoRdxFdXb
 QGZHgUqvFvYi8Lufvs+ZLGS4sAp7oD/Q+lNPXn7cniSwfY4uJiw=
 =b6+F
 -----END PGP SIGNATURE-----

Merge tag 'nfs-for-5.6-1' of git://git.linux-nfs.org/projects/anna/linux-nfs

Puyll NFS client updates from Anna Schumaker:
 "Stable bugfixes:
   - Fix memory leaks and corruption in readdir # v2.6.37+
   - Directory page cache needs to be locked when read # v2.6.37+

  New features:
   - Convert NFS to use the new mount API
   - Add "softreval" mount option to let clients use cache if server goes down
   - Add a config option to compile without UDP support
   - Limit the number of inactive delegations the client can cache at once
   - Improved readdir concurrency using iterate_shared()

  Other bugfixes and cleanups:
   - More 64-bit time conversions
   - Add additional diagnostic tracepoints
   - Check for holes in swapfiles, and add dependency on CONFIG_SWAP
   - Various xprtrdma cleanups to prepare for 5.7's changes
   - Several fixes for NFS writeback and commit handling
   - Fix acls over krb5i/krb5p mounts
   - Recover from premature loss of openstateids
   - Fix NFS v3 chacl and chmod bug
   - Compare creds using cred_fscmp()
   - Use kmemdup_nul() in more places
   - Optimize readdir cache page invalidation
   - Lease renewal and recovery fixes"

* tag 'nfs-for-5.6-1' of git://git.linux-nfs.org/projects/anna/linux-nfs: (93 commits)
  NFSv4.0: nfs4_do_fsinfo() should not do implicit lease renewals
  NFSv4: try lease recovery on NFS4ERR_EXPIRED
  NFS: Fix memory leaks
  nfs: optimise readdir cache page invalidation
  NFS: Switch readdir to using iterate_shared()
  NFS: Use kmemdup_nul() in nfs_readdir_make_qstr()
  NFS: Directory page cache pages need to be locked when read
  NFS: Fix memory leaks and corruption in readdir
  SUNRPC: Use kmemdup_nul() in rpc_parse_scope_id()
  NFS: Replace various occurrences of kstrndup() with kmemdup_nul()
  NFSv4: Limit the total number of cached delegations
  NFSv4: Add accounting for the number of active delegations held
  NFSv4: Try to return the delegation immediately when marked for return on close
  NFS: Clear NFS_DELEGATION_RETURN_IF_CLOSED when the delegation is returned
  NFSv4: nfs_inode_evict_delegation() should set NFS_DELEGATION_RETURNING
  NFS: nfs_find_open_context() should use cred_fscmp()
  NFS: nfs_access_get_cached_rcu() should use cred_fscmp()
  NFSv4: pnfs_roc() must use cred_fscmp() to compare creds
  NFS: remove unused macros
  nfs: Return EINVAL rather than ERANGE for mount parse errors
  ...
This commit is contained in:
Linus Torvalds 2020-02-07 17:39:56 -08:00
commit f43574d0ac
63 changed files with 3223 additions and 2907 deletions

View File

@ -90,7 +90,7 @@ config NFS_V4
config NFS_SWAP
bool "Provide swap over NFS support"
default n
depends on NFS_FS
depends on NFS_FS && SWAP
select SUNRPC_SWAP
help
This option enables swapon to work on files located on NFS mounts.
@ -196,3 +196,12 @@ config NFS_DEBUG
depends on NFS_FS && SUNRPC_DEBUG
select CRC32
default y
config NFS_DISABLE_UDP_SUPPORT
bool "NFS: Disable NFS UDP protocol support"
depends on NFS_FS
default y
help
Choose Y here to disable the use of NFS over UDP. NFS over UDP
on modern networks (1Gb+) can lead to data corruption caused by
fragmentation during high loads.

View File

@ -9,7 +9,7 @@ CFLAGS_nfstrace.o += -I$(src)
nfs-y := client.o dir.o file.o getroot.o inode.o super.o \
io.o direct.o pagelist.o read.o symlink.o unlink.o \
write.o namespace.o mount_clnt.o nfstrace.o \
export.o sysfs.o
export.o sysfs.o fs_context.o
nfs-$(CONFIG_ROOT_NFS) += nfsroot.o
nfs-$(CONFIG_SYSCTL) += sysctl.o
nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o

View File

@ -18,6 +18,7 @@
#include "callback.h"
#include "internal.h"
#include "nfs4session.h"
#include "nfs4trace.h"
#define CB_OP_TAGLEN_MAXSZ (512)
#define CB_OP_HDR_RES_MAXSZ (2 * 4) // opcode, status
@ -946,9 +947,13 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp)
if (hdr_arg.minorversion == 0) {
cps.clp = nfs4_find_client_ident(SVC_NET(rqstp), hdr_arg.cb_ident);
if (!cps.clp || !check_gss_callback_principal(cps.clp, rqstp)) {
if (cps.clp)
nfs_put_client(cps.clp);
if (!cps.clp) {
trace_nfs_cb_no_clp(rqstp->rq_xid, hdr_arg.cb_ident);
goto out_invalidcred;
}
if (!check_gss_callback_principal(cps.clp, rqstp)) {
trace_nfs_cb_badprinc(rqstp->rq_xid, hdr_arg.cb_ident);
nfs_put_client(cps.clp);
goto out_invalidcred;
}
}

View File

@ -474,6 +474,7 @@ void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
to->to_maxval = to->to_initval;
to->to_exponential = 0;
break;
#ifndef CONFIG_NFS_DISABLE_UDP_SUPPORT
case XPRT_TRANSPORT_UDP:
if (retrans == NFS_UNSPEC_RETRANS)
to->to_retries = NFS_DEF_UDP_RETRANS;
@ -484,6 +485,7 @@ void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
to->to_maxval = NFS_MAX_UDP_TIMEOUT;
to->to_exponential = 1;
break;
#endif
default:
BUG();
}
@ -580,8 +582,10 @@ static int nfs_start_lockd(struct nfs_server *server)
default:
nlm_init.protocol = IPPROTO_TCP;
break;
#ifndef CONFIG_NFS_DISABLE_UDP_SUPPORT
case XPRT_TRANSPORT_UDP:
nlm_init.protocol = IPPROTO_UDP;
#endif
}
host = nlmclnt_init(&nlm_init);
@ -658,28 +662,28 @@ EXPORT_SYMBOL_GPL(nfs_init_client);
* Create a version 2 or 3 client
*/
static int nfs_init_server(struct nfs_server *server,
const struct nfs_parsed_mount_data *data,
struct nfs_subversion *nfs_mod)
const struct fs_context *fc)
{
const struct nfs_fs_context *ctx = nfs_fc2context(fc);
struct rpc_timeout timeparms;
struct nfs_client_initdata cl_init = {
.hostname = data->nfs_server.hostname,
.addr = (const struct sockaddr *)&data->nfs_server.address,
.addrlen = data->nfs_server.addrlen,
.nfs_mod = nfs_mod,
.proto = data->nfs_server.protocol,
.net = data->net,
.hostname = ctx->nfs_server.hostname,
.addr = (const struct sockaddr *)&ctx->nfs_server.address,
.addrlen = ctx->nfs_server.addrlen,
.nfs_mod = ctx->nfs_mod,
.proto = ctx->nfs_server.protocol,
.net = fc->net_ns,
.timeparms = &timeparms,
.cred = server->cred,
.nconnect = data->nfs_server.nconnect,
.nconnect = ctx->nfs_server.nconnect,
.init_flags = (1UL << NFS_CS_REUSEPORT),
};
struct nfs_client *clp;
int error;
nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
data->timeo, data->retrans);
if (data->flags & NFS_MOUNT_NORESVPORT)
nfs_init_timeout_values(&timeparms, ctx->nfs_server.protocol,
ctx->timeo, ctx->retrans);
if (ctx->flags & NFS_MOUNT_NORESVPORT)
set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
/* Allocate or find a client reference we can use */
@ -690,46 +694,46 @@ static int nfs_init_server(struct nfs_server *server,
server->nfs_client = clp;
/* Initialise the client representation from the mount data */
server->flags = data->flags;
server->options = data->options;
server->flags = ctx->flags;
server->options = ctx->options;
server->caps |= NFS_CAP_HARDLINKS|NFS_CAP_SYMLINKS|NFS_CAP_FILEID|
NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|NFS_CAP_OWNER_GROUP|
NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME;
if (data->rsize)
server->rsize = nfs_block_size(data->rsize, NULL);
if (data->wsize)
server->wsize = nfs_block_size(data->wsize, NULL);
if (ctx->rsize)
server->rsize = nfs_block_size(ctx->rsize, NULL);
if (ctx->wsize)
server->wsize = nfs_block_size(ctx->wsize, NULL);
server->acregmin = data->acregmin * HZ;
server->acregmax = data->acregmax * HZ;
server->acdirmin = data->acdirmin * HZ;
server->acdirmax = data->acdirmax * HZ;
server->acregmin = ctx->acregmin * HZ;
server->acregmax = ctx->acregmax * HZ;
server->acdirmin = ctx->acdirmin * HZ;
server->acdirmax = ctx->acdirmax * HZ;
/* Start lockd here, before we might error out */
error = nfs_start_lockd(server);
if (error < 0)
goto error;
server->port = data->nfs_server.port;
server->auth_info = data->auth_info;
server->port = ctx->nfs_server.port;
server->auth_info = ctx->auth_info;
error = nfs_init_server_rpcclient(server, &timeparms,
data->selected_flavor);
ctx->selected_flavor);
if (error < 0)
goto error;
/* Preserve the values of mount_server-related mount options */
if (data->mount_server.addrlen) {
memcpy(&server->mountd_address, &data->mount_server.address,
data->mount_server.addrlen);
server->mountd_addrlen = data->mount_server.addrlen;
if (ctx->mount_server.addrlen) {
memcpy(&server->mountd_address, &ctx->mount_server.address,
ctx->mount_server.addrlen);
server->mountd_addrlen = ctx->mount_server.addrlen;
}
server->mountd_version = data->mount_server.version;
server->mountd_port = data->mount_server.port;
server->mountd_protocol = data->mount_server.protocol;
server->mountd_version = ctx->mount_server.version;
server->mountd_port = ctx->mount_server.port;
server->mountd_protocol = ctx->mount_server.protocol;
server->namelen = data->namlen;
server->namelen = ctx->namlen;
return 0;
error:
@ -951,9 +955,9 @@ EXPORT_SYMBOL_GPL(nfs_free_server);
* Create a version 2 or 3 volume record
* - keyed on server and FSID
*/
struct nfs_server *nfs_create_server(struct nfs_mount_info *mount_info,
struct nfs_subversion *nfs_mod)
struct nfs_server *nfs_create_server(struct fs_context *fc)
{
struct nfs_fs_context *ctx = nfs_fc2context(fc);
struct nfs_server *server;
struct nfs_fattr *fattr;
int error;
@ -970,18 +974,18 @@ struct nfs_server *nfs_create_server(struct nfs_mount_info *mount_info,
goto error;
/* Get a client representation */
error = nfs_init_server(server, mount_info->parsed, nfs_mod);
error = nfs_init_server(server, fc);
if (error < 0)
goto error;
/* Probe the root fh to retrieve its FSID */
error = nfs_probe_fsinfo(server, mount_info->mntfh, fattr);
error = nfs_probe_fsinfo(server, ctx->mntfh, fattr);
if (error < 0)
goto error;
if (server->nfs_client->rpc_ops->version == 3) {
if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN)
server->namelen = NFS3_MAXNAMLEN;
if (!(mount_info->parsed->flags & NFS_MOUNT_NORDIRPLUS))
if (!(ctx->flags & NFS_MOUNT_NORDIRPLUS))
server->caps |= NFS_CAP_READDIRPLUS;
} else {
if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN)
@ -989,8 +993,8 @@ struct nfs_server *nfs_create_server(struct nfs_mount_info *mount_info,
}
if (!(fattr->valid & NFS_ATTR_FATTR)) {
error = nfs_mod->rpc_ops->getattr(server, mount_info->mntfh,
fattr, NULL, NULL);
error = ctx->nfs_mod->rpc_ops->getattr(server, ctx->mntfh,
fattr, NULL, NULL);
if (error < 0) {
dprintk("nfs_create_server: getattr error = %d\n", -error);
goto error;

View File

@ -25,13 +25,32 @@
#include "internal.h"
#include "nfs4trace.h"
static void nfs_free_delegation(struct nfs_delegation *delegation)
#define NFS_DEFAULT_DELEGATION_WATERMARK (5000U)
static atomic_long_t nfs_active_delegations;
static unsigned nfs_delegation_watermark = NFS_DEFAULT_DELEGATION_WATERMARK;
static void __nfs_free_delegation(struct nfs_delegation *delegation)
{
put_cred(delegation->cred);
delegation->cred = NULL;
kfree_rcu(delegation, rcu);
}
static void nfs_mark_delegation_revoked(struct nfs_delegation *delegation)
{
if (!test_and_set_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
delegation->stateid.type = NFS4_INVALID_STATEID_TYPE;
atomic_long_dec(&nfs_active_delegations);
}
}
static void nfs_free_delegation(struct nfs_delegation *delegation)
{
nfs_mark_delegation_revoked(delegation);
__nfs_free_delegation(delegation);
}
/**
* nfs_mark_delegation_referenced - set delegation's REFERENCED flag
* @delegation: delegation to process
@ -343,7 +362,8 @@ nfs_update_inplace_delegation(struct nfs_delegation *delegation,
delegation->stateid.seqid = update->stateid.seqid;
smp_wmb();
delegation->type = update->type;
clear_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
if (test_and_clear_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
atomic_long_inc(&nfs_active_delegations);
}
}
@ -423,6 +443,8 @@ add_new:
rcu_assign_pointer(nfsi->delegation, delegation);
delegation = NULL;
atomic_long_inc(&nfs_active_delegations);
trace_nfs4_set_delegation(inode, type);
spin_lock(&inode->i_lock);
@ -432,7 +454,7 @@ add_new:
out:
spin_unlock(&clp->cl_lock);
if (delegation != NULL)
nfs_free_delegation(delegation);
__nfs_free_delegation(delegation);
if (freeme != NULL) {
nfs_do_return_delegation(inode, freeme, 0);
nfs_free_delegation(freeme);
@ -479,7 +501,7 @@ static bool nfs_delegation_need_return(struct nfs_delegation *delegation)
if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags))
ret = true;
if (test_and_clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) && !ret) {
else if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags)) {
struct inode *inode;
spin_lock(&delegation->lock);
@ -488,6 +510,8 @@ static bool nfs_delegation_need_return(struct nfs_delegation *delegation)
ret = true;
spin_unlock(&delegation->lock);
}
if (ret)
clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags) ||
test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
ret = false;
@ -607,6 +631,7 @@ void nfs_inode_evict_delegation(struct inode *inode)
delegation = nfs_inode_detach_delegation(inode);
if (delegation != NULL) {
set_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags);
nfs_do_return_delegation(inode, delegation, 1);
nfs_free_delegation(delegation);
@ -636,6 +661,40 @@ int nfs4_inode_return_delegation(struct inode *inode)
return err;
}
/**
* nfs_inode_return_delegation_on_close - asynchronously return a delegation
* @inode: inode to process
*
* This routine is called on file close in order to determine if the
* inode delegation needs to be returned immediately.
*/
void nfs4_inode_return_delegation_on_close(struct inode *inode)
{
struct nfs_delegation *delegation;
struct nfs_delegation *ret = NULL;
if (!inode)
return;
rcu_read_lock();
delegation = nfs4_get_valid_delegation(inode);
if (!delegation)
goto out;
if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) ||
atomic_long_read(&nfs_active_delegations) >= nfs_delegation_watermark) {
spin_lock(&delegation->lock);
if (delegation->inode &&
list_empty(&NFS_I(inode)->open_files) &&
!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
ret = delegation;
}
spin_unlock(&delegation->lock);
}
out:
rcu_read_unlock();
nfs_end_delegation_return(inode, ret, 0);
}
/**
* nfs4_inode_make_writeable
* @inode: pointer to inode
@ -760,13 +819,6 @@ static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *cl
rcu_read_unlock();
}
static void nfs_mark_delegation_revoked(struct nfs_server *server,
struct nfs_delegation *delegation)
{
set_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
delegation->stateid.type = NFS4_INVALID_STATEID_TYPE;
}
static void nfs_revoke_delegation(struct inode *inode,
const nfs4_stateid *stateid)
{
@ -794,7 +846,7 @@ static void nfs_revoke_delegation(struct inode *inode,
}
spin_unlock(&delegation->lock);
}
nfs_mark_delegation_revoked(NFS_SERVER(inode), delegation);
nfs_mark_delegation_revoked(delegation);
ret = true;
out:
rcu_read_unlock();
@ -833,7 +885,7 @@ void nfs_delegation_mark_returned(struct inode *inode,
delegation->stateid.seqid = stateid->seqid;
}
nfs_mark_delegation_revoked(NFS_SERVER(inode), delegation);
nfs_mark_delegation_revoked(delegation);
out_clear_returning:
clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
@ -1317,3 +1369,5 @@ out:
rcu_read_unlock();
return ret;
}
module_param_named(delegation_watermark, nfs_delegation_watermark, uint, 0644);

View File

@ -42,6 +42,7 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred,
fmode_t type, const nfs4_stateid *stateid, unsigned long pagemod_limit);
int nfs4_inode_return_delegation(struct inode *inode);
void nfs4_inode_return_delegation_on_close(struct inode *inode);
int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid);
void nfs_inode_evict_delegation(struct inode *inode);

View File

@ -58,7 +58,7 @@ static void nfs_readdir_clear_array(struct page*);
const struct file_operations nfs_dir_operations = {
.llseek = nfs_llseek_dir,
.read = generic_read_dir,
.iterate = nfs_readdir,
.iterate_shared = nfs_readdir,
.open = nfs_opendir,
.release = nfs_closedir,
.fsync = nfs_fsync_dir,
@ -162,6 +162,17 @@ typedef struct {
bool eof;
} nfs_readdir_descriptor_t;
static
void nfs_readdir_init_array(struct page *page)
{
struct nfs_cache_array *array;
array = kmap_atomic(page);
memset(array, 0, sizeof(struct nfs_cache_array));
array->eof_index = -1;
kunmap_atomic(array);
}
/*
* we are freeing strings created by nfs_add_to_readdir_array()
*/
@ -174,6 +185,7 @@ void nfs_readdir_clear_array(struct page *page)
array = kmap_atomic(page);
for (i = 0; i < array->size; i++)
kfree(array->array[i].string.name);
array->size = 0;
kunmap_atomic(array);
}
@ -186,7 +198,7 @@ static
int nfs_readdir_make_qstr(struct qstr *string, const char *name, unsigned int len)
{
string->len = len;
string->name = kmemdup(name, len, GFP_KERNEL);
string->name = kmemdup_nul(name, len, GFP_KERNEL);
if (string->name == NULL)
return -ENOMEM;
/*
@ -437,7 +449,8 @@ void nfs_force_use_readdirplus(struct inode *dir)
if (nfs_server_capable(dir, NFS_CAP_READDIRPLUS) &&
!list_empty(&nfsi->open_files)) {
set_bit(NFS_INO_ADVISE_RDPLUS, &nfsi->flags);
invalidate_mapping_pages(dir->i_mapping, 0, -1);
invalidate_mapping_pages(dir->i_mapping,
nfsi->page_index + 1, -1);
}
}
@ -610,6 +623,8 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
int status = -ENOMEM;
unsigned int array_size = ARRAY_SIZE(pages);
nfs_readdir_init_array(page);
entry.prev_cookie = 0;
entry.cookie = desc->last_cookie;
entry.eof = 0;
@ -626,8 +641,6 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
}
array = kmap(page);
memset(array, 0, sizeof(struct nfs_cache_array));
array->eof_index = -1;
status = nfs_readdir_alloc_pages(pages, array_size);
if (status < 0)
@ -682,6 +695,7 @@ int nfs_readdir_filler(void *data, struct page* page)
unlock_page(page);
return 0;
error:
nfs_readdir_clear_array(page);
unlock_page(page);
return ret;
}
@ -689,8 +703,6 @@ int nfs_readdir_filler(void *data, struct page* page)
static
void cache_page_release(nfs_readdir_descriptor_t *desc)
{
if (!desc->page->mapping)
nfs_readdir_clear_array(desc->page);
put_page(desc->page);
desc->page = NULL;
}
@ -704,19 +716,32 @@ struct page *get_cache_page(nfs_readdir_descriptor_t *desc)
/*
* Returns 0 if desc->dir_cookie was found on page desc->page_index
* and locks the page to prevent removal from the page cache.
*/
static
int find_cache_page(nfs_readdir_descriptor_t *desc)
int find_and_lock_cache_page(nfs_readdir_descriptor_t *desc)
{
struct inode *inode = file_inode(desc->file);
struct nfs_inode *nfsi = NFS_I(inode);
int res;
desc->page = get_cache_page(desc);
if (IS_ERR(desc->page))
return PTR_ERR(desc->page);
res = nfs_readdir_search_array(desc);
res = lock_page_killable(desc->page);
if (res != 0)
cache_page_release(desc);
goto error;
res = -EAGAIN;
if (desc->page->mapping != NULL) {
res = nfs_readdir_search_array(desc);
if (res == 0) {
nfsi->page_index = desc->page_index;
return 0;
}
}
unlock_page(desc->page);
error:
cache_page_release(desc);
return res;
}
@ -731,7 +756,7 @@ int readdir_search_pagecache(nfs_readdir_descriptor_t *desc)
desc->last_cookie = 0;
}
do {
res = find_cache_page(desc);
res = find_and_lock_cache_page(desc);
} while (res == -EAGAIN);
return res;
}
@ -770,7 +795,6 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc)
desc->eof = true;
kunmap(desc->page);
cache_page_release(desc);
dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n",
(unsigned long long)*desc->dir_cookie, res);
return res;
@ -816,13 +840,13 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc)
status = nfs_do_filldir(desc);
out_release:
nfs_readdir_clear_array(desc->page);
cache_page_release(desc);
out:
dfprintk(DIRCACHE, "NFS: %s: returns %d\n",
__func__, status);
return status;
out_release:
cache_page_release(desc);
goto out;
}
/* The file offset position represents the dirent entry number. A
@ -887,6 +911,8 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
break;
res = nfs_do_filldir(desc);
unlock_page(desc->page);
cache_page_release(desc);
if (res < 0)
break;
} while (!desc->eof);
@ -1142,10 +1168,17 @@ nfs_lookup_revalidate_dentry(struct inode *dir, struct dentry *dentry,
if (fhandle == NULL || fattr == NULL || IS_ERR(label))
goto out;
ret = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label);
ret = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr, label);
if (ret < 0) {
if (ret == -ESTALE || ret == -ENOENT)
switch (ret) {
case -ESTALE:
case -ENOENT:
ret = 0;
break;
case -ETIMEDOUT:
if (NFS_SERVER(inode)->flags & NFS_MOUNT_SOFTREVAL)
ret = 1;
}
goto out;
}
ret = 0;
@ -1408,7 +1441,7 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
goto out;
trace_nfs_lookup_enter(dir, dentry, flags);
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label);
error = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr, label);
if (error == -ENOENT)
goto no_entry;
if (error < 0) {
@ -1683,7 +1716,7 @@ nfs_add_or_obtain(struct dentry *dentry, struct nfs_fh *fhandle,
d_drop(dentry);
if (fhandle->size == 0) {
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, NULL);
error = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr, NULL);
if (error)
goto out_error;
}
@ -2312,11 +2345,11 @@ static int nfs_access_get_cached(struct inode *inode, const struct cred *cred, s
/* Found an entry, is our attribute cache valid? */
if (!nfs_check_cache_invalid(inode, NFS_INO_INVALID_ACCESS))
break;
if (!retry)
break;
err = -ECHILD;
if (!may_block)
goto out;
if (!retry)
goto out_zap;
spin_unlock(&inode->i_lock);
err = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
if (err)
@ -2353,7 +2386,7 @@ static int nfs_access_get_cached_rcu(struct inode *inode, const struct cred *cre
lh = rcu_dereference(nfsi->access_cache_entry_lru.prev);
cache = list_entry(lh, struct nfs_access_entry, lru);
if (lh == &nfsi->access_cache_entry_lru ||
cred != cache->cred)
cred_fscmp(cred, cache->cred) != 0)
cache = NULL;
if (cache == NULL)
goto out;
@ -2476,7 +2509,7 @@ static int nfs_do_access(struct inode *inode, const struct cred *cred, int mask)
{
struct nfs_access_entry cache;
bool may_block = (mask & MAY_NOT_BLOCK) == 0;
int cache_mask;
int cache_mask = -1;
int status;
trace_nfs_access_enter(inode);
@ -2515,7 +2548,7 @@ out_cached:
if ((mask & ~cache_mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) != 0)
status = -EACCES;
out:
trace_nfs_access_exit(inode, status);
trace_nfs_access_exit(inode, mask, cache_mask, status);
return status;
}

View File

@ -245,10 +245,10 @@ static int nfs_direct_cmp_commit_data_verf(struct nfs_direct_req *dreq,
data->ds_commit_index);
/* verifier not set so always fail */
if (verfp->committed < 0)
if (verfp->committed < 0 || data->res.verf->committed <= NFS_UNSTABLE)
return 1;
return nfs_direct_cmp_verf(verfp, &data->verf);
return nfs_direct_cmp_verf(verfp, data->res.verf);
}
/**
@ -824,7 +824,8 @@ static void nfs_direct_write_reschedule_io(struct nfs_pgio_header *hdr)
dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
/* fake unstable write to let common nfs resend pages */
hdr->verf.committed = NFS_UNSTABLE;
hdr->good_bytes = hdr->args.count;
hdr->good_bytes = hdr->args.offset + hdr->args.count -
hdr->io_start;
}
spin_unlock(&dreq->lock);
}

View File

@ -93,7 +93,7 @@ static void nfs_dns_ent_init(struct cache_head *cnew,
key = container_of(ckey, struct nfs_dns_ent, h);
kfree(new->hostname);
new->hostname = kstrndup(key->hostname, key->namelen, GFP_KERNEL);
new->hostname = kmemdup_nul(key->hostname, key->namelen, GFP_KERNEL);
if (new->hostname) {
new->namelen = key->namelen;
nfs_dns_ent_update(cnew, ckey);

View File

@ -204,44 +204,39 @@ EXPORT_SYMBOL_GPL(nfs_file_mmap);
static int
nfs_file_fsync_commit(struct file *file, int datasync)
{
struct nfs_open_context *ctx = nfs_file_open_context(file);
struct inode *inode = file_inode(file);
int do_resend, status;
int ret = 0;
int ret;
dprintk("NFS: fsync file(%pD2) datasync %d\n", file, datasync);
nfs_inc_stats(inode, NFSIOS_VFSFSYNC);
do_resend = test_and_clear_bit(NFS_CONTEXT_RESEND_WRITES, &ctx->flags);
status = nfs_commit_inode(inode, FLUSH_SYNC);
if (status == 0)
status = file_check_and_advance_wb_err(file);
if (status < 0) {
ret = status;
goto out;
}
do_resend |= test_bit(NFS_CONTEXT_RESEND_WRITES, &ctx->flags);
if (do_resend)
ret = -EAGAIN;
out:
return ret;
ret = nfs_commit_inode(inode, FLUSH_SYNC);
if (ret < 0)
return ret;
return file_check_and_advance_wb_err(file);
}
int
nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
int ret;
struct nfs_open_context *ctx = nfs_file_open_context(file);
struct inode *inode = file_inode(file);
int ret;
trace_nfs_fsync_enter(inode);
do {
for (;;) {
ret = file_write_and_wait_range(file, start, end);
if (ret != 0)
break;
ret = nfs_file_fsync_commit(file, datasync);
if (!ret)
ret = pnfs_sync_inode(inode, !!datasync);
if (ret != 0)
break;
ret = pnfs_sync_inode(inode, !!datasync);
if (ret != 0)
break;
if (!test_and_clear_bit(NFS_CONTEXT_RESEND_WRITES, &ctx->flags))
break;
/*
* If nfs_file_fsync_commit detected a server reboot, then
* resend all dirty pages that might have been covered by
@ -249,7 +244,7 @@ nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
*/
start = 0;
end = LLONG_MAX;
} while (ret == -EAGAIN);
}
trace_nfs_fsync_exit(inode, ret);
return ret;
@ -489,7 +484,19 @@ static int nfs_launder_page(struct page *page)
static int nfs_swap_activate(struct swap_info_struct *sis, struct file *file,
sector_t *span)
{
unsigned long blocks;
long long isize;
struct rpc_clnt *clnt = NFS_CLIENT(file->f_mapping->host);
struct inode *inode = file->f_mapping->host;
spin_lock(&inode->i_lock);
blocks = inode->i_blocks;
isize = inode->i_size;
spin_unlock(&inode->i_lock);
if (blocks*512 < isize) {
pr_warn("swap activate: swapfile has holes\n");
return -EINVAL;
}
*span = sis->pages;

View File

@ -1266,9 +1266,10 @@ static int ff_layout_async_handle_error(struct rpc_task *task,
static void ff_layout_io_track_ds_error(struct pnfs_layout_segment *lseg,
int idx, u64 offset, u64 length,
u32 status, int opnum, int error)
u32 *op_status, int opnum, int error)
{
struct nfs4_ff_layout_mirror *mirror;
u32 status = *op_status;
int err;
if (status == 0) {
@ -1286,10 +1287,10 @@ static void ff_layout_io_track_ds_error(struct pnfs_layout_segment *lseg,
case -ENOBUFS:
case -EPIPE:
case -EPERM:
status = NFS4ERR_NXIO;
*op_status = status = NFS4ERR_NXIO;
break;
case -EACCES:
status = NFS4ERR_ACCESS;
*op_status = status = NFS4ERR_ACCESS;
break;
default:
return;
@ -1321,16 +1322,19 @@ static int ff_layout_read_done_cb(struct rpc_task *task,
int new_idx = hdr->pgio_mirror_idx;
int err;
trace_nfs4_pnfs_read(hdr, task->tk_status);
if (task->tk_status < 0)
if (task->tk_status < 0) {
ff_layout_io_track_ds_error(hdr->lseg, hdr->pgio_mirror_idx,
hdr->args.offset, hdr->args.count,
hdr->res.op_status, OP_READ,
&hdr->res.op_status, OP_READ,
task->tk_status);
trace_ff_layout_read_error(hdr);
}
err = ff_layout_async_handle_error(task, hdr->args.context->state,
hdr->ds_clp, hdr->lseg,
hdr->pgio_mirror_idx);
trace_nfs4_pnfs_read(hdr, err);
clear_bit(NFS_IOHDR_RESEND_PNFS, &hdr->flags);
clear_bit(NFS_IOHDR_RESEND_MDS, &hdr->flags);
switch (err) {
@ -1494,16 +1498,19 @@ static int ff_layout_write_done_cb(struct rpc_task *task,
loff_t end_offs = 0;
int err;
trace_nfs4_pnfs_write(hdr, task->tk_status);
if (task->tk_status < 0)
if (task->tk_status < 0) {
ff_layout_io_track_ds_error(hdr->lseg, hdr->pgio_mirror_idx,
hdr->args.offset, hdr->args.count,
hdr->res.op_status, OP_WRITE,
&hdr->res.op_status, OP_WRITE,
task->tk_status);
trace_ff_layout_write_error(hdr);
}
err = ff_layout_async_handle_error(task, hdr->args.context->state,
hdr->ds_clp, hdr->lseg,
hdr->pgio_mirror_idx);
trace_nfs4_pnfs_write(hdr, err);
clear_bit(NFS_IOHDR_RESEND_PNFS, &hdr->flags);
clear_bit(NFS_IOHDR_RESEND_MDS, &hdr->flags);
switch (err) {
@ -1537,15 +1544,18 @@ static int ff_layout_commit_done_cb(struct rpc_task *task,
{
int err;
trace_nfs4_pnfs_commit_ds(data, task->tk_status);
if (task->tk_status < 0)
if (task->tk_status < 0) {
ff_layout_io_track_ds_error(data->lseg, data->ds_commit_index,
data->args.offset, data->args.count,
data->res.op_status, OP_COMMIT,
&data->res.op_status, OP_COMMIT,
task->tk_status);
trace_ff_layout_commit_error(data);
}
err = ff_layout_async_handle_error(task, NULL, data->ds_clp,
data->lseg, data->ds_commit_index);
trace_nfs4_pnfs_commit_ds(data, err);
switch (err) {
case -NFS4ERR_RESET_TO_PNFS:
pnfs_generic_prepare_to_resend_writes(data);

1437
fs/nfs/fs_context.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -128,7 +128,7 @@ void nfs_fscache_get_super_cookie(struct super_block *sb, const char *uniq, int
return;
key->nfs_client = nfss->nfs_client;
key->key.super.s_flags = sb->s_flags & NFS_MS_MASK;
key->key.super.s_flags = sb->s_flags & NFS_SB_MASK;
key->key.nfs_server.flags = nfss->flags;
key->key.nfs_server.rsize = nfss->rsize;
key->key.nfs_server.wsize = nfss->wsize;

View File

@ -64,66 +64,71 @@ static int nfs_superblock_set_dummy_root(struct super_block *sb, struct inode *i
/*
* get an NFS2/NFS3 root dentry from the root filehandle
*/
struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh,
const char *devname)
int nfs_get_root(struct super_block *s, struct fs_context *fc)
{
struct nfs_server *server = NFS_SB(sb);
struct nfs_fs_context *ctx = nfs_fc2context(fc);
struct nfs_server *server = NFS_SB(s);
struct nfs_fsinfo fsinfo;
struct dentry *ret;
struct dentry *root;
struct inode *inode;
void *name = kstrdup(devname, GFP_KERNEL);
int error;
char *name;
int error = -ENOMEM;
name = kstrdup(fc->source, GFP_KERNEL);
if (!name)
return ERR_PTR(-ENOMEM);
goto out;
/* get the actual root for this mount */
fsinfo.fattr = nfs_alloc_fattr();
if (fsinfo.fattr == NULL) {
kfree(name);
return ERR_PTR(-ENOMEM);
}
if (fsinfo.fattr == NULL)
goto out_name;
error = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo);
error = server->nfs_client->rpc_ops->getroot(server, ctx->mntfh, &fsinfo);
if (error < 0) {
dprintk("nfs_get_root: getattr error = %d\n", -error);
ret = ERR_PTR(error);
goto out;
nfs_errorf(fc, "NFS: Couldn't getattr on root");
goto out_fattr;
}
inode = nfs_fhget(sb, mntfh, fsinfo.fattr, NULL);
inode = nfs_fhget(s, ctx->mntfh, fsinfo.fattr, NULL);
if (IS_ERR(inode)) {
dprintk("nfs_get_root: get root inode failed\n");
ret = ERR_CAST(inode);
goto out;
error = PTR_ERR(inode);
nfs_errorf(fc, "NFS: Couldn't get root inode");
goto out_fattr;
}
error = nfs_superblock_set_dummy_root(sb, inode);
if (error != 0) {
ret = ERR_PTR(error);
goto out;
}
error = nfs_superblock_set_dummy_root(s, inode);
if (error != 0)
goto out_fattr;
/* root dentries normally start off anonymous and get spliced in later
* if the dentry tree reaches them; however if the dentry already
* exists, we'll pick it up at this point and use it as the root
*/
ret = d_obtain_root(inode);
if (IS_ERR(ret)) {
root = d_obtain_root(inode);
if (IS_ERR(root)) {
dprintk("nfs_get_root: get root dentry failed\n");
goto out;
error = PTR_ERR(root);
nfs_errorf(fc, "NFS: Couldn't get root dentry");
goto out_fattr;
}
security_d_instantiate(ret, inode);
spin_lock(&ret->d_lock);
if (IS_ROOT(ret) && !ret->d_fsdata &&
!(ret->d_flags & DCACHE_NFSFS_RENAMED)) {
ret->d_fsdata = name;
security_d_instantiate(root, inode);
spin_lock(&root->d_lock);
if (IS_ROOT(root) && !root->d_fsdata &&
!(root->d_flags & DCACHE_NFSFS_RENAMED)) {
root->d_fsdata = name;
name = NULL;
}
spin_unlock(&ret->d_lock);
out:
kfree(name);
spin_unlock(&root->d_lock);
fc->root = root;
error = 0;
out_fattr:
nfs_free_fattr(fsinfo.fattr);
return ret;
out_name:
kfree(name);
out:
return error;
}

View File

@ -1061,7 +1061,7 @@ struct nfs_open_context *nfs_find_open_context(struct inode *inode, const struct
rcu_read_lock();
list_for_each_entry_rcu(pos, &nfsi->open_files, list) {
if (cred != NULL && pos->cred != cred)
if (cred != NULL && cred_fscmp(pos->cred, cred) != 0)
continue;
if ((pos->mode & (FMODE_READ|FMODE_WRITE)) != mode)
continue;
@ -1156,7 +1156,13 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Lu) getattr failed, error=%d\n",
inode->i_sb->s_id,
(unsigned long long)NFS_FILEID(inode), status);
if (status == -ESTALE) {
switch (status) {
case -ETIMEDOUT:
/* A soft timeout occurred. Use cached information? */
if (server->flags & NFS_MOUNT_SOFTREVAL)
status = 0;
break;
case -ESTALE:
nfs_zap_caches(inode);
if (!S_ISDIR(inode->i_mode))
set_bit(NFS_INO_STALE, &NFS_I(inode)->flags);

View File

@ -4,17 +4,19 @@
*/
#include "nfs4_fs.h"
#include <linux/mount.h>
#include <linux/fs_context.h>
#include <linux/security.h>
#include <linux/crc32.h>
#include <linux/sunrpc/addr.h>
#include <linux/nfs_page.h>
#include <linux/wait_bit.h>
#define NFS_MS_MASK (SB_RDONLY|SB_NOSUID|SB_NODEV|SB_NOEXEC|SB_SYNCHRONOUS)
#define NFS_SB_MASK (SB_RDONLY|SB_NOSUID|SB_NODEV|SB_NOEXEC|SB_SYNCHRONOUS)
extern const struct export_operations nfs_export_ops;
struct nfs_string;
struct nfs_pageio_descriptor;
static inline void nfs_attr_check_mountpoint(struct super_block *parent, struct nfs_fattr *fattr)
{
@ -31,17 +33,14 @@ static inline int nfs_attr_use_mounted_on_fileid(struct nfs_fattr *fattr)
return 1;
}
struct nfs_clone_mount {
const struct super_block *sb;
const struct dentry *dentry;
struct nfs_fh *fh;
struct nfs_fattr *fattr;
char *hostname;
char *mnt_path;
struct sockaddr *addr;
size_t addrlen;
rpc_authflavor_t authflavor;
};
static inline bool nfs_lookup_is_soft_revalidate(const struct dentry *dentry)
{
if (!(NFS_SB(dentry->d_sb)->flags & NFS_MOUNT_SOFTREVAL))
return false;
if (!d_is_positive(dentry) || !NFS_FH(d_inode(dentry))->size)
return false;
return true;
}
/*
* Note: RFC 1813 doesn't limit the number of auth flavors that
@ -82,12 +81,16 @@ struct nfs_client_initdata {
/*
* In-kernel mount arguments
*/
struct nfs_parsed_mount_data {
int flags;
struct nfs_fs_context {
bool internal;
bool skip_reconfig_option_check;
bool need_mount;
bool sloppy;
unsigned int flags; /* NFS{,4}_MOUNT_* flags */
unsigned int rsize, wsize;
unsigned int timeo, retrans;
unsigned int acregmin, acregmax,
acdirmin, acdirmax;
unsigned int acregmin, acregmax;
unsigned int acdirmin, acdirmax;
unsigned int namlen;
unsigned int options;
unsigned int bsize;
@ -97,10 +100,14 @@ struct nfs_parsed_mount_data {
unsigned int version;
unsigned int minorversion;
char *fscache_uniq;
bool need_mount;
unsigned short protofamily;
unsigned short mountfamily;
struct {
struct sockaddr_storage address;
union {
struct sockaddr address;
struct sockaddr_storage _address;
};
size_t addrlen;
char *hostname;
u32 version;
@ -109,19 +116,41 @@ struct nfs_parsed_mount_data {
} mount_server;
struct {
struct sockaddr_storage address;
union {
struct sockaddr address;
struct sockaddr_storage _address;
};
size_t addrlen;
char *hostname;
char *export_path;
int port;
unsigned short protocol;
unsigned short nconnect;
unsigned short export_path_len;
} nfs_server;
void *lsm_opts;
struct net *net;
struct nfs_fh *mntfh;
struct nfs_server *server;
struct nfs_subversion *nfs_mod;
/* Information for a cloned mount. */
struct nfs_clone_mount {
struct super_block *sb;
struct dentry *dentry;
struct nfs_fattr *fattr;
unsigned int inherited_bsize;
} clone_data;
};
#define nfs_errorf(fc, fmt, ...) errorf(fc, fmt, ## __VA_ARGS__)
#define nfs_invalf(fc, fmt, ...) invalf(fc, fmt, ## __VA_ARGS__)
#define nfs_warnf(fc, fmt, ...) warnf(fc, fmt, ## __VA_ARGS__)
static inline struct nfs_fs_context *nfs_fc2context(const struct fs_context *fc)
{
return fc->fs_private;
}
/* mount_clnt.c */
struct nfs_mount_request {
struct sockaddr *sap;
@ -137,14 +166,6 @@ struct nfs_mount_request {
struct net *net;
};
struct nfs_mount_info {
void (*fill_super)(struct super_block *, struct nfs_mount_info *);
int (*set_security)(struct super_block *, struct dentry *, struct nfs_mount_info *);
struct nfs_parsed_mount_data *parsed;
struct nfs_clone_mount *cloned;
struct nfs_fh *mntfh;
};
extern int nfs_mount(struct nfs_mount_request *info);
extern void nfs_umount(const struct nfs_mount_request *info);
@ -170,13 +191,9 @@ extern struct nfs_client *nfs4_find_client_ident(struct net *, int);
extern struct nfs_client *
nfs4_find_client_sessionid(struct net *, const struct sockaddr *,
struct nfs4_sessionid *, u32);
extern struct nfs_server *nfs_create_server(struct nfs_mount_info *,
struct nfs_subversion *);
extern struct nfs_server *nfs4_create_server(
struct nfs_mount_info *,
struct nfs_subversion *);
extern struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *,
struct nfs_fh *);
extern struct nfs_server *nfs_create_server(struct fs_context *);
extern struct nfs_server *nfs4_create_server(struct fs_context *);
extern struct nfs_server *nfs4_create_referral_server(struct fs_context *);
extern int nfs4_update_server(struct nfs_server *server, const char *hostname,
struct sockaddr *sap, size_t salen,
struct net *net);
@ -227,7 +244,9 @@ static inline void nfs_fs_proc_exit(void)
extern const struct svc_version nfs4_callback_version1;
extern const struct svc_version nfs4_callback_version4;
struct nfs_pageio_descriptor;
/* fs_context.c */
extern struct file_system_type nfs_fs_type;
/* pagelist.c */
extern int __init nfs_init_nfspagecache(void);
extern void nfs_destroy_nfspagecache(void);
@ -387,23 +406,10 @@ extern int nfs_wait_atomic_killable(atomic_t *p, unsigned int mode);
/* super.c */
extern const struct super_operations nfs_sops;
extern struct file_system_type nfs_fs_type;
extern struct file_system_type nfs_xdev_fs_type;
#if IS_ENABLED(CONFIG_NFS_V4)
extern struct file_system_type nfs4_referral_fs_type;
#endif
bool nfs_auth_info_match(const struct nfs_auth_info *, rpc_authflavor_t);
struct dentry *nfs_try_mount(int, const char *, struct nfs_mount_info *,
struct nfs_subversion *);
int nfs_set_sb_security(struct super_block *, struct dentry *, struct nfs_mount_info *);
int nfs_clone_sb_security(struct super_block *, struct dentry *, struct nfs_mount_info *);
struct dentry *nfs_fs_mount_common(struct nfs_server *, int, const char *,
struct nfs_mount_info *, struct nfs_subversion *);
struct dentry *nfs_fs_mount(struct file_system_type *, int, const char *, void *);
struct dentry * nfs_xdev_mount_common(struct file_system_type *, int,
const char *, struct nfs_mount_info *);
int nfs_try_get_tree(struct fs_context *);
int nfs_get_tree_common(struct fs_context *);
void nfs_kill_super(struct super_block *);
void nfs_fill_super(struct super_block *, struct nfs_mount_info *);
extern struct rpc_stat nfs_rpcstat;
@ -430,18 +436,12 @@ static inline bool nfs_file_io_is_buffered(struct nfs_inode *nfsi)
extern char *nfs_path(char **p, struct dentry *dentry,
char *buffer, ssize_t buflen, unsigned flags);
extern struct vfsmount *nfs_d_automount(struct path *path);
struct vfsmount *nfs_submount(struct nfs_server *, struct dentry *,
struct nfs_fh *, struct nfs_fattr *);
struct vfsmount *nfs_do_submount(struct dentry *, struct nfs_fh *,
struct nfs_fattr *, rpc_authflavor_t);
int nfs_submount(struct fs_context *, struct nfs_server *);
int nfs_do_submount(struct fs_context *);
/* getroot.c */
extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *,
const char *);
extern int nfs_get_root(struct super_block *s, struct fs_context *fc);
#if IS_ENABLED(CONFIG_NFS_V4)
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, bool);
#endif
@ -460,7 +460,7 @@ int nfs_show_options(struct seq_file *, struct dentry *);
int nfs_show_devname(struct seq_file *, struct dentry *);
int nfs_show_path(struct seq_file *, struct dentry *);
int nfs_show_stats(struct seq_file *, struct dentry *);
int nfs_remount(struct super_block *sb, int *flags, char *raw_data);
int nfs_reconfigure(struct fs_context *);
/* write.c */
extern void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
@ -706,9 +706,9 @@ unsigned int nfs_page_array_len(unsigned int base, size_t len)
}
/*
* Convert a struct timespec into a 64-bit change attribute
* Convert a struct timespec64 into a 64-bit change attribute
*
* This does approximately the same thing as timespec_to_ns(),
* This does approximately the same thing as timespec64_to_ns(),
* but for calculation efficiency, we multiply the seconds by
* 1024*1024*1024.
*/
@ -777,3 +777,16 @@ static inline bool nfs_error_is_fatal_on_server(int err)
}
return nfs_error_is_fatal(err);
}
/*
* Select between a default port value and a user-specified port value.
* If a zero value is set, then autobind will be used.
*/
static inline void nfs_set_port(struct sockaddr *sap, int *port,
const unsigned short default_port)
{
if (*port == NFS_UNSPEC_PORT)
*port = default_port;
rpc_set_port(sap, *port);
}

View File

@ -29,9 +29,7 @@
*/
#define encode_dirpath_sz (1 + XDR_QUADLEN(MNTPATHLEN))
#define MNT_status_sz (1)
#define MNT_fhs_status_sz (1)
#define MNT_fhandle_sz XDR_QUADLEN(NFS2_FHSIZE)
#define MNT_fhandle3_sz (1 + XDR_QUADLEN(NFS3_FHSIZE))
#define MNT_authflav3_sz (1 + NFS_MAX_SECFLAVORS)
/*

View File

@ -19,6 +19,7 @@
#include <linux/vfs.h>
#include <linux/sunrpc/gss_api.h>
#include "internal.h"
#include "nfs.h"
#define NFSDBG_FACILITY NFSDBG_VFS
@ -139,34 +140,65 @@ EXPORT_SYMBOL_GPL(nfs_path);
*/
struct vfsmount *nfs_d_automount(struct path *path)
{
struct vfsmount *mnt;
struct nfs_fs_context *ctx;
struct fs_context *fc;
struct vfsmount *mnt = ERR_PTR(-ENOMEM);
struct nfs_server *server = NFS_SERVER(d_inode(path->dentry));
struct nfs_fh *fh = NULL;
struct nfs_fattr *fattr = NULL;
struct nfs_client *client = server->nfs_client;
int ret;
if (IS_ROOT(path->dentry))
return ERR_PTR(-ESTALE);
mnt = ERR_PTR(-ENOMEM);
fh = nfs_alloc_fhandle();
fattr = nfs_alloc_fattr();
if (fh == NULL || fattr == NULL)
goto out;
/* Open a new filesystem context, transferring parameters from the
* parent superblock, including the network namespace.
*/
fc = fs_context_for_submount(&nfs_fs_type, path->dentry);
if (IS_ERR(fc))
return ERR_CAST(fc);
mnt = server->nfs_client->rpc_ops->submount(server, path->dentry, fh, fattr);
ctx = nfs_fc2context(fc);
ctx->clone_data.dentry = path->dentry;
ctx->clone_data.sb = path->dentry->d_sb;
ctx->clone_data.fattr = nfs_alloc_fattr();
if (!ctx->clone_data.fattr)
goto out_fc;
if (fc->net_ns != client->cl_net) {
put_net(fc->net_ns);
fc->net_ns = get_net(client->cl_net);
}
/* for submounts we want the same server; referrals will reassign */
memcpy(&ctx->nfs_server.address, &client->cl_addr, client->cl_addrlen);
ctx->nfs_server.addrlen = client->cl_addrlen;
ctx->nfs_server.port = server->port;
ctx->version = client->rpc_ops->version;
ctx->minorversion = client->cl_minorversion;
ctx->nfs_mod = client->cl_nfs_mod;
__module_get(ctx->nfs_mod->owner);
ret = client->rpc_ops->submount(fc, server);
if (ret < 0) {
mnt = ERR_PTR(ret);
goto out_fc;
}
up_write(&fc->root->d_sb->s_umount);
mnt = vfs_create_mount(fc);
if (IS_ERR(mnt))
goto out;
goto out_fc;
if (nfs_mountpoint_expiry_timeout < 0)
goto out;
goto out_fc;
mntget(mnt); /* prevent immediate expiration */
mnt_set_expiry(mnt, &nfs_automount_list);
schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout);
out:
nfs_free_fattr(fattr);
nfs_free_fhandle(fh);
out_fc:
put_fs_context(fc);
return mnt;
}
@ -213,16 +245,6 @@ void nfs_release_automount_timer(void)
cancel_delayed_work(&nfs_automount_task);
}
/*
* Clone a mountpoint of the appropriate type
*/
static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server,
const char *devname,
struct nfs_clone_mount *mountdata)
{
return vfs_submount(mountdata->dentry, &nfs_xdev_fs_type, devname, mountdata);
}
/**
* nfs_do_submount - set up mountpoint when crossing a filesystem boundary
* @dentry: parent directory
@ -231,46 +253,62 @@ static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server,
* @authflavor: security flavor to use when performing the mount
*
*/
struct vfsmount *nfs_do_submount(struct dentry *dentry, struct nfs_fh *fh,
struct nfs_fattr *fattr, rpc_authflavor_t authflavor)
int nfs_do_submount(struct fs_context *fc)
{
struct nfs_clone_mount mountdata = {
.sb = dentry->d_sb,
.dentry = dentry,
.fh = fh,
.fattr = fattr,
.authflavor = authflavor,
};
struct vfsmount *mnt;
char *page = (char *) __get_free_page(GFP_USER);
char *devname;
struct nfs_fs_context *ctx = nfs_fc2context(fc);
struct dentry *dentry = ctx->clone_data.dentry;
struct nfs_server *server;
char *buffer, *p;
int ret;
if (page == NULL)
return ERR_PTR(-ENOMEM);
/* create a new volume representation */
server = ctx->nfs_mod->rpc_ops->clone_server(NFS_SB(ctx->clone_data.sb),
ctx->mntfh,
ctx->clone_data.fattr,
ctx->selected_flavor);
devname = nfs_devname(dentry, page, PAGE_SIZE);
if (IS_ERR(devname))
mnt = ERR_CAST(devname);
else
mnt = nfs_do_clone_mount(NFS_SB(dentry->d_sb), devname, &mountdata);
if (IS_ERR(server))
return PTR_ERR(server);
free_page((unsigned long)page);
return mnt;
ctx->server = server;
buffer = kmalloc(4096, GFP_USER);
if (!buffer)
return -ENOMEM;
ctx->internal = true;
ctx->clone_data.inherited_bsize = ctx->clone_data.sb->s_blocksize_bits;
p = nfs_devname(dentry, buffer, 4096);
if (IS_ERR(p)) {
nfs_errorf(fc, "NFS: Couldn't determine submount pathname");
ret = PTR_ERR(p);
} else {
ret = vfs_parse_fs_string(fc, "source", p, buffer + 4096 - p);
if (!ret)
ret = vfs_get_tree(fc);
}
kfree(buffer);
return ret;
}
EXPORT_SYMBOL_GPL(nfs_do_submount);
struct vfsmount *nfs_submount(struct nfs_server *server, struct dentry *dentry,
struct nfs_fh *fh, struct nfs_fattr *fattr)
int nfs_submount(struct fs_context *fc, struct nfs_server *server)
{
int err;
struct nfs_fs_context *ctx = nfs_fc2context(fc);
struct dentry *dentry = ctx->clone_data.dentry;
struct dentry *parent = dget_parent(dentry);
int err;
/* Look it up again to get its attributes */
err = server->nfs_client->rpc_ops->lookup(d_inode(parent), &dentry->d_name, fh, fattr, NULL);
err = server->nfs_client->rpc_ops->lookup(d_inode(parent), dentry,
ctx->mntfh, ctx->clone_data.fattr,
NULL);
dput(parent);
if (err != 0)
return ERR_PTR(err);
return err;
return nfs_do_submount(dentry, fh, fattr, server->client->cl_auth->au_flavor);
ctx->selected_flavor = server->client->cl_auth->au_flavor;
return nfs_do_submount(fc);
}
EXPORT_SYMBOL_GPL(nfs_submount);

View File

@ -360,17 +360,17 @@ static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr,
else
*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
if (attr->ia_valid & ATTR_ATIME_SET) {
if (attr->ia_valid & ATTR_ATIME_SET)
p = xdr_encode_time(p, &attr->ia_atime);
} else if (attr->ia_valid & ATTR_ATIME) {
else if (attr->ia_valid & ATTR_ATIME)
p = xdr_encode_current_server_time(p, &attr->ia_atime);
} else
else
p = xdr_time_not_set(p);
if (attr->ia_valid & ATTR_MTIME_SET) {
if (attr->ia_valid & ATTR_MTIME_SET)
xdr_encode_time(p, &attr->ia_mtime);
} else if (attr->ia_valid & ATTR_MTIME) {
else if (attr->ia_valid & ATTR_MTIME)
xdr_encode_current_server_time(p, &attr->ia_mtime);
} else
else
xdr_time_not_set(p);
}

View File

@ -27,7 +27,7 @@ static inline int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl,
#endif /* CONFIG_NFS_V3_ACL */
/* nfs3client.c */
struct nfs_server *nfs3_create_server(struct nfs_mount_info *, struct nfs_subversion *);
struct nfs_server *nfs3_create_server(struct fs_context *);
struct nfs_server *nfs3_clone_server(struct nfs_server *, struct nfs_fh *,
struct nfs_fattr *, rpc_authflavor_t);

View File

@ -46,10 +46,10 @@ static inline void nfs_init_server_aclclient(struct nfs_server *server)
}
#endif
struct nfs_server *nfs3_create_server(struct nfs_mount_info *mount_info,
struct nfs_subversion *nfs_mod)
struct nfs_server *nfs3_create_server(struct fs_context *fc)
{
struct nfs_server *server = nfs_create_server(mount_info, nfs_mod);
struct nfs_server *server = nfs_create_server(fc);
/* Create a client RPC handle for the NFS v3 ACL management interface */
if (!IS_ERR(server))
nfs_init_server_aclclient(server);

View File

@ -110,10 +110,15 @@ nfs3_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
.rpc_resp = fattr,
};
int status;
unsigned short task_flags = 0;
/* Is this is an attribute revalidation, subject to softreval? */
if (inode && (server->flags & NFS_MOUNT_SOFTREVAL))
task_flags |= RPC_TASK_TIMEOUT;
dprintk("NFS call getattr\n");
nfs_fattr_init(fattr);
status = rpc_call_sync(server->client, &msg, 0);
status = rpc_call_sync(server->client, &msg, task_flags);
dprintk("NFS reply getattr: %d\n", status);
return status;
}
@ -140,23 +145,23 @@ nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
nfs_fattr_init(fattr);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
if (status == 0) {
nfs_setattr_update_inode(inode, sattr, fattr);
if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_ACL)
nfs_zap_acl_cache(inode);
nfs_setattr_update_inode(inode, sattr, fattr);
}
dprintk("NFS reply setattr: %d\n", status);
return status;
}
static int
nfs3_proc_lookup(struct inode *dir, const struct qstr *name,
nfs3_proc_lookup(struct inode *dir, struct dentry *dentry,
struct nfs_fh *fhandle, struct nfs_fattr *fattr,
struct nfs4_label *label)
{
struct nfs3_diropargs arg = {
.fh = NFS_FH(dir),
.name = name->name,
.len = name->len
.name = dentry->d_name.name,
.len = dentry->d_name.len
};
struct nfs3_diropres res = {
.fh = fhandle,
@ -168,20 +173,25 @@ nfs3_proc_lookup(struct inode *dir, const struct qstr *name,
.rpc_resp = &res,
};
int status;
unsigned short task_flags = 0;
dprintk("NFS call lookup %s\n", name->name);
/* Is this is an attribute revalidation, subject to softreval? */
if (nfs_lookup_is_soft_revalidate(dentry))
task_flags |= RPC_TASK_TIMEOUT;
dprintk("NFS call lookup %pd2\n", dentry);
res.dir_attr = nfs_alloc_fattr();
if (res.dir_attr == NULL)
return -ENOMEM;
nfs_fattr_init(fattr);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, task_flags);
nfs_refresh_inode(dir, res.dir_attr);
if (status >= 0 && !(fattr->valid & NFS_ATTR_FATTR)) {
msg.rpc_proc = &nfs3_procedures[NFS3PROC_GETATTR];
msg.rpc_argp = fhandle;
msg.rpc_resp = fattr;
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, task_flags);
}
nfs_free_fattr(res.dir_attr);
dprintk("NFS reply lookup: %d\n", status);
@ -990,7 +1000,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
.nlmclnt_ops = &nlmclnt_fl_close_lock_ops,
.getroot = nfs3_proc_get_root,
.submount = nfs_submount,
.try_mount = nfs_try_mount,
.try_get_tree = nfs_try_get_tree,
.getattr = nfs3_proc_getattr,
.setattr = nfs3_proc_setattr,
.lookup = nfs3_proc_lookup,

View File

@ -2334,6 +2334,7 @@ static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req,
void *data)
{
struct nfs_commitres *result = data;
struct nfs_writeverf *verf = result->verf;
enum nfs_stat status;
int error;
@ -2346,7 +2347,9 @@ static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req,
result->op_status = status;
if (status != NFS3_OK)
goto out_status;
error = decode_writeverf3(xdr, &result->verf->verifier);
error = decode_writeverf3(xdr, &verf->verifier);
if (!error)
verf->committed = NFS_FILE_SYNC;
out:
return error;
out_status:

View File

@ -61,8 +61,11 @@ static int _nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep,
status = nfs4_set_rw_stateid(&args.falloc_stateid, lock->open_context,
lock, FMODE_WRITE);
if (status)
if (status) {
if (status == -EAGAIN)
status = -NFS4ERR_BAD_STATEID;
return status;
}
res.falloc_fattr = nfs_alloc_fattr();
if (!res.falloc_fattr)
@ -287,8 +290,11 @@ static ssize_t _nfs42_proc_copy(struct file *src,
} else {
status = nfs4_set_rw_stateid(&args->src_stateid,
src_lock->open_context, src_lock, FMODE_READ);
if (status)
if (status) {
if (status == -EAGAIN)
status = -NFS4ERR_BAD_STATEID;
return status;
}
}
status = nfs_filemap_write_and_wait_range(file_inode(src)->i_mapping,
pos_src, pos_src + (loff_t)count - 1);
@ -297,8 +303,11 @@ static ssize_t _nfs42_proc_copy(struct file *src,
status = nfs4_set_rw_stateid(&args->dst_stateid, dst_lock->open_context,
dst_lock, FMODE_WRITE);
if (status)
if (status) {
if (status == -EAGAIN)
status = -NFS4ERR_BAD_STATEID;
return status;
}
status = nfs_sync_inode(dst_inode);
if (status)
@ -334,14 +343,14 @@ static ssize_t _nfs42_proc_copy(struct file *src,
status = handle_async_copy(res, dst_server, src_server, src,
dst, &args->src_stateid, restart);
if (status)
return status;
goto out;
}
if ((!res->synchronous || !args->sync) &&
res->write_res.verifier.committed != NFS_FILE_SYNC) {
status = process_copy_commit(dst, pos_dst, res);
if (status)
return status;
goto out;
}
truncate_pagecache_range(dst_inode, pos_dst,
@ -546,8 +555,11 @@ static int _nfs42_proc_copy_notify(struct file *src, struct file *dst,
status = nfs4_set_rw_stateid(&args->cna_src_stateid, ctx, l_ctx,
FMODE_READ);
nfs_put_lock_context(l_ctx);
if (status)
if (status) {
if (status == -EAGAIN)
status = -NFS4ERR_BAD_STATEID;
return status;
}
status = nfs4_call_sync(src_server->client, src_server, &msg,
&args->cna_seq_args, &res->cnr_seq_res, 0);
@ -618,8 +630,11 @@ static loff_t _nfs42_proc_llseek(struct file *filep,
status = nfs4_set_rw_stateid(&args.sa_stateid, lock->open_context,
lock, FMODE_READ);
if (status)
if (status) {
if (status == -EAGAIN)
status = -NFS4ERR_BAD_STATEID;
return status;
}
status = nfs_filemap_write_and_wait_range(inode->i_mapping,
offset, LLONG_MAX);
@ -994,13 +1009,18 @@ static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f,
status = nfs4_set_rw_stateid(&args.src_stateid, src_lock->open_context,
src_lock, FMODE_READ);
if (status)
if (status) {
if (status == -EAGAIN)
status = -NFS4ERR_BAD_STATEID;
return status;
}
status = nfs4_set_rw_stateid(&args.dst_stateid, dst_lock->open_context,
dst_lock, FMODE_WRITE);
if (status)
if (status) {
if (status == -EAGAIN)
status = -NFS4ERR_BAD_STATEID;
return status;
}
res.dst_fattr = nfs_alloc_fattr();
if (!res.dst_fattr)

View File

@ -268,14 +268,13 @@ extern const struct dentry_operations nfs4_dentry_operations;
int nfs_atomic_open(struct inode *, struct dentry *, struct file *,
unsigned, umode_t);
/* super.c */
/* fs_context.c */
extern struct file_system_type nfs4_fs_type;
/* nfs4namespace.c */
struct rpc_clnt *nfs4_negotiate_security(struct rpc_clnt *, struct inode *,
const struct qstr *);
struct vfsmount *nfs4_submount(struct nfs_server *, struct dentry *,
struct nfs_fh *, struct nfs_fattr *);
int nfs4_submount(struct fs_context *, struct nfs_server *);
int nfs4_replace_transport(struct nfs_server *server,
const struct nfs4_fs_locations *locations);
@ -303,8 +302,10 @@ extern int nfs4_proc_fs_locations(struct rpc_clnt *, struct inode *, const struc
extern int nfs4_proc_get_locations(struct inode *, struct nfs4_fs_locations *,
struct page *page, const struct cred *);
extern int nfs4_proc_fsid_present(struct inode *, const struct cred *);
extern struct rpc_clnt *nfs4_proc_lookup_mountpoint(struct inode *, const struct qstr *,
struct nfs_fh *, struct nfs_fattr *);
extern struct rpc_clnt *nfs4_proc_lookup_mountpoint(struct inode *,
struct dentry *,
struct nfs_fh *,
struct nfs_fattr *);
extern int nfs4_proc_secinfo(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *);
extern const struct xattr_handler *nfs4_xattr_handlers[];
extern int nfs4_set_rw_stateid(nfs4_stateid *stateid,
@ -446,9 +447,7 @@ extern void nfs4_schedule_state_renewal(struct nfs_client *);
extern void nfs4_renewd_prepare_shutdown(struct nfs_server *);
extern void nfs4_kill_renewd(struct nfs_client *);
extern void nfs4_renew_state(struct work_struct *);
extern void nfs4_set_lease_period(struct nfs_client *clp,
unsigned long lease,
unsigned long lastrenewed);
extern void nfs4_set_lease_period(struct nfs_client *clp, unsigned long lease);
/* nfs4state.c */
@ -526,7 +525,6 @@ extern const nfs4_stateid invalid_stateid;
/* nfs4super.c */
struct nfs_mount_info;
extern struct nfs_subversion nfs_v4;
struct dentry *nfs4_try_mount(int, const char *, struct nfs_mount_info *, struct nfs_subversion *);
extern bool nfs4_disable_idmapping;
extern unsigned short max_session_slots;
extern unsigned short max_session_cb_slots;
@ -536,6 +534,9 @@ extern bool recover_lost_locks;
#define NFS4_CLIENT_ID_UNIQ_LEN (64)
extern char nfs4_client_id_uniquifier[NFS4_CLIENT_ID_UNIQ_LEN];
extern int nfs4_try_get_tree(struct fs_context *);
extern int nfs4_get_referral_tree(struct fs_context *);
/* nfs4sysctl.c */
#ifdef CONFIG_SYSCTL
int nfs4_register_sysctl(void);

View File

@ -1055,66 +1055,64 @@ out:
/*
* Create a version 4 volume record
*/
static int nfs4_init_server(struct nfs_server *server,
struct nfs_parsed_mount_data *data)
static int nfs4_init_server(struct nfs_server *server, struct fs_context *fc)
{
struct nfs_fs_context *ctx = nfs_fc2context(fc);
struct rpc_timeout timeparms;
int error;
nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
data->timeo, data->retrans);
nfs_init_timeout_values(&timeparms, ctx->nfs_server.protocol,
ctx->timeo, ctx->retrans);
/* Initialise the client representation from the mount data */
server->flags = data->flags;
server->options = data->options;
server->auth_info = data->auth_info;
server->flags = ctx->flags;
server->options = ctx->options;
server->auth_info = ctx->auth_info;
/* Use the first specified auth flavor. If this flavor isn't
* allowed by the server, use the SECINFO path to try the
* other specified flavors */
if (data->auth_info.flavor_len >= 1)
data->selected_flavor = data->auth_info.flavors[0];
if (ctx->auth_info.flavor_len >= 1)
ctx->selected_flavor = ctx->auth_info.flavors[0];
else
data->selected_flavor = RPC_AUTH_UNIX;
ctx->selected_flavor = RPC_AUTH_UNIX;
/* 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->nfs_server.protocol,
&timeparms,
data->minorversion,
data->nfs_server.nconnect,
data->net);
ctx->nfs_server.hostname,
&ctx->nfs_server.address,
ctx->nfs_server.addrlen,
ctx->client_address,
ctx->nfs_server.protocol,
&timeparms,
ctx->minorversion,
ctx->nfs_server.nconnect,
fc->net_ns);
if (error < 0)
return error;
if (data->rsize)
server->rsize = nfs_block_size(data->rsize, NULL);
if (data->wsize)
server->wsize = nfs_block_size(data->wsize, NULL);
if (ctx->rsize)
server->rsize = nfs_block_size(ctx->rsize, NULL);
if (ctx->wsize)
server->wsize = nfs_block_size(ctx->wsize, NULL);
server->acregmin = data->acregmin * HZ;
server->acregmax = data->acregmax * HZ;
server->acdirmin = data->acdirmin * HZ;
server->acdirmax = data->acdirmax * HZ;
server->port = data->nfs_server.port;
server->acregmin = ctx->acregmin * HZ;
server->acregmax = ctx->acregmax * HZ;
server->acdirmin = ctx->acdirmin * HZ;
server->acdirmax = ctx->acdirmax * HZ;
server->port = ctx->nfs_server.port;
return nfs_init_server_rpcclient(server, &timeparms,
data->selected_flavor);
ctx->selected_flavor);
}
/*
* Create a version 4 volume record
* - keyed on server and FSID
*/
/*struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data,
struct nfs_fh *mntfh)*/
struct nfs_server *nfs4_create_server(struct nfs_mount_info *mount_info,
struct nfs_subversion *nfs_mod)
struct nfs_server *nfs4_create_server(struct fs_context *fc)
{
struct nfs_fs_context *ctx = nfs_fc2context(fc);
struct nfs_server *server;
bool auth_probe;
int error;
@ -1125,14 +1123,14 @@ struct nfs_server *nfs4_create_server(struct nfs_mount_info *mount_info,
server->cred = get_cred(current_cred());
auth_probe = mount_info->parsed->auth_info.flavor_len < 1;
auth_probe = ctx->auth_info.flavor_len < 1;
/* set up the general RPC client */
error = nfs4_init_server(server, mount_info->parsed);
error = nfs4_init_server(server, fc);
if (error < 0)
goto error;
error = nfs4_server_common_setup(server, mount_info->mntfh, auth_probe);
error = nfs4_server_common_setup(server, ctx->mntfh, auth_probe);
if (error < 0)
goto error;
@ -1146,9 +1144,9 @@ error:
/*
* Create an NFS4 referral server record
*/
struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
struct nfs_fh *mntfh)
struct nfs_server *nfs4_create_referral_server(struct fs_context *fc)
{
struct nfs_fs_context *ctx = nfs_fc2context(fc);
struct nfs_client *parent_client;
struct nfs_server *server, *parent_server;
bool auth_probe;
@ -1158,7 +1156,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
if (!server)
return ERR_PTR(-ENOMEM);
parent_server = NFS_SB(data->sb);
parent_server = NFS_SB(ctx->clone_data.sb);
parent_client = parent_server->nfs_client;
server->cred = get_cred(parent_server->cred);
@ -1168,10 +1166,11 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
/* Get a client representation */
#if IS_ENABLED(CONFIG_SUNRPC_XPRT_RDMA)
rpc_set_port(data->addr, NFS_RDMA_PORT);
error = nfs4_set_client(server, data->hostname,
data->addr,
data->addrlen,
rpc_set_port(&ctx->nfs_server.address, NFS_RDMA_PORT);
error = nfs4_set_client(server,
ctx->nfs_server.hostname,
&ctx->nfs_server.address,
ctx->nfs_server.addrlen,
parent_client->cl_ipaddr,
XPRT_TRANSPORT_RDMA,
parent_server->client->cl_timeout,
@ -1182,10 +1181,11 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
goto init_server;
#endif /* IS_ENABLED(CONFIG_SUNRPC_XPRT_RDMA) */
rpc_set_port(data->addr, NFS_PORT);
error = nfs4_set_client(server, data->hostname,
data->addr,
data->addrlen,
rpc_set_port(&ctx->nfs_server.address, NFS_PORT);
error = nfs4_set_client(server,
ctx->nfs_server.hostname,
&ctx->nfs_server.address,
ctx->nfs_server.addrlen,
parent_client->cl_ipaddr,
XPRT_TRANSPORT_TCP,
parent_server->client->cl_timeout,
@ -1198,13 +1198,14 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
#if IS_ENABLED(CONFIG_SUNRPC_XPRT_RDMA)
init_server:
#endif
error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout, data->authflavor);
error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout,
ctx->selected_flavor);
if (error < 0)
goto error;
auth_probe = parent_server->auth_info.flavor_len < 1;
error = nfs4_server_common_setup(server, mntfh, auth_probe);
error = nfs4_server_common_setup(server, ctx->mntfh, auth_probe);
if (error < 0)
goto error;

View File

@ -7,6 +7,7 @@
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/falloc.h>
#include <linux/mount.h>
#include <linux/nfs_fs.h>
#include "delegation.h"
#include "internal.h"

View File

@ -8,6 +8,7 @@
* NFSv4 namespace
*/
#include <linux/module.h>
#include <linux/dcache.h>
#include <linux/mount.h>
#include <linux/namei.h>
@ -21,37 +22,64 @@
#include <linux/inet.h>
#include "internal.h"
#include "nfs4_fs.h"
#include "nfs.h"
#include "dns_resolve.h"
#define NFSDBG_FACILITY NFSDBG_VFS
/*
* Convert the NFSv4 pathname components into a standard posix path.
*
* Note that the resulting string will be placed at the end of the buffer
* Work out the length that an NFSv4 path would render to as a standard posix
* path, with a leading slash but no terminating slash.
*/
static inline char *nfs4_pathname_string(const struct nfs4_pathname *pathname,
char *buffer, ssize_t buflen)
static ssize_t nfs4_pathname_len(const struct nfs4_pathname *pathname)
{
char *end = buffer + buflen;
int n;
ssize_t len = 0;
int i;
*--end = '\0';
buflen--;
for (i = 0; i < pathname->ncomponents; i++) {
const struct nfs4_string *component = &pathname->components[i];
n = pathname->ncomponents;
while (--n >= 0) {
const struct nfs4_string *component = &pathname->components[n];
buflen -= component->len + 1;
if (buflen < 0)
goto Elong;
end -= component->len;
memcpy(end, component->data, component->len);
*--end = '/';
if (component->len > NAME_MAX)
goto too_long;
len += 1 + component->len; /* Adding "/foo" */
if (len > PATH_MAX)
goto too_long;
}
return end;
Elong:
return ERR_PTR(-ENAMETOOLONG);
return len;
too_long:
return -ENAMETOOLONG;
}
/*
* Convert the NFSv4 pathname components into a standard posix path.
*/
static char *nfs4_pathname_string(const struct nfs4_pathname *pathname,
unsigned short *_len)
{
ssize_t len;
char *buf, *p;
int i;
len = nfs4_pathname_len(pathname);
if (len < 0)
return ERR_PTR(len);
*_len = len;
p = buf = kmalloc(len + 1, GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
for (i = 0; i < pathname->ncomponents; i++) {
const struct nfs4_string *component = &pathname->components[i];
*p++ = '/';
memcpy(p, component->data, component->len);
p += component->len;
}
*p = 0;
return buf;
}
/*
@ -100,21 +128,36 @@ static char *nfs4_path(struct dentry *dentry, char *buffer, ssize_t buflen)
*/
static int nfs4_validate_fspath(struct dentry *dentry,
const struct nfs4_fs_locations *locations,
char *page, char *page2)
struct nfs_fs_context *ctx)
{
const char *path, *fs_path;
const char *path;
char *fs_path;
unsigned short len;
char *buf;
int n;
path = nfs4_path(dentry, page, PAGE_SIZE);
if (IS_ERR(path))
buf = kmalloc(4096, GFP_KERNEL);
if (!buf)
return -ENOMEM;
path = nfs4_path(dentry, buf, 4096);
if (IS_ERR(path)) {
kfree(buf);
return PTR_ERR(path);
}
fs_path = nfs4_pathname_string(&locations->fs_path, page2, PAGE_SIZE);
if (IS_ERR(fs_path))
fs_path = nfs4_pathname_string(&locations->fs_path, &len);
if (IS_ERR(fs_path)) {
kfree(buf);
return PTR_ERR(fs_path);
}
if (strncmp(path, fs_path, strlen(fs_path)) != 0) {
n = strncmp(path, fs_path, len);
kfree(buf);
kfree(fs_path);
if (n != 0) {
dprintk("%s: path %s does not begin with fsroot %s\n",
__func__, path, fs_path);
__func__, path, ctx->nfs_server.export_path);
return -ENOENT;
}
@ -236,55 +279,77 @@ out:
return new;
}
static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
char *page, char *page2,
const struct nfs4_fs_location *location)
static int try_location(struct fs_context *fc,
const struct nfs4_fs_location *location)
{
const size_t addr_bufsize = sizeof(struct sockaddr_storage);
struct net *net = rpc_net_ns(NFS_SB(mountdata->sb)->client);
struct vfsmount *mnt = ERR_PTR(-ENOENT);
char *mnt_path;
unsigned int maxbuflen;
unsigned int s;
mnt_path = nfs4_pathname_string(&location->rootpath, page2, PAGE_SIZE);
if (IS_ERR(mnt_path))
return ERR_CAST(mnt_path);
mountdata->mnt_path = mnt_path;
maxbuflen = mnt_path - 1 - page2;
mountdata->addr = kmalloc(addr_bufsize, GFP_KERNEL);
if (mountdata->addr == NULL)
return ERR_PTR(-ENOMEM);
struct nfs_fs_context *ctx = nfs_fc2context(fc);
unsigned int len, s;
char *export_path, *source, *p;
int ret = -ENOENT;
/* Allocate a buffer big enough to hold any of the hostnames plus a
* terminating char and also a buffer big enough to hold the hostname
* plus a colon plus the path.
*/
len = 0;
for (s = 0; s < location->nservers; s++) {
const struct nfs4_string *buf = &location->servers[s];
if (buf->len > len)
len = buf->len;
}
if (buf->len <= 0 || buf->len >= maxbuflen)
continue;
kfree(ctx->nfs_server.hostname);
ctx->nfs_server.hostname = kmalloc(len + 1, GFP_KERNEL);
if (!ctx->nfs_server.hostname)
return -ENOMEM;
export_path = nfs4_pathname_string(&location->rootpath,
&ctx->nfs_server.export_path_len);
if (IS_ERR(export_path))
return PTR_ERR(export_path);
ctx->nfs_server.export_path = export_path;
source = kmalloc(len + 1 + ctx->nfs_server.export_path_len + 1,
GFP_KERNEL);
if (!source)
return -ENOMEM;
kfree(fc->source);
fc->source = source;
for (s = 0; s < location->nservers; s++) {
const struct nfs4_string *buf = &location->servers[s];
if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len))
continue;
mountdata->addrlen = nfs_parse_server_name(buf->data, buf->len,
mountdata->addr, addr_bufsize, net);
if (mountdata->addrlen == 0)
ctx->nfs_server.addrlen =
nfs_parse_server_name(buf->data, buf->len,
&ctx->nfs_server.address,
sizeof(ctx->nfs_server._address),
fc->net_ns);
if (ctx->nfs_server.addrlen == 0)
continue;
memcpy(page2, buf->data, buf->len);
page2[buf->len] = '\0';
mountdata->hostname = page2;
rpc_set_port(&ctx->nfs_server.address, NFS_PORT);
snprintf(page, PAGE_SIZE, "%s:%s",
mountdata->hostname,
mountdata->mnt_path);
memcpy(ctx->nfs_server.hostname, buf->data, buf->len);
ctx->nfs_server.hostname[buf->len] = '\0';
mnt = vfs_submount(mountdata->dentry, &nfs4_referral_fs_type, page, mountdata);
if (!IS_ERR(mnt))
break;
p = source;
memcpy(p, buf->data, buf->len);
p += buf->len;
*p++ = ':';
memcpy(p, ctx->nfs_server.export_path, ctx->nfs_server.export_path_len);
p += ctx->nfs_server.export_path_len;
*p = 0;
ret = nfs4_get_referral_tree(fc);
if (ret == 0)
return 0;
}
kfree(mountdata->addr);
return mnt;
return ret;
}
/**
@ -293,38 +358,23 @@ static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
* @locations: array of NFSv4 server location information
*
*/
static struct vfsmount *nfs_follow_referral(struct dentry *dentry,
const struct nfs4_fs_locations *locations)
static int nfs_follow_referral(struct fs_context *fc,
const struct nfs4_fs_locations *locations)
{
struct vfsmount *mnt = ERR_PTR(-ENOENT);
struct nfs_clone_mount mountdata = {
.sb = dentry->d_sb,
.dentry = dentry,
.authflavor = NFS_SB(dentry->d_sb)->client->cl_auth->au_flavor,
};
char *page = NULL, *page2 = NULL;
struct nfs_fs_context *ctx = nfs_fc2context(fc);
int loc, error;
if (locations == NULL || locations->nlocations <= 0)
goto out;
return -ENOENT;
dprintk("%s: referral at %pd2\n", __func__, dentry);
page = (char *) __get_free_page(GFP_USER);
if (!page)
goto out;
page2 = (char *) __get_free_page(GFP_USER);
if (!page2)
goto out;
dprintk("%s: referral at %pd2\n", __func__, ctx->clone_data.dentry);
/* Ensure fs path is a prefix of current dentry path */
error = nfs4_validate_fspath(dentry, locations, page, page2);
if (error < 0) {
mnt = ERR_PTR(error);
goto out;
}
error = nfs4_validate_fspath(ctx->clone_data.dentry, locations, ctx);
if (error < 0)
return error;
error = -ENOENT;
for (loc = 0; loc < locations->nlocations; loc++) {
const struct nfs4_fs_location *location = &locations->locations[loc];
@ -332,15 +382,12 @@ static struct vfsmount *nfs_follow_referral(struct dentry *dentry,
location->rootpath.ncomponents == 0)
continue;
mnt = try_location(&mountdata, page, page2, location);
if (!IS_ERR(mnt))
break;
error = try_location(fc, location);
if (error == 0)
return 0;
}
out:
free_page((unsigned long) page);
free_page((unsigned long) page2);
return mnt;
return error;
}
/*
@ -348,71 +395,72 @@ out:
* @dentry - dentry of referral
*
*/
static struct vfsmount *nfs_do_refmount(struct rpc_clnt *client, struct dentry *dentry)
static int nfs_do_refmount(struct fs_context *fc, struct rpc_clnt *client)
{
struct vfsmount *mnt = ERR_PTR(-ENOMEM);
struct dentry *parent;
struct nfs_fs_context *ctx = nfs_fc2context(fc);
struct dentry *dentry, *parent;
struct nfs4_fs_locations *fs_locations = NULL;
struct page *page;
int err;
int err = -ENOMEM;
/* BUG_ON(IS_ROOT(dentry)); */
page = alloc_page(GFP_KERNEL);
if (page == NULL)
return mnt;
if (!page)
return -ENOMEM;
fs_locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL);
if (fs_locations == NULL)
if (!fs_locations)
goto out_free;
/* Get locations */
mnt = ERR_PTR(-ENOENT);
dentry = ctx->clone_data.dentry;
parent = dget_parent(dentry);
dprintk("%s: getting locations for %pd2\n",
__func__, dentry);
err = nfs4_proc_fs_locations(client, d_inode(parent), &dentry->d_name, fs_locations, page);
dput(parent);
if (err != 0 ||
fs_locations->nlocations <= 0 ||
fs_locations->fs_path.ncomponents <= 0)
goto out_free;
if (err != 0)
goto out_free_2;
mnt = nfs_follow_referral(dentry, fs_locations);
err = -ENOENT;
if (fs_locations->nlocations <= 0 ||
fs_locations->fs_path.ncomponents <= 0)
goto out_free_2;
err = nfs_follow_referral(fc, fs_locations);
out_free_2:
kfree(fs_locations);
out_free:
__free_page(page);
kfree(fs_locations);
return mnt;
return err;
}
struct vfsmount *nfs4_submount(struct nfs_server *server, struct dentry *dentry,
struct nfs_fh *fh, struct nfs_fattr *fattr)
int nfs4_submount(struct fs_context *fc, struct nfs_server *server)
{
rpc_authflavor_t flavor = server->client->cl_auth->au_flavor;
struct nfs_fs_context *ctx = nfs_fc2context(fc);
struct dentry *dentry = ctx->clone_data.dentry;
struct dentry *parent = dget_parent(dentry);
struct inode *dir = d_inode(parent);
const struct qstr *name = &dentry->d_name;
struct rpc_clnt *client;
struct vfsmount *mnt;
int ret;
/* Look it up again to get its attributes and sec flavor */
client = nfs4_proc_lookup_mountpoint(dir, name, fh, fattr);
client = nfs4_proc_lookup_mountpoint(dir, dentry, ctx->mntfh,
ctx->clone_data.fattr);
dput(parent);
if (IS_ERR(client))
return ERR_CAST(client);
return PTR_ERR(client);
if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
mnt = nfs_do_refmount(client, dentry);
goto out;
ctx->selected_flavor = client->cl_auth->au_flavor;
if (ctx->clone_data.fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
ret = nfs_do_refmount(fc, client);
} else {
ret = nfs_do_submount(fc);
}
if (client->cl_auth->au_flavor != flavor)
flavor = client->cl_auth->au_flavor;
mnt = nfs_do_submount(dentry, fh, fattr, flavor);
out:
rpc_shutdown_client(client);
return mnt;
return ret;
}
/*
@ -453,7 +501,7 @@ static int nfs4_try_replacing_one_location(struct nfs_server *server,
rpc_set_port(sap, NFS_PORT);
error = -ENOMEM;
hostname = kstrndup(buf->data, buf->len, GFP_KERNEL);
hostname = kmemdup_nul(buf->data, buf->len, GFP_KERNEL);
if (hostname == NULL)
break;

View File

@ -1097,11 +1097,12 @@ static int nfs4_call_sync_custom(struct rpc_task_setup *task_setup)
return ret;
}
static int nfs4_call_sync_sequence(struct rpc_clnt *clnt,
struct nfs_server *server,
struct rpc_message *msg,
struct nfs4_sequence_args *args,
struct nfs4_sequence_res *res)
static int nfs4_do_call_sync(struct rpc_clnt *clnt,
struct nfs_server *server,
struct rpc_message *msg,
struct nfs4_sequence_args *args,
struct nfs4_sequence_res *res,
unsigned short task_flags)
{
struct nfs_client *clp = server->nfs_client;
struct nfs4_call_sync_data data = {
@ -1113,12 +1114,23 @@ static int nfs4_call_sync_sequence(struct rpc_clnt *clnt,
.rpc_client = clnt,
.rpc_message = msg,
.callback_ops = clp->cl_mvops->call_sync_ops,
.callback_data = &data
.callback_data = &data,
.flags = task_flags,
};
return nfs4_call_sync_custom(&task_setup);
}
static int nfs4_call_sync_sequence(struct rpc_clnt *clnt,
struct nfs_server *server,
struct rpc_message *msg,
struct nfs4_sequence_args *args,
struct nfs4_sequence_res *res)
{
return nfs4_do_call_sync(clnt, server, msg, args, res, 0);
}
int nfs4_call_sync(struct rpc_clnt *clnt,
struct nfs_server *server,
struct rpc_message *msg,
@ -3187,6 +3199,11 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir,
exception.retry = 1;
continue;
}
if (status == -NFS4ERR_EXPIRED) {
nfs4_schedule_lease_recovery(server->nfs_client);
exception.retry = 1;
continue;
}
if (status == -EAGAIN) {
/* We must have found a delegation */
exception.retry = 1;
@ -3239,6 +3256,8 @@ static int _nfs4_do_setattr(struct inode *inode,
nfs_put_lock_context(l_ctx);
if (status == -EIO)
return -EBADF;
else if (status == -EAGAIN)
goto zero_stateid;
} else {
zero_stateid:
nfs4_stateid_copy(&arg->stateid, &zero_stateid);
@ -4064,11 +4083,18 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
.rpc_argp = &args,
.rpc_resp = &res,
};
unsigned short task_flags = 0;
/* Is this is an attribute revalidation, subject to softreval? */
if (inode && (server->flags & NFS_MOUNT_SOFTREVAL))
task_flags |= RPC_TASK_TIMEOUT;
nfs4_bitmap_copy_adjust(bitmask, nfs4_bitmask(server, label), inode);
nfs_fattr_init(fattr);
return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 0);
return nfs4_do_call_sync(server->client, server, &msg,
&args.seq_args, &res.seq_res, task_flags);
}
int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
@ -4156,7 +4182,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
}
static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
const struct qstr *name, struct nfs_fh *fhandle,
struct dentry *dentry, struct nfs_fh *fhandle,
struct nfs_fattr *fattr, struct nfs4_label *label)
{
struct nfs_server *server = NFS_SERVER(dir);
@ -4164,7 +4190,7 @@ static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
struct nfs4_lookup_arg args = {
.bitmask = server->attr_bitmask,
.dir_fh = NFS_FH(dir),
.name = name,
.name = &dentry->d_name,
};
struct nfs4_lookup_res res = {
.server = server,
@ -4177,13 +4203,20 @@ static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
.rpc_argp = &args,
.rpc_resp = &res,
};
unsigned short task_flags = 0;
/* Is this is an attribute revalidation, subject to softreval? */
if (nfs_lookup_is_soft_revalidate(dentry))
task_flags |= RPC_TASK_TIMEOUT;
args.bitmask = nfs4_bitmask(server, label);
nfs_fattr_init(fattr);
dprintk("NFS call lookup %s\n", name->name);
status = nfs4_call_sync(clnt, server, &msg, &args.seq_args, &res.seq_res, 0);
dprintk("NFS call lookup %pd2\n", dentry);
nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 0);
status = nfs4_do_call_sync(clnt, server, &msg,
&args.seq_args, &res.seq_res, task_flags);
dprintk("NFS reply lookup: %d\n", status);
return status;
}
@ -4197,16 +4230,17 @@ static void nfs_fixup_secinfo_attributes(struct nfs_fattr *fattr)
}
static int nfs4_proc_lookup_common(struct rpc_clnt **clnt, struct inode *dir,
const struct qstr *name, struct nfs_fh *fhandle,
struct dentry *dentry, struct nfs_fh *fhandle,
struct nfs_fattr *fattr, struct nfs4_label *label)
{
struct nfs4_exception exception = {
.interruptible = true,
};
struct rpc_clnt *client = *clnt;
const struct qstr *name = &dentry->d_name;
int err;
do {
err = _nfs4_proc_lookup(client, dir, name, fhandle, fattr, label);
err = _nfs4_proc_lookup(client, dir, dentry, fhandle, fattr, label);
trace_nfs4_lookup(dir, name, err);
switch (err) {
case -NFS4ERR_BADNAME:
@ -4241,14 +4275,14 @@ out:
return err;
}
static int nfs4_proc_lookup(struct inode *dir, const struct qstr *name,
static int nfs4_proc_lookup(struct inode *dir, struct dentry *dentry,
struct nfs_fh *fhandle, struct nfs_fattr *fattr,
struct nfs4_label *label)
{
int status;
struct rpc_clnt *client = NFS_CLIENT(dir);
status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr, label);
status = nfs4_proc_lookup_common(&client, dir, dentry, fhandle, fattr, label);
if (client != NFS_CLIENT(dir)) {
rpc_shutdown_client(client);
nfs_fixup_secinfo_attributes(fattr);
@ -4257,13 +4291,13 @@ static int nfs4_proc_lookup(struct inode *dir, const struct qstr *name,
}
struct rpc_clnt *
nfs4_proc_lookup_mountpoint(struct inode *dir, const struct qstr *name,
nfs4_proc_lookup_mountpoint(struct inode *dir, struct dentry *dentry,
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
struct rpc_clnt *client = NFS_CLIENT(dir);
int status;
status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr, NULL);
status = nfs4_proc_lookup_common(&client, dir, dentry, fhandle, fattr, NULL);
if (status < 0)
return ERR_PTR(status);
return (client == NFS_CLIENT(dir)) ? rpc_clone_client(client) : client;
@ -5019,16 +5053,13 @@ static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, str
struct nfs4_exception exception = {
.interruptible = true,
};
unsigned long now = jiffies;
int err;
do {
err = _nfs4_do_fsinfo(server, fhandle, fsinfo);
trace_nfs4_fsinfo(server, fhandle, fsinfo->fattr, err);
if (err == 0) {
nfs4_set_lease_period(server->nfs_client,
fsinfo->lease_time * HZ,
now);
nfs4_set_lease_period(server->nfs_client, fsinfo->lease_time * HZ);
break;
}
err = nfs4_handle_exception(server, err, &exception);
@ -5582,10 +5613,9 @@ out:
*/
static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
{
struct page *pages[NFS4ACL_MAXPAGES + 1] = {NULL, };
struct page **pages;
struct nfs_getaclargs args = {
.fh = NFS_FH(inode),
.acl_pages = pages,
.acl_len = buflen,
};
struct nfs_getaclres res = {
@ -5596,11 +5626,19 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
.rpc_argp = &args,
.rpc_resp = &res,
};
unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE) + 1;
unsigned int npages;
int ret = -ENOMEM, i;
struct nfs_server *server = NFS_SERVER(inode);
if (npages > ARRAY_SIZE(pages))
return -ERANGE;
if (buflen == 0)
buflen = server->rsize;
npages = DIV_ROUND_UP(buflen, PAGE_SIZE) + 1;
pages = kmalloc_array(npages, sizeof(struct page *), GFP_NOFS);
if (!pages)
return -ENOMEM;
args.acl_pages = pages;
for (i = 0; i < npages; i++) {
pages[i] = alloc_page(GFP_KERNEL);
@ -5646,6 +5684,7 @@ out_free:
__free_page(pages[i]);
if (res.acl_scratch)
__free_page(res.acl_scratch);
kfree(pages);
return ret;
}
@ -6084,6 +6123,7 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
.callback_data = &setclientid,
.flags = RPC_TASK_TIMEOUT | RPC_TASK_NO_ROUND_ROBIN,
};
unsigned long now = jiffies;
int status;
/* nfs_client_id4 */
@ -6116,6 +6156,9 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
clp->cl_acceptor = rpcauth_stringify_acceptor(setclientid.sc_cred);
put_rpccred(setclientid.sc_cred);
}
if (status == 0)
do_renew_lease(clp, now);
out:
trace_nfs4_setclientid(clp, status);
dprintk("NFS reply setclientid: %d\n", status);
@ -6859,7 +6902,7 @@ static void nfs4_handle_setlk_error(struct nfs_server *server, struct nfs4_lock_
case -NFS4ERR_STALE_STATEID:
lsp->ls_seqid.flags &= ~NFS_SEQID_CONFIRMED;
nfs4_schedule_lease_recovery(server->nfs_client);
};
}
}
static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *fl, int recovery_type)
@ -8203,6 +8246,7 @@ static int _nfs4_proc_exchange_id(struct nfs_client *clp, const struct cred *cre
struct rpc_task *task;
struct nfs41_exchange_id_args *argp;
struct nfs41_exchange_id_res *resp;
unsigned long now = jiffies;
int status;
task = nfs4_run_exchange_id(clp, cred, sp4_how, NULL);
@ -8223,6 +8267,8 @@ static int _nfs4_proc_exchange_id(struct nfs_client *clp, const struct cred *cre
if (status != 0)
goto out;
do_renew_lease(clp, now);
clp->cl_clientid = resp->clientid;
clp->cl_exchange_flags = resp->flags;
clp->cl_seqid = resp->seqid;
@ -8626,7 +8672,7 @@ static int _nfs4_proc_create_session(struct nfs_client *clp,
case -EACCES:
case -EAGAIN:
goto out;
};
}
clp->cl_seqid++;
if (!status) {
@ -10001,7 +10047,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
.file_ops = &nfs4_file_operations,
.getroot = nfs4_proc_get_root,
.submount = nfs4_submount,
.try_mount = nfs4_try_mount,
.try_get_tree = nfs4_try_get_tree,
.getattr = nfs4_proc_getattr,
.setattr = nfs4_proc_setattr,
.lookup = nfs4_proc_lookup,

View File

@ -138,15 +138,12 @@ nfs4_kill_renewd(struct nfs_client *clp)
*
* @clp: pointer to nfs_client
* @lease: new value for lease period
* @lastrenewed: time at which lease was last renewed
*/
void nfs4_set_lease_period(struct nfs_client *clp,
unsigned long lease,
unsigned long lastrenewed)
unsigned long lease)
{
spin_lock(&clp->cl_lock);
clp->cl_lease_time = lease;
clp->cl_last_renewal = lastrenewed;
spin_unlock(&clp->cl_lock);
/* Cap maximum reconnect timeout at 1/2 lease period */

View File

@ -92,17 +92,15 @@ static int nfs4_setup_state_renewal(struct nfs_client *clp)
{
int status;
struct nfs_fsinfo fsinfo;
unsigned long now;
if (!test_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state)) {
nfs4_schedule_state_renewal(clp);
return 0;
}
now = jiffies;
status = nfs4_proc_get_lease_time(clp, &fsinfo);
if (status == 0) {
nfs4_set_lease_period(clp, fsinfo.lease_time * HZ, now);
nfs4_set_lease_period(clp, fsinfo.lease_time * HZ);
nfs4_schedule_state_renewal(clp);
}
@ -766,6 +764,7 @@ void nfs4_put_open_state(struct nfs4_state *state)
list_del(&state->open_states);
spin_unlock(&inode->i_lock);
spin_unlock(&owner->so_lock);
nfs4_inode_return_delegation_on_close(inode);
iput(inode);
nfs4_free_open_state(state);
nfs4_put_state_owner(owner);
@ -1135,7 +1134,7 @@ static void nfs_increment_seqid(int status, struct nfs_seqid *seqid)
case -NFS4ERR_MOVED:
/* Non-seqid mutating errors */
return;
};
}
/*
* Note: no locking needed as we are guaranteed to be first
* on the sequence list

View File

@ -4,6 +4,7 @@
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mount.h>
#include <linux/nfs4_mount.h>
#include <linux/nfs_fs.h>
#include "delegation.h"
@ -18,36 +19,6 @@
static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc);
static void nfs4_evict_inode(struct inode *inode);
static struct dentry *nfs4_remote_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data);
static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data);
static struct dentry *nfs4_remote_referral_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data);
static struct file_system_type nfs4_remote_fs_type = {
.owner = THIS_MODULE,
.name = "nfs4",
.mount = nfs4_remote_mount,
.kill_sb = nfs_kill_super,
.fs_flags = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
};
static struct file_system_type nfs4_remote_referral_fs_type = {
.owner = THIS_MODULE,
.name = "nfs4",
.mount = nfs4_remote_referral_mount,
.kill_sb = nfs_kill_super,
.fs_flags = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
};
struct file_system_type nfs4_referral_fs_type = {
.owner = THIS_MODULE,
.name = "nfs4",
.mount = nfs4_referral_mount,
.kill_sb = nfs_kill_super,
.fs_flags = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
};
static const struct super_operations nfs4_sops = {
.alloc_inode = nfs_alloc_inode,
@ -61,16 +32,15 @@ static const struct super_operations nfs4_sops = {
.show_devname = nfs_show_devname,
.show_path = nfs_show_path,
.show_stats = nfs_show_stats,
.remount_fs = nfs_remount,
};
struct nfs_subversion nfs_v4 = {
.owner = THIS_MODULE,
.nfs_fs = &nfs4_fs_type,
.rpc_vers = &nfs_version4,
.rpc_ops = &nfs_v4_clientops,
.sops = &nfs4_sops,
.xattr = nfs4_xattr_handlers,
.owner = THIS_MODULE,
.nfs_fs = &nfs4_fs_type,
.rpc_vers = &nfs_version4,
.rpc_ops = &nfs_v4_clientops,
.sops = &nfs4_sops,
.xattr = nfs4_xattr_handlers,
};
static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc)
@ -101,53 +71,6 @@ static void nfs4_evict_inode(struct inode *inode)
nfs_clear_inode(inode);
}
/*
* Get the superblock for the NFS4 root partition
*/
static struct dentry *
nfs4_remote_mount(struct file_system_type *fs_type, int flags,
const char *dev_name, void *info)
{
struct nfs_mount_info *mount_info = info;
struct nfs_server *server;
struct dentry *mntroot = ERR_PTR(-ENOMEM);
mount_info->set_security = nfs_set_sb_security;
/* Get a volume representation */
server = nfs4_create_server(mount_info, &nfs_v4);
if (IS_ERR(server)) {
mntroot = ERR_CAST(server);
goto out;
}
mntroot = nfs_fs_mount_common(server, flags, dev_name, mount_info, &nfs_v4);
out:
return mntroot;
}
static struct vfsmount *nfs_do_root_mount(struct file_system_type *fs_type,
int flags, void *data, const char *hostname)
{
struct vfsmount *root_mnt;
char *root_devname;
size_t len;
len = strlen(hostname) + 5;
root_devname = kmalloc(len, GFP_KERNEL);
if (root_devname == NULL)
return ERR_PTR(-ENOMEM);
/* Does hostname needs to be enclosed in brackets? */
if (strchr(hostname, ':'))
snprintf(root_devname, len, "[%s]:/", hostname);
else
snprintf(root_devname, len, "%s:/", hostname);
root_mnt = vfs_kern_mount(fs_type, flags, root_devname, data);
kfree(root_devname);
return root_mnt;
}
struct nfs_referral_count {
struct list_head list;
const struct task_struct *task;
@ -214,111 +137,125 @@ static void nfs_referral_loop_unprotect(void)
kfree(p);
}
static struct dentry *nfs_follow_remote_path(struct vfsmount *root_mnt,
const char *export_path)
static int do_nfs4_mount(struct nfs_server *server,
struct fs_context *fc,
const char *hostname,
const char *export_path)
{
struct nfs_fs_context *root_ctx;
struct fs_context *root_fc;
struct vfsmount *root_mnt;
struct dentry *dentry;
int err;
size_t len;
int ret;
struct fs_parameter param = {
.key = "source",
.type = fs_value_is_string,
.dirfd = -1,
};
if (IS_ERR(server))
return PTR_ERR(server);
root_fc = vfs_dup_fs_context(fc);
if (IS_ERR(root_fc)) {
nfs_free_server(server);
return PTR_ERR(root_fc);
}
kfree(root_fc->source);
root_fc->source = NULL;
root_ctx = nfs_fc2context(root_fc);
root_ctx->internal = true;
root_ctx->server = server;
/* We leave export_path unset as it's not used to find the root. */
len = strlen(hostname) + 5;
param.string = kmalloc(len, GFP_KERNEL);
if (param.string == NULL) {
put_fs_context(root_fc);
return -ENOMEM;
}
/* Does hostname needs to be enclosed in brackets? */
if (strchr(hostname, ':'))
param.size = snprintf(param.string, len, "[%s]:/", hostname);
else
param.size = snprintf(param.string, len, "%s:/", hostname);
ret = vfs_parse_fs_param(root_fc, &param);
kfree(param.string);
if (ret < 0) {
put_fs_context(root_fc);
return ret;
}
root_mnt = fc_mount(root_fc);
put_fs_context(root_fc);
if (IS_ERR(root_mnt))
return ERR_CAST(root_mnt);
return PTR_ERR(root_mnt);
err = nfs_referral_loop_protect();
if (err) {
ret = nfs_referral_loop_protect();
if (ret) {
mntput(root_mnt);
return ERR_PTR(err);
return ret;
}
dentry = mount_subtree(root_mnt, export_path);
nfs_referral_loop_unprotect();
return dentry;
if (IS_ERR(dentry))
return PTR_ERR(dentry);
fc->root = dentry;
return 0;
}
struct dentry *nfs4_try_mount(int flags, const char *dev_name,
struct nfs_mount_info *mount_info,
struct nfs_subversion *nfs_mod)
int nfs4_try_get_tree(struct fs_context *fc)
{
char *export_path;
struct vfsmount *root_mnt;
struct dentry *res;
struct nfs_parsed_mount_data *data = mount_info->parsed;
struct nfs_fs_context *ctx = nfs_fc2context(fc);
int err;
dfprintk(MOUNT, "--> nfs4_try_mount()\n");
dfprintk(MOUNT, "--> nfs4_try_get_tree()\n");
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,
data->nfs_server.hostname);
data->nfs_server.export_path = export_path;
res = nfs_follow_remote_path(root_mnt, export_path);
dfprintk(MOUNT, "<-- nfs4_try_mount() = %d%s\n",
PTR_ERR_OR_ZERO(res),
IS_ERR(res) ? " [error]" : "");
return res;
}
static struct dentry *
nfs4_remote_referral_mount(struct file_system_type *fs_type, int flags,
const char *dev_name, void *raw_data)
{
struct nfs_mount_info mount_info = {
.fill_super = nfs_fill_super,
.set_security = nfs_clone_sb_security,
.cloned = raw_data,
};
struct nfs_server *server;
struct dentry *mntroot = ERR_PTR(-ENOMEM);
dprintk("--> nfs4_referral_get_sb()\n");
mount_info.mntfh = nfs_alloc_fhandle();
if (mount_info.cloned == NULL || mount_info.mntfh == NULL)
goto out;
/* create a new volume representation */
server = nfs4_create_referral_server(mount_info.cloned, mount_info.mntfh);
if (IS_ERR(server)) {
mntroot = ERR_CAST(server);
goto out;
/* We create a mount for the server's root, walk to the requested
* location and then create another mount for that.
*/
err= do_nfs4_mount(nfs4_create_server(fc),
fc, ctx->nfs_server.hostname,
ctx->nfs_server.export_path);
if (err) {
nfs_errorf(fc, "NFS4: Couldn't follow remote path");
dfprintk(MOUNT, "<-- nfs4_try_get_tree() = %d [error]\n", err);
} else {
dfprintk(MOUNT, "<-- nfs4_try_get_tree() = 0\n");
}
mntroot = nfs_fs_mount_common(server, flags, dev_name, &mount_info, &nfs_v4);
out:
nfs_free_fhandle(mount_info.mntfh);
return mntroot;
return err;
}
/*
* Create an NFS4 server record on referral traversal
*/
static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data)
int nfs4_get_referral_tree(struct fs_context *fc)
{
struct nfs_clone_mount *data = raw_data;
char *export_path;
struct vfsmount *root_mnt;
struct dentry *res;
struct nfs_fs_context *ctx = nfs_fc2context(fc);
int err;
dprintk("--> nfs4_referral_mount()\n");
export_path = data->mnt_path;
data->mnt_path = "/";
root_mnt = nfs_do_root_mount(&nfs4_remote_referral_fs_type,
flags, data, data->hostname);
data->mnt_path = export_path;
res = nfs_follow_remote_path(root_mnt, export_path);
dprintk("<-- nfs4_referral_mount() = %d%s\n",
PTR_ERR_OR_ZERO(res),
IS_ERR(res) ? " [error]" : "");
return res;
/* create a new volume representation */
err = do_nfs4_mount(nfs4_create_referral_server(fc),
fc, ctx->nfs_server.hostname,
ctx->nfs_server.export_path);
if (err) {
nfs_errorf(fc, "NFS4: Couldn't follow remote path");
dfprintk(MOUNT, "<-- nfs4_get_referral_tree() = %d [error]\n", err);
} else {
dfprintk(MOUNT, "<-- nfs4_get_referral_tree() = 0\n");
}
return err;
}
static int __init init_nfs_v4(void)
{
int err;

View File

@ -24,4 +24,8 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(pnfs_mds_fallback_read_done);
EXPORT_TRACEPOINT_SYMBOL_GPL(pnfs_mds_fallback_write_done);
EXPORT_TRACEPOINT_SYMBOL_GPL(pnfs_mds_fallback_read_pagelist);
EXPORT_TRACEPOINT_SYMBOL_GPL(pnfs_mds_fallback_write_pagelist);
EXPORT_TRACEPOINT_SYMBOL_GPL(ff_layout_read_error);
EXPORT_TRACEPOINT_SYMBOL_GPL(ff_layout_write_error);
EXPORT_TRACEPOINT_SYMBOL_GPL(ff_layout_commit_error);
#endif

View File

@ -155,6 +155,9 @@ TRACE_DEFINE_ENUM(NFS4ERR_WRONG_CRED);
TRACE_DEFINE_ENUM(NFS4ERR_WRONG_TYPE);
TRACE_DEFINE_ENUM(NFS4ERR_XDEV);
TRACE_DEFINE_ENUM(NFS4ERR_RESET_TO_MDS);
TRACE_DEFINE_ENUM(NFS4ERR_RESET_TO_PNFS);
#define show_nfsv4_errors(error) \
__print_symbolic(error, \
{ NFS4_OK, "OK" }, \
@ -305,7 +308,10 @@ TRACE_DEFINE_ENUM(NFS4ERR_XDEV);
{ NFS4ERR_WRONGSEC, "WRONGSEC" }, \
{ NFS4ERR_WRONG_CRED, "WRONG_CRED" }, \
{ NFS4ERR_WRONG_TYPE, "WRONG_TYPE" }, \
{ NFS4ERR_XDEV, "XDEV" })
{ NFS4ERR_XDEV, "XDEV" }, \
/* ***** Internal to Linux NFS client ***** */ \
{ NFS4ERR_RESET_TO_MDS, "RESET_TO_MDS" }, \
{ NFS4ERR_RESET_TO_PNFS, "RESET_TO_PNFS" })
#define show_open_flags(flags) \
__print_flags(flags, "|", \
@ -352,7 +358,7 @@ DECLARE_EVENT_CLASS(nfs4_clientid_event,
),
TP_fast_assign(
__entry->error = error;
__entry->error = error < 0 ? -error : 0;
__assign_str(dstaddr, clp->cl_hostname);
),
@ -432,7 +438,8 @@ TRACE_EVENT(nfs4_sequence_done,
__entry->target_highest_slotid =
res->sr_target_highest_slotid;
__entry->status_flags = res->sr_status_flags;
__entry->error = res->sr_status;
__entry->error = res->sr_status < 0 ?
-res->sr_status : 0;
),
TP_printk(
"error=%ld (%s) session=0x%08x slot_nr=%u seq_nr=%u "
@ -640,7 +647,7 @@ TRACE_EVENT(nfs4_state_mgr_failed,
),
TP_fast_assign(
__entry->error = status;
__entry->error = status < 0 ? -status : 0;
__entry->state = clp->cl_state;
__assign_str(hostname, clp->cl_hostname);
__assign_str(section, section);
@ -659,7 +666,7 @@ TRACE_EVENT(nfs4_xdr_status,
TP_PROTO(
const struct xdr_stream *xdr,
u32 op,
int error
u32 error
),
TP_ARGS(xdr, op, error),
@ -691,6 +698,41 @@ TRACE_EVENT(nfs4_xdr_status,
)
);
DECLARE_EVENT_CLASS(nfs4_cb_error_class,
TP_PROTO(
__be32 xid,
u32 cb_ident
),
TP_ARGS(xid, cb_ident),
TP_STRUCT__entry(
__field(u32, xid)
__field(u32, cbident)
),
TP_fast_assign(
__entry->xid = be32_to_cpu(xid);
__entry->cbident = cb_ident;
),
TP_printk(
"xid=0x%08x cb_ident=0x%08x",
__entry->xid, __entry->cbident
)
);
#define DEFINE_CB_ERROR_EVENT(name) \
DEFINE_EVENT(nfs4_cb_error_class, nfs_cb_##name, \
TP_PROTO( \
__be32 xid, \
u32 cb_ident \
), \
TP_ARGS(xid, cb_ident))
DEFINE_CB_ERROR_EVENT(no_clp);
DEFINE_CB_ERROR_EVENT(badprinc);
DECLARE_EVENT_CLASS(nfs4_open_event,
TP_PROTO(
const struct nfs_open_context *ctx,
@ -849,7 +891,7 @@ TRACE_EVENT(nfs4_close,
__entry->fileid = NFS_FILEID(inode);
__entry->fhandle = nfs_fhandle_hash(NFS_FH(inode));
__entry->fmode = (__force unsigned int)state->state;
__entry->error = error;
__entry->error = error < 0 ? -error : 0;
__entry->stateid_seq =
be32_to_cpu(args->stateid.seqid);
__entry->stateid_hash =
@ -914,7 +956,7 @@ DECLARE_EVENT_CLASS(nfs4_lock_event,
TP_fast_assign(
const struct inode *inode = state->inode;
__entry->error = error;
__entry->error = error < 0 ? -error : 0;
__entry->cmd = cmd;
__entry->type = request->fl_type;
__entry->start = request->fl_start;
@ -986,7 +1028,7 @@ TRACE_EVENT(nfs4_set_lock,
TP_fast_assign(
const struct inode *inode = state->inode;
__entry->error = error;
__entry->error = error < 0 ? -error : 0;
__entry->cmd = cmd;
__entry->type = request->fl_type;
__entry->start = request->fl_start;
@ -1164,7 +1206,7 @@ TRACE_EVENT(nfs4_delegreturn_exit,
TP_fast_assign(
__entry->dev = res->server->s_dev;
__entry->fhandle = nfs_fhandle_hash(args->fhandle);
__entry->error = error;
__entry->error = error < 0 ? -error : 0;
__entry->stateid_seq =
be32_to_cpu(args->stateid->seqid);
__entry->stateid_hash =
@ -1204,7 +1246,7 @@ DECLARE_EVENT_CLASS(nfs4_test_stateid_event,
TP_fast_assign(
const struct inode *inode = state->inode;
__entry->error = error;
__entry->error = error < 0 ? -error : 0;
__entry->dev = inode->i_sb->s_dev;
__entry->fileid = NFS_FILEID(inode);
__entry->fhandle = nfs_fhandle_hash(NFS_FH(inode));
@ -1306,7 +1348,7 @@ TRACE_EVENT(nfs4_lookupp,
TP_fast_assign(
__entry->dev = inode->i_sb->s_dev;
__entry->ino = NFS_FILEID(inode);
__entry->error = error;
__entry->error = error < 0 ? -error : 0;
),
TP_printk(
@ -1342,7 +1384,7 @@ TRACE_EVENT(nfs4_rename,
__entry->dev = olddir->i_sb->s_dev;
__entry->olddir = NFS_FILEID(olddir);
__entry->newdir = NFS_FILEID(newdir);
__entry->error = error;
__entry->error = error < 0 ? -error : 0;
__assign_str(oldname, oldname->name);
__assign_str(newname, newname->name);
),
@ -1433,7 +1475,7 @@ DECLARE_EVENT_CLASS(nfs4_inode_stateid_event,
__entry->dev = inode->i_sb->s_dev;
__entry->fileid = NFS_FILEID(inode);
__entry->fhandle = nfs_fhandle_hash(NFS_FH(inode));
__entry->error = error;
__entry->error = error < 0 ? -error : 0;
__entry->stateid_seq =
be32_to_cpu(stateid->seqid);
__entry->stateid_hash =
@ -1489,7 +1531,7 @@ DECLARE_EVENT_CLASS(nfs4_getattr_event,
__entry->valid = fattr->valid;
__entry->fhandle = nfs_fhandle_hash(fhandle);
__entry->fileid = (fattr->valid & NFS_ATTR_FATTR_FILEID) ? fattr->fileid : 0;
__entry->error = error;
__entry->error = error < 0 ? -error : 0;
),
TP_printk(
@ -1536,7 +1578,7 @@ DECLARE_EVENT_CLASS(nfs4_inode_callback_event,
),
TP_fast_assign(
__entry->error = error;
__entry->error = error < 0 ? -error : 0;
__entry->fhandle = nfs_fhandle_hash(fhandle);
if (!IS_ERR_OR_NULL(inode)) {
__entry->fileid = NFS_FILEID(inode);
@ -1593,7 +1635,7 @@ DECLARE_EVENT_CLASS(nfs4_inode_stateid_callback_event,
),
TP_fast_assign(
__entry->error = error;
__entry->error = error < 0 ? -error : 0;
__entry->fhandle = nfs_fhandle_hash(fhandle);
if (!IS_ERR_OR_NULL(inode)) {
__entry->fileid = NFS_FILEID(inode);
@ -1694,7 +1736,8 @@ DECLARE_EVENT_CLASS(nfs4_read_event,
__field(u32, fhandle)
__field(u64, fileid)
__field(loff_t, offset)
__field(size_t, count)
__field(u32, arg_count)
__field(u32, res_count)
__field(unsigned long, error)
__field(int, stateid_seq)
__field(u32, stateid_hash)
@ -1702,13 +1745,18 @@ DECLARE_EVENT_CLASS(nfs4_read_event,
TP_fast_assign(
const struct inode *inode = hdr->inode;
const struct nfs_inode *nfsi = NFS_I(inode);
const struct nfs_fh *fh = hdr->args.fh ?
hdr->args.fh : &nfsi->fh;
const struct nfs4_state *state =
hdr->args.context->state;
__entry->dev = inode->i_sb->s_dev;
__entry->fileid = NFS_FILEID(inode);
__entry->fhandle = nfs_fhandle_hash(NFS_FH(inode));
__entry->fileid = nfsi->fileid;
__entry->fhandle = nfs_fhandle_hash(fh);
__entry->offset = hdr->args.offset;
__entry->count = hdr->args.count;
__entry->arg_count = hdr->args.count;
__entry->res_count = hdr->res.count;
__entry->error = error < 0 ? -error : 0;
__entry->stateid_seq =
be32_to_cpu(state->stateid.seqid);
@ -1718,14 +1766,14 @@ DECLARE_EVENT_CLASS(nfs4_read_event,
TP_printk(
"error=%ld (%s) fileid=%02x:%02x:%llu fhandle=0x%08x "
"offset=%lld count=%zu stateid=%d:0x%08x",
"offset=%lld count=%u res=%u stateid=%d:0x%08x",
-__entry->error,
show_nfsv4_errors(__entry->error),
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid,
__entry->fhandle,
(long long)__entry->offset,
__entry->count,
__entry->arg_count, __entry->res_count,
__entry->stateid_seq, __entry->stateid_hash
)
);
@ -1754,7 +1802,8 @@ DECLARE_EVENT_CLASS(nfs4_write_event,
__field(u32, fhandle)
__field(u64, fileid)
__field(loff_t, offset)
__field(size_t, count)
__field(u32, arg_count)
__field(u32, res_count)
__field(unsigned long, error)
__field(int, stateid_seq)
__field(u32, stateid_hash)
@ -1762,13 +1811,18 @@ DECLARE_EVENT_CLASS(nfs4_write_event,
TP_fast_assign(
const struct inode *inode = hdr->inode;
const struct nfs_inode *nfsi = NFS_I(inode);
const struct nfs_fh *fh = hdr->args.fh ?
hdr->args.fh : &nfsi->fh;
const struct nfs4_state *state =
hdr->args.context->state;
__entry->dev = inode->i_sb->s_dev;
__entry->fileid = NFS_FILEID(inode);
__entry->fhandle = nfs_fhandle_hash(NFS_FH(inode));
__entry->fileid = nfsi->fileid;
__entry->fhandle = nfs_fhandle_hash(fh);
__entry->offset = hdr->args.offset;
__entry->count = hdr->args.count;
__entry->arg_count = hdr->args.count;
__entry->res_count = hdr->res.count;
__entry->error = error < 0 ? -error : 0;
__entry->stateid_seq =
be32_to_cpu(state->stateid.seqid);
@ -1778,14 +1832,14 @@ DECLARE_EVENT_CLASS(nfs4_write_event,
TP_printk(
"error=%ld (%s) fileid=%02x:%02x:%llu fhandle=0x%08x "
"offset=%lld count=%zu stateid=%d:0x%08x",
"offset=%lld count=%u res=%u stateid=%d:0x%08x",
-__entry->error,
show_nfsv4_errors(__entry->error),
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid,
__entry->fhandle,
(long long)__entry->offset,
__entry->count,
__entry->arg_count, __entry->res_count,
__entry->stateid_seq, __entry->stateid_hash
)
);
@ -1814,24 +1868,28 @@ DECLARE_EVENT_CLASS(nfs4_commit_event,
__field(dev_t, dev)
__field(u32, fhandle)
__field(u64, fileid)
__field(loff_t, offset)
__field(size_t, count)
__field(unsigned long, error)
__field(loff_t, offset)
__field(u32, count)
),
TP_fast_assign(
const struct inode *inode = data->inode;
const struct nfs_inode *nfsi = NFS_I(inode);
const struct nfs_fh *fh = data->args.fh ?
data->args.fh : &nfsi->fh;
__entry->dev = inode->i_sb->s_dev;
__entry->fileid = NFS_FILEID(inode);
__entry->fhandle = nfs_fhandle_hash(NFS_FH(inode));
__entry->fileid = nfsi->fileid;
__entry->fhandle = nfs_fhandle_hash(fh);
__entry->offset = data->args.offset;
__entry->count = data->args.count;
__entry->error = error;
__entry->error = error < 0 ? -error : 0;
),
TP_printk(
"error=%ld (%s) fileid=%02x:%02x:%llu fhandle=0x%08x "
"offset=%lld count=%zu",
"offset=%lld count=%u",
-__entry->error,
show_nfsv4_errors(__entry->error),
MAJOR(__entry->dev), MINOR(__entry->dev),
@ -1896,7 +1954,7 @@ TRACE_EVENT(nfs4_layoutget,
__entry->iomode = args->iomode;
__entry->offset = args->offset;
__entry->count = args->length;
__entry->error = error;
__entry->error = error < 0 ? -error : 0;
__entry->stateid_seq =
be32_to_cpu(state->stateid.seqid);
__entry->stateid_hash =
@ -2094,6 +2152,115 @@ DEFINE_PNFS_LAYOUT_EVENT(pnfs_mds_fallback_write_done);
DEFINE_PNFS_LAYOUT_EVENT(pnfs_mds_fallback_read_pagelist);
DEFINE_PNFS_LAYOUT_EVENT(pnfs_mds_fallback_write_pagelist);
DECLARE_EVENT_CLASS(nfs4_flexfiles_io_event,
TP_PROTO(
const struct nfs_pgio_header *hdr
),
TP_ARGS(hdr),
TP_STRUCT__entry(
__field(unsigned long, error)
__field(dev_t, dev)
__field(u32, fhandle)
__field(u64, fileid)
__field(loff_t, offset)
__field(u32, count)
__field(int, stateid_seq)
__field(u32, stateid_hash)
__string(dstaddr, hdr->ds_clp ?
rpc_peeraddr2str(hdr->ds_clp->cl_rpcclient,
RPC_DISPLAY_ADDR) : "unknown")
),
TP_fast_assign(
const struct inode *inode = hdr->inode;
__entry->error = hdr->res.op_status;
__entry->fhandle = nfs_fhandle_hash(hdr->args.fh);
__entry->fileid = NFS_FILEID(inode);
__entry->dev = inode->i_sb->s_dev;
__entry->offset = hdr->args.offset;
__entry->count = hdr->args.count;
__entry->stateid_seq =
be32_to_cpu(hdr->args.stateid.seqid);
__entry->stateid_hash =
nfs_stateid_hash(&hdr->args.stateid);
__assign_str(dstaddr, hdr->ds_clp ?
rpc_peeraddr2str(hdr->ds_clp->cl_rpcclient,
RPC_DISPLAY_ADDR) : "unknown");
),
TP_printk(
"error=%ld (%s) fileid=%02x:%02x:%llu fhandle=0x%08x "
"offset=%llu count=%u stateid=%d:0x%08x dstaddr=%s",
-__entry->error,
show_nfsv4_errors(__entry->error),
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid,
__entry->fhandle,
__entry->offset, __entry->count,
__entry->stateid_seq, __entry->stateid_hash,
__get_str(dstaddr)
)
);
#define DEFINE_NFS4_FLEXFILES_IO_EVENT(name) \
DEFINE_EVENT(nfs4_flexfiles_io_event, name, \
TP_PROTO( \
const struct nfs_pgio_header *hdr \
), \
TP_ARGS(hdr))
DEFINE_NFS4_FLEXFILES_IO_EVENT(ff_layout_read_error);
DEFINE_NFS4_FLEXFILES_IO_EVENT(ff_layout_write_error);
TRACE_EVENT(ff_layout_commit_error,
TP_PROTO(
const struct nfs_commit_data *data
),
TP_ARGS(data),
TP_STRUCT__entry(
__field(unsigned long, error)
__field(dev_t, dev)
__field(u32, fhandle)
__field(u64, fileid)
__field(loff_t, offset)
__field(u32, count)
__string(dstaddr, data->ds_clp ?
rpc_peeraddr2str(data->ds_clp->cl_rpcclient,
RPC_DISPLAY_ADDR) : "unknown")
),
TP_fast_assign(
const struct inode *inode = data->inode;
__entry->error = data->res.op_status;
__entry->fhandle = nfs_fhandle_hash(data->args.fh);
__entry->fileid = NFS_FILEID(inode);
__entry->dev = inode->i_sb->s_dev;
__entry->offset = data->args.offset;
__entry->count = data->args.count;
__assign_str(dstaddr, data->ds_clp ?
rpc_peeraddr2str(data->ds_clp->cl_rpcclient,
RPC_DISPLAY_ADDR) : "unknown");
),
TP_printk(
"error=%ld (%s) fileid=%02x:%02x:%llu fhandle=0x%08x "
"offset=%llu count=%u dstaddr=%s",
-__entry->error,
show_nfsv4_errors(__entry->error),
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid,
__entry->fhandle,
__entry->offset, __entry->count,
__get_str(dstaddr)
)
);
#endif /* CONFIG_NFS_V4_1 */
#endif /* _TRACE_NFS4_H */

View File

@ -1061,7 +1061,7 @@ static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *ve
static __be32 *
xdr_encode_nfstime4(__be32 *p, const struct timespec64 *t)
{
p = xdr_encode_hyper(p, (__s64)t->tv_sec);
p = xdr_encode_hyper(p, t->tv_sec);
*p++ = cpu_to_be32(t->tv_nsec);
return p;
}
@ -4313,11 +4313,14 @@ static int decode_write_verifier(struct xdr_stream *xdr, struct nfs_write_verifi
static int decode_commit(struct xdr_stream *xdr, struct nfs_commitres *res)
{
struct nfs_writeverf *verf = res->verf;
int status;
status = decode_op_hdr(xdr, OP_COMMIT);
if (!status)
status = decode_write_verifier(xdr, &res->verf->verifier);
status = decode_write_verifier(xdr, &verf->verifier);
if (!status)
verf->committed = NFS_FILE_SYNC;
return status;
}

View File

@ -198,7 +198,66 @@ 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);
TRACE_EVENT(nfs_access_exit,
TP_PROTO(
const struct inode *inode,
unsigned int mask,
unsigned int permitted,
int error
),
TP_ARGS(inode, mask, permitted, error),
TP_STRUCT__entry(
__field(unsigned long, 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)
__field(unsigned int, mask)
__field(unsigned int, permitted)
),
TP_fast_assign(
const struct nfs_inode *nfsi = NFS_I(inode);
__entry->error = error < 0 ? -error : 0;
__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_peek_iversion_raw(inode);
__entry->size = i_size_read(inode);
__entry->nfsi_flags = nfsi->flags;
__entry->cache_validity = nfsi->cache_validity;
__entry->mask = mask;
__entry->permitted = permitted;
),
TP_printk(
"error=%ld (%s) fileid=%02x:%02x:%llu fhandle=0x%08x "
"type=%u (%s) version=%llu size=%lld "
"cache_validity=0x%lx (%s) nfs_flags=0x%lx (%s) "
"mask=0x%x permitted=0x%x",
-__entry->error, nfs_show_status(__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),
__entry->mask, __entry->permitted
)
);
TRACE_DEFINE_ENUM(LOOKUP_FOLLOW);
TRACE_DEFINE_ENUM(LOOKUP_DIRECTORY);
@ -818,75 +877,85 @@ TRACE_EVENT(nfs_sillyrename_unlink,
TRACE_EVENT(nfs_initiate_read,
TP_PROTO(
const struct inode *inode,
loff_t offset, unsigned long count
const struct nfs_pgio_header *hdr
),
TP_ARGS(inode, offset, count),
TP_ARGS(hdr),
TP_STRUCT__entry(
__field(loff_t, offset)
__field(unsigned long, count)
__field(dev_t, dev)
__field(u32, fhandle)
__field(u64, fileid)
__field(loff_t, offset)
__field(u32, count)
),
TP_fast_assign(
const struct inode *inode = hdr->inode;
const struct nfs_inode *nfsi = NFS_I(inode);
const struct nfs_fh *fh = hdr->args.fh ?
hdr->args.fh : &nfsi->fh;
__entry->offset = offset;
__entry->count = count;
__entry->offset = hdr->args.offset;
__entry->count = hdr->args.count;
__entry->dev = inode->i_sb->s_dev;
__entry->fileid = nfsi->fileid;
__entry->fhandle = nfs_fhandle_hash(&nfsi->fh);
__entry->fhandle = nfs_fhandle_hash(fh);
),
TP_printk(
"fileid=%02x:%02x:%llu fhandle=0x%08x "
"offset=%lld count=%lu",
"offset=%lld count=%u",
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid,
__entry->fhandle,
__entry->offset, __entry->count
(long long)__entry->offset, __entry->count
)
);
TRACE_EVENT(nfs_readpage_done,
TP_PROTO(
const struct inode *inode,
int status, loff_t offset, bool eof
const struct rpc_task *task,
const struct nfs_pgio_header *hdr
),
TP_ARGS(inode, status, offset, eof),
TP_ARGS(task, hdr),
TP_STRUCT__entry(
__field(int, status)
__field(loff_t, offset)
__field(bool, eof)
__field(dev_t, dev)
__field(u32, fhandle)
__field(u64, fileid)
__field(loff_t, offset)
__field(u32, arg_count)
__field(u32, res_count)
__field(bool, eof)
__field(int, status)
),
TP_fast_assign(
const struct inode *inode = hdr->inode;
const struct nfs_inode *nfsi = NFS_I(inode);
const struct nfs_fh *fh = hdr->args.fh ?
hdr->args.fh : &nfsi->fh;
__entry->status = status;
__entry->offset = offset;
__entry->eof = eof;
__entry->status = task->tk_status;
__entry->offset = hdr->args.offset;
__entry->arg_count = hdr->args.count;
__entry->res_count = hdr->res.count;
__entry->eof = hdr->res.eof;
__entry->dev = inode->i_sb->s_dev;
__entry->fileid = nfsi->fileid;
__entry->fhandle = nfs_fhandle_hash(&nfsi->fh);
__entry->fhandle = nfs_fhandle_hash(fh);
),
TP_printk(
"fileid=%02x:%02x:%llu fhandle=0x%08x "
"offset=%lld status=%d%s",
"offset=%lld count=%u res=%u status=%d%s",
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid,
__entry->fhandle,
__entry->offset, __entry->status,
(long long)__entry->offset, __entry->arg_count,
__entry->res_count, __entry->status,
__entry->eof ? " eof" : ""
)
);
@ -903,90 +972,144 @@ TRACE_DEFINE_ENUM(NFS_FILE_SYNC);
TRACE_EVENT(nfs_initiate_write,
TP_PROTO(
const struct inode *inode,
loff_t offset, unsigned long count,
enum nfs3_stable_how stable
const struct nfs_pgio_header *hdr
),
TP_ARGS(inode, offset, count, stable),
TP_ARGS(hdr),
TP_STRUCT__entry(
__field(loff_t, offset)
__field(unsigned long, count)
__field(enum nfs3_stable_how, stable)
__field(dev_t, dev)
__field(u32, fhandle)
__field(u64, fileid)
__field(loff_t, offset)
__field(u32, count)
__field(enum nfs3_stable_how, stable)
),
TP_fast_assign(
const struct inode *inode = hdr->inode;
const struct nfs_inode *nfsi = NFS_I(inode);
const struct nfs_fh *fh = hdr->args.fh ?
hdr->args.fh : &nfsi->fh;
__entry->offset = offset;
__entry->count = count;
__entry->stable = stable;
__entry->offset = hdr->args.offset;
__entry->count = hdr->args.count;
__entry->stable = hdr->args.stable;
__entry->dev = inode->i_sb->s_dev;
__entry->fileid = nfsi->fileid;
__entry->fhandle = nfs_fhandle_hash(&nfsi->fh);
__entry->fhandle = nfs_fhandle_hash(fh);
),
TP_printk(
"fileid=%02x:%02x:%llu fhandle=0x%08x "
"offset=%lld count=%lu stable=%s",
"offset=%lld count=%u stable=%s",
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid,
__entry->fhandle,
__entry->offset, __entry->count,
(long long)__entry->offset, __entry->count,
nfs_show_stable(__entry->stable)
)
);
TRACE_EVENT(nfs_writeback_done,
TP_PROTO(
const struct inode *inode,
int status,
loff_t offset,
struct nfs_writeverf *writeverf
const struct rpc_task *task,
const struct nfs_pgio_header *hdr
),
TP_ARGS(inode, status, offset, writeverf),
TP_ARGS(task, hdr),
TP_STRUCT__entry(
__field(int, status)
__field(loff_t, offset)
__field(enum nfs3_stable_how, stable)
__field(unsigned long long, verifier)
__field(dev_t, dev)
__field(u32, fhandle)
__field(u64, fileid)
__field(loff_t, offset)
__field(u32, arg_count)
__field(u32, res_count)
__field(int, status)
__field(enum nfs3_stable_how, stable)
__array(char, verifier, NFS4_VERIFIER_SIZE)
),
TP_fast_assign(
const struct inode *inode = hdr->inode;
const struct nfs_inode *nfsi = NFS_I(inode);
const struct nfs_fh *fh = hdr->args.fh ?
hdr->args.fh : &nfsi->fh;
const struct nfs_writeverf *verf = hdr->res.verf;
__entry->status = status;
__entry->offset = offset;
__entry->stable = writeverf->committed;
memcpy(&__entry->verifier, &writeverf->verifier,
sizeof(__entry->verifier));
__entry->status = task->tk_status;
__entry->offset = hdr->args.offset;
__entry->arg_count = hdr->args.count;
__entry->res_count = hdr->res.count;
__entry->stable = verf->committed;
memcpy(__entry->verifier,
&verf->verifier,
NFS4_VERIFIER_SIZE);
__entry->dev = inode->i_sb->s_dev;
__entry->fileid = nfsi->fileid;
__entry->fhandle = nfs_fhandle_hash(&nfsi->fh);
__entry->fhandle = nfs_fhandle_hash(fh);
),
TP_printk(
"fileid=%02x:%02x:%llu fhandle=0x%08x "
"offset=%lld status=%d stable=%s "
"verifier 0x%016llx",
"offset=%lld count=%u res=%u status=%d stable=%s "
"verifier=%s",
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid,
__entry->fhandle,
__entry->offset, __entry->status,
(long long)__entry->offset, __entry->arg_count,
__entry->res_count, __entry->status,
nfs_show_stable(__entry->stable),
__entry->verifier
__print_hex_str(__entry->verifier, NFS4_VERIFIER_SIZE)
)
);
DECLARE_EVENT_CLASS(nfs_page_error_class,
TP_PROTO(
const struct nfs_page *req,
int error
),
TP_ARGS(req, error),
TP_STRUCT__entry(
__field(const void *, req)
__field(pgoff_t, index)
__field(unsigned int, offset)
__field(unsigned int, pgbase)
__field(unsigned int, bytes)
__field(int, error)
),
TP_fast_assign(
__entry->req = req;
__entry->index = req->wb_index;
__entry->offset = req->wb_offset;
__entry->pgbase = req->wb_pgbase;
__entry->bytes = req->wb_bytes;
__entry->error = error;
),
TP_printk(
"req=%p index=%lu offset=%u pgbase=%u bytes=%u error=%d",
__entry->req, __entry->index, __entry->offset,
__entry->pgbase, __entry->bytes, __entry->error
)
);
#define DEFINE_NFS_PAGEERR_EVENT(name) \
DEFINE_EVENT(nfs_page_error_class, name, \
TP_PROTO( \
const struct nfs_page *req, \
int error \
), \
TP_ARGS(req, error))
DEFINE_NFS_PAGEERR_EVENT(nfs_write_error);
DEFINE_NFS_PAGEERR_EVENT(nfs_comp_error);
DEFINE_NFS_PAGEERR_EVENT(nfs_commit_error);
TRACE_EVENT(nfs_initiate_commit,
TP_PROTO(
const struct nfs_commit_data *data
@ -995,71 +1118,81 @@ TRACE_EVENT(nfs_initiate_commit,
TP_ARGS(data),
TP_STRUCT__entry(
__field(loff_t, offset)
__field(unsigned long, count)
__field(dev_t, dev)
__field(u32, fhandle)
__field(u64, fileid)
__field(loff_t, offset)
__field(u32, count)
),
TP_fast_assign(
const struct inode *inode = data->inode;
const struct nfs_inode *nfsi = NFS_I(inode);
const struct nfs_fh *fh = data->args.fh ?
data->args.fh : &nfsi->fh;
__entry->offset = data->args.offset;
__entry->count = data->args.count;
__entry->dev = inode->i_sb->s_dev;
__entry->fileid = nfsi->fileid;
__entry->fhandle = nfs_fhandle_hash(&nfsi->fh);
__entry->fhandle = nfs_fhandle_hash(fh);
),
TP_printk(
"fileid=%02x:%02x:%llu fhandle=0x%08x "
"offset=%lld count=%lu",
"offset=%lld count=%u",
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid,
__entry->fhandle,
__entry->offset, __entry->count
(long long)__entry->offset, __entry->count
)
);
TRACE_EVENT(nfs_commit_done,
TP_PROTO(
const struct rpc_task *task,
const struct nfs_commit_data *data
),
TP_ARGS(data),
TP_ARGS(task, data),
TP_STRUCT__entry(
__field(int, status)
__field(loff_t, offset)
__field(unsigned long long, verifier)
__field(dev_t, dev)
__field(u32, fhandle)
__field(u64, fileid)
__field(loff_t, offset)
__field(int, status)
__field(enum nfs3_stable_how, stable)
__array(char, verifier, NFS4_VERIFIER_SIZE)
),
TP_fast_assign(
const struct inode *inode = data->inode;
const struct nfs_inode *nfsi = NFS_I(inode);
const struct nfs_fh *fh = data->args.fh ?
data->args.fh : &nfsi->fh;
const struct nfs_writeverf *verf = data->res.verf;
__entry->status = data->res.op_status;
__entry->status = task->tk_status;
__entry->offset = data->args.offset;
memcpy(&__entry->verifier, &data->verf.verifier,
sizeof(__entry->verifier));
__entry->stable = verf->committed;
memcpy(__entry->verifier,
&verf->verifier,
NFS4_VERIFIER_SIZE);
__entry->dev = inode->i_sb->s_dev;
__entry->fileid = nfsi->fileid;
__entry->fhandle = nfs_fhandle_hash(&nfsi->fh);
__entry->fhandle = nfs_fhandle_hash(fh);
),
TP_printk(
"fileid=%02x:%02x:%llu fhandle=0x%08x "
"offset=%lld status=%d verifier 0x%016llx",
"offset=%lld status=%d stable=%s verifier=%s",
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid,
__entry->fhandle,
__entry->offset, __entry->status,
__entry->verifier
(long long)__entry->offset, __entry->status,
nfs_show_stable(__entry->stable),
__print_hex_str(__entry->verifier, NFS4_VERIFIER_SIZE)
)
);

View File

@ -1425,7 +1425,7 @@ retry:
/* lo ref dropped in pnfs_roc_release() */
layoutreturn = pnfs_prepare_layoutreturn(lo, &stateid, &iomode);
/* If the creds don't match, we can't compound the layoutreturn */
if (!layoutreturn || cred != lo->plh_lc_cred)
if (!layoutreturn || cred_fscmp(cred, lo->plh_lc_cred) != 0)
goto out_noroc;
roc = layoutreturn;
@ -1998,8 +1998,6 @@ lookup_again:
trace_pnfs_update_layout(ino, pos, count,
iomode, lo, lseg,
PNFS_UPDATE_LAYOUT_INVALID_OPEN);
if (status != -EAGAIN)
goto out_unlock;
spin_unlock(&ino->i_lock);
nfs4_schedule_stateid_recovery(server, ctx->state);
pnfs_clear_first_layoutget(lo);

View File

@ -79,6 +79,10 @@ enum pnfs_try_status {
PNFS_TRY_AGAIN = 2,
};
/* error codes for internal use */
#define NFS4ERR_RESET_TO_MDS 12001
#define NFS4ERR_RESET_TO_PNFS 12002
#ifdef CONFIG_NFS_V4_1
#define LAYOUT_NFSV4_1_MODULE_PREFIX "nfs-layouttype4"
@ -91,10 +95,6 @@ enum pnfs_try_status {
#define NFS4_DEF_DS_RETRANS 5
#define PNFS_DEVICE_RETRY_TIMEOUT (120*HZ)
/* error codes for internal use */
#define NFS4ERR_RESET_TO_MDS 12001
#define NFS4ERR_RESET_TO_PNFS 12002
enum {
NFS_LAYOUT_RO_FAILED = 0, /* get ro layout failed stop trying */
NFS_LAYOUT_RW_FAILED, /* get rw layout failed stop trying */

View File

@ -31,12 +31,11 @@ EXPORT_SYMBOL_GPL(pnfs_generic_rw_release);
/* Fake up some data that will cause nfs_commit_release to retry the writes. */
void pnfs_generic_prepare_to_resend_writes(struct nfs_commit_data *data)
{
struct nfs_page *first = nfs_list_entry(data->pages.next);
struct nfs_writeverf *verf = data->res.verf;
data->task.tk_status = 0;
memcpy(&data->verf.verifier, &first->wb_verf,
sizeof(data->verf.verifier));
data->verf.verifier.data[0]++; /* ensure verifier mismatch */
memset(&verf->verifier, 0, sizeof(verf->verifier));
verf->committed = NFS_UNSTABLE;
}
EXPORT_SYMBOL_GPL(pnfs_generic_prepare_to_resend_writes);

View File

@ -108,10 +108,15 @@ nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
.rpc_resp = fattr,
};
int status;
unsigned short task_flags = 0;
/* Is this is an attribute revalidation, subject to softreval? */
if (inode && (server->flags & NFS_MOUNT_SOFTREVAL))
task_flags |= RPC_TASK_TIMEOUT;
dprintk("NFS call getattr\n");
nfs_fattr_init(fattr);
status = rpc_call_sync(server->client, &msg, 0);
status = rpc_call_sync(server->client, &msg, task_flags);
dprintk("NFS reply getattr: %d\n", status);
return status;
}
@ -147,14 +152,14 @@ nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
}
static int
nfs_proc_lookup(struct inode *dir, const struct qstr *name,
nfs_proc_lookup(struct inode *dir, struct dentry *dentry,
struct nfs_fh *fhandle, struct nfs_fattr *fattr,
struct nfs4_label *label)
{
struct nfs_diropargs arg = {
.fh = NFS_FH(dir),
.name = name->name,
.len = name->len
.name = dentry->d_name.name,
.len = dentry->d_name.len
};
struct nfs_diropok res = {
.fh = fhandle,
@ -166,10 +171,15 @@ nfs_proc_lookup(struct inode *dir, const struct qstr *name,
.rpc_resp = &res,
};
int status;
unsigned short task_flags = 0;
dprintk("NFS call lookup %s\n", name->name);
/* Is this is an attribute revalidation, subject to softreval? */
if (nfs_lookup_is_soft_revalidate(dentry))
task_flags |= RPC_TASK_TIMEOUT;
dprintk("NFS call lookup %pd2\n", dentry);
nfs_fattr_init(fattr);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, task_flags);
dprintk("NFS reply lookup: %d\n", status);
return status;
}
@ -710,7 +720,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
.file_ops = &nfs_file_operations,
.getroot = nfs_proc_get_root,
.submount = nfs_submount,
.try_mount = nfs_try_mount,
.try_get_tree = nfs_try_get_tree,
.getattr = nfs_proc_getattr,
.setattr = nfs_proc_setattr,
.lookup = nfs_proc_lookup,

View File

@ -214,7 +214,7 @@ static void nfs_initiate_read(struct nfs_pgio_header *hdr,
task_setup_data->flags |= swap_flags;
rpc_ops->read_setup(hdr, msg);
trace_nfs_initiate_read(inode, hdr->io_start, hdr->good_bytes);
trace_nfs_initiate_read(hdr);
}
static void
@ -247,8 +247,7 @@ static int nfs_readpage_done(struct rpc_task *task,
return status;
nfs_add_stats(inode, NFSIOS_SERVERREADBYTES, hdr->res.count);
trace_nfs_readpage_done(inode, task->tk_status,
hdr->args.offset, hdr->res.eof);
trace_nfs_readpage_done(task, hdr);
if (task->tk_status == -ESTALE) {
set_bit(NFS_INO_STALE, &NFS_I(inode)->flags);
@ -282,6 +281,8 @@ static void nfs_readpage_retry(struct rpc_task *task,
argp->offset += resp->count;
argp->pgbase += resp->count;
argp->count -= resp->count;
resp->count = 0;
resp->eof = 0;
rpc_restart_call_prepare(task);
}

File diff suppressed because it is too large Load Diff

View File

@ -243,13 +243,24 @@ out:
/* A writeback failed: mark the page as bad, and invalidate the page cache */
static void nfs_set_pageerror(struct address_space *mapping)
{
struct inode *inode = mapping->host;
nfs_zap_mapping(mapping->host, mapping);
/* Force file size revalidation */
spin_lock(&inode->i_lock);
NFS_I(inode)->cache_validity |= NFS_INO_REVAL_FORCED |
NFS_INO_REVAL_PAGECACHE |
NFS_INO_INVALID_SIZE;
spin_unlock(&inode->i_lock);
}
static void nfs_mapping_set_error(struct page *page, int error)
{
struct address_space *mapping = page_file_mapping(page);
SetPageError(page);
mapping_set_error(page_file_mapping(page), error);
mapping_set_error(mapping, error);
nfs_set_pageerror(mapping);
}
/*
@ -592,7 +603,7 @@ release_request:
static void nfs_write_error(struct nfs_page *req, int error)
{
nfs_set_pageerror(page_file_mapping(req->wb_page));
trace_nfs_write_error(req, error);
nfs_mapping_set_error(req->wb_page, error);
nfs_inode_remove_request(req);
nfs_end_page_writeback(req);
@ -998,7 +1009,7 @@ static void nfs_write_completion(struct nfs_pgio_header *hdr)
nfs_list_remove_request(req);
if (test_bit(NFS_IOHDR_ERROR, &hdr->flags) &&
(hdr->good_bytes < bytes)) {
nfs_set_pageerror(page_file_mapping(req->wb_page));
trace_nfs_comp_error(req, hdr->error);
nfs_mapping_set_error(req->wb_page, hdr->error);
goto remove_req;
}
@ -1403,8 +1414,7 @@ static void nfs_initiate_write(struct nfs_pgio_header *hdr,
task_setup_data->priority = priority;
rpc_ops->write_setup(hdr, msg, &task_setup_data->rpc_client);
trace_nfs_initiate_write(hdr->inode, hdr->io_start, hdr->good_bytes,
hdr->args.stable);
trace_nfs_initiate_write(hdr);
}
/* If a nfs_flush_* function fails, it should remove reqs from @head and
@ -1568,8 +1578,7 @@ static int nfs_writeback_done(struct rpc_task *task,
return status;
nfs_add_stats(inode, NFSIOS_SERVERWRITTENBYTES, hdr->res.count);
trace_nfs_writeback_done(inode, task->tk_status,
hdr->args.offset, hdr->res.verf);
trace_nfs_writeback_done(task, hdr);
if (hdr->res.verf->committed < hdr->args.stable &&
task->tk_status >= 0) {
@ -1649,6 +1658,8 @@ static void nfs_writeback_result(struct rpc_task *task,
*/
argp->stable = NFS_FILE_SYNC;
}
resp->count = 0;
resp->verf->committed = 0;
rpc_restart_call_prepare(task);
}
}
@ -1824,11 +1835,12 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata)
/* Call the NFS version-specific code */
NFS_PROTO(data->inode)->commit_done(task, data);
trace_nfs_commit_done(data);
trace_nfs_commit_done(task, data);
}
static void nfs_commit_release_pages(struct nfs_commit_data *data)
{
const struct nfs_writeverf *verf = data->res.verf;
struct nfs_page *req;
int status = data->task.tk_status;
struct nfs_commit_info cinfo;
@ -1847,6 +1859,7 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data)
(long long)req_offset(req));
if (status < 0) {
if (req->wb_page) {
trace_nfs_commit_error(req, status);
nfs_mapping_set_error(req->wb_page, status);
nfs_inode_remove_request(req);
}
@ -1856,7 +1869,8 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data)
/* Okay, COMMIT succeeded, apparently. Check the verifier
* returned by the server against all stored verfs. */
if (!nfs_write_verifier_cmp(&req->wb_verf, &data->verf.verifier)) {
if (verf->committed > NFS_UNSTABLE &&
!nfs_write_verifier_cmp(&req->wb_verf, &verf->verifier)) {
/* We have a match */
if (req->wb_page)
nfs_inode_remove_request(req);

View File

@ -168,6 +168,9 @@ struct nfs_inode {
struct rw_semaphore rmdir_sem;
struct mutex commit_mutex;
/* track last access to cached pages */
unsigned long page_index;
#if IS_ENABLED(CONFIG_NFS_V4)
struct nfs4_cached_acl *nfs4_acl;
/* NFSv4 state */

View File

@ -152,6 +152,7 @@ struct nfs_server {
#define NFS_MOUNT_LOCAL_FLOCK 0x100000
#define NFS_MOUNT_LOCAL_FCNTL 0x200000
#define NFS_MOUNT_SOFTERR 0x400000
#define NFS_MOUNT_SOFTREVAL 0x800000
unsigned int caps; /* server capabilities */
unsigned int rsize; /* read size */

View File

@ -1639,6 +1639,7 @@ struct nfs_subversion;
struct nfs_mount_info;
struct nfs_client_initdata;
struct nfs_pageio_descriptor;
struct fs_context;
/*
* RPC procedure vector for NFSv2/NFSv3 demuxing
@ -1653,16 +1654,14 @@ struct nfs_rpc_ops {
int (*getroot) (struct nfs_server *, struct nfs_fh *,
struct nfs_fsinfo *);
struct vfsmount *(*submount) (struct nfs_server *, struct dentry *,
struct nfs_fh *, struct nfs_fattr *);
struct dentry *(*try_mount) (int, const char *, struct nfs_mount_info *,
struct nfs_subversion *);
int (*submount) (struct fs_context *, struct nfs_server *);
int (*try_get_tree) (struct fs_context *);
int (*getattr) (struct nfs_server *, struct nfs_fh *,
struct nfs_fattr *, struct nfs4_label *,
struct inode *);
int (*setattr) (struct dentry *, struct nfs_fattr *,
struct iattr *);
int (*lookup) (struct inode *, const struct qstr *,
int (*lookup) (struct inode *, struct dentry *,
struct nfs_fh *, struct nfs_fattr *,
struct nfs4_label *);
int (*lookupp) (struct inode *, struct nfs_fh *,
@ -1723,7 +1722,7 @@ struct nfs_rpc_ops {
struct nfs_client *(*init_client) (struct nfs_client *,
const struct nfs_client_initdata *);
void (*free_client) (struct nfs_client *);
struct nfs_server *(*create_server)(struct nfs_mount_info *, struct nfs_subversion *);
struct nfs_server *(*create_server)(struct fs_context *);
struct nfs_server *(*clone_server)(struct nfs_server *, struct nfs_fh *,
struct nfs_fattr *, rpc_authflavor_t);
};

View File

@ -113,7 +113,6 @@ struct rpc_authops {
int (*hash_cred)(struct auth_cred *, unsigned int);
struct rpc_cred * (*lookup_cred)(struct rpc_auth *, struct auth_cred *, int);
struct rpc_cred * (*crcreate)(struct rpc_auth*, struct auth_cred *, int, gfp_t);
int (*list_pseudoflavors)(rpc_authflavor_t *, int);
rpc_authflavor_t (*info2flavor)(struct rpcsec_gss_info *);
int (*flavor2info)(rpc_authflavor_t,
struct rpcsec_gss_info *);
@ -158,7 +157,6 @@ rpc_authflavor_t rpcauth_get_pseudoflavor(rpc_authflavor_t,
struct rpcsec_gss_info *);
int rpcauth_get_gssinfo(rpc_authflavor_t,
struct rpcsec_gss_info *);
int rpcauth_list_flavors(rpc_authflavor_t *, int);
struct rpc_cred * rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred *, int, gfp_t);
void rpcauth_init_cred(struct rpc_cred *, const struct auth_cred *, struct rpc_auth *, const struct rpc_credops *);
struct rpc_cred * rpcauth_lookupcred(struct rpc_auth *, int);

View File

@ -150,9 +150,6 @@ struct gss_api_mech *gss_mech_get_by_name(const char *);
/* Similar, but get by pseudoflavor. */
struct gss_api_mech *gss_mech_get_by_pseudoflavor(u32);
/* Fill in an array with a list of supported pseudoflavors */
int gss_mech_list_pseudoflavors(rpc_authflavor_t *, int);
struct gss_api_mech * gss_mech_get(struct gss_api_mech *);
/* For every successful gss_mech_get or gss_mech_get_by_* call there must be a

View File

@ -729,6 +729,7 @@ TRACE_EVENT(xprtrdma_post_send,
TP_STRUCT__entry(
__field(const void *, req)
__field(const void *, sc)
__field(unsigned int, task_id)
__field(unsigned int, client_id)
__field(int, num_sge)
@ -743,14 +744,15 @@ TRACE_EVENT(xprtrdma_post_send,
__entry->client_id = rqst->rq_task->tk_client ?
rqst->rq_task->tk_client->cl_clid : -1;
__entry->req = req;
__entry->sc = req->rl_sendctx;
__entry->num_sge = req->rl_wr.num_sge;
__entry->signaled = req->rl_wr.send_flags & IB_SEND_SIGNALED;
__entry->status = status;
),
TP_printk("task:%u@%u req=%p (%d SGE%s) %sstatus=%d",
TP_printk("task:%u@%u req=%p sc=%p (%d SGE%s) %sstatus=%d",
__entry->task_id, __entry->client_id,
__entry->req, __entry->num_sge,
__entry->req, __entry->sc, __entry->num_sge,
(__entry->num_sge == 1 ? "" : "s"),
(__entry->signaled ? "signaled " : ""),
__entry->status
@ -849,6 +851,7 @@ TRACE_EVENT(xprtrdma_wc_send,
TP_STRUCT__entry(
__field(const void *, req)
__field(const void *, sc)
__field(unsigned int, unmap_count)
__field(unsigned int, status)
__field(unsigned int, vendor_err)
@ -856,13 +859,14 @@ TRACE_EVENT(xprtrdma_wc_send,
TP_fast_assign(
__entry->req = sc->sc_req;
__entry->sc = sc;
__entry->unmap_count = sc->sc_unmap_count;
__entry->status = wc->status;
__entry->vendor_err = __entry->status ? wc->vendor_err : 0;
),
TP_printk("req=%p, unmapped %u pages: %s (%u/0x%x)",
__entry->req, __entry->unmap_count,
TP_printk("req=%p sc=%p unmapped=%u: %s (%u/0x%x)",
__entry->req, __entry->sc, __entry->unmap_count,
rdma_show_wc_status(__entry->status),
__entry->status, __entry->vendor_err
)

View File

@ -185,6 +185,7 @@ DECLARE_EVENT_CLASS(rpc_task_running,
DEFINE_RPC_RUNNING_EVENT(begin);
DEFINE_RPC_RUNNING_EVENT(run_action);
DEFINE_RPC_RUNNING_EVENT(complete);
DEFINE_RPC_RUNNING_EVENT(signalled);
DEFINE_RPC_RUNNING_EVENT(end);
DECLARE_EVENT_CLASS(rpc_task_queued,

View File

@ -175,7 +175,7 @@ static int rpc_parse_scope_id(struct net *net, const char *buf,
return 0;
len = (buf + buflen) - delim - 1;
p = kstrndup(delim + 1, len, GFP_KERNEL);
p = kmemdup_nul(delim + 1, len, GFP_KERNEL);
if (p) {
u32 scope_id = 0;
struct net_device *dev;

View File

@ -221,55 +221,6 @@ rpcauth_get_gssinfo(rpc_authflavor_t pseudoflavor, struct rpcsec_gss_info *info)
}
EXPORT_SYMBOL_GPL(rpcauth_get_gssinfo);
/**
* rpcauth_list_flavors - discover registered flavors and pseudoflavors
* @array: array to fill in
* @size: size of "array"
*
* Returns the number of array items filled in, or a negative errno.
*
* The returned array is not sorted by any policy. Callers should not
* rely on the order of the items in the returned array.
*/
int
rpcauth_list_flavors(rpc_authflavor_t *array, int size)
{
const struct rpc_authops *ops;
rpc_authflavor_t flavor, pseudos[4];
int i, len, result = 0;
rcu_read_lock();
for (flavor = 0; flavor < RPC_AUTH_MAXFLAVOR; flavor++) {
ops = rcu_dereference(auth_flavors[flavor]);
if (result >= size) {
result = -ENOMEM;
break;
}
if (ops == NULL)
continue;
if (ops->list_pseudoflavors == NULL) {
array[result++] = ops->au_flavor;
continue;
}
len = ops->list_pseudoflavors(pseudos, ARRAY_SIZE(pseudos));
if (len < 0) {
result = len;
break;
}
for (i = 0; i < len; i++) {
if (result >= size) {
result = -ENOMEM;
break;
}
array[result++] = pseudos[i];
}
}
rcu_read_unlock();
return result;
}
EXPORT_SYMBOL_GPL(rpcauth_list_flavors);
struct rpc_auth *
rpcauth_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
{

View File

@ -2118,7 +2118,6 @@ static const struct rpc_authops authgss_ops = {
.hash_cred = gss_hash_cred,
.lookup_cred = gss_lookup_cred,
.crcreate = gss_create_cred,
.list_pseudoflavors = gss_mech_list_pseudoflavors,
.info2flavor = gss_mech_info2flavor,
.flavor2info = gss_mech_flavor2info,
};

View File

@ -219,35 +219,6 @@ gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
return gm;
}
/**
* gss_mech_list_pseudoflavors - Discover registered GSS pseudoflavors
* @array_ptr: array to fill in
* @size: size of "array"
*
* Returns the number of array items filled in, or a negative errno.
*
* The returned array is not sorted by any policy. Callers should not
* rely on the order of the items in the returned array.
*/
int gss_mech_list_pseudoflavors(rpc_authflavor_t *array_ptr, int size)
{
struct gss_api_mech *pos = NULL;
int j, i = 0;
rcu_read_lock();
list_for_each_entry_rcu(pos, &registered_mechs, gm_list) {
for (j = 0; j < pos->gm_pf_num; j++) {
if (i >= size) {
spin_unlock(&registered_mechs_lock);
return -ENOMEM;
}
array_ptr[i++] = pos->gm_pfs[j].pseudoflavor;
}
}
rcu_read_unlock();
return i;
}
/**
* gss_svc_to_pseudoflavor - map a GSS service number to a pseudoflavor
* @gm: GSS mechanism handle

View File

@ -2130,6 +2130,7 @@ call_connect_status(struct rpc_task *task)
case -ENETUNREACH:
case -EHOSTUNREACH:
case -EPIPE:
case -EPROTO:
xprt_conditional_disconnect(task->tk_rqstp->rq_xprt,
task->tk_rqstp->rq_connect_cookie);
if (RPC_IS_SOFTCONN(task))

View File

@ -846,6 +846,8 @@ void rpc_signal_task(struct rpc_task *task)
if (!RPC_IS_ACTIVATED(task))
return;
trace_rpc_task_signalled(task, task->tk_action);
set_bit(RPC_TASK_SIGNALLED, &task->tk_runstate);
smp_mb__after_atomic();
queue = READ_ONCE(task->tk_waitqueue);
@ -949,7 +951,7 @@ static void __rpc_execute(struct rpc_task *task)
* clean up after sleeping on some queue, we don't
* break the loop here, but go around once more.
*/
dprintk("RPC: %5u got signal\n", task->tk_pid);
trace_rpc_task_signalled(task, task->tk_action);
set_bit(RPC_TASK_SIGNALLED, &task->tk_runstate);
task->tk_rpc_status = -ERESTARTSYS;
rpc_exit(task, -ERESTARTSYS);

View File

@ -1079,7 +1079,7 @@ void xdr_enter_page(struct xdr_stream *xdr, unsigned int len)
}
EXPORT_SYMBOL_GPL(xdr_enter_page);
static struct kvec empty_iov = {.iov_base = NULL, .iov_len = 0};
static const struct kvec empty_iov = {.iov_base = NULL, .iov_len = 0};
void
xdr_buf_from_iov(struct kvec *iov, struct xdr_buf *buf)

View File

@ -194,6 +194,10 @@ create_req:
req = rpcrdma_req_create(r_xprt, size, GFP_KERNEL);
if (!req)
return NULL;
if (rpcrdma_req_setup(r_xprt, req)) {
rpcrdma_req_destroy(req);
return NULL;
}
xprt->bc_alloc_count++;
rqst = &req->rl_slot;

View File

@ -50,28 +50,6 @@
# define RPCDBG_FACILITY RPCDBG_TRANS
#endif
/**
* frwr_is_supported - Check if device supports FRWR
* @device: interface adapter to check
*
* Returns true if device supports FRWR, otherwise false
*/
bool frwr_is_supported(struct ib_device *device)
{
struct ib_device_attr *attrs = &device->attrs;
if (!(attrs->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS))
goto out_not_supported;
if (attrs->max_fast_reg_page_list_len == 0)
goto out_not_supported;
return true;
out_not_supported:
pr_info("rpcrdma: 'frwr' mode is not supported by device %s\n",
device->name);
return false;
}
/**
* frwr_release_mr - Destroy one MR
* @mr: MR allocated by frwr_init_mr
@ -170,26 +148,48 @@ out_list_err:
}
/**
* frwr_open - Prepare an endpoint for use with FRWR
* @ia: interface adapter this endpoint will use
* @ep: endpoint to prepare
* frwr_query_device - Prepare a transport for use with FRWR
* @r_xprt: controlling transport instance
* @device: RDMA device to query
*
* On success, sets:
* ep->rep_attr.cap.max_send_wr
* ep->rep_attr.cap.max_recv_wr
* ep->rep_attr
* ep->rep_max_requests
* ia->ri_max_segs
* ia->ri_max_rdma_segs
*
* And these FRWR-related fields:
* ia->ri_max_frwr_depth
* ia->ri_mrtype
*
* On failure, a negative errno is returned.
* Return values:
* On success, returns zero.
* %-EINVAL - the device does not support FRWR memory registration
* %-ENOMEM - the device is not sufficiently capable for NFS/RDMA
*/
int frwr_open(struct rpcrdma_ia *ia, struct rpcrdma_ep *ep)
int frwr_query_device(struct rpcrdma_xprt *r_xprt,
const struct ib_device *device)
{
struct ib_device_attr *attrs = &ia->ri_id->device->attrs;
const struct ib_device_attr *attrs = &device->attrs;
struct rpcrdma_ia *ia = &r_xprt->rx_ia;
struct rpcrdma_ep *ep = &r_xprt->rx_ep;
int max_qp_wr, depth, delta;
unsigned int max_sge;
if (!(attrs->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS) ||
attrs->max_fast_reg_page_list_len == 0) {
pr_err("rpcrdma: 'frwr' mode is not supported by device %s\n",
device->name);
return -EINVAL;
}
max_sge = min_t(unsigned int, attrs->max_send_sge,
RPCRDMA_MAX_SEND_SGES);
if (max_sge < RPCRDMA_MIN_SEND_SGES) {
pr_err("rpcrdma: HCA provides only %u send SGEs\n", max_sge);
return -ENOMEM;
}
ep->rep_attr.cap.max_send_sge = max_sge;
ep->rep_attr.cap.max_recv_sge = 1;
ia->ri_mrtype = IB_MR_TYPE_MEM_REG;
if (attrs->device_cap_flags & IB_DEVICE_SG_GAPS_REG)
@ -199,14 +199,12 @@ int frwr_open(struct rpcrdma_ia *ia, struct rpcrdma_ep *ep)
* capability, but perform optimally when the MRs are not larger
* than a page.
*/
if (attrs->max_sge_rd > 1)
if (attrs->max_sge_rd > RPCRDMA_MAX_HDR_SEGS)
ia->ri_max_frwr_depth = attrs->max_sge_rd;
else
ia->ri_max_frwr_depth = attrs->max_fast_reg_page_list_len;
if (ia->ri_max_frwr_depth > RPCRDMA_MAX_DATA_SEGS)
ia->ri_max_frwr_depth = RPCRDMA_MAX_DATA_SEGS;
dprintk("RPC: %s: max FR page list depth = %u\n",
__func__, ia->ri_max_frwr_depth);
/* Add room for frwr register and invalidate WRs.
* 1. FRWR reg WR for head
@ -230,7 +228,7 @@ int frwr_open(struct rpcrdma_ia *ia, struct rpcrdma_ep *ep)
} while (delta > 0);
}
max_qp_wr = ia->ri_id->device->attrs.max_qp_wr;
max_qp_wr = attrs->max_qp_wr;
max_qp_wr -= RPCRDMA_BACKWARD_WRS;
max_qp_wr -= 1;
if (max_qp_wr < RPCRDMA_MIN_SLOT_TABLE)
@ -241,7 +239,7 @@ int frwr_open(struct rpcrdma_ia *ia, struct rpcrdma_ep *ep)
if (ep->rep_attr.cap.max_send_wr > max_qp_wr) {
ep->rep_max_requests = max_qp_wr / depth;
if (!ep->rep_max_requests)
return -EINVAL;
return -ENOMEM;
ep->rep_attr.cap.max_send_wr = ep->rep_max_requests * depth;
}
ep->rep_attr.cap.max_send_wr += RPCRDMA_BACKWARD_WRS;
@ -250,32 +248,24 @@ int frwr_open(struct rpcrdma_ia *ia, struct rpcrdma_ep *ep)
ep->rep_attr.cap.max_recv_wr += RPCRDMA_BACKWARD_WRS;
ep->rep_attr.cap.max_recv_wr += 1; /* for ib_drain_rq */
ia->ri_max_segs =
ia->ri_max_rdma_segs =
DIV_ROUND_UP(RPCRDMA_MAX_DATA_SEGS, ia->ri_max_frwr_depth);
/* Reply chunks require segments for head and tail buffers */
ia->ri_max_segs += 2;
if (ia->ri_max_segs > RPCRDMA_MAX_HDR_SEGS)
ia->ri_max_segs = RPCRDMA_MAX_HDR_SEGS;
ia->ri_max_rdma_segs += 2;
if (ia->ri_max_rdma_segs > RPCRDMA_MAX_HDR_SEGS)
ia->ri_max_rdma_segs = RPCRDMA_MAX_HDR_SEGS;
/* Ensure the underlying device is capable of conveying the
* largest r/wsize NFS will ask for. This guarantees that
* failing over from one RDMA device to another will not
* break NFS I/O.
*/
if ((ia->ri_max_rdma_segs * ia->ri_max_frwr_depth) < RPCRDMA_MAX_SEGS)
return -ENOMEM;
return 0;
}
/**
* frwr_maxpages - Compute size of largest payload
* @r_xprt: transport
*
* Returns maximum size of an RPC message, in pages.
*
* FRWR mode conveys a list of pages per chunk segment. The
* maximum length of that list is the FRWR page list depth.
*/
size_t frwr_maxpages(struct rpcrdma_xprt *r_xprt)
{
struct rpcrdma_ia *ia = &r_xprt->rx_ia;
return min_t(unsigned int, RPCRDMA_MAX_DATA_SEGS,
(ia->ri_max_segs - 2) * ia->ri_max_frwr_depth);
}
/**
* frwr_map - Register a memory region
* @r_xprt: controlling transport

View File

@ -111,7 +111,7 @@ static unsigned int rpcrdma_max_reply_header_size(unsigned int maxsegs)
*/
void rpcrdma_set_max_header_sizes(struct rpcrdma_xprt *r_xprt)
{
unsigned int maxsegs = r_xprt->rx_ia.ri_max_segs;
unsigned int maxsegs = r_xprt->rx_ia.ri_max_rdma_segs;
struct rpcrdma_ep *ep = &r_xprt->rx_ep;
ep->rep_max_inline_send =
@ -145,7 +145,7 @@ static bool rpcrdma_args_inline(struct rpcrdma_xprt *r_xprt,
remaining -= min_t(unsigned int,
PAGE_SIZE - offset, remaining);
offset = 0;
if (++count > r_xprt->rx_ia.ri_max_send_sges)
if (++count > r_xprt->rx_ep.rep_attr.cap.max_send_sge)
return false;
}
}
@ -580,22 +580,19 @@ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc)
/* Prepare an SGE for the RPC-over-RDMA transport header.
*/
static bool rpcrdma_prepare_hdr_sge(struct rpcrdma_xprt *r_xprt,
static void rpcrdma_prepare_hdr_sge(struct rpcrdma_xprt *r_xprt,
struct rpcrdma_req *req, u32 len)
{
struct rpcrdma_sendctx *sc = req->rl_sendctx;
struct rpcrdma_regbuf *rb = req->rl_rdmabuf;
struct ib_sge *sge = &sc->sc_sges[req->rl_wr.num_sge++];
if (!rpcrdma_regbuf_dma_map(r_xprt, rb))
return false;
sge->addr = rdmab_addr(rb);
sge->length = len;
sge->lkey = rdmab_lkey(rb);
ib_dma_sync_single_for_device(rdmab_device(rb), sge->addr, sge->length,
DMA_TO_DEVICE);
return true;
}
/* The head iovec is straightforward, as it is usually already
@ -836,10 +833,9 @@ inline int rpcrdma_prepare_send_sges(struct rpcrdma_xprt *r_xprt,
req->rl_wr.num_sge = 0;
req->rl_wr.opcode = IB_WR_SEND;
ret = -EIO;
if (!rpcrdma_prepare_hdr_sge(r_xprt, req, hdrlen))
goto out_unmap;
rpcrdma_prepare_hdr_sge(r_xprt, req, hdrlen);
ret = -EIO;
switch (rtype) {
case rpcrdma_noch_pullup:
if (!rpcrdma_prepare_noch_pullup(r_xprt, req, xdr))
@ -909,7 +905,7 @@ rpcrdma_marshal_req(struct rpcrdma_xprt *r_xprt, struct rpc_rqst *rqst)
goto out_err;
*p++ = rqst->rq_xid;
*p++ = rpcrdma_version;
*p++ = cpu_to_be32(r_xprt->rx_buf.rb_max_requests);
*p++ = r_xprt->rx_buf.rb_max_requests;
/* When the ULP employs a GSS flavor that guarantees integrity
* or privacy, direct data placement of individual data items
@ -1480,8 +1476,8 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep)
if (credits == 0)
credits = 1; /* don't deadlock */
else if (credits > buf->rb_max_requests)
credits = buf->rb_max_requests;
else if (credits > r_xprt->rx_ep.rep_max_requests)
credits = r_xprt->rx_ep.rep_max_requests;
if (buf->rb_credits != credits)
rpcrdma_update_cwnd(r_xprt, credits);
rpcrdma_post_recvs(r_xprt, false);

View File

@ -316,7 +316,8 @@ xprt_setup_rdma(struct xprt_create *args)
if (args->addrlen > sizeof(xprt->addr))
return ERR_PTR(-EBADF);
xprt = xprt_alloc(args->net, sizeof(struct rpcrdma_xprt), 0, 0);
xprt = xprt_alloc(args->net, sizeof(struct rpcrdma_xprt), 0,
xprt_rdma_slot_table_entries);
if (!xprt)
return ERR_PTR(-ENOMEM);
@ -358,19 +359,13 @@ xprt_setup_rdma(struct xprt_create *args)
if (rc)
goto out3;
INIT_DELAYED_WORK(&new_xprt->rx_connect_worker,
xprt_rdma_connect_worker);
xprt->max_payload = frwr_maxpages(new_xprt);
if (xprt->max_payload == 0)
goto out4;
xprt->max_payload <<= PAGE_SHIFT;
dprintk("RPC: %s: transport data payload maximum: %zu bytes\n",
__func__, xprt->max_payload);
if (!try_module_get(THIS_MODULE))
goto out4;
INIT_DELAYED_WORK(&new_xprt->rx_connect_worker,
xprt_rdma_connect_worker);
xprt->max_payload = RPCRDMA_MAX_DATA_SEGS << PAGE_SHIFT;
dprintk("RPC: %s: %s:%s\n", __func__,
xprt->address_strings[RPC_DISPLAY_ADDR],
xprt->address_strings[RPC_DISPLAY_PORT]);

View File

@ -74,9 +74,13 @@
/*
* internal functions
*/
static int rpcrdma_sendctxs_create(struct rpcrdma_xprt *r_xprt);
static void rpcrdma_sendctxs_destroy(struct rpcrdma_xprt *r_xprt);
static void rpcrdma_sendctx_put_locked(struct rpcrdma_xprt *r_xprt,
struct rpcrdma_sendctx *sc);
static int rpcrdma_reqs_setup(struct rpcrdma_xprt *r_xprt);
static void rpcrdma_reqs_reset(struct rpcrdma_xprt *r_xprt);
static void rpcrdma_rep_destroy(struct rpcrdma_rep *rep);
static void rpcrdma_reps_unmap(struct rpcrdma_xprt *r_xprt);
static void rpcrdma_mrs_create(struct rpcrdma_xprt *r_xprt);
static void rpcrdma_mrs_destroy(struct rpcrdma_xprt *r_xprt);
@ -174,7 +178,7 @@ rpcrdma_wc_receive(struct ib_cq *cq, struct ib_wc *wc)
return;
out_flushed:
rpcrdma_recv_buffer_put(rep);
rpcrdma_rep_destroy(rep);
}
static void rpcrdma_update_cm_private(struct rpcrdma_xprt *r_xprt,
@ -366,18 +370,6 @@ rpcrdma_ia_open(struct rpcrdma_xprt *xprt)
goto out_err;
}
switch (xprt_rdma_memreg_strategy) {
case RPCRDMA_FRWR:
if (frwr_is_supported(ia->ri_id->device))
break;
/*FALLTHROUGH*/
default:
pr_err("rpcrdma: Device %s does not support memreg mode %d\n",
ia->ri_id->device->name, xprt_rdma_memreg_strategy);
rc = -EINVAL;
goto out_err;
}
return 0;
out_err:
@ -391,6 +383,8 @@ out_err:
*
* Divest transport H/W resources associated with this adapter,
* but allow it to be restored later.
*
* Caller must hold the transport send lock.
*/
void
rpcrdma_ia_remove(struct rpcrdma_ia *ia)
@ -398,8 +392,6 @@ rpcrdma_ia_remove(struct rpcrdma_ia *ia)
struct rpcrdma_xprt *r_xprt = container_of(ia, struct rpcrdma_xprt,
rx_ia);
struct rpcrdma_ep *ep = &r_xprt->rx_ep;
struct rpcrdma_buffer *buf = &r_xprt->rx_buf;
struct rpcrdma_req *req;
/* This is similar to rpcrdma_ep_destroy, but:
* - Don't cancel the connect worker.
@ -422,12 +414,9 @@ rpcrdma_ia_remove(struct rpcrdma_ia *ia)
* mappings and MRs are gone.
*/
rpcrdma_reps_unmap(r_xprt);
list_for_each_entry(req, &buf->rb_allreqs, rl_all) {
rpcrdma_regbuf_dma_unmap(req->rl_rdmabuf);
rpcrdma_regbuf_dma_unmap(req->rl_sendbuf);
rpcrdma_regbuf_dma_unmap(req->rl_recvbuf);
}
rpcrdma_reqs_reset(r_xprt);
rpcrdma_mrs_destroy(r_xprt);
rpcrdma_sendctxs_destroy(r_xprt);
ib_dealloc_pd(ia->ri_pd);
ia->ri_pd = NULL;
@ -470,30 +459,20 @@ int rpcrdma_ep_create(struct rpcrdma_xprt *r_xprt)
struct rpcrdma_ia *ia = &r_xprt->rx_ia;
struct rpcrdma_connect_private *pmsg = &ep->rep_cm_private;
struct ib_cq *sendcq, *recvcq;
unsigned int max_sge;
int rc;
ep->rep_max_requests = xprt_rdma_slot_table_entries;
ep->rep_max_requests = r_xprt->rx_xprt.max_reqs;
ep->rep_inline_send = xprt_rdma_max_inline_write;
ep->rep_inline_recv = xprt_rdma_max_inline_read;
max_sge = min_t(unsigned int, ia->ri_id->device->attrs.max_send_sge,
RPCRDMA_MAX_SEND_SGES);
if (max_sge < RPCRDMA_MIN_SEND_SGES) {
pr_warn("rpcrdma: HCA provides only %d send SGEs\n", max_sge);
return -ENOMEM;
}
ia->ri_max_send_sges = max_sge;
rc = frwr_open(ia, ep);
rc = frwr_query_device(r_xprt, ia->ri_id->device);
if (rc)
return rc;
r_xprt->rx_buf.rb_max_requests = cpu_to_be32(ep->rep_max_requests);
ep->rep_attr.event_handler = rpcrdma_qp_event_handler;
ep->rep_attr.qp_context = ep;
ep->rep_attr.srq = NULL;
ep->rep_attr.cap.max_send_sge = max_sge;
ep->rep_attr.cap.max_recv_sge = 1;
ep->rep_attr.cap.max_inline_data = 0;
ep->rep_attr.sq_sig_type = IB_SIGNAL_REQ_WR;
ep->rep_attr.qp_type = IB_QPT_RC;
@ -716,6 +695,10 @@ retry:
rpcrdma_reset_cwnd(r_xprt);
rpcrdma_post_recvs(r_xprt, true);
rc = rpcrdma_sendctxs_create(r_xprt);
if (rc)
goto out;
rc = rdma_connect(ia->ri_id, &ep->rep_remote_cma);
if (rc)
goto out;
@ -730,6 +713,11 @@ retry:
goto out;
}
rc = rpcrdma_reqs_setup(r_xprt);
if (rc) {
rpcrdma_ep_disconnect(ep, ia);
goto out;
}
rpcrdma_mrs_create(r_xprt);
out:
@ -768,6 +756,7 @@ rpcrdma_ep_disconnect(struct rpcrdma_ep *ep, struct rpcrdma_ia *ia)
rpcrdma_xprt_drain(r_xprt);
rpcrdma_reqs_reset(r_xprt);
rpcrdma_mrs_destroy(r_xprt);
rpcrdma_sendctxs_destroy(r_xprt);
}
/* Fixed-size circular FIFO queue. This implementation is wait-free and
@ -787,20 +776,24 @@ rpcrdma_ep_disconnect(struct rpcrdma_ep *ep, struct rpcrdma_ia *ia)
* queue activity, and rpcrdma_xprt_drain has flushed all remaining
* Send requests.
*/
static void rpcrdma_sendctxs_destroy(struct rpcrdma_buffer *buf)
static void rpcrdma_sendctxs_destroy(struct rpcrdma_xprt *r_xprt)
{
struct rpcrdma_buffer *buf = &r_xprt->rx_buf;
unsigned long i;
if (!buf->rb_sc_ctxs)
return;
for (i = 0; i <= buf->rb_sc_last; i++)
kfree(buf->rb_sc_ctxs[i]);
kfree(buf->rb_sc_ctxs);
buf->rb_sc_ctxs = NULL;
}
static struct rpcrdma_sendctx *rpcrdma_sendctx_create(struct rpcrdma_ia *ia)
static struct rpcrdma_sendctx *rpcrdma_sendctx_create(struct rpcrdma_ep *ep)
{
struct rpcrdma_sendctx *sc;
sc = kzalloc(struct_size(sc, sc_sges, ia->ri_max_send_sges),
sc = kzalloc(struct_size(sc, sc_sges, ep->rep_attr.cap.max_send_sge),
GFP_KERNEL);
if (!sc)
return NULL;
@ -820,21 +813,22 @@ static int rpcrdma_sendctxs_create(struct rpcrdma_xprt *r_xprt)
* the ->send_request call to fail temporarily before too many
* Sends are posted.
*/
i = buf->rb_max_requests + RPCRDMA_MAX_BC_REQUESTS;
dprintk("RPC: %s: allocating %lu send_ctxs\n", __func__, i);
i = r_xprt->rx_ep.rep_max_requests + RPCRDMA_MAX_BC_REQUESTS;
buf->rb_sc_ctxs = kcalloc(i, sizeof(sc), GFP_KERNEL);
if (!buf->rb_sc_ctxs)
return -ENOMEM;
buf->rb_sc_last = i - 1;
for (i = 0; i <= buf->rb_sc_last; i++) {
sc = rpcrdma_sendctx_create(&r_xprt->rx_ia);
sc = rpcrdma_sendctx_create(&r_xprt->rx_ep);
if (!sc)
return -ENOMEM;
buf->rb_sc_ctxs[i] = sc;
}
buf->rb_sc_head = 0;
buf->rb_sc_tail = 0;
return 0;
}
@ -933,7 +927,7 @@ rpcrdma_mrs_create(struct rpcrdma_xprt *r_xprt)
struct rpcrdma_ia *ia = &r_xprt->rx_ia;
unsigned int count;
for (count = 0; count < ia->ri_max_segs; count++) {
for (count = 0; count < ia->ri_max_rdma_segs; count++) {
struct rpcrdma_mr *mr;
int rc;
@ -1005,32 +999,19 @@ struct rpcrdma_req *rpcrdma_req_create(struct rpcrdma_xprt *r_xprt, size_t size,
gfp_t flags)
{
struct rpcrdma_buffer *buffer = &r_xprt->rx_buf;
struct rpcrdma_regbuf *rb;
struct rpcrdma_req *req;
size_t maxhdrsize;
req = kzalloc(sizeof(*req), flags);
if (req == NULL)
goto out1;
/* Compute maximum header buffer size in bytes */
maxhdrsize = rpcrdma_fixed_maxsz + 3 +
r_xprt->rx_ia.ri_max_segs * rpcrdma_readchunk_maxsz;
maxhdrsize *= sizeof(__be32);
rb = rpcrdma_regbuf_alloc(__roundup_pow_of_two(maxhdrsize),
DMA_TO_DEVICE, flags);
if (!rb)
goto out2;
req->rl_rdmabuf = rb;
xdr_buf_init(&req->rl_hdrbuf, rdmab_data(rb), rdmab_length(rb));
req->rl_sendbuf = rpcrdma_regbuf_alloc(size, DMA_TO_DEVICE, flags);
if (!req->rl_sendbuf)
goto out3;
goto out2;
req->rl_recvbuf = rpcrdma_regbuf_alloc(size, DMA_NONE, flags);
if (!req->rl_recvbuf)
goto out4;
goto out3;
INIT_LIST_HEAD(&req->rl_free_mrs);
INIT_LIST_HEAD(&req->rl_registered);
@ -1039,10 +1020,8 @@ struct rpcrdma_req *rpcrdma_req_create(struct rpcrdma_xprt *r_xprt, size_t size,
spin_unlock(&buffer->rb_lock);
return req;
out4:
kfree(req->rl_sendbuf);
out3:
kfree(req->rl_rdmabuf);
kfree(req->rl_sendbuf);
out2:
kfree(req);
out1:
@ -1050,10 +1029,71 @@ out1:
}
/**
* rpcrdma_reqs_reset - Reset all reqs owned by a transport
* rpcrdma_req_setup - Per-connection instance setup of an rpcrdma_req object
* @r_xprt: controlling transport instance
* @req: rpcrdma_req object to set up
*
* ASSUMPTION: the rb_allreqs list is stable for the duration,
* Returns zero on success, and a negative errno on failure.
*/
int rpcrdma_req_setup(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
{
struct rpcrdma_regbuf *rb;
size_t maxhdrsize;
/* Compute maximum header buffer size in bytes */
maxhdrsize = rpcrdma_fixed_maxsz + 3 +
r_xprt->rx_ia.ri_max_rdma_segs * rpcrdma_readchunk_maxsz;
maxhdrsize *= sizeof(__be32);
rb = rpcrdma_regbuf_alloc(__roundup_pow_of_two(maxhdrsize),
DMA_TO_DEVICE, GFP_KERNEL);
if (!rb)
goto out;
if (!__rpcrdma_regbuf_dma_map(r_xprt, rb))
goto out_free;
req->rl_rdmabuf = rb;
xdr_buf_init(&req->rl_hdrbuf, rdmab_data(rb), rdmab_length(rb));
return 0;
out_free:
rpcrdma_regbuf_free(rb);
out:
return -ENOMEM;
}
/* ASSUMPTION: the rb_allreqs list is stable for the duration,
* and thus can be walked without holding rb_lock. Eg. the
* caller is holding the transport send lock to exclude
* device removal or disconnection.
*/
static int rpcrdma_reqs_setup(struct rpcrdma_xprt *r_xprt)
{
struct rpcrdma_buffer *buf = &r_xprt->rx_buf;
struct rpcrdma_req *req;
int rc;
list_for_each_entry(req, &buf->rb_allreqs, rl_all) {
rc = rpcrdma_req_setup(r_xprt, req);
if (rc)
return rc;
}
return 0;
}
static void rpcrdma_req_reset(struct rpcrdma_req *req)
{
/* Credits are valid for only one connection */
req->rl_slot.rq_cong = 0;
rpcrdma_regbuf_free(req->rl_rdmabuf);
req->rl_rdmabuf = NULL;
rpcrdma_regbuf_dma_unmap(req->rl_sendbuf);
rpcrdma_regbuf_dma_unmap(req->rl_recvbuf);
}
/* ASSUMPTION: the rb_allreqs list is stable for the duration,
* and thus can be walked without holding rb_lock. Eg. the
* caller is holding the transport send lock to exclude
* device removal or disconnection.
@ -1063,14 +1103,16 @@ static void rpcrdma_reqs_reset(struct rpcrdma_xprt *r_xprt)
struct rpcrdma_buffer *buf = &r_xprt->rx_buf;
struct rpcrdma_req *req;
list_for_each_entry(req, &buf->rb_allreqs, rl_all) {
/* Credits are valid only for one connection */
req->rl_slot.rq_cong = 0;
}
list_for_each_entry(req, &buf->rb_allreqs, rl_all)
rpcrdma_req_reset(req);
}
static struct rpcrdma_rep *rpcrdma_rep_create(struct rpcrdma_xprt *r_xprt,
bool temp)
/* No locking needed here. This function is called only by the
* Receive completion handler.
*/
static noinline
struct rpcrdma_rep *rpcrdma_rep_create(struct rpcrdma_xprt *r_xprt,
bool temp)
{
struct rpcrdma_rep *rep;
@ -1083,6 +1125,9 @@ static struct rpcrdma_rep *rpcrdma_rep_create(struct rpcrdma_xprt *r_xprt,
if (!rep->rr_rdmabuf)
goto out_free;
if (!rpcrdma_regbuf_dma_map(r_xprt, rep->rr_rdmabuf))
goto out_free_regbuf;
xdr_buf_init(&rep->rr_hdrbuf, rdmab_data(rep->rr_rdmabuf),
rdmab_length(rep->rr_rdmabuf));
rep->rr_cqe.done = rpcrdma_wc_receive;
@ -1095,12 +1140,17 @@ static struct rpcrdma_rep *rpcrdma_rep_create(struct rpcrdma_xprt *r_xprt,
list_add(&rep->rr_all, &r_xprt->rx_buf.rb_all_reps);
return rep;
out_free_regbuf:
rpcrdma_regbuf_free(rep->rr_rdmabuf);
out_free:
kfree(rep);
out:
return NULL;
}
/* No locking needed here. This function is invoked only by the
* Receive completion handler, or during transport shutdown.
*/
static void rpcrdma_rep_destroy(struct rpcrdma_rep *rep)
{
list_del(&rep->rr_all);
@ -1130,8 +1180,10 @@ static void rpcrdma_reps_unmap(struct rpcrdma_xprt *r_xprt)
struct rpcrdma_buffer *buf = &r_xprt->rx_buf;
struct rpcrdma_rep *rep;
list_for_each_entry(rep, &buf->rb_all_reps, rr_all)
list_for_each_entry(rep, &buf->rb_all_reps, rr_all) {
rpcrdma_regbuf_dma_unmap(rep->rr_rdmabuf);
rep->rr_temp = true;
}
}
static void rpcrdma_reps_destroy(struct rpcrdma_buffer *buf)
@ -1153,7 +1205,6 @@ int rpcrdma_buffer_create(struct rpcrdma_xprt *r_xprt)
struct rpcrdma_buffer *buf = &r_xprt->rx_buf;
int i, rc;
buf->rb_max_requests = r_xprt->rx_ep.rep_max_requests;
buf->rb_bc_srv_max_requests = 0;
spin_lock_init(&buf->rb_lock);
INIT_LIST_HEAD(&buf->rb_mrs);
@ -1165,7 +1216,7 @@ int rpcrdma_buffer_create(struct rpcrdma_xprt *r_xprt)
INIT_LIST_HEAD(&buf->rb_all_reps);
rc = -ENOMEM;
for (i = 0; i < buf->rb_max_requests; i++) {
for (i = 0; i < r_xprt->rx_xprt.max_reqs; i++) {
struct rpcrdma_req *req;
req = rpcrdma_req_create(r_xprt, RPCRDMA_V1_DEF_INLINE_SIZE * 2,
@ -1177,10 +1228,6 @@ int rpcrdma_buffer_create(struct rpcrdma_xprt *r_xprt)
init_llist_head(&buf->rb_free_reps);
rc = rpcrdma_sendctxs_create(r_xprt);
if (rc)
goto out;
return 0;
out:
rpcrdma_buffer_destroy(buf);
@ -1256,7 +1303,6 @@ static void rpcrdma_mrs_destroy(struct rpcrdma_xprt *r_xprt)
void
rpcrdma_buffer_destroy(struct rpcrdma_buffer *buf)
{
rpcrdma_sendctxs_destroy(buf);
rpcrdma_reps_destroy(buf);
while (!list_empty(&buf->rb_send_bufs)) {
@ -1497,7 +1543,7 @@ void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, bool temp)
{
struct rpcrdma_buffer *buf = &r_xprt->rx_buf;
struct rpcrdma_ep *ep = &r_xprt->rx_ep;
struct ib_recv_wr *i, *wr, *bad_wr;
struct ib_recv_wr *wr, *bad_wr;
struct rpcrdma_rep *rep;
int needed, count, rc;
@ -1524,23 +1570,15 @@ void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, bool temp)
if (!rep)
break;
trace_xprtrdma_post_recv(rep);
rep->rr_recv_wr.next = wr;
wr = &rep->rr_recv_wr;
--needed;
++count;
}
if (!wr)
goto out;
for (i = wr; i; i = i->next) {
rep = container_of(i, struct rpcrdma_rep, rr_recv_wr);
if (!rpcrdma_regbuf_dma_map(r_xprt, rep->rr_rdmabuf))
goto release_wrs;
trace_xprtrdma_post_recv(rep);
++count;
}
rc = ib_post_recv(r_xprt->rx_ia.ri_id->qp, wr,
(const struct ib_recv_wr **)&bad_wr);
out:
@ -1557,11 +1595,4 @@ out:
}
ep->rep_receive_count += count;
return;
release_wrs:
for (i = wr; i;) {
rep = container_of(i, struct rpcrdma_rep, rr_recv_wr);
i = i->next;
rpcrdma_recv_buffer_put(rep);
}
}

View File

@ -71,9 +71,8 @@ struct rpcrdma_ia {
struct rdma_cm_id *ri_id;
struct ib_pd *ri_pd;
int ri_async_rc;
unsigned int ri_max_segs;
unsigned int ri_max_rdma_segs;
unsigned int ri_max_frwr_depth;
unsigned int ri_max_send_sges;
bool ri_implicit_roundup;
enum ib_mr_type ri_mrtype;
unsigned long ri_flags;
@ -99,7 +98,7 @@ struct rpcrdma_ep {
wait_queue_head_t rep_connect_wait;
struct rpcrdma_connect_private rep_cm_private;
struct rdma_conn_param rep_remote_cma;
unsigned int rep_max_requests; /* set by /proc */
unsigned int rep_max_requests; /* depends on device */
unsigned int rep_inline_send; /* negotiated */
unsigned int rep_inline_recv; /* negotiated */
int rep_receive_count;
@ -373,7 +372,7 @@ struct rpcrdma_buffer {
struct llist_head rb_free_reps;
u32 rb_max_requests;
__be32 rb_max_requests;
u32 rb_credits; /* most recent credit grant */
u32 rb_bc_srv_max_requests;
@ -479,6 +478,7 @@ void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, bool temp);
*/
struct rpcrdma_req *rpcrdma_req_create(struct rpcrdma_xprt *r_xprt, size_t size,
gfp_t flags);
int rpcrdma_req_setup(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req);
void rpcrdma_req_destroy(struct rpcrdma_req *req);
int rpcrdma_buffer_create(struct rpcrdma_xprt *);
void rpcrdma_buffer_destroy(struct rpcrdma_buffer *);
@ -535,12 +535,11 @@ rpcrdma_data_dir(bool writing)
/* Memory registration calls xprtrdma/frwr_ops.c
*/
bool frwr_is_supported(struct ib_device *device);
void frwr_reset(struct rpcrdma_req *req);
int frwr_open(struct rpcrdma_ia *ia, struct rpcrdma_ep *ep);
int frwr_query_device(struct rpcrdma_xprt *r_xprt,
const struct ib_device *device);
int frwr_init_mr(struct rpcrdma_ia *ia, struct rpcrdma_mr *mr);
void frwr_release_mr(struct rpcrdma_mr *mr);
size_t frwr_maxpages(struct rpcrdma_xprt *r_xprt);
struct rpcrdma_mr_seg *frwr_map(struct rpcrdma_xprt *r_xprt,
struct rpcrdma_mr_seg *seg,
int nsegs, bool writing, __be32 xid,
@ -583,7 +582,6 @@ static inline void rpcrdma_set_xdrlen(struct xdr_buf *xdr, size_t len)
/* RPC/RDMA module init - xprtrdma/transport.c
*/
extern unsigned int xprt_rdma_slot_table_entries;
extern unsigned int xprt_rdma_max_inline_read;
extern unsigned int xprt_rdma_max_inline_write;
void xprt_rdma_format_addresses(struct rpc_xprt *xprt, struct sockaddr *sap);