pnfs/blocklayout: Fix device leaks on parse failure

bl_parse_concat() and bl_parse_stripe() allocate a child device array and
then parse each child in turn.  If parsing a child fails, the failed child is
not counted in nr_children and the parent may be left with a children array
that bl_free_device() will not release when nr_children is zero.

Release the failed child and the already parsed children before returning the
error.  Also make bl_free_device() release the child array whenever the
children pointer is set, so that partially initialised concat or stripe
devices are cleaned up correctly.

bl_parse_scsi() can also fail after assigning d->bdev_file and dropping the
file reference.  Clear the pointer after fput() so that an outer cleanup path
does not put it again.

Fixes: 5c83746a0c ("pnfs/blocklayout: in-kernel GETDEVICEINFO XDR parsing")
Signed-off-by: ZhangGuoDong <zhangguodong@kylinos.cn>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
This commit is contained in:
ZhangGuoDong 2026-06-25 11:20:38 +08:00 committed by Trond Myklebust
parent d05c2007b3
commit c056f817e4

View File

@ -85,15 +85,17 @@ bl_free_device(struct pnfs_block_dev *dev)
{
bl_unregister_dev(dev);
if (dev->nr_children) {
if (dev->children) {
int i;
for (i = 0; i < dev->nr_children; i++)
bl_free_device(&dev->children[i]);
kfree(dev->children);
} else {
if (dev->bdev_file)
fput(dev->bdev_file);
dev->children = NULL;
dev->nr_children = 0;
} else if (dev->bdev_file) {
fput(dev->bdev_file);
dev->bdev_file = NULL;
}
}
@ -437,6 +439,7 @@ bl_parse_scsi(struct nfs_server *server, struct pnfs_block_dev *d,
out_blkdev_put:
fput(d->bdev_file);
d->bdev_file = NULL;
return error;
}
@ -472,8 +475,11 @@ bl_parse_concat(struct nfs_server *server, struct pnfs_block_dev *d,
for (i = 0; i < v->concat.volumes_count; i++) {
ret = bl_parse_deviceid(server, &d->children[i],
volumes, v->concat.volumes[i], gfp_mask);
if (ret)
if (ret) {
bl_free_device(&d->children[i]);
bl_free_device(d);
return ret;
}
d->nr_children++;
d->children[i].start += len;
@ -501,8 +507,11 @@ bl_parse_stripe(struct nfs_server *server, struct pnfs_block_dev *d,
for (i = 0; i < v->stripe.volumes_count; i++) {
ret = bl_parse_deviceid(server, &d->children[i],
volumes, v->stripe.volumes[i], gfp_mask);
if (ret)
if (ret) {
bl_free_device(&d->children[i]);
bl_free_device(d);
return ret;
}
d->nr_children++;
len += d->children[i].len;