Commit 14abcb0b authored by Trond Myklebust's avatar Trond Myklebust

NFSv4: Fix up mirror allocation

There are a number of callers of nfs_pageio_complete() that want to
continue using the nfs_pageio_descriptor without needing to call
nfs_pageio_init() again. Examples include nfs_pageio_resend() and
nfs_pageio_cond_complete().

The problem is that nfs_pageio_complete() also calls
nfs_pageio_cleanup_mirroring(), which frees up the array of mirrors.
This can lead to writeback errors, in the next call to
nfs_pageio_setup_mirroring().

Fix by simply moving the allocation of the mirrors to
nfs_pageio_setup_mirroring().

Link: https://bugzilla.kernel.org/show_bug.cgi?id=196709Reported-by: default avatarJianhongYin <yin-jianhong@163.com>
Cc: stable@vger.kernel.org # 4.0+
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@primarydata.com>
parent ef954844
...@@ -714,9 +714,6 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc, ...@@ -714,9 +714,6 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
int io_flags, int io_flags,
gfp_t gfp_flags) gfp_t gfp_flags)
{ {
struct nfs_pgio_mirror *new;
int i;
desc->pg_moreio = 0; desc->pg_moreio = 0;
desc->pg_inode = inode; desc->pg_inode = inode;
desc->pg_ops = pg_ops; desc->pg_ops = pg_ops;
...@@ -732,21 +729,9 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc, ...@@ -732,21 +729,9 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
desc->pg_mirror_count = 1; desc->pg_mirror_count = 1;
desc->pg_mirror_idx = 0; desc->pg_mirror_idx = 0;
if (pg_ops->pg_get_mirror_count) { desc->pg_mirrors_dynamic = NULL;
/* until we have a request, we don't have an lseg and no desc->pg_mirrors = desc->pg_mirrors_static;
* idea how many mirrors there will be */ nfs_pageio_mirror_init(&desc->pg_mirrors[0], bsize);
new = kcalloc(NFS_PAGEIO_DESCRIPTOR_MIRROR_MAX,
sizeof(struct nfs_pgio_mirror), gfp_flags);
desc->pg_mirrors_dynamic = new;
desc->pg_mirrors = new;
for (i = 0; i < NFS_PAGEIO_DESCRIPTOR_MIRROR_MAX; i++)
nfs_pageio_mirror_init(&desc->pg_mirrors[i], bsize);
} else {
desc->pg_mirrors_dynamic = NULL;
desc->pg_mirrors = desc->pg_mirrors_static;
nfs_pageio_mirror_init(&desc->pg_mirrors[0], bsize);
}
} }
EXPORT_SYMBOL_GPL(nfs_pageio_init); EXPORT_SYMBOL_GPL(nfs_pageio_init);
...@@ -865,32 +850,52 @@ static int nfs_generic_pg_pgios(struct nfs_pageio_descriptor *desc) ...@@ -865,32 +850,52 @@ static int nfs_generic_pg_pgios(struct nfs_pageio_descriptor *desc)
return ret; return ret;
} }
static struct nfs_pgio_mirror *
nfs_pageio_alloc_mirrors(struct nfs_pageio_descriptor *desc,
unsigned int mirror_count)
{
struct nfs_pgio_mirror *ret;
unsigned int i;
kfree(desc->pg_mirrors_dynamic);
desc->pg_mirrors_dynamic = NULL;
if (mirror_count == 1)
return desc->pg_mirrors_static;
ret = kmalloc_array(mirror_count, sizeof(*ret), GFP_NOFS);
if (ret != NULL) {
for (i = 0; i < mirror_count; i++)
nfs_pageio_mirror_init(&ret[i], desc->pg_bsize);
desc->pg_mirrors_dynamic = ret;
}
return ret;
}
/* /*
* nfs_pageio_setup_mirroring - determine if mirroring is to be used * nfs_pageio_setup_mirroring - determine if mirroring is to be used
* by calling the pg_get_mirror_count op * by calling the pg_get_mirror_count op
*/ */
static int nfs_pageio_setup_mirroring(struct nfs_pageio_descriptor *pgio, static void nfs_pageio_setup_mirroring(struct nfs_pageio_descriptor *pgio,
struct nfs_page *req) struct nfs_page *req)
{ {
int mirror_count = 1; unsigned int mirror_count = 1;
if (!pgio->pg_ops->pg_get_mirror_count) if (pgio->pg_ops->pg_get_mirror_count)
return 0; mirror_count = pgio->pg_ops->pg_get_mirror_count(pgio, req);
if (mirror_count == pgio->pg_mirror_count || pgio->pg_error < 0)
mirror_count = pgio->pg_ops->pg_get_mirror_count(pgio, req); return;
if (pgio->pg_error < 0)
return pgio->pg_error;
if (!mirror_count || mirror_count > NFS_PAGEIO_DESCRIPTOR_MIRROR_MAX)
return -EINVAL;
if (WARN_ON_ONCE(!pgio->pg_mirrors_dynamic)) if (!mirror_count || mirror_count > NFS_PAGEIO_DESCRIPTOR_MIRROR_MAX) {
return -EINVAL; pgio->pg_error = -EINVAL;
return;
}
pgio->pg_mirrors = nfs_pageio_alloc_mirrors(pgio, mirror_count);
if (pgio->pg_mirrors == NULL) {
pgio->pg_error = -ENOMEM;
pgio->pg_mirrors = pgio->pg_mirrors_static;
mirror_count = 1;
}
pgio->pg_mirror_count = mirror_count; pgio->pg_mirror_count = mirror_count;
return 0;
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment