[PATCH v2] nvdimm/pmem: Release gendisk on probe failure

From: Hemanth Selam

Date: Mon Aug 31 2026 - 00:35:19 EST


pmem namespace probe allocates a gendisk early, fills it in, and only hands
it to devres once device_add_disk() has succeeded. Until that handover the
probe itself owns the disk, and the error paths in between release it
through a common label.

Initializing the namespace's badblocks state sits between those two points
but returns directly on failure, so the gendisk, its queue and its bdev
inode are left allocated with nothing to free them. The devres release
action has not been registered at that point, so unbinding or destroying
the namespace does not reclaim them either, and they stay allocated for the
rest of the boot. Nothing is user visible, as the disk was never added, so
the only effect is the memory.

devm_init_badblocks() fails only if a one page GFP_KERNEL allocation fails,
which needs memory exhaustion, so the path is hard to reach in practice.

Release the gendisk through the existing cleanup path on this failure.

Fixes: 0caeef63e6d2 ("libnvdimm: Add a poison list and export badblocks")
Assisted-by: Cursor:claude-opus-5
Signed-off-by: Hemanth Selam <hemanth.selam@xxxxxxxxx>
---
Changes in v2, all from Alison's review of v1:
- Retitled, and the changelog rewritten as background, problem, impact and
resolution rather than a walk through the error labels.
- Fixes: re-derived. b95f5f4391fa only replaced one failing call with
another; the early return that leaks the disk was added a week earlier by
0caeef63e6d2, when the disk was already allocated by alloc_disk_node()
and the only put_disk() was in the detach path.
- Says what the leak's fate is: the devres release action is registered
only after device_add_disk(), so nothing reclaims the disk on unbind.
- The object counts are gone, see below for why.

How this was found: reviewing pmem's probe error paths with an AI assistant,
hence the Assisted-by tag. The finding was then confirmed against the code
and measured as follows.

How it was tested: this is a unit test of an error path that needs memory
exhaustion to reach, not a user visible scenario. The badblocks init in
pmem_attach_disk() was forced to fail with a throwaway kernel change, a
namespace was bound 32 times in a guest with memmap=256M!1G, and
disk_release() calls were counted with ftrace's function profiler:

without the patch with the patch
disk_release() calls 0 of 32 32 of 32
bdev_cache active 48 -> 72 (+24) 48 -> 48 (+0)

v1 quoted the bdev_cache delta, which is what drew the question about 64
attempts and 60 objects: slab accounting does not track this one for one,
as the +24 above shows for 32 probes. disk_release() does, so that is the
number reported here and the slab figure is only context.

drivers/nvdimm/pmem.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
index 30a51c365ce8..648fc7d66063 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -563,8 +563,10 @@ static int pmem_attach_disk(struct device *dev,
nvdimm_namespace_disk_name(ndns, disk->disk_name);
set_capacity(disk, (pmem->size - pmem->pfn_pad - pmem->data_offset)
/ 512);
- if (devm_init_badblocks(dev, &pmem->bb))
- return -ENOMEM;
+ if (devm_init_badblocks(dev, &pmem->bb)) {
+ rc = -ENOMEM;
+ goto out;
+ }
nvdimm_badblocks_populate(nd_region, &pmem->bb, &bb_range);
disk->bb = &pmem->bb;

--
2.43.7