On 1/18/26 3:32 PM, John Groves wrote:
> From: John Groves <[email protected]>
> 
> 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.
> 
> Signed-off-by: John Groves <[email protected]>

Reviewed-by: Dave Jiang <[email protected]>

> ---
>  drivers/dax/fsdev.c | 16 ++++++++++++++++
>  drivers/dax/super.c | 38 +++++++++++++++++++++++++++++++++++++-
>  include/linux/dax.h |  1 +
>  3 files changed, 54 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/dax/fsdev.c b/drivers/dax/fsdev.c
> index 5d17ad39227f..4949aa41dcf4 100644
> --- a/drivers/dax/fsdev.c
> +++ b/drivers/dax/fsdev.c
> @@ -119,6 +119,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
> @@ -301,6 +308,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);
>  }
> diff --git a/drivers/dax/super.c b/drivers/dax/super.c
> index c00b9dff4a06..ba0b4cd18a77 100644
> --- a/drivers/dax/super.c
> +++ b/drivers/dax/super.c
> @@ -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);
> diff --git a/include/linux/dax.h b/include/linux/dax.h
> index fe1315135fdd..5aaaca135737 100644
> --- a/include/linux/dax.h
> +++ b/include/linux/dax.h
> @@ -247,6 +247,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,


Reply via email to