[PATCH V12 01/12] dax: replace exported dax_dev_get() with non-allocating dax_dev_find()
John Groves <[email protected]> Mon, 3 Aug 2026 02:28:26 +0000
| Newsgroups | org.kernel.vger.linux-fsdevel,dev.linux.lists.fuse-devel,dev.linux.lists.nvdimm,org.kernel.vger.linux-cxl,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <0100019fc5737596-636bde4f-7fc7-46a4-b011-63870098df09-000000@email.amazonses.com> |
From: John Groves <[email protected]> This fix is in response to a Sashiko review, and some subsequent analysis. dax_dev_get() uses iget5_locked() which creates a new inode if no matching one exists. This is correct for the internal caller (alloc_dax), but dangerous for external callers that look up devices from user-supplied or metadata-supplied dev_t values: 1. A new inode is created with DAXDEV_ALIVE set but no backing driver, no ops, and no IDA-allocated minor number. 2. On teardown, dax_destroy_inode() warns because kill_dax() was never called, and dax_free_inode() calls ida_free() for a minor that was never ida_alloc'd -- potentially freeing the minor of a real device. Add dax_dev_find() which uses ilookup5() for lookup-only semantics: it returns an existing dax_device with an elevated inode reference, or NULL if no device with the given dev_t exists. It never creates inodes. A dax_alive() check under dax_read_lock() guards against returning a device that is concurrently being torn down by kill_dax(). Make dax_dev_get() static again (internal to super.c for alloc_dax), export dax_dev_find() instead, and update the two external callers (famfs_inode.c, famfs.c). Also add the missing CONFIG_DAX=n stub. About the 'fixes' tag: this removes the export of dax_dev_get(), which was flawed, and replaces is with dax_dev_find(). It feels like the fixes tag makes sense for correcting an ABI error. Fixes: 2ae624d5a555d ("dax: export dax_dev_get()") Reviewed-by: Dave Jiang <[email protected]> Reviewed-by: Alison Schofield <[email protected]> Signed-off-by: John Groves <[email protected]> --- drivers/dax/super.c | 38 ++++++++++++++++++++++++++++++++++++-- include/linux/dax.h | 6 +++++- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/drivers/dax/super.c b/drivers/dax/super.c index 25cf99dd9360..0fc14d4f8198 100644 --- a/drivers/dax/super.c +++ b/drivers/dax/super.c @@ -521,7 +521,7 @@ static int dax_set(struct inode *inode, void *data) return 0; } -struct dax_device *dax_dev_get(dev_t devt) +static struct dax_device *dax_dev_get(dev_t devt) { struct dax_device *dax_dev; struct inode *inode; @@ -544,7 +544,41 @@ struct dax_device *dax_dev_get(dev_t devt) return dax_dev; } -EXPORT_SYMBOL_GPL(dax_dev_get); + +/** + * dax_dev_find - look up an existing dax_device by dev_t + * @devt: the device number to find + * + * Returns a dax_device with an elevated inode reference, or NULL if no + * device with the given dev_t exists. Unlike dax_dev_get(), this never + * allocates a new inode -- it is safe for external callers that are looking + * up devices from user-supplied or metadata-supplied dev_t values. + * + * Caller must put_dax() the returned device when done. + */ +struct dax_device *dax_dev_find(dev_t devt) +{ + struct dax_device *dax_dev; + struct inode *inode; + int id; + + inode = ilookup5(dax_superblock, hash_32(devt + DAXFS_MAGIC, 31), + dax_test, &devt); + if (!inode) + return NULL; + + dax_dev = to_dax_dev(inode); + id = dax_read_lock(); + if (!dax_alive(dax_dev)) { + dax_read_unlock(id); + iput(inode); + return NULL; + } + dax_read_unlock(id); + + return dax_dev; +} +EXPORT_SYMBOL_GPL(dax_dev_find); struct dax_device *alloc_dax(void *private, const struct dax_operations *ops) { diff --git a/include/linux/dax.h b/include/linux/dax.h index fe6c3ded1b50..29113eb95e72 100644 --- a/include/linux/dax.h +++ b/include/linux/dax.h @@ -54,7 +54,7 @@ struct dax_device *alloc_dax(void *private, const struct dax_operations *ops); void *dax_holder(struct dax_device *dax_dev); void put_dax(struct dax_device *dax_dev); void kill_dax(struct dax_device *dax_dev); -struct dax_device *dax_dev_get(dev_t devt); +struct dax_device *dax_dev_find(dev_t devt); void dax_write_cache(struct dax_device *dax_dev, bool wc); bool dax_write_cache_enabled(struct dax_device *dax_dev); bool dax_synchronous(struct dax_device *dax_dev); @@ -92,6 +92,10 @@ static inline void put_dax(struct dax_device *dax_dev) static inline void kill_dax(struct dax_device *dax_dev) { } +static inline struct dax_device *dax_dev_find(dev_t devt) +{ + return NULL; +} static inline void dax_write_cache(struct dax_device *dax_dev, bool wc) { } -- 2.53.0