NFS CLient Updates for Linux 6.6

New Features:
   * Enable the NFS v4.2 READ_PLUS operation by default
 
 Stable Fixes:
   * NFSv4/pnfs: minor fix for cleanup path in nfs4_get_device_info
   * NFS: Fix a potential data corruption
 
 Bugfixes:
   * Fix various READ_PLUS issues including:
     * smatch warnings
     * xdr size calculations
     * scratch buffer handling
     * 32bit / highmem xdr page handling
   * Fix checkpatch errors in file.c
   * Fix redundant readdir request after an EOF
   * Fix handling of COPY ERR_OFFLOAD_NO_REQ
   * Fix assignment of xprtdata.cred
 
 Cleanups:
   * Remove unused xprtrdma function declarations
   * Clean up an integer overflow check to avoid a warning
   * Clean up #includes in dns_resolve.c
   * Clean up nfs4_get_device_info so we don't pass a NULL pointer to __free_page()
   * Clean up sunrpc TCP socket timeout configuration
   * Guard against READDIR loops when entry names are too long
   * Use EXCHID4_FLAG_USE_PNFS_DS for DS servers
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEnZ5MQTpR7cLU7KEp18tUv7ClQOsFAmTwzwYACgkQ18tUv7Cl
 QOtIhBAA+BOh7MB6yjlyctFxABJiXz2x2Dehxy7Ox15LfnyStqQAUEpk35CXWvjC
 iNxpZJ486+WrzM76WGEaRbECK9nTQLK1yacR3V1zpnDwHWIJA6VHN6qU4JAfSMu7
 XbhWkHWry6d7PXhvqHlaiYvPX2pF39wUzfH+vLlzS2QLIkpT6LnG0zVRJTQvLCmq
 zE5xD+NCQ1Dpo9VnouuzW7VVfm532hI7GQNrpo0E0vWKgeQD+/fOpDu23MW8A1Ua
 ZgVMAc7vScgDZH8/20Ze5PH4jAEB4gwEIzjreQlIXr7Tf+mE7qn435lgOuvdMQCW
 eHhdNriZ2X6HMLhNFFpup8bkRKGCCTooTHC1W66n9CuxIAuVT5DNwBbakpagHSZf
 J4ho81hEgBfc5zppISVjV6eFK4brM0rF9AliaIw8r/qGcMmO1CILi9tLGiheiJcT
 LuId7U2sE/vfIa6SiBt7rx37/MkrgLlAgjpk4dCRJQW+gKVBi09sMGnDlgaRvCZz
 T0WCsK4DgI9q2rScpwJYJbNWbC2Q8qUtYWW9LSRvwhbNdm/VbRnEHWA7eOwqqm8r
 KkkF4chyoTJqpnF3SjxT/lyFk6GwsD+wXafOmEeuFA6Si3dHDU9i3aUf+cCXhwRI
 uUzCUHYcCKnv4QVGPuAbIdxMgueNCuLoeWgTClVlqidv7GRyz7Y=
 =rjmq
 -----END PGP SIGNATURE-----

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

Pull NFS client updates from Anna Schumaker:
 "New Features:
   - Enable the NFS v4.2 READ_PLUS operation by default

  Stable Fixes:
   - NFSv4/pnfs: minor fix for cleanup path in nfs4_get_device_info
   - NFS: Fix a potential data corruption

  Bugfixes:
   - Fix various READ_PLUS issues including:
      - smatch warnings
      - xdr size calculations
      - scratch buffer handling
      - 32bit / highmem xdr page handling
   - Fix checkpatch errors in file.c
   - Fix redundant readdir request after an EOF
   - Fix handling of COPY ERR_OFFLOAD_NO_REQ
   - Fix assignment of xprtdata.cred

  Cleanups:
   - Remove unused xprtrdma function declarations
   - Clean up an integer overflow check to avoid a warning
   - Clean up #includes in dns_resolve.c
   - Clean up nfs4_get_device_info so we don't pass a NULL pointer
     to __free_page()
   - Clean up sunrpc TCP socket timeout configuration
   - Guard against READDIR loops when entry names are too long
   - Use EXCHID4_FLAG_USE_PNFS_DS for DS servers"

* tag 'nfs-for-6.6-1' of git://git.linux-nfs.org/projects/anna/linux-nfs: (22 commits)
  pNFS: Fix assignment of xprtdata.cred
  NFSv4.2: fix handling of COPY ERR_OFFLOAD_NO_REQ
  NFS: Guard against READDIR loop when entry names exceed MAXNAMELEN
  NFSv4.1: use EXCHGID4_FLAG_USE_PNFS_DS for DS server
  NFS/pNFS: Set the connect timeout for the pNFS flexfiles driver
  SUNRPC: Don't override connect timeouts in rpc_clnt_add_xprt()
  SUNRPC: Allow specification of TCP client connect timeout at setup
  SUNRPC: Refactor and simplify connect timeout
  SUNRPC: Set the TCP_SYNCNT to match the socket timeout
  NFS: Fix a potential data corruption
  nfs: fix redundant readdir request after get eof
  nfs/blocklayout: Use the passed in gfp flags
  filemap: Fix errors in file.c
  NFSv4/pnfs: minor fix for cleanup path in nfs4_get_device_info
  NFS: Move common includes outside ifdef
  SUNRPC: clean up integer overflow check
  xprtrdma: Remove unused function declaration rpcrdma_bc_post_recv()
  NFS: Enable the READ_PLUS operation by default
  SUNRPC: kmap() the xdr pages during decode
  NFSv4.2: Rework scratch handling for READ_PLUS (again)
  ...
