From nobody Mon Apr 6 17:26:12 2026 Received: from relay.hostedemail.com (smtprelay0011.hostedemail.com [216.40.44.11]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A6CE52DB7B7; Thu, 19 Mar 2026 01:30:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=216.40.44.11 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773883824; cv=none; b=kg0aRuL8apvx/XMeANu2kk5vQxSirkqx8lj2R7YBCaH7VbL5rand93NUccSt2XQSouo+C8iJtG4PDLVXxHQPEIP3PlSlKSzxQaQWAqtduP/maypsYvYt+UMlNkxqjovM/+KnvBuFVtnPTEJnvaRGahZibXaYbhU5KWXz3yLeRD4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773883824; c=relaxed/simple; bh=EYgs9oY8SZc2u+RfPuZ/4kEb1/RHVm54MSMI2GbEowI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QtoJ5ffVbxELqwMraGw3yYrtE3yJucGiqyJMJFaLQNe33q2wsvDxsjC1uEuhDDhLU4WDScgznKuKzug9sV2AH9pkrEQiKiBONQC27kT/AOeR/ZcmCNXu2Hq3cbkImEDzrMNo1BDY2iWStJ6dI4cmTnlZorH8Y338CYEZkHU6+jo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=groves.net; spf=pass smtp.mailfrom=groves.net; arc=none smtp.client-ip=216.40.44.11 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=groves.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=groves.net Received: from omf11.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay08.hostedemail.com (Postfix) with ESMTP id 48A76140277; Thu, 19 Mar 2026 01:30:18 +0000 (UTC) Received: from [HIDDEN] (Authenticated sender: john@groves.net) by omf11.hostedemail.com (Postfix) with ESMTPA id 569052002F; Thu, 19 Mar 2026 01:30:07 +0000 (UTC) From: John Groves To: John Groves , Miklos Szeredi , Dan Williams , Bernd Schubert , Alison Schofield Cc: John Groves , Jonathan Corbet , Shuah Khan , Vishal Verma , Dave Jiang , Matthew Wilcox , Jan Kara , Alexander Viro , David Hildenbrand , Christian Brauner , "Darrick J . Wong" , Randy Dunlap , Jeff Layton , Amir Goldstein , Jonathan Cameron , Stefan Hajnoczi , Joanne Koong , Josef Bacik , Bagas Sanjaya , Chen Linxuan , James Morse , Fuad Tabba , Sean Christopherson , Shivank Garg , Ackerley Tng , Gregory Price , Aravind Ramesh , Ajay Joshi , venkataravis@micron.com, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, nvdimm@lists.linux.dev, linux-cxl@vger.kernel.org, linux-fsdevel@vger.kernel.org, John Groves Subject: [PATCH V8 6/8] dax: Add dax_set_ops() for setting dax_operations at bind time Date: Wed, 18 Mar 2026 20:30:05 -0500 Message-ID: <20260319013005.4511-1-john@groves.net> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260318202737.4344.dax@groves.net> References: <20260318202737.4344.dax@groves.net> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Queue-Id: 569052002F X-Stat-Signature: jyk5zjweg8k3w8k6fu9z93qowpue78g6 X-Rspamd-Server: rspamout03 X-Session-Marker: 6A6F686E4067726F7665732E6E6574 X-Session-ID: U2FsdGVkX1/cqMgvZOv/sKnlWCv+t+cNQ5jUdmhhhsA= X-HE-Tag: 1773883807-52540 X-HE-Meta: U2FsdGVkX18qSRGOV56IRDCpo8K8tA4/v9vAl3DGm2Qdbzh8uap6q4HGpmUslfRpaBd/8Y/EvlY2AXgM2DdlS6fxk9InURsUJVWRifvGqBa4+Z+34bJYsafLOszfcm458qr8PqufFZ7578fPFEHZuyeVqEOzl9J36NcLfnf5i3wAV1wwmMEurmIfJU/DKVWGtebwd5GCOXdBFk78N8tzqnDrrDx1FMV7Lcotw21t2+t4HcYRIAszhMIGLOICA5tDhgCCwGjzrPEa8/DMz4UFMJ67N33ywnIdUovEiDOT2NR/CGtfEbpTCCn0VrfVGh03CYuuNYL/z+SBSt30q4MT88Sh+3SIrI/Z Content-Type: text/plain; charset="utf-8" From: John Groves 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 Signed-off-by: John Groves --- 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 5a1e504c9281..36d39f3ef135 100644 --- a/drivers/dax/fsdev.c +++ b/drivers/dax/fsdev.c @@ -117,6 +117,13 @@ static void fsdev_kill(void *dev_dax) kill_dev_dax(dev_dax); } =20 +static void fsdev_clear_ops(void *data) +{ + struct dev_dax *dev_dax =3D 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 @@ -310,6 +317,15 @@ static int fsdev_dax_probe(struct dev_dax *dev_dax) if (rc) return rc; =20 + /* Set the dax operations for fs-dax access path */ + rc =3D dax_set_ops(dax_dev, &dev_dax_ops); + if (rc) + return rc; + + rc =3D 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, pgof= f_t pgoff, long nr_pages, if (!dax_alive(dax_dev)) return -ENXIO; =20 + if (!dax_dev->ops) + return -EOPNOTSUPP; + if (nr_pages < 0) return -EINVAL; =20 @@ -207,6 +210,10 @@ int dax_zero_page_range(struct dax_device *dax_dev, pg= off_t pgoff, =20 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); =20 +/** + * 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 *o= ps) +{ + if (ops) { + /* Setting ops: fail if already set */ + if (cmpxchg(&dax_dev->ops, NULL, ops) !=3D NULL) + return -EBUSY; + } else { + /* Clearing ops: always allowed */ + dax_dev->ops =3D 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 996493f5c538..8d469a23c485 100644 --- a/include/linux/dax.h +++ b/include/linux/dax.h @@ -245,6 +245,7 @@ static inline void dax_break_layout_final(struct inode = *inode) =20 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 *o= ps); 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, --=20 2.53.0