Commit 700ecbc1 authored by John Groves's avatar John Groves Committed by Ira Weiny
Browse files

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: default avatarDave Jiang <dave.jiang@intel.com>
Reviewed-by: default avatarJonathan Cameron <jonathan.cameron@huawei.com>
Signed-off-by: default avatarJohn Groves <john@groves.net>
Link: https://patch.msgid.link/0100019d311d65a0-b9c1419e-f3a0-4afd-b0bd-848f18ff5950-000000@email.amazonses.com


Signed-off-by: default avatarIra Weiny <ira.weiny@intel.com>
parent 099c81a1
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -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);
}
+37 −1
Original line number Diff line number Diff line
@@ -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);
+1 −0
Original line number Diff line number Diff line
@@ -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,