This commit is contained in:
Linus Torvalds 2023-08-31 15:36:41 -07:00
commit 99d99825fc
27 changed files with 171 additions and 63 deletions

View file

@ -209,8 +209,6 @@ config NFS_DISABLE_UDP_SUPPORT
config NFS_V4_2_READ_PLUS config NFS_V4_2_READ_PLUS
bool "NFS: Enable support for the NFSv4.2 READ_PLUS operation" bool "NFS: Enable support for the NFSv4.2 READ_PLUS operation"
depends on NFS_V4_2 depends on NFS_V4_2
default n default y
help help
This is intended for developers only. The READ_PLUS operation has Choose Y here to enable use of the NFS v4.2 READ_PLUS operation.
been shown to have issues under specific conditions and should not
be used in production.

View file

@ -404,7 +404,7 @@ bl_parse_concat(struct nfs_server *server, struct pnfs_block_dev *d,
int ret, i; int ret, i;
d->children = kcalloc(v->concat.volumes_count, d->children = kcalloc(v->concat.volumes_count,
sizeof(struct pnfs_block_dev), GFP_KERNEL); sizeof(struct pnfs_block_dev), gfp_mask);
if (!d->children) if (!d->children)
return -ENOMEM; return -ENOMEM;
@ -433,7 +433,7 @@ bl_parse_stripe(struct nfs_server *server, struct pnfs_block_dev *d,
int ret, i; int ret, i;
d->children = kcalloc(v->stripe.volumes_count, d->children = kcalloc(v->stripe.volumes_count,
sizeof(struct pnfs_block_dev), GFP_KERNEL); sizeof(struct pnfs_block_dev), gfp_mask);
if (!d->children) if (!d->children)
return -ENOMEM; return -ENOMEM;

View file

@ -517,6 +517,8 @@ int nfs_create_rpc_client(struct nfs_client *clp,
.authflavor = flavor, .authflavor = flavor,
.cred = cl_init->cred, .cred = cl_init->cred,
.xprtsec = cl_init->xprtsec, .xprtsec = cl_init->xprtsec,
.connect_timeout = cl_init->connect_timeout,
.reconnect_timeout = cl_init->reconnect_timeout,
}; };
if (test_bit(NFS_CS_DISCRTRY, &clp->cl_flags)) if (test_bit(NFS_CS_DISCRTRY, &clp->cl_flags))

View file

@ -1089,6 +1089,17 @@ static void nfs_do_filldir(struct nfs_readdir_descriptor *desc,
for (i = desc->cache_entry_index; i < array->size; i++) { for (i = desc->cache_entry_index; i < array->size; i++) {
struct nfs_cache_array_entry *ent; struct nfs_cache_array_entry *ent;
/*
* nfs_readdir_handle_cache_misses return force clear at
* (cache_misses > NFS_READDIR_CACHE_MISS_THRESHOLD) for
* readdir heuristic, NFS_READDIR_CACHE_MISS_THRESHOLD + 1
* entries need be emitted here.
*/
if (first_emit && i > NFS_READDIR_CACHE_MISS_THRESHOLD + 2) {
desc->eob = true;
break;
}
ent = &array->array[i]; ent = &array->array[i];
if (!dir_emit(desc->ctx, ent->name, ent->name_len, if (!dir_emit(desc->ctx, ent->name, ent->name_len,
nfs_compat_user_ino64(ent->ino), ent->d_type)) { nfs_compat_user_ino64(ent->ino), ent->d_type)) {
@ -1107,10 +1118,6 @@ static void nfs_do_filldir(struct nfs_readdir_descriptor *desc,
desc->ctx->pos = desc->dir_cookie; desc->ctx->pos = desc->dir_cookie;
else else
desc->ctx->pos++; desc->ctx->pos++;
if (first_emit && i > NFS_READDIR_CACHE_MISS_THRESHOLD + 1) {
desc->eob = true;
break;
}
} }
if (array->folio_is_eof) if (array->folio_is_eof)
desc->eof = !desc->eob; desc->eof = !desc->eob;

View file

@ -472,13 +472,31 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, struct iov_iter *iter,
return result; return result;
} }
static void nfs_direct_add_page_head(struct list_head *list,
struct nfs_page *req)
{
struct nfs_page *head = req->wb_head;
if (!list_empty(&head->wb_list) || !nfs_lock_request(head))
return;
if (!list_empty(&head->wb_list)) {
nfs_unlock_request(head);
return;
}
list_add(&head->wb_list, list);
kref_get(&head->wb_kref);
kref_get(&head->wb_kref);
}
static void nfs_direct_join_group(struct list_head *list, struct inode *inode) static void nfs_direct_join_group(struct list_head *list, struct inode *inode)
{ {
struct nfs_page *req, *subreq; struct nfs_page *req, *subreq;
list_for_each_entry(req, list, wb_list) { list_for_each_entry(req, list, wb_list) {
if (req->wb_head != req) if (req->wb_head != req) {
nfs_direct_add_page_head(&req->wb_list, req);
continue; continue;
}
subreq = req->wb_this_page; subreq = req->wb_this_page;
if (subreq == req) if (subreq == req)
continue; continue;

View file

@ -7,14 +7,16 @@
* Resolves DNS hostnames into valid ip addresses * Resolves DNS hostnames into valid ip addresses
*/ */
#ifdef CONFIG_NFS_USE_KERNEL_DNS
#include <linux/module.h> #include <linux/module.h>
#include <linux/sunrpc/clnt.h> #include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/addr.h> #include <linux/sunrpc/addr.h>
#include <linux/dns_resolver.h>
#include "dns_resolve.h" #include "dns_resolve.h"
#ifdef CONFIG_NFS_USE_KERNEL_DNS
#include <linux/dns_resolver.h>
ssize_t nfs_dns_resolve_name(struct net *net, char *name, size_t namelen, ssize_t nfs_dns_resolve_name(struct net *net, char *name, size_t namelen,
struct sockaddr_storage *ss, size_t salen) struct sockaddr_storage *ss, size_t salen)
{ {
@ -35,7 +37,6 @@ ssize_t nfs_dns_resolve_name(struct net *net, char *name, size_t namelen,
#else #else
#include <linux/module.h>
#include <linux/hash.h> #include <linux/hash.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/kmod.h> #include <linux/kmod.h>
@ -43,15 +44,12 @@ ssize_t nfs_dns_resolve_name(struct net *net, char *name, size_t namelen,
#include <linux/socket.h> #include <linux/socket.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/inet.h> #include <linux/inet.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/addr.h>
#include <linux/sunrpc/cache.h> #include <linux/sunrpc/cache.h>
#include <linux/sunrpc/svcauth.h> #include <linux/sunrpc/svcauth.h>
#include <linux/sunrpc/rpc_pipe_fs.h> #include <linux/sunrpc/rpc_pipe_fs.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include "nfs4_fs.h" #include "nfs4_fs.h"
#include "dns_resolve.h"
#include "cache_lib.h" #include "cache_lib.h"
#include "netns.h" #include "netns.h"

View file

@ -200,7 +200,7 @@ nfs_file_splice_read(struct file *in, loff_t *ppos, struct pipe_inode_info *pipe
EXPORT_SYMBOL_GPL(nfs_file_splice_read); EXPORT_SYMBOL_GPL(nfs_file_splice_read);
int int
nfs_file_mmap(struct file * file, struct vm_area_struct * vma) nfs_file_mmap(struct file *file, struct vm_area_struct *vma)
{ {
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
int status; int status;

View file

@ -82,6 +82,8 @@ struct nfs_client_initdata {
const struct rpc_timeout *timeparms; const struct rpc_timeout *timeparms;
const struct cred *cred; const struct cred *cred;
struct xprtsec_parms xprtsec; struct xprtsec_parms xprtsec;
unsigned long connect_timeout;
unsigned long reconnect_timeout;
}; };
/* /*
@ -493,6 +495,7 @@ extern const struct nfs_pgio_completion_ops nfs_async_read_completion_ops;
extern void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, extern void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio,
struct inode *inode, bool force_mds, struct inode *inode, bool force_mds,
const struct nfs_pgio_completion_ops *compl_ops); const struct nfs_pgio_completion_ops *compl_ops);
extern bool nfs_read_alloc_scratch(struct nfs_pgio_header *hdr, size_t size);
extern int nfs_read_add_folio(struct nfs_pageio_descriptor *pgio, extern int nfs_read_add_folio(struct nfs_pageio_descriptor *pgio,
struct nfs_open_context *ctx, struct nfs_open_context *ctx,
struct folio *folio); struct folio *folio);

View file

@ -949,7 +949,7 @@ int nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
error = decode_filename_inline(xdr, &entry->name, &entry->len); error = decode_filename_inline(xdr, &entry->name, &entry->len);
if (unlikely(error)) if (unlikely(error))
return -EAGAIN; return error == -ENAMETOOLONG ? -ENAMETOOLONG : -EAGAIN;
/* /*
* The type (size and byte order) of nfscookie isn't defined in * The type (size and byte order) of nfscookie isn't defined in

View file

@ -86,6 +86,7 @@ struct nfs_client *nfs3_set_ds_client(struct nfs_server *mds_srv,
int ds_proto, unsigned int ds_timeo, unsigned int ds_retrans) int ds_proto, unsigned int ds_timeo, unsigned int ds_retrans)
{ {
struct rpc_timeout ds_timeout; struct rpc_timeout ds_timeout;
unsigned long connect_timeout = ds_timeo * (ds_retrans + 1) * HZ / 10;
struct nfs_client *mds_clp = mds_srv->nfs_client; struct nfs_client *mds_clp = mds_srv->nfs_client;
struct nfs_client_initdata cl_init = { struct nfs_client_initdata cl_init = {
.addr = ds_addr, .addr = ds_addr,
@ -98,6 +99,8 @@ struct nfs_client *nfs3_set_ds_client(struct nfs_server *mds_srv,
.timeparms = &ds_timeout, .timeparms = &ds_timeout,
.cred = mds_srv->cred, .cred = mds_srv->cred,
.xprtsec = mds_clp->cl_xprtsec, .xprtsec = mds_clp->cl_xprtsec,
.connect_timeout = connect_timeout,
.reconnect_timeout = connect_timeout,
}; };
struct nfs_client *clp; struct nfs_client *clp;
char buf[INET6_ADDRSTRLEN + 1]; char buf[INET6_ADDRSTRLEN + 1];

View file

@ -1991,7 +1991,7 @@ int nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
error = decode_inline_filename3(xdr, &entry->name, &entry->len); error = decode_inline_filename3(xdr, &entry->name, &entry->len);
if (unlikely(error)) if (unlikely(error))
return -EAGAIN; return error == -ENAMETOOLONG ? -ENAMETOOLONG : -EAGAIN;
error = decode_cookie3(xdr, &new_cookie); error = decode_cookie3(xdr, &new_cookie);
if (unlikely(error)) if (unlikely(error))

View file

@ -13,6 +13,7 @@
* more? Need to consider not to pre-alloc too much for a compound. * more? Need to consider not to pre-alloc too much for a compound.
*/ */
#define PNFS_LAYOUTSTATS_MAXDEV (4) #define PNFS_LAYOUTSTATS_MAXDEV (4)
#define READ_PLUS_SCRATCH_SIZE (16)
/* nfs4.2proc.c */ /* nfs4.2proc.c */
#ifdef CONFIG_NFS_V4_2 #ifdef CONFIG_NFS_V4_2

View file

@ -471,8 +471,9 @@ ssize_t nfs42_proc_copy(struct file *src, loff_t pos_src,
continue; continue;
} }
break; break;
} else if (err == -NFS4ERR_OFFLOAD_NO_REQS && !args.sync) { } else if (err == -NFS4ERR_OFFLOAD_NO_REQS &&
args.sync = true; args.sync != res.synchronous) {
args.sync = res.synchronous;
dst_exception.retry = 1; dst_exception.retry = 1;
continue; continue;
} else if ((err == -ESTALE || } else if ((err == -ESTALE ||

View file

@ -54,10 +54,16 @@
(1 /* data_content4 */ + \ (1 /* data_content4 */ + \
2 /* data_info4.di_offset */ + \ 2 /* data_info4.di_offset */ + \
1 /* data_info4.di_length */) 1 /* data_info4.di_length */)
#define NFS42_READ_PLUS_HOLE_SEGMENT_SIZE \
(1 /* data_content4 */ + \
2 /* data_info4.di_offset */ + \
2 /* data_info4.di_length */)
#define READ_PLUS_SEGMENT_SIZE_DIFF (NFS42_READ_PLUS_HOLE_SEGMENT_SIZE - \
NFS42_READ_PLUS_DATA_SEGMENT_SIZE)
#define decode_read_plus_maxsz (op_decode_hdr_maxsz + \ #define decode_read_plus_maxsz (op_decode_hdr_maxsz + \
1 /* rpr_eof */ + \ 1 /* rpr_eof */ + \
1 /* rpr_contents count */ + \ 1 /* rpr_contents count */ + \
NFS42_READ_PLUS_DATA_SEGMENT_SIZE) NFS42_READ_PLUS_HOLE_SEGMENT_SIZE)
#define encode_seek_maxsz (op_encode_hdr_maxsz + \ #define encode_seek_maxsz (op_encode_hdr_maxsz + \
encode_stateid_maxsz + \ encode_stateid_maxsz + \
2 /* offset */ + \ 2 /* offset */ + \
@ -617,8 +623,8 @@ static void nfs4_xdr_enc_read_plus(struct rpc_rqst *req,
encode_putfh(xdr, args->fh, &hdr); encode_putfh(xdr, args->fh, &hdr);
encode_read_plus(xdr, args, &hdr); encode_read_plus(xdr, args, &hdr);
rpc_prepare_reply_pages(req, args->pages, args->pgbase, rpc_prepare_reply_pages(req, args->pages, args->pgbase, args->count,
args->count, hdr.replen); hdr.replen - READ_PLUS_SEGMENT_SIZE_DIFF);
encode_nops(&hdr); encode_nops(&hdr);
} }
@ -1056,13 +1062,12 @@ static int decode_read_plus(struct xdr_stream *xdr, struct nfs_pgio_res *res)
res->eof = be32_to_cpup(p++); res->eof = be32_to_cpup(p++);
segments = be32_to_cpup(p++); segments = be32_to_cpup(p++);
if (segments == 0) if (segments == 0)
return status; return 0;
segs = kmalloc_array(segments, sizeof(*segs), GFP_KERNEL); segs = kmalloc_array(segments, sizeof(*segs), GFP_KERNEL);
if (!segs) if (!segs)
return -ENOMEM; return -ENOMEM;
status = -EIO;
for (i = 0; i < segments; i++) { for (i = 0; i < segments; i++) {
status = decode_read_plus_segment(xdr, &segs[i]); status = decode_read_plus_segment(xdr, &segs[i]);
if (status < 0) if (status < 0)
@ -1428,7 +1433,7 @@ static int nfs4_xdr_dec_read_plus(struct rpc_rqst *rqstp,
struct compound_hdr hdr; struct compound_hdr hdr;
int status; int status;
xdr_set_scratch_buffer(xdr, res->scratch, sizeof(res->scratch)); xdr_set_scratch_buffer(xdr, res->scratch, READ_PLUS_SCRATCH_SIZE);
status = decode_compound_hdr(xdr, &hdr); status = decode_compound_hdr(xdr, &hdr);
if (status) if (status)

View file

@ -232,6 +232,8 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
__set_bit(NFS_CS_DISCRTRY, &clp->cl_flags); __set_bit(NFS_CS_DISCRTRY, &clp->cl_flags);
__set_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags); __set_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags);
if (test_bit(NFS_CS_DS, &cl_init->init_flags))
__set_bit(NFS_CS_DS, &clp->cl_flags);
/* /*
* Set up the connection to the server before we add add to the * Set up the connection to the server before we add add to the
* global list. * global list.
@ -1007,6 +1009,7 @@ struct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv,
if (mds_srv->flags & NFS_MOUNT_NORESVPORT) if (mds_srv->flags & NFS_MOUNT_NORESVPORT)
__set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags); __set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
__set_bit(NFS_CS_DS, &cl_init.init_flags);
/* /*
* Set an authflavor equual to the MDS value. Use the MDS nfs_client * Set an authflavor equual to the MDS value. Use the MDS nfs_client
* cl_ipaddr so as to use the same EXCHANGE_ID co_ownerid as the MDS * cl_ipaddr so as to use the same EXCHANGE_ID co_ownerid as the MDS

View file

@ -5438,18 +5438,8 @@ static bool nfs4_read_plus_not_supported(struct rpc_task *task,
return false; return false;
} }
static inline void nfs4_read_plus_scratch_free(struct nfs_pgio_header *hdr)
{
if (hdr->res.scratch) {
kfree(hdr->res.scratch);
hdr->res.scratch = NULL;
}
}
static int nfs4_read_done(struct rpc_task *task, struct nfs_pgio_header *hdr) static int nfs4_read_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
{ {
nfs4_read_plus_scratch_free(hdr);
if (!nfs4_sequence_done(task, &hdr->res.seq_res)) if (!nfs4_sequence_done(task, &hdr->res.seq_res))
return -EAGAIN; return -EAGAIN;
if (nfs4_read_stateid_changed(task, &hdr->args)) if (nfs4_read_stateid_changed(task, &hdr->args))
@ -5469,8 +5459,7 @@ static bool nfs42_read_plus_support(struct nfs_pgio_header *hdr,
/* Note: We don't use READ_PLUS with pNFS yet */ /* Note: We don't use READ_PLUS with pNFS yet */
if (nfs_server_capable(hdr->inode, NFS_CAP_READ_PLUS) && !hdr->ds_clp) { if (nfs_server_capable(hdr->inode, NFS_CAP_READ_PLUS) && !hdr->ds_clp) {
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ_PLUS]; msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ_PLUS];
hdr->res.scratch = kmalloc(32, GFP_KERNEL); return nfs_read_alloc_scratch(hdr, READ_PLUS_SCRATCH_SIZE);
return hdr->res.scratch != NULL;
} }
return false; return false;
} }
@ -8798,6 +8787,8 @@ nfs4_run_exchange_id(struct nfs_client *clp, const struct cred *cred,
#ifdef CONFIG_NFS_V4_1_MIGRATION #ifdef CONFIG_NFS_V4_1_MIGRATION
calldata->args.flags |= EXCHGID4_FLAG_SUPP_MOVED_MIGR; calldata->args.flags |= EXCHGID4_FLAG_SUPP_MOVED_MIGR;
#endif #endif
if (test_bit(NFS_CS_DS, &clp->cl_flags))
calldata->args.flags |= EXCHGID4_FLAG_USE_PNFS_DS;
msg.rpc_argp = &calldata->args; msg.rpc_argp = &calldata->args;
msg.rpc_resp = &calldata->res; msg.rpc_resp = &calldata->res;
task_setup_data.callback_data = calldata; task_setup_data.callback_data = calldata;
@ -8875,6 +8866,8 @@ static int _nfs4_proc_exchange_id(struct nfs_client *clp, const struct cred *cre
/* Save the EXCHANGE_ID verifier session trunk tests */ /* Save the EXCHANGE_ID verifier session trunk tests */
memcpy(clp->cl_confirm.data, argp->verifier.data, memcpy(clp->cl_confirm.data, argp->verifier.data,
sizeof(clp->cl_confirm.data)); sizeof(clp->cl_confirm.data));
if (resp->flags & EXCHGID4_FLAG_USE_PNFS_DS)
set_bit(NFS_CS_DS, &clp->cl_flags);
out: out:
trace_nfs4_exchange_id(clp, status); trace_nfs4_exchange_id(clp, status);
rpc_put_task(task); rpc_put_task(task);

View file

@ -154,7 +154,7 @@ nfs4_get_device_info(struct nfs_server *server,
set_bit(NFS_DEVICEID_NOCACHE, &d->flags); set_bit(NFS_DEVICEID_NOCACHE, &d->flags);
out_free_pages: out_free_pages:
for (i = 0; i < max_pages; i++) while (--i >= 0)
__free_page(pages[i]); __free_page(pages[i]);
kfree(pages); kfree(pages);
out_free_pdev: out_free_pdev:

View file

@ -852,6 +852,7 @@ static int _nfs4_pnfs_v3_ds_connect(struct nfs_server *mds_srv,
{ {
struct nfs_client *clp = ERR_PTR(-EIO); struct nfs_client *clp = ERR_PTR(-EIO);
struct nfs4_pnfs_ds_addr *da; struct nfs4_pnfs_ds_addr *da;
unsigned long connect_timeout = timeo * (retrans + 1) * HZ / 10;
int status = 0; int status = 0;
dprintk("--> %s DS %s\n", __func__, ds->ds_remotestr); dprintk("--> %s DS %s\n", __func__, ds->ds_remotestr);
@ -870,6 +871,8 @@ static int _nfs4_pnfs_v3_ds_connect(struct nfs_server *mds_srv,
.dstaddr = (struct sockaddr *)&da->da_addr, .dstaddr = (struct sockaddr *)&da->da_addr,
.addrlen = da->da_addrlen, .addrlen = da->da_addrlen,
.servername = clp->cl_hostname, .servername = clp->cl_hostname,
.connect_timeout = connect_timeout,
.reconnect_timeout = connect_timeout,
}; };
if (da->da_transport != clp->cl_proto) if (da->da_transport != clp->cl_proto)
@ -943,7 +946,7 @@ static int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv,
* Test this address for session trunking and * Test this address for session trunking and
* add as an alias * add as an alias
*/ */
xprtdata.cred = nfs4_get_clid_cred(clp), xprtdata.cred = nfs4_get_clid_cred(clp);
rpc_clnt_add_xprt(clp->cl_rpcclient, &xprt_args, rpc_clnt_add_xprt(clp->cl_rpcclient, &xprt_args,
rpc_clnt_setup_test_and_add_xprt, rpc_clnt_setup_test_and_add_xprt,
&rpcdata); &rpcdata);

View file

@ -47,6 +47,8 @@ static struct nfs_pgio_header *nfs_readhdr_alloc(void)
static void nfs_readhdr_free(struct nfs_pgio_header *rhdr) static void nfs_readhdr_free(struct nfs_pgio_header *rhdr)
{ {
if (rhdr->res.scratch != NULL)
kfree(rhdr->res.scratch);
kmem_cache_free(nfs_rdata_cachep, rhdr); kmem_cache_free(nfs_rdata_cachep, rhdr);
} }
@ -108,6 +110,14 @@ void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio)
} }
EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds); EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds);
bool nfs_read_alloc_scratch(struct nfs_pgio_header *hdr, size_t size)
{
WARN_ON(hdr->res.scratch != NULL);
hdr->res.scratch = kmalloc(size, GFP_KERNEL);
return hdr->res.scratch != NULL;
}
EXPORT_SYMBOL_GPL(nfs_read_alloc_scratch);
static void nfs_readpage_release(struct nfs_page *req, int error) static void nfs_readpage_release(struct nfs_page *req, int error)
{ {
struct folio *folio = nfs_page_to_folio(req); struct folio *folio = nfs_page_to_folio(req);

View file

@ -148,6 +148,8 @@ struct rpc_create_args {
const struct cred *cred; const struct cred *cred;
unsigned int max_connect; unsigned int max_connect;
struct xprtsec_parms xprtsec; struct xprtsec_parms xprtsec;
unsigned long connect_timeout;
unsigned long reconnect_timeout;
}; };
struct rpc_add_xprt_test { struct rpc_add_xprt_test {

View file

@ -226,6 +226,7 @@ struct xdr_stream {
struct kvec *iov; /* pointer to the current kvec */ struct kvec *iov; /* pointer to the current kvec */
struct kvec scratch; /* Scratch buffer */ struct kvec scratch; /* Scratch buffer */
struct page **page_ptr; /* pointer to the current page */ struct page **page_ptr; /* pointer to the current page */
void *page_kaddr; /* kmapped address of the current page */
unsigned int nwords; /* Remaining decode buffer length */ unsigned int nwords; /* Remaining decode buffer length */
struct rpc_rqst *rqst; /* For debugging */ struct rpc_rqst *rqst; /* For debugging */
@ -257,6 +258,7 @@ extern void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf,
__be32 *p, struct rpc_rqst *rqst); __be32 *p, struct rpc_rqst *rqst);
extern void xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf, extern void xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf,
struct page **pages, unsigned int len); struct page **pages, unsigned int len);
extern void xdr_finish_decode(struct xdr_stream *xdr);
extern __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes); extern __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes);
extern unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len); extern unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len);
extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len); extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len);
@ -777,9 +779,7 @@ xdr_stream_decode_uint32_array(struct xdr_stream *xdr,
if (unlikely(xdr_stream_decode_u32(xdr, &len) < 0)) if (unlikely(xdr_stream_decode_u32(xdr, &len) < 0))
return -EBADMSG; return -EBADMSG;
if (len > SIZE_MAX / sizeof(*p)) p = xdr_inline_decode(xdr, size_mul(len, sizeof(*p)));
return -EBADMSG;
p = xdr_inline_decode(xdr, len * sizeof(*p));
if (unlikely(!p)) if (unlikely(!p))
return -EBADMSG; return -EBADMSG;
if (array == NULL) if (array == NULL)

View file

@ -351,6 +351,8 @@ struct xprt_create {
struct rpc_xprt_switch *bc_xps; struct rpc_xprt_switch *bc_xps;
unsigned int flags; unsigned int flags;
struct xprtsec_parms xprtsec; struct xprtsec_parms xprtsec;
unsigned long connect_timeout;
unsigned long reconnect_timeout;
}; };
struct xprt_class { struct xprt_class {

View file

@ -534,6 +534,8 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
.servername = args->servername, .servername = args->servername,
.bc_xprt = args->bc_xprt, .bc_xprt = args->bc_xprt,
.xprtsec = args->xprtsec, .xprtsec = args->xprtsec,
.connect_timeout = args->connect_timeout,
.reconnect_timeout = args->reconnect_timeout,
}; };
char servername[48]; char servername[48];
struct rpc_clnt *clnt; struct rpc_clnt *clnt;
@ -2602,6 +2604,7 @@ call_decode(struct rpc_task *task)
case 0: case 0:
task->tk_action = rpc_exit_task; task->tk_action = rpc_exit_task;
task->tk_status = rpcauth_unwrap_resp(task, &xdr); task->tk_status = rpcauth_unwrap_resp(task, &xdr);
xdr_finish_decode(&xdr);
return; return;
case -EAGAIN: case -EAGAIN:
task->tk_status = 0; task->tk_status = 0;
@ -3069,6 +3072,11 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt,
} }
xprt->resvport = resvport; xprt->resvport = resvport;
xprt->reuseport = reuseport; xprt->reuseport = reuseport;
if (xprtargs->connect_timeout)
connect_timeout = xprtargs->connect_timeout;
if (xprtargs->reconnect_timeout)
reconnect_timeout = xprtargs->reconnect_timeout;
if (xprt->ops->set_connect_timeout != NULL) if (xprt->ops->set_connect_timeout != NULL)
xprt->ops->set_connect_timeout(xprt, xprt->ops->set_connect_timeout(xprt,
connect_timeout, connect_timeout,

View file

@ -1394,6 +1394,8 @@ svc_process_common(struct svc_rqst *rqstp)
rc = process.dispatch(rqstp); rc = process.dispatch(rqstp);
if (procp->pc_release) if (procp->pc_release)
procp->pc_release(rqstp); procp->pc_release(rqstp);
xdr_finish_decode(xdr);
if (!rc) if (!rc)
goto dropit; goto dropit;
if (rqstp->rq_auth_stat != rpc_auth_ok) if (rqstp->rq_auth_stat != rpc_auth_ok)

View file

@ -1338,6 +1338,14 @@ static unsigned int xdr_set_tail_base(struct xdr_stream *xdr,
return xdr_set_iov(xdr, buf->tail, base, len); return xdr_set_iov(xdr, buf->tail, base, len);
} }
static void xdr_stream_unmap_current_page(struct xdr_stream *xdr)
{
if (xdr->page_kaddr) {
kunmap_local(xdr->page_kaddr);
xdr->page_kaddr = NULL;
}
}
static unsigned int xdr_set_page_base(struct xdr_stream *xdr, static unsigned int xdr_set_page_base(struct xdr_stream *xdr,
unsigned int base, unsigned int len) unsigned int base, unsigned int len)
{ {
@ -1355,12 +1363,18 @@ static unsigned int xdr_set_page_base(struct xdr_stream *xdr,
if (len > maxlen) if (len > maxlen)
len = maxlen; len = maxlen;
xdr_stream_unmap_current_page(xdr);
xdr_stream_page_set_pos(xdr, base); xdr_stream_page_set_pos(xdr, base);
base += xdr->buf->page_base; base += xdr->buf->page_base;
pgnr = base >> PAGE_SHIFT; pgnr = base >> PAGE_SHIFT;
xdr->page_ptr = &xdr->buf->pages[pgnr]; xdr->page_ptr = &xdr->buf->pages[pgnr];
kaddr = page_address(*xdr->page_ptr);
if (PageHighMem(*xdr->page_ptr)) {
xdr->page_kaddr = kmap_local_page(*xdr->page_ptr);
kaddr = xdr->page_kaddr;
} else
kaddr = page_address(*xdr->page_ptr);
pgoff = base & ~PAGE_MASK; pgoff = base & ~PAGE_MASK;
xdr->p = (__be32*)(kaddr + pgoff); xdr->p = (__be32*)(kaddr + pgoff);
@ -1414,6 +1428,7 @@ void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p,
struct rpc_rqst *rqst) struct rpc_rqst *rqst)
{ {
xdr->buf = buf; xdr->buf = buf;
xdr->page_kaddr = NULL;
xdr_reset_scratch_buffer(xdr); xdr_reset_scratch_buffer(xdr);
xdr->nwords = XDR_QUADLEN(buf->len); xdr->nwords = XDR_QUADLEN(buf->len);
if (xdr_set_iov(xdr, buf->head, 0, buf->len) == 0 && if (xdr_set_iov(xdr, buf->head, 0, buf->len) == 0 &&
@ -1446,6 +1461,16 @@ void xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf,
} }
EXPORT_SYMBOL_GPL(xdr_init_decode_pages); EXPORT_SYMBOL_GPL(xdr_init_decode_pages);
/**
* xdr_finish_decode - Clean up the xdr_stream after decoding data.
* @xdr: pointer to xdr_stream struct
*/
void xdr_finish_decode(struct xdr_stream *xdr)
{
xdr_stream_unmap_current_page(xdr);
}
EXPORT_SYMBOL(xdr_finish_decode);
static __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) static __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
{ {
unsigned int nwords = XDR_QUADLEN(nbytes); unsigned int nwords = XDR_QUADLEN(nbytes);

View file

@ -593,7 +593,6 @@ void xprt_rdma_cleanup(void);
int xprt_rdma_bc_setup(struct rpc_xprt *, unsigned int); int xprt_rdma_bc_setup(struct rpc_xprt *, unsigned int);
size_t xprt_rdma_bc_maxpayload(struct rpc_xprt *); size_t xprt_rdma_bc_maxpayload(struct rpc_xprt *);
unsigned int xprt_rdma_bc_max_slots(struct rpc_xprt *); unsigned int xprt_rdma_bc_max_slots(struct rpc_xprt *);
int rpcrdma_bc_post_recv(struct rpcrdma_xprt *, unsigned int);
void rpcrdma_bc_receive_call(struct rpcrdma_xprt *, struct rpcrdma_rep *); void rpcrdma_bc_receive_call(struct rpcrdma_xprt *, struct rpcrdma_rep *);
int xprt_rdma_bc_send_reply(struct rpc_rqst *rqst); int xprt_rdma_bc_send_reply(struct rpc_rqst *rqst);
void xprt_rdma_bc_free_rqst(struct rpc_rqst *); void xprt_rdma_bc_free_rqst(struct rpc_rqst *);

View file

@ -2237,9 +2237,13 @@ static void xs_tcp_set_socket_timeouts(struct rpc_xprt *xprt,
struct socket *sock) struct socket *sock)
{ {
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt); struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
struct net *net = sock_net(sock->sk);
unsigned long connect_timeout;
unsigned long syn_retries;
unsigned int keepidle; unsigned int keepidle;
unsigned int keepcnt; unsigned int keepcnt;
unsigned int timeo; unsigned int timeo;
unsigned long t;
spin_lock(&xprt->transport_lock); spin_lock(&xprt->transport_lock);
keepidle = DIV_ROUND_UP(xprt->timeout->to_initval, HZ); keepidle = DIV_ROUND_UP(xprt->timeout->to_initval, HZ);
@ -2257,6 +2261,35 @@ static void xs_tcp_set_socket_timeouts(struct rpc_xprt *xprt,
/* TCP user timeout (see RFC5482) */ /* TCP user timeout (see RFC5482) */
tcp_sock_set_user_timeout(sock->sk, timeo); tcp_sock_set_user_timeout(sock->sk, timeo);
/* Connect timeout */
connect_timeout = max_t(unsigned long,
DIV_ROUND_UP(xprt->connect_timeout, HZ), 1);
syn_retries = max_t(unsigned long,
READ_ONCE(net->ipv4.sysctl_tcp_syn_retries), 1);
for (t = 0; t <= syn_retries && (1UL << t) < connect_timeout; t++)
;
if (t <= syn_retries)
tcp_sock_set_syncnt(sock->sk, t - 1);
}
static void xs_tcp_do_set_connect_timeout(struct rpc_xprt *xprt,
unsigned long connect_timeout)
{
struct sock_xprt *transport =
container_of(xprt, struct sock_xprt, xprt);
struct rpc_timeout to;
unsigned long initval;
memcpy(&to, xprt->timeout, sizeof(to));
/* Arbitrary lower limit */
initval = max_t(unsigned long, connect_timeout, XS_TCP_INIT_REEST_TO);
to.to_initval = initval;
to.to_maxval = initval;
to.to_retries = 0;
memcpy(&transport->tcp_timeout, &to, sizeof(transport->tcp_timeout));
xprt->timeout = &transport->tcp_timeout;
xprt->connect_timeout = connect_timeout;
} }
static void xs_tcp_set_connect_timeout(struct rpc_xprt *xprt, static void xs_tcp_set_connect_timeout(struct rpc_xprt *xprt,
@ -2264,25 +2297,12 @@ static void xs_tcp_set_connect_timeout(struct rpc_xprt *xprt,
unsigned long reconnect_timeout) unsigned long reconnect_timeout)
{ {
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt); struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
struct rpc_timeout to;
unsigned long initval;
spin_lock(&xprt->transport_lock); spin_lock(&xprt->transport_lock);
if (reconnect_timeout < xprt->max_reconnect_timeout) if (reconnect_timeout < xprt->max_reconnect_timeout)
xprt->max_reconnect_timeout = reconnect_timeout; xprt->max_reconnect_timeout = reconnect_timeout;
if (connect_timeout < xprt->connect_timeout) { if (connect_timeout < xprt->connect_timeout)
memcpy(&to, xprt->timeout, sizeof(to)); xs_tcp_do_set_connect_timeout(xprt, connect_timeout);
initval = DIV_ROUND_UP(connect_timeout, to.to_retries + 1);
/* Arbitrary lower limit */
if (initval < XS_TCP_INIT_REEST_TO << 1)
initval = XS_TCP_INIT_REEST_TO << 1;
to.to_initval = initval;
to.to_maxval = initval;
memcpy(&transport->tcp_timeout, &to,
sizeof(transport->tcp_timeout));
xprt->timeout = &transport->tcp_timeout;
xprt->connect_timeout = connect_timeout;
}
set_bit(XPRT_SOCK_UPD_TIMEOUT, &transport->sock_state); set_bit(XPRT_SOCK_UPD_TIMEOUT, &transport->sock_state);
spin_unlock(&xprt->transport_lock); spin_unlock(&xprt->transport_lock);
} }
@ -3335,8 +3355,13 @@ static struct rpc_xprt *xs_setup_tcp(struct xprt_create *args)
xprt->timeout = &xs_tcp_default_timeout; xprt->timeout = &xs_tcp_default_timeout;
xprt->max_reconnect_timeout = xprt->timeout->to_maxval; xprt->max_reconnect_timeout = xprt->timeout->to_maxval;
if (args->reconnect_timeout)
xprt->max_reconnect_timeout = args->reconnect_timeout;
xprt->connect_timeout = xprt->timeout->to_initval * xprt->connect_timeout = xprt->timeout->to_initval *
(xprt->timeout->to_retries + 1); (xprt->timeout->to_retries + 1);
if (args->connect_timeout)
xs_tcp_do_set_connect_timeout(xprt, args->connect_timeout);
INIT_WORK(&transport->recv_worker, xs_stream_data_receive_workfn); INIT_WORK(&transport->recv_worker, xs_stream_data_receive_workfn);
INIT_WORK(&transport->error_worker, xs_error_handle); INIT_WORK(&transport->error_worker, xs_error_handle);