Skip to content

Commit 303a780

Browse files
committed
NFSv4.2: Rework scratch handling for READ_PLUS (again)
I found that the read code might send multiple requests using the same nfs_pgio_header, but nfs4_proc_read_setup() is only called once. This is how we ended up occasionally double-freeing the scratch buffer, but also means we set a NULL pointer but non-zero length to the xdr scratch buffer. This results in an oops the first time decoding needs to copy something to scratch, which frequently happens when decoding READ_PLUS hole segments. I fix this by moving scratch handling into the pageio read code. I provide a function to allocate scratch space for decoding read replies, and free the scratch buffer when the nfs_pgio_header is freed. Fixes: fbd2a05 (NFSv4.2: Rework scratch handling for READ_PLUS) Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
1 parent 8d18f6c commit 303a780

5 files changed

Lines changed: 14 additions & 13 deletions

File tree

fs/nfs/internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,7 @@ extern const struct nfs_pgio_completion_ops nfs_async_read_completion_ops;
493493
extern void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio,
494494
struct inode *inode, bool force_mds,
495495
const struct nfs_pgio_completion_ops *compl_ops);
496+
extern bool nfs_read_alloc_scratch(struct nfs_pgio_header *hdr, size_t size);
496497
extern int nfs_read_add_folio(struct nfs_pageio_descriptor *pgio,
497498
struct nfs_open_context *ctx,
498499
struct folio *folio);

fs/nfs/nfs42.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* more? Need to consider not to pre-alloc too much for a compound.
1414
*/
1515
#define PNFS_LAYOUTSTATS_MAXDEV (4)
16+
#define READ_PLUS_SCRATCH_SIZE (16)
1617

1718
/* nfs4.2proc.c */
1819
#ifdef CONFIG_NFS_V4_2

fs/nfs/nfs42xdr.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1433,7 +1433,7 @@ static int nfs4_xdr_dec_read_plus(struct rpc_rqst *rqstp,
14331433
struct compound_hdr hdr;
14341434
int status;
14351435

1436-
xdr_set_scratch_buffer(xdr, res->scratch, sizeof(res->scratch));
1436+
xdr_set_scratch_buffer(xdr, res->scratch, READ_PLUS_SCRATCH_SIZE);
14371437

14381438
status = decode_compound_hdr(xdr, &hdr);
14391439
if (status)

fs/nfs/nfs4proc.c

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5438,18 +5438,8 @@ static bool nfs4_read_plus_not_supported(struct rpc_task *task,
54385438
return false;
54395439
}
54405440

5441-
static inline void nfs4_read_plus_scratch_free(struct nfs_pgio_header *hdr)
5442-
{
5443-
if (hdr->res.scratch) {
5444-
kfree(hdr->res.scratch);
5445-
hdr->res.scratch = NULL;
5446-
}
5447-
}
5448-
54495441
static int nfs4_read_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
54505442
{
5451-
nfs4_read_plus_scratch_free(hdr);
5452-
54535443
if (!nfs4_sequence_done(task, &hdr->res.seq_res))
54545444
return -EAGAIN;
54555445
if (nfs4_read_stateid_changed(task, &hdr->args))
@@ -5469,8 +5459,7 @@ static bool nfs42_read_plus_support(struct nfs_pgio_header *hdr,
54695459
/* Note: We don't use READ_PLUS with pNFS yet */
54705460
if (nfs_server_capable(hdr->inode, NFS_CAP_READ_PLUS) && !hdr->ds_clp) {
54715461
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ_PLUS];
5472-
hdr->res.scratch = kmalloc(32, GFP_KERNEL);
5473-
return hdr->res.scratch != NULL;
5462+
return nfs_read_alloc_scratch(hdr, READ_PLUS_SCRATCH_SIZE);
54745463
}
54755464
return false;
54765465
}

fs/nfs/read.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ static struct nfs_pgio_header *nfs_readhdr_alloc(void)
4747

4848
static void nfs_readhdr_free(struct nfs_pgio_header *rhdr)
4949
{
50+
if (rhdr->res.scratch != NULL)
51+
kfree(rhdr->res.scratch);
5052
kmem_cache_free(nfs_rdata_cachep, rhdr);
5153
}
5254

@@ -108,6 +110,14 @@ void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio)
108110
}
109111
EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds);
110112

113+
bool nfs_read_alloc_scratch(struct nfs_pgio_header *hdr, size_t size)
114+
{
115+
WARN_ON(hdr->res.scratch != NULL);
116+
hdr->res.scratch = kmalloc(size, GFP_KERNEL);
117+
return hdr->res.scratch != NULL;
118+
}
119+
EXPORT_SYMBOL_GPL(nfs_read_alloc_scratch);
120+
111121
static void nfs_readpage_release(struct nfs_page *req, int error)
112122
{
113123
struct folio *folio = nfs_page_to_folio(req);

0 commit comments

Comments
 (0)