dax: Add dax_set_ops() for setting dax_operations at bind time

Add a new dax_set_ops() function that allows drivers to set the
dax_operations after the dax_device has been allocated. This is needed
for fsdev_dax where the operations need to be set during probe and
cleared during unbind.

The fsdev driver uses devm_add_action_or_reset() for cleanup consistency,
avoiding the complexity of mixing devm-managed resources with manual
cleanup in a remove() callback. This ensures cleanup happens automatically
in the correct reverse order when the device is unbound.

Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Signed-off-by: John Groves <john@groves.net>
Link: https://patch.msgid.link/0100019d311d65a0-b9c1419e-f3a0-4afd-b0bd-848f18ff5950-000000@email.amazonses.com
Signed-off-by: Ira Weiny <ira.weiny@intel.com>
This commit is contained in:
John Groves 2026-03-27 21:05:03 +00:00 committed by Ira Weiny
parent 099c81a1f0
commit 700ecbc1f5
3 changed files with 54 additions and 1 deletions

View File

@ -117,6 +117,13 @@ static void fsdev_kill(void *dev_dax)
kill_dev_dax(dev_dax);
}
static void fsdev_clear_ops(void *data)
{
struct dev_dax *dev_dax = data;
dax_set_ops(dev_dax->dax_dev, NULL);
}
/*
* Page map operations for FS-DAX mode
* Similar to fsdax_pagemap_ops in drivers/nvdimm/pmem.c
@ -303,6 +310,15 @@ static int fsdev_dax_probe(struct dev_dax *dev_dax)
if (rc)
return rc;
/* Set the dax operations for fs-dax access path */
rc = dax_set_ops(dax_dev, &dev_dax_ops);
if (rc)
return rc;
rc = devm_add_action_or_reset(dev, fsdev_clear_ops, dev_dax);
if (rc)
return rc;
run_dax(dax_dev);
return devm_add_action_or_reset(dev, fsdev_kill, dev_dax);
}

View File

@ -157,6 +157,9 @@ long dax_direct_access(struct dax_device *dax_dev, pgoff_t pgoff, long nr_pages,
if (!dax_alive(dax_dev))
return -ENXIO;
if (!dax_dev->ops)
return -EOPNOTSUPP;
if (nr_pages < 0)
return -EINVAL;
@ -207,6 +210,10 @@ int dax_zero_page_range(struct dax_device *dax_dev, pgoff_t pgoff,
if (!dax_alive(dax_dev))
return -ENXIO;
if (!dax_dev->ops)
return -EOPNOTSUPP;
/*
* There are no callers that want to zero more than one page as of now.
* Once users are there, this check can be removed after the
@ -223,7 +230,7 @@ EXPORT_SYMBOL_GPL(dax_zero_page_range);
size_t dax_recovery_write(struct dax_device *dax_dev, pgoff_t pgoff,
void *addr, size_t bytes, struct iov_iter *iter)
{
if (!dax_dev->ops->recovery_write)
if (!dax_dev->ops || !dax_dev->ops->recovery_write)
return 0;
return dax_dev->ops->recovery_write(dax_dev, pgoff, addr, bytes, iter);
}
@ -307,6 +314,35 @@ void set_dax_nomc(struct dax_device *dax_dev)
}
EXPORT_SYMBOL_GPL(set_dax_nomc);
/**
* dax_set_ops - set the dax_operations for a dax_device
* @dax_dev: the dax_device to configure
* @ops: the operations to set (may be NULL to clear)
*
* This allows drivers to set the dax_operations after the dax_device
* has been allocated. This is needed when the device is created before
* the driver that needs specific ops is bound (e.g., fsdev_dax binding
* to a dev_dax created by hmem).
*
* When setting non-NULL ops, fails if ops are already set (returns -EBUSY).
* When clearing ops (NULL), always succeeds.
*
* Return: 0 on success, -EBUSY if ops already set
*/
int dax_set_ops(struct dax_device *dax_dev, const struct dax_operations *ops)
{
if (ops) {
/* Setting ops: fail if already set */
if (cmpxchg(&dax_dev->ops, NULL, ops) != NULL)
return -EBUSY;
} else {
/* Clearing ops: always allowed */
dax_dev->ops = NULL;
}
return 0;
}
EXPORT_SYMBOL_GPL(dax_set_ops);
bool dax_alive(struct dax_device *dax_dev)
{
lockdep_assert_held(&dax_srcu);

View File

@ -243,6 +243,7 @@ static inline void dax_break_layout_final(struct inode *inode)
bool dax_alive(struct dax_device *dax_dev);
void *dax_get_private(struct dax_device *dax_dev);
int dax_set_ops(struct dax_device *dax_dev, const struct dax_operations *ops);
long dax_direct_access(struct dax_device *dax_dev, pgoff_t pgoff, long nr_pages,
enum dax_access_mode mode, void **kaddr, unsigned long *pfn);
size_t dax_copy_from_iter(struct dax_device *dax_dev, pgoff_t pgoff, void *addr,