mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 18:13:41 +02:00
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:
parent
099c81a1f0
commit
700ecbc1f5
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user