linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/8] cleanup: Introduce ACQUIRE(), a guard() for conditional locks
@ 2025-07-11 23:49 Dan Williams
  2025-07-11 23:49 ` [PATCH v3 1/8] cleanup: Introduce ACQUIRE() and ACQUIRE_ERR() " Dan Williams
                   ` (9 more replies)
  0 siblings, 10 replies; 32+ messages in thread
From: Dan Williams @ 2025-07-11 23:49 UTC (permalink / raw)
  To: linux-cxl
  Cc: linux-kernel, Alison Schofield, Dave Jiang, David Lechner,
	Davidlohr Bueso, Fabio M. De Francesco, Ingo Molnar, Ira Weiny,
	Jonathan Cameron, Linus Torvalds, Peter Zijlstra (Intel),
	Shiju Jose, Vishal Verma

Changes since v2 [1]:
- Pick up Acks and Reviews
- Whitespace fixups for cleanup.h changes (Jonathan)
- Use consistent local variable style for ACQUIRE_ERR() (Jonathan)
  - Not addressed: switch to less compact style ACQUIRE_ERR()
- Not addressed: pickup checkpatch change for ACQUIRE_ERR() style in
  this series (Alison)
- Drop the cxl_decoder_detach() CLASS() and convert to a helper function (Jonathan)
- Refactor attach_target() to make it easier to read (Jonathan)

[1]: http://lore.kernel.org/20250619050416.782871-1-dan.j.williams@intel.com

For those new to this set, the motivation for this work is that the CXL
subsystem adopted scope-based-cleanup helpers and achieved some decent
cleanups. However, that work stalled with conditional locks. It stalled
due to the pain points of scoped_cond_guard(). See patch1.

In the interim, approaches like rwsem_read_intr_acquire() attempted to
workaround the pain points, but started a "parallel universe" of helpers
that is not sustainable.

    0c6e6f1357cb cxl/edac: Add CXL memory device patrol scrub control feature

Peter fixed all of this up in a manner consistent with existing guards.
Take that proposal and run with it to unblock further cleanups of "goto"
in unwind paths in the CXL subsystem.

Potential follow-on work identified by this effort:

- __GUARD_IS_ERR() asm helper [2]
- Checkpatch fixups for proposed ACQUIRE_ERR() style [3]

[2]: http://lore.kernel.org/20250514064624.GA24938@noisy.programming.kicks-ass.net
[3]: http://lore.kernel.org/aGXDMZB6omShJpoj@aschofie-mobl2.lan

Dan Williams (7):
  cxl/mbox: Convert poison list mutex to ACQUIRE()
  cxl/decoder: Move decoder register programming to a helper
  cxl/decoder: Drop pointless locking
  cxl/region: Split commit_store() into __commit() and queue_reset()
    helpers
  cxl/region: Move ready-to-probe state check to a helper
  cxl/region: Consolidate cxl_decoder_kill_region() and
    cxl_region_detach()
  cxl: Convert to ACQUIRE() for conditional rwsem locking

Peter Zijlstra (1):
  cleanup: Introduce ACQUIRE() and ACQUIRE_ERR() for conditional locks

 drivers/cxl/core/cdat.c   |   6 +-
 drivers/cxl/core/core.h   |  32 ++-
 drivers/cxl/core/edac.c   |  44 ++--
 drivers/cxl/core/hdm.c    | 118 +++++-----
 drivers/cxl/core/mbox.c   |  13 +-
 drivers/cxl/core/memdev.c |  50 ++--
 drivers/cxl/core/port.c   |  27 +--
 drivers/cxl/core/region.c | 473 ++++++++++++++++++++------------------
 drivers/cxl/cxl.h         |  13 +-
 drivers/cxl/cxlmem.h      |   4 +-
 include/linux/cleanup.h   |  95 ++++++--
 include/linux/mutex.h     |   2 +-
 include/linux/rwsem.h     |   3 +-
 13 files changed, 480 insertions(+), 400 deletions(-)


base-commit: e04c78d86a9699d136910cfc0bdcf01087e3267e
-- 
2.50.0


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH v3 1/8] cleanup: Introduce ACQUIRE() and ACQUIRE_ERR() for conditional locks
  2025-07-11 23:49 [PATCH v3 0/8] cleanup: Introduce ACQUIRE(), a guard() for conditional locks Dan Williams
@ 2025-07-11 23:49 ` Dan Williams
  2025-07-15 15:34   ` Jonathan Cameron
  2025-07-11 23:49 ` [PATCH v3 2/8] cxl/mbox: Convert poison list mutex to ACQUIRE() Dan Williams
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 32+ messages in thread
From: Dan Williams @ 2025-07-11 23:49 UTC (permalink / raw)
  To: linux-cxl
  Cc: linux-kernel, Peter Zijlstra, Ingo Molnar, Linus Torvalds,
	David Lechner, Fabio M. De Francesco

From: Peter Zijlstra <peterz@infradead.org>

scoped_cond_guard(), automatic cleanup for conditional locks, has a couple
pain points:

* It causes existing straight-line code to be re-indented into a new
  bracketed scope. While this can be mitigated by a new helper function
  to contain the scope, that is not always a comfortable conversion.

* The return code from the conditional lock is tossed in favor of a scheme
  to pass a 'return err;' statement to the macro.

Other attempts to clean this up, to behave more like guard() [1], got hung
up trying to both establish and evaluate the conditional lock in one
statement.

ACQUIRE() solves this by reflecting the result of the condition in the
automatic variable established by the lock CLASS(). The result is
separately retrieved with the ACQUIRE_ERR() helper, effectively a PTR_ERR()
operation.

Link: http://lore.kernel.org/all/Z1LBnX9TpZLR5Dkf@gmail.com [1]
Link: http://patch.msgid.link/20250512105026.GP4439@noisy.programming.kicks-ass.net
Link: http://patch.msgid.link/20250512185817.GA1808@noisy.programming.kicks-ass.net
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: David Lechner <dlechner@baylibre.com>
Cc: "Fabio M. De Francesco" <fabio.m.de.francesco@linux.intel.com>
Signed-off-by: "Peter Zijlstra (Intel)" <peterz@infradead.org>
[djbw: wrap Peter's proposal with changelog and comments]
Co-developed-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 include/linux/cleanup.h | 95 +++++++++++++++++++++++++++++++++++------
 include/linux/mutex.h   |  2 +-
 include/linux/rwsem.h   |  2 +-
 3 files changed, 83 insertions(+), 16 deletions(-)

diff --git a/include/linux/cleanup.h b/include/linux/cleanup.h
index 7093e1d08af0..4eb83dd71cfe 100644
--- a/include/linux/cleanup.h
+++ b/include/linux/cleanup.h
@@ -3,6 +3,8 @@
 #define _LINUX_CLEANUP_H
 
 #include <linux/compiler.h>
+#include <linux/err.h>
+#include <linux/args.h>
 
 /**
  * DOC: scope-based cleanup helpers
@@ -61,9 +63,21 @@
  * Observe the lock is held for the remainder of the "if ()" block not
  * the remainder of "func()".
  *
- * Now, when a function uses both __free() and guard(), or multiple
- * instances of __free(), the LIFO order of variable definition order
- * matters. GCC documentation says:
+ * The ACQUIRE() macro can be used in all places that guard() can be
+ * used and additionally support conditional locks
+ *
+ *
+ *	DEFINE_GUARD_COND(pci_dev, _try, pci_dev_trylock(_T))
+ *	...
+ *	ACQUIRE(pci_dev_try, lock)(dev);
+ *	rc = ACQUIRE_ERR(pci_dev_try, &lock);
+ *	if (rc)
+ *		return rc;
+ *	// @lock is held
+ *
+ * Now, when a function uses both __free() and guard()/ACQUIRE(), or
+ * multiple instances of __free(), the LIFO order of variable definition
+ * order matters. GCC documentation says:
  *
  * "When multiple variables in the same scope have cleanup attributes,
  * at exit from the scope their associated cleanup functions are run in
@@ -305,14 +319,46 @@ static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \
  *      acquire fails.
  *
  *      Only for conditional locks.
+ *
+ * ACQUIRE(name, var):
+ *	a named instance of the (guard) class, suitable for conditional
+ *	locks when paired with ACQUIRE_ERR().
+ *
+ * ACQUIRE_ERR(name, &var):
+ *	a helper that is effectively a PTR_ERR() conversion of the guard
+ *	pointer. Returns 0 when the lock was acquired and a negative
+ *	error code otherwise.
  */
 
 #define __DEFINE_CLASS_IS_CONDITIONAL(_name, _is_cond)	\
 static __maybe_unused const bool class_##_name##_is_conditional = _is_cond
 
-#define __DEFINE_GUARD_LOCK_PTR(_name, _exp) \
-	static inline void * class_##_name##_lock_ptr(class_##_name##_t *_T) \
-	{ return (void *)(__force unsigned long)*(_exp); }
+#define __GUARD_IS_ERR(_ptr)                                       \
+	({                                                         \
+		unsigned long _rc = (__force unsigned long)(_ptr); \
+		unlikely((_rc - 1) >= -MAX_ERRNO - 1);             \
+	})
+
+#define __DEFINE_GUARD_LOCK_PTR(_name, _exp)                                \
+	static inline void *class_##_name##_lock_ptr(class_##_name##_t *_T) \
+	{                                                                   \
+		void *_ptr = (void *)(__force unsigned long)*(_exp);        \
+		if (IS_ERR(_ptr)) {                                         \
+			_ptr = NULL;                                        \
+		}                                                           \
+		return _ptr;                                                \
+	}                                                                   \
+	static inline int class_##_name##_lock_err(class_##_name##_t *_T)   \
+	{                                                                   \
+		long _rc = (__force unsigned long)*(_exp);                  \
+		if (!_rc) {                                                 \
+			_rc = -EBUSY;                                       \
+		}                                                           \
+		if (!IS_ERR_VALUE(_rc)) {                                   \
+			_rc = 0;                                            \
+		}                                                           \
+		return _rc;                                                 \
+	}
 
 #define DEFINE_CLASS_IS_GUARD(_name) \
 	__DEFINE_CLASS_IS_CONDITIONAL(_name, false); \
@@ -323,23 +369,37 @@ static __maybe_unused const bool class_##_name##_is_conditional = _is_cond
 	__DEFINE_GUARD_LOCK_PTR(_name, _T)
 
 #define DEFINE_GUARD(_name, _type, _lock, _unlock) \
-	DEFINE_CLASS(_name, _type, if (_T) { _unlock; }, ({ _lock; _T; }), _type _T); \
+	DEFINE_CLASS(_name, _type, if (!__GUARD_IS_ERR(_T)) { _unlock; }, ({ _lock; _T; }), _type _T); \
 	DEFINE_CLASS_IS_GUARD(_name)
 
-#define DEFINE_GUARD_COND(_name, _ext, _condlock) \
+#define DEFINE_GUARD_COND_4(_name, _ext, _lock, _cond) \
 	__DEFINE_CLASS_IS_CONDITIONAL(_name##_ext, true); \
 	EXTEND_CLASS(_name, _ext, \
-		     ({ void *_t = _T; if (_T && !(_condlock)) _t = NULL; _t; }), \
+		     ({ void *_t = _T; int _RET = (_lock); if (_T && !(_cond)) _t = ERR_PTR(_RET); _t; }), \
 		     class_##_name##_t _T) \
 	static inline void * class_##_name##_ext##_lock_ptr(class_##_name##_t *_T) \
-	{ return class_##_name##_lock_ptr(_T); }
+	{ return class_##_name##_lock_ptr(_T); } \
+	static inline int class_##_name##_ext##_lock_err(class_##_name##_t *_T) \
+	{ return class_##_name##_lock_err(_T); }
+
+/*
+ * Default binary condition; success on 'true'.
+ */
+#define DEFINE_GUARD_COND_3(_name, _ext, _lock) \
+	DEFINE_GUARD_COND_4(_name, _ext, _lock, _RET)
+
+#define DEFINE_GUARD_COND(X...) CONCATENATE(DEFINE_GUARD_COND_, COUNT_ARGS(X))(X)
 
 #define guard(_name) \
 	CLASS(_name, __UNIQUE_ID(guard))
 
 #define __guard_ptr(_name) class_##_name##_lock_ptr
+#define __guard_err(_name) class_##_name##_lock_err
 #define __is_cond_ptr(_name) class_##_name##_is_conditional
 
+#define ACQUIRE(_name, _var)     CLASS(_name, _var)
+#define ACQUIRE_ERR(_name, _var) __guard_err(_name)(_var)
+
 /*
  * Helper macro for scoped_guard().
  *
@@ -401,7 +461,7 @@ typedef struct {							\
 									\
 static inline void class_##_name##_destructor(class_##_name##_t *_T)	\
 {									\
-	if (_T->lock) { _unlock; }					\
+	if (!__GUARD_IS_ERR(_T->lock)) { _unlock; }			\
 }									\
 									\
 __DEFINE_GUARD_LOCK_PTR(_name, &_T->lock)
@@ -433,15 +493,22 @@ __DEFINE_CLASS_IS_CONDITIONAL(_name, false);				\
 __DEFINE_UNLOCK_GUARD(_name, void, _unlock, __VA_ARGS__)		\
 __DEFINE_LOCK_GUARD_0(_name, _lock)
 
-#define DEFINE_LOCK_GUARD_1_COND(_name, _ext, _condlock)		\
+#define DEFINE_LOCK_GUARD_1_COND_4(_name, _ext, _lock, _cond)		\
 	__DEFINE_CLASS_IS_CONDITIONAL(_name##_ext, true);		\
 	EXTEND_CLASS(_name, _ext,					\
 		     ({ class_##_name##_t _t = { .lock = l }, *_T = &_t;\
-		        if (_T->lock && !(_condlock)) _T->lock = NULL;	\
+		        int _RET = (_lock);                             \
+		        if (_T->lock && !(_cond)) _T->lock = ERR_PTR(_RET);\
 			_t; }),						\
 		     typeof_member(class_##_name##_t, lock) l)		\
 	static inline void * class_##_name##_ext##_lock_ptr(class_##_name##_t *_T) \
-	{ return class_##_name##_lock_ptr(_T); }
+	{ return class_##_name##_lock_ptr(_T); } \
+	static inline int class_##_name##_ext##_lock_err(class_##_name##_t *_T) \
+	{ return class_##_name##_lock_err(_T); }
+
+#define DEFINE_LOCK_GUARD_1_COND_3(_name, _ext, _lock) \
+	DEFINE_LOCK_GUARD_1_COND_4(_name, _ext, _lock, _RET)
 
+#define DEFINE_LOCK_GUARD_1_COND(X...) CONCATENATE(DEFINE_LOCK_GUARD_1_COND_, COUNT_ARGS(X))(X)
 
 #endif /* _LINUX_CLEANUP_H */
diff --git a/include/linux/mutex.h b/include/linux/mutex.h
index a039fa8c1780..9d5d7ed5c101 100644
--- a/include/linux/mutex.h
+++ b/include/linux/mutex.h
@@ -224,7 +224,7 @@ extern int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock);
 
 DEFINE_GUARD(mutex, struct mutex *, mutex_lock(_T), mutex_unlock(_T))
 DEFINE_GUARD_COND(mutex, _try, mutex_trylock(_T))
-DEFINE_GUARD_COND(mutex, _intr, mutex_lock_interruptible(_T) == 0)
+DEFINE_GUARD_COND(mutex, _intr, mutex_lock_interruptible(_T), _RET == 0)
 
 extern unsigned long mutex_get_owner(struct mutex *lock);
 
diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h
index c8b543d428b0..c810deb88d13 100644
--- a/include/linux/rwsem.h
+++ b/include/linux/rwsem.h
@@ -240,7 +240,7 @@ extern void up_write(struct rw_semaphore *sem);
 
 DEFINE_GUARD(rwsem_read, struct rw_semaphore *, down_read(_T), up_read(_T))
 DEFINE_GUARD_COND(rwsem_read, _try, down_read_trylock(_T))
-DEFINE_GUARD_COND(rwsem_read, _intr, down_read_interruptible(_T) == 0)
+DEFINE_GUARD_COND(rwsem_read, _intr, down_read_interruptible(_T), _RET == 0)
 
 DEFINE_GUARD(rwsem_write, struct rw_semaphore *, down_write(_T), up_write(_T))
 DEFINE_GUARD_COND(rwsem_write, _try, down_write_trylock(_T))
-- 
2.50.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v3 2/8] cxl/mbox: Convert poison list mutex to ACQUIRE()
  2025-07-11 23:49 [PATCH v3 0/8] cleanup: Introduce ACQUIRE(), a guard() for conditional locks Dan Williams
  2025-07-11 23:49 ` [PATCH v3 1/8] cleanup: Introduce ACQUIRE() and ACQUIRE_ERR() " Dan Williams
@ 2025-07-11 23:49 ` Dan Williams
  2025-07-11 23:49 ` [PATCH v3 3/8] cxl/decoder: Move decoder register programming to a helper Dan Williams
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 32+ messages in thread
From: Dan Williams @ 2025-07-11 23:49 UTC (permalink / raw)
  To: linux-cxl
  Cc: linux-kernel, Linus Torvalds, Peter Zijlstra, Davidlohr Bueso,
	Jonathan Cameron, Dave Jiang, Alison Schofield, Vishal Verma,
	Ira Weiny

Towards removing all explicit unlock calls in the CXL subsystem, convert
the conditional poison list mutex to use a conditional lock guard.

Rename the lock to have the compiler validate that all existing call sites
are converted.

Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
Cc: Dave Jiang <dave.jiang@intel.com>
Cc: Alison Schofield <alison.schofield@intel.com>
Cc: Vishal Verma <vishal.l.verma@intel.com>
Cc: Ira Weiny <ira.weiny@intel.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 drivers/cxl/core/mbox.c | 7 +++----
 drivers/cxl/cxlmem.h    | 4 ++--
 2 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
index 2689e6453c5a..81b21effe8cf 100644
--- a/drivers/cxl/core/mbox.c
+++ b/drivers/cxl/core/mbox.c
@@ -1401,8 +1401,8 @@ int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len,
 	int nr_records = 0;
 	int rc;
 
-	rc = mutex_lock_interruptible(&mds->poison.lock);
-	if (rc)
+	ACQUIRE(mutex_intr, lock)(&mds->poison.mutex);
+	if ((rc = ACQUIRE_ERR(mutex_intr, &lock)))
 		return rc;
 
 	po = mds->poison.list_out;
@@ -1437,7 +1437,6 @@ int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len,
 		}
 	} while (po->flags & CXL_POISON_FLAG_MORE);
 
-	mutex_unlock(&mds->poison.lock);
 	return rc;
 }
 EXPORT_SYMBOL_NS_GPL(cxl_mem_get_poison, "CXL");
@@ -1473,7 +1472,7 @@ int cxl_poison_state_init(struct cxl_memdev_state *mds)
 		return rc;
 	}
 
-	mutex_init(&mds->poison.lock);
+	mutex_init(&mds->poison.mutex);
 	return 0;
 }
 EXPORT_SYMBOL_NS_GPL(cxl_poison_state_init, "CXL");
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index 551b0ba2caa1..f5b20641e57c 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -254,7 +254,7 @@ enum security_cmd_enabled_bits {
  * @max_errors: Maximum media error records held in device cache
  * @enabled_cmds: All poison commands enabled in the CEL
  * @list_out: The poison list payload returned by device
- * @lock: Protect reads of the poison list
+ * @mutex: Protect reads of the poison list
  *
  * Reads of the poison list are synchronized to ensure that a reader
  * does not get an incomplete list because their request overlapped
@@ -265,7 +265,7 @@ struct cxl_poison_state {
 	u32 max_errors;
 	DECLARE_BITMAP(enabled_cmds, CXL_POISON_ENABLED_MAX);
 	struct cxl_mbox_poison_out *list_out;
-	struct mutex lock;  /* Protect reads of poison list */
+	struct mutex mutex;  /* Protect reads of poison list */
 };
 
 /*
-- 
2.50.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v3 3/8] cxl/decoder: Move decoder register programming to a helper
  2025-07-11 23:49 [PATCH v3 0/8] cleanup: Introduce ACQUIRE(), a guard() for conditional locks Dan Williams
  2025-07-11 23:49 ` [PATCH v3 1/8] cleanup: Introduce ACQUIRE() and ACQUIRE_ERR() " Dan Williams
  2025-07-11 23:49 ` [PATCH v3 2/8] cxl/mbox: Convert poison list mutex to ACQUIRE() Dan Williams
@ 2025-07-11 23:49 ` Dan Williams
  2025-07-11 23:49 ` [PATCH v3 4/8] cxl/decoder: Drop pointless locking Dan Williams
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 32+ messages in thread
From: Dan Williams @ 2025-07-11 23:49 UTC (permalink / raw)
  To: linux-cxl
  Cc: linux-kernel, Davidlohr Bueso, Jonathan Cameron, Dave Jiang,
	Alison Schofield, Vishal Verma, Ira Weiny, Peter Zijlstra (Intel)

In preparation for converting to rw_semaphore_acquire semantics move the
contents of an open-coded {down,up}_read(&cxl_dpa_rwsem) section to a
helper function.

Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
Cc: Dave Jiang <dave.jiang@intel.com>
Cc: Alison Schofield <alison.schofield@intel.com>
Cc: Vishal Verma <vishal.l.verma@intel.com>
Cc: Ira Weiny <ira.weiny@intel.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 drivers/cxl/core/hdm.c | 77 +++++++++++++++++++++++-------------------
 1 file changed, 42 insertions(+), 35 deletions(-)

diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index ab1007495f6b..81556d12e9b8 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -764,14 +764,53 @@ static int cxld_await_commit(void __iomem *hdm, int id)
 	return -ETIMEDOUT;
 }
 
+static void setup_hw_decoder(struct cxl_decoder *cxld, void __iomem *hdm)
+{
+	int id = cxld->id;
+	u64 base, size;
+	u32 ctrl;
+
+	/* common decoder settings */
+	ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(cxld->id));
+	cxld_set_interleave(cxld, &ctrl);
+	cxld_set_type(cxld, &ctrl);
+	base = cxld->hpa_range.start;
+	size = range_len(&cxld->hpa_range);
+
+	writel(upper_32_bits(base), hdm + CXL_HDM_DECODER0_BASE_HIGH_OFFSET(id));
+	writel(lower_32_bits(base), hdm + CXL_HDM_DECODER0_BASE_LOW_OFFSET(id));
+	writel(upper_32_bits(size), hdm + CXL_HDM_DECODER0_SIZE_HIGH_OFFSET(id));
+	writel(lower_32_bits(size), hdm + CXL_HDM_DECODER0_SIZE_LOW_OFFSET(id));
+
+	if (is_switch_decoder(&cxld->dev)) {
+		struct cxl_switch_decoder *cxlsd =
+			to_cxl_switch_decoder(&cxld->dev);
+		void __iomem *tl_hi = hdm + CXL_HDM_DECODER0_TL_HIGH(id);
+		void __iomem *tl_lo = hdm + CXL_HDM_DECODER0_TL_LOW(id);
+		u64 targets;
+
+		cxlsd_set_targets(cxlsd, &targets);
+		writel(upper_32_bits(targets), tl_hi);
+		writel(lower_32_bits(targets), tl_lo);
+	} else {
+		struct cxl_endpoint_decoder *cxled =
+			to_cxl_endpoint_decoder(&cxld->dev);
+		void __iomem *sk_hi = hdm + CXL_HDM_DECODER0_SKIP_HIGH(id);
+		void __iomem *sk_lo = hdm + CXL_HDM_DECODER0_SKIP_LOW(id);
+
+		writel(upper_32_bits(cxled->skip), sk_hi);
+		writel(lower_32_bits(cxled->skip), sk_lo);
+	}
+
+	writel(ctrl, hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id));
+}
+
 static int cxl_decoder_commit(struct cxl_decoder *cxld)
 {
 	struct cxl_port *port = to_cxl_port(cxld->dev.parent);
 	struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev);
 	void __iomem *hdm = cxlhdm->regs.hdm_decoder;
 	int id = cxld->id, rc;
-	u64 base, size;
-	u32 ctrl;
 
 	if (cxld->flags & CXL_DECODER_F_ENABLE)
 		return 0;
@@ -804,39 +843,7 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)
 	}
 
 	down_read(&cxl_dpa_rwsem);
-	/* common decoder settings */
-	ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(cxld->id));
-	cxld_set_interleave(cxld, &ctrl);
-	cxld_set_type(cxld, &ctrl);
-	base = cxld->hpa_range.start;
-	size = range_len(&cxld->hpa_range);
-
-	writel(upper_32_bits(base), hdm + CXL_HDM_DECODER0_BASE_HIGH_OFFSET(id));
-	writel(lower_32_bits(base), hdm + CXL_HDM_DECODER0_BASE_LOW_OFFSET(id));
-	writel(upper_32_bits(size), hdm + CXL_HDM_DECODER0_SIZE_HIGH_OFFSET(id));
-	writel(lower_32_bits(size), hdm + CXL_HDM_DECODER0_SIZE_LOW_OFFSET(id));
-
-	if (is_switch_decoder(&cxld->dev)) {
-		struct cxl_switch_decoder *cxlsd =
-			to_cxl_switch_decoder(&cxld->dev);
-		void __iomem *tl_hi = hdm + CXL_HDM_DECODER0_TL_HIGH(id);
-		void __iomem *tl_lo = hdm + CXL_HDM_DECODER0_TL_LOW(id);
-		u64 targets;
-
-		cxlsd_set_targets(cxlsd, &targets);
-		writel(upper_32_bits(targets), tl_hi);
-		writel(lower_32_bits(targets), tl_lo);
-	} else {
-		struct cxl_endpoint_decoder *cxled =
-			to_cxl_endpoint_decoder(&cxld->dev);
-		void __iomem *sk_hi = hdm + CXL_HDM_DECODER0_SKIP_HIGH(id);
-		void __iomem *sk_lo = hdm + CXL_HDM_DECODER0_SKIP_LOW(id);
-
-		writel(upper_32_bits(cxled->skip), sk_hi);
-		writel(lower_32_bits(cxled->skip), sk_lo);
-	}
-
-	writel(ctrl, hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id));
+	setup_hw_decoder(cxld, hdm);
 	up_read(&cxl_dpa_rwsem);
 
 	port->commit_end++;
-- 
2.50.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v3 4/8] cxl/decoder: Drop pointless locking
  2025-07-11 23:49 [PATCH v3 0/8] cleanup: Introduce ACQUIRE(), a guard() for conditional locks Dan Williams
                   ` (2 preceding siblings ...)
  2025-07-11 23:49 ` [PATCH v3 3/8] cxl/decoder: Move decoder register programming to a helper Dan Williams
@ 2025-07-11 23:49 ` Dan Williams
  2025-07-11 23:49 ` [PATCH v3 5/8] cxl/region: Split commit_store() into __commit() and queue_reset() helpers Dan Williams
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 32+ messages in thread
From: Dan Williams @ 2025-07-11 23:49 UTC (permalink / raw)
  To: linux-cxl
  Cc: linux-kernel, Davidlohr Bueso, Jonathan Cameron, Dave Jiang,
	Alison Schofield, Vishal Verma, Ira Weiny, Peter Zijlstra (Intel)

cxl_dpa_rwsem coordinates changes to dpa allocation settings for a given
decoder. cxl_decoder_reset() has no need for a consistent snapshot of the
dpa settings since it is merely clearing out whatever was there previously.

Otherwise, cxl_region_rwsem protects against 'reset' racing 'setup'.

In preparation for converting to rw_semaphore_acquire semantics, drop this
locking.

Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
Cc: Dave Jiang <dave.jiang@intel.com>
Cc: Alison Schofield <alison.schofield@intel.com>
Cc: Vishal Verma <vishal.l.verma@intel.com>
Cc: Ira Weiny <ira.weiny@intel.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Reviewed-by: Davidlohr Bueso <dave@stgolabs.net>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 drivers/cxl/core/hdm.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index 81556d12e9b8..e9cb34e30248 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -914,7 +914,6 @@ static void cxl_decoder_reset(struct cxl_decoder *cxld)
 			"%s: out of order reset, expected decoder%d.%d\n",
 			dev_name(&cxld->dev), port->id, port->commit_end);
 
-	down_read(&cxl_dpa_rwsem);
 	ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id));
 	ctrl &= ~CXL_HDM_DECODER0_CTRL_COMMIT;
 	writel(ctrl, hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id));
@@ -923,7 +922,6 @@ static void cxl_decoder_reset(struct cxl_decoder *cxld)
 	writel(0, hdm + CXL_HDM_DECODER0_SIZE_LOW_OFFSET(id));
 	writel(0, hdm + CXL_HDM_DECODER0_BASE_HIGH_OFFSET(id));
 	writel(0, hdm + CXL_HDM_DECODER0_BASE_LOW_OFFSET(id));
-	up_read(&cxl_dpa_rwsem);
 
 	cxld->flags &= ~CXL_DECODER_F_ENABLE;
 
-- 
2.50.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v3 5/8] cxl/region: Split commit_store() into __commit() and queue_reset() helpers
  2025-07-11 23:49 [PATCH v3 0/8] cleanup: Introduce ACQUIRE(), a guard() for conditional locks Dan Williams
                   ` (3 preceding siblings ...)
  2025-07-11 23:49 ` [PATCH v3 4/8] cxl/decoder: Drop pointless locking Dan Williams
@ 2025-07-11 23:49 ` Dan Williams
  2025-07-14 21:49   ` Fabio M. De Francesco
  2025-07-11 23:49 ` [PATCH v3 6/8] cxl/region: Move ready-to-probe state check to a helper Dan Williams
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 32+ messages in thread
From: Dan Williams @ 2025-07-11 23:49 UTC (permalink / raw)
  To: linux-cxl
  Cc: linux-kernel, Davidlohr Bueso, Jonathan Cameron, Dave Jiang,
	Alison Schofield, Vishal Verma, Ira Weiny, Peter Zijlstra (Intel)

The complexity of dropping the lock is removed in favor of splitting commit
operations to a helper, and leaving all the complexities of "decommit" for
commit_store() to coordinate the different locking contexts.

The CPU cache-invalidation in the decommit path is solely handled now by
cxl_region_decode_reset(). Previously the CPU caches were being needlessly
flushed twice in the decommit path where the first flush had no guarantee
that the memory would not be immediately re-dirtied.

Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
Cc: Dave Jiang <dave.jiang@intel.com>
Cc: Alison Schofield <alison.schofield@intel.com>
Cc: Vishal Verma <vishal.l.verma@intel.com>
Cc: Ira Weiny <ira.weiny@intel.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 drivers/cxl/core/region.c | 99 +++++++++++++++++++++++++++------------
 1 file changed, 70 insertions(+), 29 deletions(-)

diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 6e5e1460068d..3a77aec2c447 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -349,30 +349,42 @@ static int cxl_region_decode_commit(struct cxl_region *cxlr)
 	return rc;
 }
 
-static ssize_t commit_store(struct device *dev, struct device_attribute *attr,
-			    const char *buf, size_t len)
+static int queue_reset(struct cxl_region *cxlr)
 {
-	struct cxl_region *cxlr = to_cxl_region(dev);
 	struct cxl_region_params *p = &cxlr->params;
-	bool commit;
-	ssize_t rc;
+	int rc;
 
-	rc = kstrtobool(buf, &commit);
+	rc = down_write_killable(&cxl_region_rwsem);
 	if (rc)
 		return rc;
 
+	/* Already in the requested state? */
+	if (p->state < CXL_CONFIG_COMMIT)
+		goto out;
+
+	p->state = CXL_CONFIG_RESET_PENDING;
+
+out:
+	up_write(&cxl_region_rwsem);
+
+	return rc;
+}
+
+static int __commit(struct cxl_region *cxlr)
+{
+	struct cxl_region_params *p = &cxlr->params;
+	int rc;
+
 	rc = down_write_killable(&cxl_region_rwsem);
 	if (rc)
 		return rc;
 
 	/* Already in the requested state? */
-	if (commit && p->state >= CXL_CONFIG_COMMIT)
-		goto out;
-	if (!commit && p->state < CXL_CONFIG_COMMIT)
+	if (p->state >= CXL_CONFIG_COMMIT)
 		goto out;
 
 	/* Not ready to commit? */
-	if (commit && p->state < CXL_CONFIG_ACTIVE) {
+	if (p->state < CXL_CONFIG_ACTIVE) {
 		rc = -ENXIO;
 		goto out;
 	}
@@ -385,31 +397,60 @@ static ssize_t commit_store(struct device *dev, struct device_attribute *attr,
 	if (rc)
 		goto out;
 
-	if (commit) {
-		rc = cxl_region_decode_commit(cxlr);
-		if (rc == 0)
-			p->state = CXL_CONFIG_COMMIT;
-	} else {
-		p->state = CXL_CONFIG_RESET_PENDING;
-		up_write(&cxl_region_rwsem);
-		device_release_driver(&cxlr->dev);
-		down_write(&cxl_region_rwsem);
-
-		/*
-		 * The lock was dropped, so need to revalidate that the reset is
-		 * still pending.
-		 */
-		if (p->state == CXL_CONFIG_RESET_PENDING) {
-			cxl_region_decode_reset(cxlr, p->interleave_ways);
-			p->state = CXL_CONFIG_ACTIVE;
-		}
-	}
+	rc = cxl_region_decode_commit(cxlr);
+	if (rc == 0)
+		p->state = CXL_CONFIG_COMMIT;
 
 out:
 	up_write(&cxl_region_rwsem);
 
+	return rc;
+}
+
+static ssize_t commit_store(struct device *dev, struct device_attribute *attr,
+			    const char *buf, size_t len)
+{
+	struct cxl_region *cxlr = to_cxl_region(dev);
+	struct cxl_region_params *p = &cxlr->params;
+	bool commit;
+	ssize_t rc;
+
+	rc = kstrtobool(buf, &commit);
 	if (rc)
 		return rc;
+
+	if (commit) {
+		rc = __commit(cxlr);
+		if (rc)
+			return rc;
+		return len;
+	}
+
+	rc = queue_reset(cxlr);
+	if (rc)
+		return rc;
+
+	/*
+	 * Unmap the region and depend the reset-pending state to ensure
+	 * it does not go active again until post reset
+	 */
+	device_release_driver(&cxlr->dev);
+
+	/*
+	 * With the reset pending take cxl_region_rwsem unconditionally
+	 * to ensure the reset gets handled before returning.
+	 */
+	guard(rwsem_write)(&cxl_region_rwsem);
+
+	/*
+	 * Revalidate that the reset is still pending in case another
+	 * thread already handled this reset.
+	 */
+	if (p->state == CXL_CONFIG_RESET_PENDING) {
+		cxl_region_decode_reset(cxlr, p->interleave_ways);
+		p->state = CXL_CONFIG_ACTIVE;
+	}
+
 	return len;
 }
 
-- 
2.50.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v3 6/8] cxl/region: Move ready-to-probe state check to a helper
  2025-07-11 23:49 [PATCH v3 0/8] cleanup: Introduce ACQUIRE(), a guard() for conditional locks Dan Williams
                   ` (4 preceding siblings ...)
  2025-07-11 23:49 ` [PATCH v3 5/8] cxl/region: Split commit_store() into __commit() and queue_reset() helpers Dan Williams
@ 2025-07-11 23:49 ` Dan Williams
  2025-07-14 22:02   ` Fabio M. De Francesco
  2025-07-15 15:33   ` Jonathan Cameron
  2025-07-11 23:49 ` [PATCH v3 7/8] cxl/region: Consolidate cxl_decoder_kill_region() and cxl_region_detach() Dan Williams
                   ` (3 subsequent siblings)
  9 siblings, 2 replies; 32+ messages in thread
From: Dan Williams @ 2025-07-11 23:49 UTC (permalink / raw)
  To: linux-cxl
  Cc: linux-kernel, Davidlohr Bueso, Jonathan Cameron, Dave Jiang,
	Alison Schofield, Vishal Verma, Ira Weiny, Peter Zijlstra (Intel)

Rather than unlocking the region rwsem in the middle of cxl_region_probe()
create a helper for determining when the region is ready-to-probe.

Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
Cc: Dave Jiang <dave.jiang@intel.com>
Cc: Alison Schofield <alison.schofield@intel.com>
Cc: Vishal Verma <vishal.l.verma@intel.com>
Cc: Ira Weiny <ira.weiny@intel.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 drivers/cxl/core/region.c | 24 ++++++++++++++++++------
 1 file changed, 18 insertions(+), 6 deletions(-)

diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 3a77aec2c447..2a97fa9a394f 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -3572,9 +3572,8 @@ static void shutdown_notifiers(void *_cxlr)
 	unregister_mt_adistance_algorithm(&cxlr->adist_notifier);
 }
 
-static int cxl_region_probe(struct device *dev)
+static int cxl_region_can_probe(struct cxl_region *cxlr)
 {
-	struct cxl_region *cxlr = to_cxl_region(dev);
 	struct cxl_region_params *p = &cxlr->params;
 	int rc;
 
@@ -3597,15 +3596,28 @@ static int cxl_region_probe(struct device *dev)
 		goto out;
 	}
 
-	/*
-	 * From this point on any path that changes the region's state away from
-	 * CXL_CONFIG_COMMIT is also responsible for releasing the driver.
-	 */
 out:
 	up_read(&cxl_region_rwsem);
 
 	if (rc)
 		return rc;
+	return 0;
+}
+
+static int cxl_region_probe(struct device *dev)
+{
+	struct cxl_region *cxlr = to_cxl_region(dev);
+	struct cxl_region_params *p = &cxlr->params;
+	int rc;
+
+	rc = cxl_region_can_probe(cxlr);
+	if (rc)
+		return rc;
+
+	/*
+	 * From this point on any path that changes the region's state away from
+	 * CXL_CONFIG_COMMIT is also responsible for releasing the driver.
+	 */
 
 	cxlr->memory_notifier.notifier_call = cxl_region_perf_attrs_callback;
 	cxlr->memory_notifier.priority = CXL_CALLBACK_PRI;
-- 
2.50.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v3 7/8] cxl/region: Consolidate cxl_decoder_kill_region() and cxl_region_detach()
  2025-07-11 23:49 [PATCH v3 0/8] cleanup: Introduce ACQUIRE(), a guard() for conditional locks Dan Williams
                   ` (5 preceding siblings ...)
  2025-07-11 23:49 ` [PATCH v3 6/8] cxl/region: Move ready-to-probe state check to a helper Dan Williams
@ 2025-07-11 23:49 ` Dan Williams
  2025-07-14 18:17   ` Dave Jiang
                     ` (2 more replies)
  2025-07-11 23:49 ` [PATCH v3 8/8] cxl: Convert to ACQUIRE() for conditional rwsem locking Dan Williams
                   ` (2 subsequent siblings)
  9 siblings, 3 replies; 32+ messages in thread
From: Dan Williams @ 2025-07-11 23:49 UTC (permalink / raw)
  To: linux-cxl
  Cc: linux-kernel, Davidlohr Bueso, Jonathan Cameron, Dave Jiang,
	Alison Schofield, Vishal Verma, Ira Weiny, Peter Zijlstra (Intel)

Both detach_target() and cxld_unregister() want to tear down a cxl_region
when an endpoint decoder is either detached or destroyed.

When a region is to be destroyed cxl_region_detach() releases
cxl_region_rwsem unbinds the cxl_region driver and re-acquires the rwsem.

This "reverse" locking pattern is difficult to reason about, not amenable
to scope-based cleanup, and the minor differences in the calling context of
detach_target() and cxld_unregister() currently results in the
cxl_decoder_kill_region() wrapper.

Introduce cxl_decoder_detach() to wrap a core __cxl_decoder_detach() that
serves both cases. I.e. either detaching a known position in a region
(interruptible), or detaching an endpoint decoder if it is found to be a
member of a region (uninterruptible).

Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
Cc: Dave Jiang <dave.jiang@intel.com>
Cc: Alison Schofield <alison.schofield@intel.com>
Cc: Vishal Verma <vishal.l.verma@intel.com>
Cc: Ira Weiny <ira.weiny@intel.com>
Acked-by: "Peter Zijlstra (Intel)" <peterz@infradead.org>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 drivers/cxl/core/core.h   |  15 +++++-
 drivers/cxl/core/port.c   |   9 ++--
 drivers/cxl/core/region.c | 103 ++++++++++++++++++++++----------------
 3 files changed, 75 insertions(+), 52 deletions(-)

diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index 29b61828a847..2be37084409f 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -12,6 +12,11 @@ extern const struct device_type cxl_pmu_type;
 
 extern struct attribute_group cxl_base_attribute_group;
 
+enum cxl_detach_mode {
+	DETACH_ONLY,
+	DETACH_INVALIDATE,
+};
+
 #ifdef CONFIG_CXL_REGION
 extern struct device_attribute dev_attr_create_pmem_region;
 extern struct device_attribute dev_attr_create_ram_region;
@@ -20,7 +25,11 @@ extern struct device_attribute dev_attr_region;
 extern const struct device_type cxl_pmem_region_type;
 extern const struct device_type cxl_dax_region_type;
 extern const struct device_type cxl_region_type;
-void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled);
+
+int cxl_decoder_detach(struct cxl_region *cxlr,
+		       struct cxl_endpoint_decoder *cxled, int pos,
+		       enum cxl_detach_mode mode);
+
 #define CXL_REGION_ATTR(x) (&dev_attr_##x.attr)
 #define CXL_REGION_TYPE(x) (&cxl_region_type)
 #define SET_CXL_REGION_ATTR(x) (&dev_attr_##x.attr),
@@ -48,7 +57,9 @@ static inline int cxl_get_poison_by_endpoint(struct cxl_port *port)
 {
 	return 0;
 }
-static inline void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled)
+static inline int cxl_decoder_detach(struct cxl_region *cxlr,
+				     struct cxl_endpoint_decoder *cxled,
+				     int pos, enum cxl_detach_mode mode)
 {
 }
 static inline int cxl_region_init(void)
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index eb46c6764d20..087a20a9ee1c 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -2001,12 +2001,9 @@ EXPORT_SYMBOL_NS_GPL(cxl_decoder_add, "CXL");
 
 static void cxld_unregister(void *dev)
 {
-	struct cxl_endpoint_decoder *cxled;
-
-	if (is_endpoint_decoder(dev)) {
-		cxled = to_cxl_endpoint_decoder(dev);
-		cxl_decoder_kill_region(cxled);
-	}
+	if (is_endpoint_decoder(dev))
+		cxl_decoder_detach(NULL, to_cxl_endpoint_decoder(dev), -1,
+				   DETACH_INVALIDATE);
 
 	device_unregister(dev);
 }
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 2a97fa9a394f..4314aaed8ad8 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -2135,27 +2135,43 @@ static int cxl_region_attach(struct cxl_region *cxlr,
 	return 0;
 }
 
-static int cxl_region_detach(struct cxl_endpoint_decoder *cxled)
+static struct cxl_region *
+__cxl_decoder_detach(struct cxl_region *cxlr,
+		     struct cxl_endpoint_decoder *cxled, int pos,
+		     enum cxl_detach_mode mode)
 {
-	struct cxl_port *iter, *ep_port = cxled_to_port(cxled);
-	struct cxl_region *cxlr = cxled->cxld.region;
 	struct cxl_region_params *p;
-	int rc = 0;
 
 	lockdep_assert_held_write(&cxl_region_rwsem);
 
-	if (!cxlr)
-		return 0;
+	if (!cxled) {
+		p = &cxlr->params;
 
-	p = &cxlr->params;
-	get_device(&cxlr->dev);
+		if (pos >= p->interleave_ways) {
+			dev_dbg(&cxlr->dev, "position %d out of range %d\n",
+				pos, p->interleave_ways);
+			return ERR_PTR(-ENXIO);
+		}
+
+		if (!p->targets[pos])
+			return NULL;
+		cxled = p->targets[pos];
+	} else {
+		cxlr = cxled->cxld.region;
+		if (!cxlr)
+			return NULL;
+		p = &cxlr->params;
+	}
+
+	if (mode == DETACH_INVALIDATE)
+		cxled->part = -1;
 
 	if (p->state > CXL_CONFIG_ACTIVE) {
 		cxl_region_decode_reset(cxlr, p->interleave_ways);
 		p->state = CXL_CONFIG_ACTIVE;
 	}
 
-	for (iter = ep_port; !is_cxl_root(iter);
+	for (struct cxl_port *iter = cxled_to_port(cxled); !is_cxl_root(iter);
 	     iter = to_cxl_port(iter->dev.parent))
 		cxl_port_detach_region(iter, cxlr, cxled);
 
@@ -2166,7 +2182,7 @@ static int cxl_region_detach(struct cxl_endpoint_decoder *cxled)
 		dev_WARN_ONCE(&cxlr->dev, 1, "expected %s:%s at position %d\n",
 			      dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
 			      cxled->pos);
-		goto out;
+		return NULL;
 	}
 
 	if (p->state == CXL_CONFIG_ACTIVE) {
@@ -2180,21 +2196,42 @@ static int cxl_region_detach(struct cxl_endpoint_decoder *cxled)
 		.end = -1,
 	};
 
-	/* notify the region driver that one of its targets has departed */
-	up_write(&cxl_region_rwsem);
-	device_release_driver(&cxlr->dev);
-	down_write(&cxl_region_rwsem);
-out:
-	put_device(&cxlr->dev);
-	return rc;
+	get_device(&cxlr->dev);
+	return cxlr;
 }
 
-void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled)
+/*
+ * Cleanup a decoder's interest in a region. There are 2 cases to
+ * handle, removing an unknown @cxled from a known position in a region
+ * (detach_target()) or removing a known @cxled from an unknown @cxlr
+ * (cxld_unregister())
+ *
+ * When the detachment finds a region release the region driver.
+ */
+int cxl_decoder_detach(struct cxl_region *cxlr,
+		       struct cxl_endpoint_decoder *cxled, int pos,
+		       enum cxl_detach_mode mode)
 {
-	down_write(&cxl_region_rwsem);
-	cxled->part = -1;
-	cxl_region_detach(cxled);
+	struct cxl_region *detach;
+
+	/* when the decoder is being destroyed lock unconditionally */
+	if (mode == DETACH_INVALIDATE)
+		down_write(&cxl_region_rwsem);
+	else {
+		int rc = down_write_killable(&cxl_region_rwsem);
+
+		if (rc)
+			return rc;
+	}
+
+	detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);
 	up_write(&cxl_region_rwsem);
+
+	if (detach) {
+		device_release_driver(&detach->dev);
+		put_device(&detach->dev);
+	}
+	return 0;
 }
 
 static int attach_target(struct cxl_region *cxlr,
@@ -2225,29 +2262,7 @@ static int attach_target(struct cxl_region *cxlr,
 
 static int detach_target(struct cxl_region *cxlr, int pos)
 {
-	struct cxl_region_params *p = &cxlr->params;
-	int rc;
-
-	rc = down_write_killable(&cxl_region_rwsem);
-	if (rc)
-		return rc;
-
-	if (pos >= p->interleave_ways) {
-		dev_dbg(&cxlr->dev, "position %d out of range %d\n", pos,
-			p->interleave_ways);
-		rc = -ENXIO;
-		goto out;
-	}
-
-	if (!p->targets[pos]) {
-		rc = 0;
-		goto out;
-	}
-
-	rc = cxl_region_detach(p->targets[pos]);
-out:
-	up_write(&cxl_region_rwsem);
-	return rc;
+	return cxl_decoder_detach(cxlr, NULL, pos, DETACH_ONLY);
 }
 
 static size_t store_targetN(struct cxl_region *cxlr, const char *buf, int pos,
-- 
2.50.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v3 8/8] cxl: Convert to ACQUIRE() for conditional rwsem locking
  2025-07-11 23:49 [PATCH v3 0/8] cleanup: Introduce ACQUIRE(), a guard() for conditional locks Dan Williams
                   ` (6 preceding siblings ...)
  2025-07-11 23:49 ` [PATCH v3 7/8] cxl/region: Consolidate cxl_decoder_kill_region() and cxl_region_detach() Dan Williams
@ 2025-07-11 23:49 ` Dan Williams
  2025-07-14 16:28   ` Shiju Jose
                     ` (3 more replies)
  2025-07-16 20:52 ` [PATCH v3 0/8] cleanup: Introduce ACQUIRE(), a guard() for conditional locks Dave Jiang
  2025-07-30 15:10 ` Andy Shevchenko
  9 siblings, 4 replies; 32+ messages in thread
From: Dan Williams @ 2025-07-11 23:49 UTC (permalink / raw)
  To: linux-cxl
  Cc: linux-kernel, David Lechner, Peter Zijlstra, Linus Torvalds,
	Ingo Molnar, Fabio M. De Francesco, Davidlohr Bueso,
	Jonathan Cameron, Dave Jiang, Alison Schofield, Vishal Verma,
	Ira Weiny, Shiju Jose

Use ACQUIRE() to cleanup conditional locking paths in the CXL driver
The ACQUIRE() macro and its associated ACQUIRE_ERR() helpers, like
scoped_cond_guard(), arrange for scoped-based conditional locking. Unlike
scoped_cond_guard(), these macros arrange for an ERR_PTR() to be retrieved
representing the state of the conditional lock.

The goal of this conversion is to complete the removal of all explicit
unlock calls in the subsystem. I.e. the methods to acquire a lock are
solely via guard(), scoped_guard() (for limited cases), or ACQUIRE(). All
unlock is implicit / scope-based. In order to make sure all lock sites are
converted, the existing rwsem's are consolidated and renamed in 'struct
cxl_rwsem'. While that makes the patch noisier it gives a clean cut-off
between old-world (explicit unlock allowed), and new world (explicit unlock
deleted).

Cc: David Lechner <dlechner@baylibre.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: "Fabio M. De Francesco" <fabio.m.de.francesco@linux.intel.com>
Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
Cc: Dave Jiang <dave.jiang@intel.com>
Cc: Alison Schofield <alison.schofield@intel.com>
Cc: Vishal Verma <vishal.l.verma@intel.com>
Cc: Ira Weiny <ira.weiny@intel.com>
Cc: Shiju Jose <shiju.jose@huawei.com>
Acked-by: "Peter Zijlstra (Intel)" <peterz@infradead.org>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 drivers/cxl/core/cdat.c   |   6 +-
 drivers/cxl/core/core.h   |  17 ++-
 drivers/cxl/core/edac.c   |  44 +++---
 drivers/cxl/core/hdm.c    |  41 +++---
 drivers/cxl/core/mbox.c   |   6 +-
 drivers/cxl/core/memdev.c |  50 +++----
 drivers/cxl/core/port.c   |  18 +--
 drivers/cxl/core/region.c | 295 ++++++++++++++++----------------------
 drivers/cxl/cxl.h         |  13 +-
 include/linux/rwsem.h     |   1 +
 10 files changed, 212 insertions(+), 279 deletions(-)

diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
index 0ccef2f2a26a..c0af645425f4 100644
--- a/drivers/cxl/core/cdat.c
+++ b/drivers/cxl/core/cdat.c
@@ -336,7 +336,7 @@ static int match_cxlrd_hb(struct device *dev, void *data)
 	cxlrd = to_cxl_root_decoder(dev);
 	cxlsd = &cxlrd->cxlsd;
 
-	guard(rwsem_read)(&cxl_region_rwsem);
+	guard(rwsem_read)(&cxl_rwsem.region);
 	for (int i = 0; i < cxlsd->nr_targets; i++) {
 		if (host_bridge == cxlsd->target[i]->dport_dev)
 			return 1;
@@ -987,7 +987,7 @@ void cxl_region_shared_upstream_bandwidth_update(struct cxl_region *cxlr)
 	bool is_root;
 	int rc;
 
-	lockdep_assert_held(&cxl_dpa_rwsem);
+	lockdep_assert_held(&cxl_rwsem.dpa);
 
 	struct xarray *usp_xa __free(free_perf_xa) =
 		kzalloc(sizeof(*usp_xa), GFP_KERNEL);
@@ -1057,7 +1057,7 @@ void cxl_region_perf_data_calculate(struct cxl_region *cxlr,
 {
 	struct cxl_dpa_perf *perf;
 
-	lockdep_assert_held(&cxl_dpa_rwsem);
+	lockdep_assert_held(&cxl_rwsem.dpa);
 
 	perf = cxled_get_dpa_perf(cxled);
 	if (IS_ERR(perf))
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index 2be37084409f..f796731deedf 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -5,6 +5,7 @@
 #define __CXL_CORE_H__
 
 #include <cxl/mailbox.h>
+#include <linux/rwsem.h>
 
 extern const struct device_type cxl_nvdimm_bridge_type;
 extern const struct device_type cxl_nvdimm_type;
@@ -107,8 +108,20 @@ u16 cxl_rcrb_to_aer(struct device *dev, resource_size_t rcrb);
 #define PCI_RCRB_CAP_HDR_NEXT_MASK	GENMASK(15, 8)
 #define PCI_CAP_EXP_SIZEOF		0x3c
 
-extern struct rw_semaphore cxl_dpa_rwsem;
-extern struct rw_semaphore cxl_region_rwsem;
+struct cxl_rwsem {
+	/*
+	 * All changes to HPA (interleave configuration) occur with this
+	 * lock held for write.
+	 */
+	struct rw_semaphore region;
+	/*
+	 * All changes to a device DPA space occur with this lock held
+	 * for write.
+	 */
+	struct rw_semaphore dpa;
+};
+
+extern struct cxl_rwsem cxl_rwsem;
 
 int cxl_memdev_init(void);
 void cxl_memdev_exit(void);
diff --git a/drivers/cxl/core/edac.c b/drivers/cxl/core/edac.c
index 2cbc664e5d62..f1ebdbe222c8 100644
--- a/drivers/cxl/core/edac.c
+++ b/drivers/cxl/core/edac.c
@@ -115,10 +115,9 @@ static int cxl_scrub_get_attrbs(struct cxl_patrol_scrub_context *cxl_ps_ctx,
 						flags, min_cycle);
 	}
 
-	struct rw_semaphore *region_lock __free(rwsem_read_release) =
-		rwsem_read_intr_acquire(&cxl_region_rwsem);
-	if (!region_lock)
-		return -EINTR;
+	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
+	if ((ret = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
+		return ret;
 
 	cxlr = cxl_ps_ctx->cxlr;
 	p = &cxlr->params;
@@ -154,10 +153,9 @@ static int cxl_scrub_set_attrbs_region(struct device *dev,
 	struct cxl_region *cxlr;
 	int ret, i;
 
-	struct rw_semaphore *region_lock __free(rwsem_read_release) =
-		rwsem_read_intr_acquire(&cxl_region_rwsem);
-	if (!region_lock)
-		return -EINTR;
+	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
+	if ((ret = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
+		return ret;
 
 	cxlr = cxl_ps_ctx->cxlr;
 	p = &cxlr->params;
@@ -1332,16 +1330,15 @@ cxl_mem_perform_sparing(struct device *dev,
 	struct cxl_memdev_sparing_in_payload sparing_pi;
 	struct cxl_event_dram *rec = NULL;
 	u16 validity_flags = 0;
+	int ret;
 
-	struct rw_semaphore *region_lock __free(rwsem_read_release) =
-		rwsem_read_intr_acquire(&cxl_region_rwsem);
-	if (!region_lock)
-		return -EINTR;
+	ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
+	if ((ret = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
+		return ret;
 
-	struct rw_semaphore *dpa_lock __free(rwsem_read_release) =
-		rwsem_read_intr_acquire(&cxl_dpa_rwsem);
-	if (!dpa_lock)
-		return -EINTR;
+	ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
+	if ((ret = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
+		return ret;
 
 	if (!cxl_sparing_ctx->cap_safe_when_in_use) {
 		/* Memory to repair must be offline */
@@ -1779,16 +1776,15 @@ static int cxl_mem_perform_ppr(struct cxl_ppr_context *cxl_ppr_ctx)
 	struct cxl_memdev_ppr_maintenance_attrbs maintenance_attrbs;
 	struct cxl_memdev *cxlmd = cxl_ppr_ctx->cxlmd;
 	struct cxl_mem_repair_attrbs attrbs = { 0 };
+	int ret;
 
-	struct rw_semaphore *region_lock __free(rwsem_read_release) =
-		rwsem_read_intr_acquire(&cxl_region_rwsem);
-	if (!region_lock)
-		return -EINTR;
+	ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
+	if ((ret = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
+		return ret;
 
-	struct rw_semaphore *dpa_lock __free(rwsem_read_release) =
-		rwsem_read_intr_acquire(&cxl_dpa_rwsem);
-	if (!dpa_lock)
-		return -EINTR;
+	ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
+	if ((ret = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
+		return ret;
 
 	if (!cxl_ppr_ctx->media_accessible || !cxl_ppr_ctx->data_retained) {
 		/* Memory to repair must be offline */
diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index e9cb34e30248..865a71bce251 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -16,7 +16,10 @@
  * for enumerating these registers and capabilities.
  */
 
-DECLARE_RWSEM(cxl_dpa_rwsem);
+struct cxl_rwsem cxl_rwsem = {
+	.region = __RWSEM_INITIALIZER(cxl_rwsem.region),
+	.dpa = __RWSEM_INITIALIZER(cxl_rwsem.dpa),
+};
 
 static int add_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
 			   int *target_map)
@@ -214,7 +217,7 @@ void cxl_dpa_debug(struct seq_file *file, struct cxl_dev_state *cxlds)
 {
 	struct resource *p1, *p2;
 
-	guard(rwsem_read)(&cxl_dpa_rwsem);
+	guard(rwsem_read)(&cxl_rwsem.dpa);
 	for (p1 = cxlds->dpa_res.child; p1; p1 = p1->sibling) {
 		__cxl_dpa_debug(file, p1, 0);
 		for (p2 = p1->child; p2; p2 = p2->sibling)
@@ -266,7 +269,7 @@ static void __cxl_dpa_release(struct cxl_endpoint_decoder *cxled)
 	struct resource *res = cxled->dpa_res;
 	resource_size_t skip_start;
 
-	lockdep_assert_held_write(&cxl_dpa_rwsem);
+	lockdep_assert_held_write(&cxl_rwsem.dpa);
 
 	/* save @skip_start, before @res is released */
 	skip_start = res->start - cxled->skip;
@@ -281,7 +284,7 @@ static void __cxl_dpa_release(struct cxl_endpoint_decoder *cxled)
 
 static void cxl_dpa_release(void *cxled)
 {
-	guard(rwsem_write)(&cxl_dpa_rwsem);
+	guard(rwsem_write)(&cxl_rwsem.dpa);
 	__cxl_dpa_release(cxled);
 }
 
@@ -293,7 +296,7 @@ static void devm_cxl_dpa_release(struct cxl_endpoint_decoder *cxled)
 {
 	struct cxl_port *port = cxled_to_port(cxled);
 
-	lockdep_assert_held_write(&cxl_dpa_rwsem);
+	lockdep_assert_held_write(&cxl_rwsem.dpa);
 	devm_remove_action(&port->dev, cxl_dpa_release, cxled);
 	__cxl_dpa_release(cxled);
 }
@@ -361,7 +364,7 @@ static int __cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled,
 	struct resource *res;
 	int rc;
 
-	lockdep_assert_held_write(&cxl_dpa_rwsem);
+	lockdep_assert_held_write(&cxl_rwsem.dpa);
 
 	if (!len) {
 		dev_warn(dev, "decoder%d.%d: empty reservation attempted\n",
@@ -470,7 +473,7 @@ int cxl_dpa_setup(struct cxl_dev_state *cxlds, const struct cxl_dpa_info *info)
 {
 	struct device *dev = cxlds->dev;
 
-	guard(rwsem_write)(&cxl_dpa_rwsem);
+	guard(rwsem_write)(&cxl_rwsem.dpa);
 
 	if (cxlds->nr_partitions)
 		return -EBUSY;
@@ -516,9 +519,8 @@ int devm_cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled,
 	struct cxl_port *port = cxled_to_port(cxled);
 	int rc;
 
-	down_write(&cxl_dpa_rwsem);
-	rc = __cxl_dpa_reserve(cxled, base, len, skipped);
-	up_write(&cxl_dpa_rwsem);
+	scoped_guard(rwsem_write, &cxl_rwsem.dpa)
+		rc = __cxl_dpa_reserve(cxled, base, len, skipped);
 
 	if (rc)
 		return rc;
@@ -529,7 +531,7 @@ EXPORT_SYMBOL_NS_GPL(devm_cxl_dpa_reserve, "CXL");
 
 resource_size_t cxl_dpa_size(struct cxl_endpoint_decoder *cxled)
 {
-	guard(rwsem_read)(&cxl_dpa_rwsem);
+	guard(rwsem_read)(&cxl_rwsem.dpa);
 	if (cxled->dpa_res)
 		return resource_size(cxled->dpa_res);
 
@@ -540,7 +542,7 @@ resource_size_t cxl_dpa_resource_start(struct cxl_endpoint_decoder *cxled)
 {
 	resource_size_t base = -1;
 
-	lockdep_assert_held(&cxl_dpa_rwsem);
+	lockdep_assert_held(&cxl_rwsem.dpa);
 	if (cxled->dpa_res)
 		base = cxled->dpa_res->start;
 
@@ -552,7 +554,7 @@ int cxl_dpa_free(struct cxl_endpoint_decoder *cxled)
 	struct cxl_port *port = cxled_to_port(cxled);
 	struct device *dev = &cxled->cxld.dev;
 
-	guard(rwsem_write)(&cxl_dpa_rwsem);
+	guard(rwsem_write)(&cxl_rwsem.dpa);
 	if (!cxled->dpa_res)
 		return 0;
 	if (cxled->cxld.region) {
@@ -582,7 +584,7 @@ int cxl_dpa_set_part(struct cxl_endpoint_decoder *cxled,
 	struct device *dev = &cxled->cxld.dev;
 	int part;
 
-	guard(rwsem_write)(&cxl_dpa_rwsem);
+	guard(rwsem_write)(&cxl_rwsem.dpa);
 	if (cxled->cxld.flags & CXL_DECODER_F_ENABLE)
 		return -EBUSY;
 
@@ -614,7 +616,7 @@ static int __cxl_dpa_alloc(struct cxl_endpoint_decoder *cxled, u64 size)
 	struct resource *p, *last;
 	int part;
 
-	guard(rwsem_write)(&cxl_dpa_rwsem);
+	guard(rwsem_write)(&cxl_rwsem.dpa);
 	if (cxled->cxld.region) {
 		dev_dbg(dev, "decoder attached to %s\n",
 			dev_name(&cxled->cxld.region->dev));
@@ -842,9 +844,8 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)
 		}
 	}
 
-	down_read(&cxl_dpa_rwsem);
-	setup_hw_decoder(cxld, hdm);
-	up_read(&cxl_dpa_rwsem);
+	scoped_guard(rwsem_read, &cxl_rwsem.dpa)
+		setup_hw_decoder(cxld, hdm);
 
 	port->commit_end++;
 	rc = cxld_await_commit(hdm, cxld->id);
@@ -882,7 +883,7 @@ void cxl_port_commit_reap(struct cxl_decoder *cxld)
 {
 	struct cxl_port *port = to_cxl_port(cxld->dev.parent);
 
-	lockdep_assert_held_write(&cxl_region_rwsem);
+	lockdep_assert_held_write(&cxl_rwsem.region);
 
 	/*
 	 * Once the highest committed decoder is disabled, free any other
@@ -1030,7 +1031,7 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
 		else
 			cxld->target_type = CXL_DECODER_DEVMEM;
 
-		guard(rwsem_write)(&cxl_region_rwsem);
+		guard(rwsem_write)(&cxl_rwsem.region);
 		if (cxld->id != cxl_num_decoders_committed(port)) {
 			dev_warn(&port->dev,
 				 "decoder%d.%d: Committed out of order\n",
diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
index 81b21effe8cf..92cd3cbdd8ec 100644
--- a/drivers/cxl/core/mbox.c
+++ b/drivers/cxl/core/mbox.c
@@ -909,8 +909,8 @@ void cxl_event_trace_record(const struct cxl_memdev *cxlmd,
 		 * translations. Take topology mutation locks and lookup
 		 * { HPA, REGION } from { DPA, MEMDEV } in the event record.
 		 */
-		guard(rwsem_read)(&cxl_region_rwsem);
-		guard(rwsem_read)(&cxl_dpa_rwsem);
+		guard(rwsem_read)(&cxl_rwsem.region);
+		guard(rwsem_read)(&cxl_rwsem.dpa);
 
 		dpa = le64_to_cpu(evt->media_hdr.phys_addr) & CXL_DPA_MASK;
 		cxlr = cxl_dpa_to_region(cxlmd, dpa);
@@ -1265,7 +1265,7 @@ int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd)
 	/* synchronize with cxl_mem_probe() and decoder write operations */
 	guard(device)(&cxlmd->dev);
 	endpoint = cxlmd->endpoint;
-	guard(rwsem_read)(&cxl_region_rwsem);
+	guard(rwsem_read)(&cxl_rwsem.region);
 	/*
 	 * Require an endpoint to be safe otherwise the driver can not
 	 * be sure that the device is unmapped.
diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
index f88a13adf7fa..f5fbd34310fd 100644
--- a/drivers/cxl/core/memdev.c
+++ b/drivers/cxl/core/memdev.c
@@ -232,15 +232,13 @@ int cxl_trigger_poison_list(struct cxl_memdev *cxlmd)
 	if (!port || !is_cxl_endpoint(port))
 		return -EINVAL;
 
-	rc = down_read_interruptible(&cxl_region_rwsem);
-	if (rc)
+	ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
+	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
 		return rc;
 
-	rc = down_read_interruptible(&cxl_dpa_rwsem);
-	if (rc) {
-		up_read(&cxl_region_rwsem);
+	ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
+	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
 		return rc;
-	}
 
 	if (cxl_num_decoders_committed(port) == 0) {
 		/* No regions mapped to this memdev */
@@ -249,8 +247,6 @@ int cxl_trigger_poison_list(struct cxl_memdev *cxlmd)
 		/* Regions mapped, collect poison by endpoint */
 		rc =  cxl_get_poison_by_endpoint(port);
 	}
-	up_read(&cxl_dpa_rwsem);
-	up_read(&cxl_region_rwsem);
 
 	return rc;
 }
@@ -292,19 +288,17 @@ int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa)
 	if (!IS_ENABLED(CONFIG_DEBUG_FS))
 		return 0;
 
-	rc = down_read_interruptible(&cxl_region_rwsem);
-	if (rc)
+	ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
+	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
 		return rc;
 
-	rc = down_read_interruptible(&cxl_dpa_rwsem);
-	if (rc) {
-		up_read(&cxl_region_rwsem);
+	ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
+	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
 		return rc;
-	}
 
 	rc = cxl_validate_poison_dpa(cxlmd, dpa);
 	if (rc)
-		goto out;
+		return rc;
 
 	inject.address = cpu_to_le64(dpa);
 	mbox_cmd = (struct cxl_mbox_cmd) {
@@ -314,7 +308,7 @@ int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa)
 	};
 	rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd);
 	if (rc)
-		goto out;
+		return rc;
 
 	cxlr = cxl_dpa_to_region(cxlmd, dpa);
 	if (cxlr)
@@ -327,11 +321,8 @@ int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa)
 		.length = cpu_to_le32(1),
 	};
 	trace_cxl_poison(cxlmd, cxlr, &record, 0, 0, CXL_POISON_TRACE_INJECT);
-out:
-	up_read(&cxl_dpa_rwsem);
-	up_read(&cxl_region_rwsem);
 
-	return rc;
+	return 0;
 }
 EXPORT_SYMBOL_NS_GPL(cxl_inject_poison, "CXL");
 
@@ -347,19 +338,17 @@ int cxl_clear_poison(struct cxl_memdev *cxlmd, u64 dpa)
 	if (!IS_ENABLED(CONFIG_DEBUG_FS))
 		return 0;
 
-	rc = down_read_interruptible(&cxl_region_rwsem);
-	if (rc)
+	ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
+	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
 		return rc;
 
-	rc = down_read_interruptible(&cxl_dpa_rwsem);
-	if (rc) {
-		up_read(&cxl_region_rwsem);
+	ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
+	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
 		return rc;
-	}
 
 	rc = cxl_validate_poison_dpa(cxlmd, dpa);
 	if (rc)
-		goto out;
+		return rc;
 
 	/*
 	 * In CXL 3.0 Spec 8.2.9.8.4.3, the Clear Poison mailbox command
@@ -378,7 +367,7 @@ int cxl_clear_poison(struct cxl_memdev *cxlmd, u64 dpa)
 
 	rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd);
 	if (rc)
-		goto out;
+		return rc;
 
 	cxlr = cxl_dpa_to_region(cxlmd, dpa);
 	if (cxlr)
@@ -391,11 +380,8 @@ int cxl_clear_poison(struct cxl_memdev *cxlmd, u64 dpa)
 		.length = cpu_to_le32(1),
 	};
 	trace_cxl_poison(cxlmd, cxlr, &record, 0, 0, CXL_POISON_TRACE_CLEAR);
-out:
-	up_read(&cxl_dpa_rwsem);
-	up_read(&cxl_region_rwsem);
 
-	return rc;
+	return 0;
 }
 EXPORT_SYMBOL_NS_GPL(cxl_clear_poison, "CXL");
 
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 087a20a9ee1c..bacf1380dc4d 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -30,18 +30,12 @@
  * instantiated by the core.
  */
 
-/*
- * All changes to the interleave configuration occur with this lock held
- * for write.
- */
-DECLARE_RWSEM(cxl_region_rwsem);
-
 static DEFINE_IDA(cxl_port_ida);
 static DEFINE_XARRAY(cxl_root_buses);
 
 int cxl_num_decoders_committed(struct cxl_port *port)
 {
-	lockdep_assert_held(&cxl_region_rwsem);
+	lockdep_assert_held(&cxl_rwsem.region);
 
 	return port->commit_end + 1;
 }
@@ -176,7 +170,7 @@ static ssize_t target_list_show(struct device *dev,
 	ssize_t offset;
 	int rc;
 
-	guard(rwsem_read)(&cxl_region_rwsem);
+	guard(rwsem_read)(&cxl_rwsem.region);
 	rc = emit_target_list(cxlsd, buf);
 	if (rc < 0)
 		return rc;
@@ -196,7 +190,7 @@ static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
 	struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(dev);
 	struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
 	struct cxl_dev_state *cxlds = cxlmd->cxlds;
-	/* without @cxl_dpa_rwsem, make sure @part is not reloaded */
+	/* without @cxl_rwsem.dpa, make sure @part is not reloaded */
 	int part = READ_ONCE(cxled->part);
 	const char *desc;
 
@@ -235,7 +229,7 @@ static ssize_t dpa_resource_show(struct device *dev, struct device_attribute *at
 {
 	struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(dev);
 
-	guard(rwsem_read)(&cxl_dpa_rwsem);
+	guard(rwsem_read)(&cxl_rwsem.dpa);
 	return sysfs_emit(buf, "%#llx\n", (u64)cxl_dpa_resource_start(cxled));
 }
 static DEVICE_ATTR_RO(dpa_resource);
@@ -560,7 +554,7 @@ static ssize_t decoders_committed_show(struct device *dev,
 {
 	struct cxl_port *port = to_cxl_port(dev);
 
-	guard(rwsem_read)(&cxl_region_rwsem);
+	guard(rwsem_read)(&cxl_rwsem.region);
 	return sysfs_emit(buf, "%d\n", cxl_num_decoders_committed(port));
 }
 
@@ -1722,7 +1716,7 @@ static int decoder_populate_targets(struct cxl_switch_decoder *cxlsd,
 	if (xa_empty(&port->dports))
 		return -EINVAL;
 
-	guard(rwsem_write)(&cxl_region_rwsem);
+	guard(rwsem_write)(&cxl_rwsem.region);
 	for (i = 0; i < cxlsd->cxld.interleave_ways; i++) {
 		struct cxl_dport *dport = find_dport(port, target_map[i]);
 
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 4314aaed8ad8..ad60c93be803 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -141,16 +141,12 @@ static ssize_t uuid_show(struct device *dev, struct device_attribute *attr,
 	struct cxl_region_params *p = &cxlr->params;
 	ssize_t rc;
 
-	rc = down_read_interruptible(&cxl_region_rwsem);
-	if (rc)
+	ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
+	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
 		return rc;
 	if (cxlr->mode != CXL_PARTMODE_PMEM)
-		rc = sysfs_emit(buf, "\n");
-	else
-		rc = sysfs_emit(buf, "%pUb\n", &p->uuid);
-	up_read(&cxl_region_rwsem);
-
-	return rc;
+		return sysfs_emit(buf, "\n");
+	return sysfs_emit(buf, "%pUb\n", &p->uuid);
 }
 
 static int is_dup(struct device *match, void *data)
@@ -162,7 +158,7 @@ static int is_dup(struct device *match, void *data)
 	if (!is_cxl_region(match))
 		return 0;
 
-	lockdep_assert_held(&cxl_region_rwsem);
+	lockdep_assert_held(&cxl_rwsem.region);
 	cxlr = to_cxl_region(match);
 	p = &cxlr->params;
 
@@ -192,27 +188,22 @@ static ssize_t uuid_store(struct device *dev, struct device_attribute *attr,
 	if (uuid_is_null(&temp))
 		return -EINVAL;
 
-	rc = down_write_killable(&cxl_region_rwsem);
-	if (rc)
+	ACQUIRE(rwsem_write_kill, region_rwsem)(&cxl_rwsem.region);
+	if ((rc = ACQUIRE_ERR(rwsem_write_kill, &region_rwsem)))
 		return rc;
 
 	if (uuid_equal(&p->uuid, &temp))
-		goto out;
+		return len;
 
-	rc = -EBUSY;
 	if (p->state >= CXL_CONFIG_ACTIVE)
-		goto out;
+		return -EBUSY;
 
 	rc = bus_for_each_dev(&cxl_bus_type, NULL, &temp, is_dup);
 	if (rc < 0)
-		goto out;
+		return rc;
 
 	uuid_copy(&p->uuid, &temp);
-out:
-	up_write(&cxl_region_rwsem);
 
-	if (rc)
-		return rc;
 	return len;
 }
 static DEVICE_ATTR_RW(uuid);
@@ -354,20 +345,17 @@ static int queue_reset(struct cxl_region *cxlr)
 	struct cxl_region_params *p = &cxlr->params;
 	int rc;
 
-	rc = down_write_killable(&cxl_region_rwsem);
-	if (rc)
+	ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
+	if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
 		return rc;
 
 	/* Already in the requested state? */
 	if (p->state < CXL_CONFIG_COMMIT)
-		goto out;
+		return 0;
 
 	p->state = CXL_CONFIG_RESET_PENDING;
 
-out:
-	up_write(&cxl_region_rwsem);
-
-	return rc;
+	return 0;
 }
 
 static int __commit(struct cxl_region *cxlr)
@@ -375,19 +363,17 @@ static int __commit(struct cxl_region *cxlr)
 	struct cxl_region_params *p = &cxlr->params;
 	int rc;
 
-	rc = down_write_killable(&cxl_region_rwsem);
-	if (rc)
+	ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
+	if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
 		return rc;
 
 	/* Already in the requested state? */
 	if (p->state >= CXL_CONFIG_COMMIT)
-		goto out;
+		return 0;
 
 	/* Not ready to commit? */
-	if (p->state < CXL_CONFIG_ACTIVE) {
-		rc = -ENXIO;
-		goto out;
-	}
+	if (p->state < CXL_CONFIG_ACTIVE)
+		return -ENXIO;
 
 	/*
 	 * Invalidate caches before region setup to drop any speculative
@@ -395,16 +381,15 @@ static int __commit(struct cxl_region *cxlr)
 	 */
 	rc = cxl_region_invalidate_memregion(cxlr);
 	if (rc)
-		goto out;
+		return rc;
 
 	rc = cxl_region_decode_commit(cxlr);
-	if (rc == 0)
-		p->state = CXL_CONFIG_COMMIT;
+	if (rc)
+		return rc;
 
-out:
-	up_write(&cxl_region_rwsem);
+	p->state = CXL_CONFIG_COMMIT;
 
-	return rc;
+	return 0;
 }
 
 static ssize_t commit_store(struct device *dev, struct device_attribute *attr,
@@ -437,10 +422,10 @@ static ssize_t commit_store(struct device *dev, struct device_attribute *attr,
 	device_release_driver(&cxlr->dev);
 
 	/*
-	 * With the reset pending take cxl_region_rwsem unconditionally
+	 * With the reset pending take cxl_rwsem.region unconditionally
 	 * to ensure the reset gets handled before returning.
 	 */
-	guard(rwsem_write)(&cxl_region_rwsem);
+	guard(rwsem_write)(&cxl_rwsem.region);
 
 	/*
 	 * Revalidate that the reset is still pending in case another
@@ -461,13 +446,10 @@ static ssize_t commit_show(struct device *dev, struct device_attribute *attr,
 	struct cxl_region_params *p = &cxlr->params;
 	ssize_t rc;
 
-	rc = down_read_interruptible(&cxl_region_rwsem);
-	if (rc)
+	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
+	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
 		return rc;
-	rc = sysfs_emit(buf, "%d\n", p->state >= CXL_CONFIG_COMMIT);
-	up_read(&cxl_region_rwsem);
-
-	return rc;
+	return sysfs_emit(buf, "%d\n", p->state >= CXL_CONFIG_COMMIT);
 }
 static DEVICE_ATTR_RW(commit);
 
@@ -491,15 +473,12 @@ static ssize_t interleave_ways_show(struct device *dev,
 {
 	struct cxl_region *cxlr = to_cxl_region(dev);
 	struct cxl_region_params *p = &cxlr->params;
-	ssize_t rc;
+	int rc;
 
-	rc = down_read_interruptible(&cxl_region_rwsem);
-	if (rc)
+	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
+	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
 		return rc;
-	rc = sysfs_emit(buf, "%d\n", p->interleave_ways);
-	up_read(&cxl_region_rwsem);
-
-	return rc;
+	return sysfs_emit(buf, "%d\n", p->interleave_ways);
 }
 
 static const struct attribute_group *get_cxl_region_target_group(void);
@@ -534,23 +513,21 @@ static ssize_t interleave_ways_store(struct device *dev,
 		return -EINVAL;
 	}
 
-	rc = down_write_killable(&cxl_region_rwsem);
-	if (rc)
+	ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
+	if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
 		return rc;
-	if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) {
-		rc = -EBUSY;
-		goto out;
-	}
+
+	if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE)
+		return -EBUSY;
 
 	save = p->interleave_ways;
 	p->interleave_ways = val;
 	rc = sysfs_update_group(&cxlr->dev.kobj, get_cxl_region_target_group());
-	if (rc)
+	if (rc) {
 		p->interleave_ways = save;
-out:
-	up_write(&cxl_region_rwsem);
-	if (rc)
 		return rc;
+	}
+
 	return len;
 }
 static DEVICE_ATTR_RW(interleave_ways);
@@ -561,15 +538,12 @@ static ssize_t interleave_granularity_show(struct device *dev,
 {
 	struct cxl_region *cxlr = to_cxl_region(dev);
 	struct cxl_region_params *p = &cxlr->params;
-	ssize_t rc;
+	int rc;
 
-	rc = down_read_interruptible(&cxl_region_rwsem);
-	if (rc)
+	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
+	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
 		return rc;
-	rc = sysfs_emit(buf, "%d\n", p->interleave_granularity);
-	up_read(&cxl_region_rwsem);
-
-	return rc;
+	return sysfs_emit(buf, "%d\n", p->interleave_granularity);
 }
 
 static ssize_t interleave_granularity_store(struct device *dev,
@@ -602,19 +576,15 @@ static ssize_t interleave_granularity_store(struct device *dev,
 	if (cxld->interleave_ways > 1 && val != cxld->interleave_granularity)
 		return -EINVAL;
 
-	rc = down_write_killable(&cxl_region_rwsem);
-	if (rc)
+	ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
+	if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
 		return rc;
-	if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) {
-		rc = -EBUSY;
-		goto out;
-	}
+
+	if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE)
+		return -EBUSY;
 
 	p->interleave_granularity = val;
-out:
-	up_write(&cxl_region_rwsem);
-	if (rc)
-		return rc;
+
 	return len;
 }
 static DEVICE_ATTR_RW(interleave_granularity);
@@ -625,17 +595,15 @@ static ssize_t resource_show(struct device *dev, struct device_attribute *attr,
 	struct cxl_region *cxlr = to_cxl_region(dev);
 	struct cxl_region_params *p = &cxlr->params;
 	u64 resource = -1ULL;
-	ssize_t rc;
+	int rc;
 
-	rc = down_read_interruptible(&cxl_region_rwsem);
-	if (rc)
+	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
+	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
 		return rc;
+
 	if (p->res)
 		resource = p->res->start;
-	rc = sysfs_emit(buf, "%#llx\n", resource);
-	up_read(&cxl_region_rwsem);
-
-	return rc;
+	return sysfs_emit(buf, "%#llx\n", resource);
 }
 static DEVICE_ATTR_RO(resource);
 
@@ -663,7 +631,7 @@ static int alloc_hpa(struct cxl_region *cxlr, resource_size_t size)
 	struct resource *res;
 	u64 remainder = 0;
 
-	lockdep_assert_held_write(&cxl_region_rwsem);
+	lockdep_assert_held_write(&cxl_rwsem.region);
 
 	/* Nothing to do... */
 	if (p->res && resource_size(p->res) == size)
@@ -705,7 +673,7 @@ static void cxl_region_iomem_release(struct cxl_region *cxlr)
 	struct cxl_region_params *p = &cxlr->params;
 
 	if (device_is_registered(&cxlr->dev))
-		lockdep_assert_held_write(&cxl_region_rwsem);
+		lockdep_assert_held_write(&cxl_rwsem.region);
 	if (p->res) {
 		/*
 		 * Autodiscovered regions may not have been able to insert their
@@ -722,7 +690,7 @@ static int free_hpa(struct cxl_region *cxlr)
 {
 	struct cxl_region_params *p = &cxlr->params;
 
-	lockdep_assert_held_write(&cxl_region_rwsem);
+	lockdep_assert_held_write(&cxl_rwsem.region);
 
 	if (!p->res)
 		return 0;
@@ -746,15 +714,14 @@ static ssize_t size_store(struct device *dev, struct device_attribute *attr,
 	if (rc)
 		return rc;
 
-	rc = down_write_killable(&cxl_region_rwsem);
-	if (rc)
+	ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
+	if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
 		return rc;
 
 	if (val)
 		rc = alloc_hpa(cxlr, val);
 	else
 		rc = free_hpa(cxlr);
-	up_write(&cxl_region_rwsem);
 
 	if (rc)
 		return rc;
@@ -770,15 +737,12 @@ static ssize_t size_show(struct device *dev, struct device_attribute *attr,
 	u64 size = 0;
 	ssize_t rc;
 
-	rc = down_read_interruptible(&cxl_region_rwsem);
-	if (rc)
+	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
+	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
 		return rc;
 	if (p->res)
 		size = resource_size(p->res);
-	rc = sysfs_emit(buf, "%#llx\n", size);
-	up_read(&cxl_region_rwsem);
-
-	return rc;
+	return sysfs_emit(buf, "%#llx\n", size);
 }
 static DEVICE_ATTR_RW(size);
 
@@ -804,26 +768,20 @@ static size_t show_targetN(struct cxl_region *cxlr, char *buf, int pos)
 	struct cxl_endpoint_decoder *cxled;
 	int rc;
 
-	rc = down_read_interruptible(&cxl_region_rwsem);
-	if (rc)
+	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
+	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
 		return rc;
 
 	if (pos >= p->interleave_ways) {
 		dev_dbg(&cxlr->dev, "position %d out of range %d\n", pos,
 			p->interleave_ways);
-		rc = -ENXIO;
-		goto out;
+		return -ENXIO;
 	}
 
 	cxled = p->targets[pos];
 	if (!cxled)
-		rc = sysfs_emit(buf, "\n");
-	else
-		rc = sysfs_emit(buf, "%s\n", dev_name(&cxled->cxld.dev));
-out:
-	up_read(&cxl_region_rwsem);
-
-	return rc;
+		return sysfs_emit(buf, "\n");
+	return sysfs_emit(buf, "%s\n", dev_name(&cxled->cxld.dev));
 }
 
 static int check_commit_order(struct device *dev, void *data)
@@ -938,7 +896,7 @@ cxl_port_pick_region_decoder(struct cxl_port *port,
 	/*
 	 * This decoder is pinned registered as long as the endpoint decoder is
 	 * registered, and endpoint decoder unregistration holds the
-	 * cxl_region_rwsem over unregister events, so no need to hold on to
+	 * cxl_rwsem.region over unregister events, so no need to hold on to
 	 * this extra reference.
 	 */
 	put_device(dev);
@@ -1129,7 +1087,7 @@ static int cxl_port_attach_region(struct cxl_port *port,
 	unsigned long index;
 	int rc = -EBUSY;
 
-	lockdep_assert_held_write(&cxl_region_rwsem);
+	lockdep_assert_held_write(&cxl_rwsem.region);
 
 	cxl_rr = cxl_rr_load(port, cxlr);
 	if (cxl_rr) {
@@ -1239,7 +1197,7 @@ static void cxl_port_detach_region(struct cxl_port *port,
 	struct cxl_region_ref *cxl_rr;
 	struct cxl_ep *ep = NULL;
 
-	lockdep_assert_held_write(&cxl_region_rwsem);
+	lockdep_assert_held_write(&cxl_rwsem.region);
 
 	cxl_rr = cxl_rr_load(port, cxlr);
 	if (!cxl_rr)
@@ -2142,7 +2100,7 @@ __cxl_decoder_detach(struct cxl_region *cxlr,
 {
 	struct cxl_region_params *p;
 
-	lockdep_assert_held_write(&cxl_region_rwsem);
+	lockdep_assert_held_write(&cxl_rwsem.region);
 
 	if (!cxled) {
 		p = &cxlr->params;
@@ -2215,18 +2173,18 @@ int cxl_decoder_detach(struct cxl_region *cxlr,
 	struct cxl_region *detach;
 
 	/* when the decoder is being destroyed lock unconditionally */
-	if (mode == DETACH_INVALIDATE)
-		down_write(&cxl_region_rwsem);
-	else {
-		int rc = down_write_killable(&cxl_region_rwsem);
+	if (mode == DETACH_INVALIDATE) {
+		guard(rwsem_write)(&cxl_rwsem.region);
+		detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);
+	} else {
+		int rc;
 
-		if (rc)
+		ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
+		if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
 			return rc;
+		detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);
 	}
 
-	detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);
-	up_write(&cxl_region_rwsem);
-
 	if (detach) {
 		device_release_driver(&detach->dev);
 		put_device(&detach->dev);
@@ -2234,29 +2192,35 @@ int cxl_decoder_detach(struct cxl_region *cxlr,
 	return 0;
 }
 
+static int __attach_target(struct cxl_region *cxlr,
+			   struct cxl_endpoint_decoder *cxled, int pos,
+			   unsigned int state)
+{
+	int rc;
+
+	if (state == TASK_INTERRUPTIBLE) {
+		ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
+		if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
+			return rc;
+		guard(rwsem_read)(&cxl_rwsem.dpa);
+		return cxl_region_attach(cxlr, cxled, pos);
+	}
+	guard(rwsem_write)(&cxl_rwsem.region);
+	guard(rwsem_read)(&cxl_rwsem.dpa);
+	return cxl_region_attach(cxlr, cxled, pos);
+}
+
 static int attach_target(struct cxl_region *cxlr,
 			 struct cxl_endpoint_decoder *cxled, int pos,
 			 unsigned int state)
 {
-	int rc = 0;
-
-	if (state == TASK_INTERRUPTIBLE)
-		rc = down_write_killable(&cxl_region_rwsem);
-	else
-		down_write(&cxl_region_rwsem);
-	if (rc)
-		return rc;
-
-	down_read(&cxl_dpa_rwsem);
-	rc = cxl_region_attach(cxlr, cxled, pos);
-	up_read(&cxl_dpa_rwsem);
-	up_write(&cxl_region_rwsem);
+	int rc = __attach_target(cxlr, cxled, pos, state);
 
-	if (rc)
-		dev_warn(cxled->cxld.dev.parent,
-			"failed to attach %s to %s: %d\n",
-			dev_name(&cxled->cxld.dev), dev_name(&cxlr->dev), rc);
+	if (rc == 0)
+		return 0;
 
+	dev_warn(cxled->cxld.dev.parent, "failed to attach %s to %s: %d\n",
+		 dev_name(&cxled->cxld.dev), dev_name(&cxlr->dev), rc);
 	return rc;
 }
 
@@ -2516,7 +2480,7 @@ static int cxl_region_perf_attrs_callback(struct notifier_block *nb,
 		return NOTIFY_DONE;
 
 	/*
-	 * No need to hold cxl_region_rwsem; region parameters are stable
+	 * No need to hold cxl_rwsem.region; region parameters are stable
 	 * within the cxl_region driver.
 	 */
 	region_nid = phys_to_target_node(cxlr->params.res->start);
@@ -2539,7 +2503,7 @@ static int cxl_region_calculate_adistance(struct notifier_block *nb,
 	int region_nid;
 
 	/*
-	 * No need to hold cxl_region_rwsem; region parameters are stable
+	 * No need to hold cxl_rwsem.region; region parameters are stable
 	 * within the cxl_region driver.
 	 */
 	region_nid = phys_to_target_node(cxlr->params.res->start);
@@ -2688,17 +2652,13 @@ static ssize_t region_show(struct device *dev, struct device_attribute *attr,
 	struct cxl_decoder *cxld = to_cxl_decoder(dev);
 	ssize_t rc;
 
-	rc = down_read_interruptible(&cxl_region_rwsem);
-	if (rc)
+	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
+	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
 		return rc;
 
 	if (cxld->region)
-		rc = sysfs_emit(buf, "%s\n", dev_name(&cxld->region->dev));
-	else
-		rc = sysfs_emit(buf, "\n");
-	up_read(&cxl_region_rwsem);
-
-	return rc;
+		return sysfs_emit(buf, "%s\n", dev_name(&cxld->region->dev));
+	return sysfs_emit(buf, "\n");
 }
 DEVICE_ATTR_RO(region);
 
@@ -3037,7 +2997,7 @@ static int cxl_pmem_region_alloc(struct cxl_region *cxlr)
 	struct device *dev;
 	int i;
 
-	guard(rwsem_read)(&cxl_region_rwsem);
+	guard(rwsem_read)(&cxl_rwsem.region);
 	if (p->state != CXL_CONFIG_COMMIT)
 		return -ENXIO;
 
@@ -3049,7 +3009,7 @@ static int cxl_pmem_region_alloc(struct cxl_region *cxlr)
 	cxlr_pmem->hpa_range.start = p->res->start;
 	cxlr_pmem->hpa_range.end = p->res->end;
 
-	/* Snapshot the region configuration underneath the cxl_region_rwsem */
+	/* Snapshot the region configuration underneath the cxl_rwsem.region */
 	cxlr_pmem->nr_mappings = p->nr_targets;
 	for (i = 0; i < p->nr_targets; i++) {
 		struct cxl_endpoint_decoder *cxled = p->targets[i];
@@ -3126,7 +3086,7 @@ static struct cxl_dax_region *cxl_dax_region_alloc(struct cxl_region *cxlr)
 	struct cxl_dax_region *cxlr_dax;
 	struct device *dev;
 
-	guard(rwsem_read)(&cxl_region_rwsem);
+	guard(rwsem_read)(&cxl_rwsem.region);
 	if (p->state != CXL_CONFIG_COMMIT)
 		return ERR_PTR(-ENXIO);
 
@@ -3326,7 +3286,7 @@ static int match_region_by_range(struct device *dev, const void *data)
 	cxlr = to_cxl_region(dev);
 	p = &cxlr->params;
 
-	guard(rwsem_read)(&cxl_region_rwsem);
+	guard(rwsem_read)(&cxl_rwsem.region);
 	if (p->res && p->res->start == r->start && p->res->end == r->end)
 		return 1;
 
@@ -3386,7 +3346,7 @@ static int __construct_region(struct cxl_region *cxlr,
 	struct resource *res;
 	int rc;
 
-	guard(rwsem_write)(&cxl_region_rwsem);
+	guard(rwsem_write)(&cxl_rwsem.region);
 	p = &cxlr->params;
 	if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) {
 		dev_err(cxlmd->dev.parent,
@@ -3522,10 +3482,10 @@ int cxl_add_to_region(struct cxl_endpoint_decoder *cxled)
 
 	attach_target(cxlr, cxled, -1, TASK_UNINTERRUPTIBLE);
 
-	down_read(&cxl_region_rwsem);
-	p = &cxlr->params;
-	attach = p->state == CXL_CONFIG_COMMIT;
-	up_read(&cxl_region_rwsem);
+	scoped_guard(rwsem_read, &cxl_rwsem.region) {
+		p = &cxlr->params;
+		attach = p->state == CXL_CONFIG_COMMIT;
+	}
 
 	if (attach) {
 		/*
@@ -3550,7 +3510,7 @@ u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, u64 spa)
 	if (!endpoint)
 		return ~0ULL;
 
-	guard(rwsem_write)(&cxl_region_rwsem);
+	guard(rwsem_write)(&cxl_rwsem.region);
 
 	xa_for_each(&endpoint->regions, index, iter) {
 		struct cxl_region_params *p = &iter->region->params;
@@ -3592,30 +3552,23 @@ static int cxl_region_can_probe(struct cxl_region *cxlr)
 	struct cxl_region_params *p = &cxlr->params;
 	int rc;
 
-	rc = down_read_interruptible(&cxl_region_rwsem);
-	if (rc) {
+	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
+	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem))) {
 		dev_dbg(&cxlr->dev, "probe interrupted\n");
 		return rc;
 	}
 
 	if (p->state < CXL_CONFIG_COMMIT) {
 		dev_dbg(&cxlr->dev, "config state: %d\n", p->state);
-		rc = -ENXIO;
-		goto out;
+		return -ENXIO;
 	}
 
 	if (test_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags)) {
 		dev_err(&cxlr->dev,
 			"failed to activate, re-commit region and retry\n");
-		rc = -ENXIO;
-		goto out;
+		return -ENXIO;
 	}
 
-out:
-	up_read(&cxl_region_rwsem);
-
-	if (rc)
-		return rc;
 	return 0;
 }
 
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 3f1695c96abc..50799a681231 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -469,7 +469,7 @@ enum cxl_config_state {
  * @nr_targets: number of targets
  * @cache_size: extended linear cache size if exists, otherwise zero.
  *
- * State transitions are protected by the cxl_region_rwsem
+ * State transitions are protected by cxl_rwsem.region
  */
 struct cxl_region_params {
 	enum cxl_config_state state;
@@ -912,15 +912,4 @@ bool cxl_endpoint_decoder_reset_detected(struct cxl_port *port);
 #endif
 
 u16 cxl_gpf_get_dvsec(struct device *dev);
-
-static inline struct rw_semaphore *rwsem_read_intr_acquire(struct rw_semaphore *rwsem)
-{
-	if (down_read_interruptible(rwsem))
-		return NULL;
-
-	return rwsem;
-}
-
-DEFINE_FREE(rwsem_read_release, struct rw_semaphore *, if (_T) up_read(_T))
-
 #endif /* __CXL_H__ */
diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h
index c810deb88d13..cbafdc12e743 100644
--- a/include/linux/rwsem.h
+++ b/include/linux/rwsem.h
@@ -244,6 +244,7 @@ DEFINE_GUARD_COND(rwsem_read, _intr, down_read_interruptible(_T), _RET == 0)
 
 DEFINE_GUARD(rwsem_write, struct rw_semaphore *, down_write(_T), up_write(_T))
 DEFINE_GUARD_COND(rwsem_write, _try, down_write_trylock(_T))
+DEFINE_GUARD_COND(rwsem_write, _kill, down_write_killable(_T), _RET == 0)
 
 /*
  * downgrade write lock to read lock
-- 
2.50.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* RE: [PATCH v3 8/8] cxl: Convert to ACQUIRE() for conditional rwsem locking
  2025-07-11 23:49 ` [PATCH v3 8/8] cxl: Convert to ACQUIRE() for conditional rwsem locking Dan Williams
@ 2025-07-14 16:28   ` Shiju Jose
  2025-07-14 19:21     ` dan.j.williams
  2025-07-14 18:39   ` Dave Jiang
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 32+ messages in thread
From: Shiju Jose @ 2025-07-14 16:28 UTC (permalink / raw)
  To: Dan Williams, linux-cxl@vger.kernel.org
  Cc: linux-kernel@vger.kernel.org, David Lechner, Peter Zijlstra,
	Linus Torvalds, Ingo Molnar, Fabio M. De Francesco,
	Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
	Vishal Verma, Ira Weiny

>-----Original Message-----
>From: Dan Williams <dan.j.williams@intel.com>
>Sent: 12 July 2025 00:50
>To: linux-cxl@vger.kernel.org
>Cc: linux-kernel@vger.kernel.org; David Lechner <dlechner@baylibre.com>;
>Peter Zijlstra <peterz@infradead.org>; Linus Torvalds <torvalds@linux-
>foundation.org>; Ingo Molnar <mingo@kernel.org>; Fabio M. De Francesco
><fabio.m.de.francesco@linux.intel.com>; Davidlohr Bueso <dave@stgolabs.net>;
>Jonathan Cameron <jonathan.cameron@huawei.com>; Dave Jiang
><dave.jiang@intel.com>; Alison Schofield <alison.schofield@intel.com>; Vishal
>Verma <vishal.l.verma@intel.com>; Ira Weiny <ira.weiny@intel.com>; Shiju Jose
><shiju.jose@huawei.com>
>Subject: [PATCH v3 8/8] cxl: Convert to ACQUIRE() for conditional rwsem locking
>
>Use ACQUIRE() to cleanup conditional locking paths in the CXL driver The
>ACQUIRE() macro and its associated ACQUIRE_ERR() helpers, like
>scoped_cond_guard(), arrange for scoped-based conditional locking. Unlike
>scoped_cond_guard(), these macros arrange for an ERR_PTR() to be retrieved
>representing the state of the conditional lock.
>
>The goal of this conversion is to complete the removal of all explicit unlock calls
>in the subsystem. I.e. the methods to acquire a lock are solely via guard(),
>scoped_guard() (for limited cases), or ACQUIRE(). All unlock is implicit / scope-
>based. In order to make sure all lock sites are converted, the existing rwsem's
>are consolidated and renamed in 'struct cxl_rwsem'. While that makes the patch
>noisier it gives a clean cut-off between old-world (explicit unlock allowed), and
>new world (explicit unlock deleted).
>
>Cc: David Lechner <dlechner@baylibre.com>
>Cc: Peter Zijlstra <peterz@infradead.org>
>Cc: Linus Torvalds <torvalds@linux-foundation.org>
>Cc: Ingo Molnar <mingo@kernel.org>
>Cc: "Fabio M. De Francesco" <fabio.m.de.francesco@linux.intel.com>
>Cc: Davidlohr Bueso <dave@stgolabs.net>
>Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
>Cc: Dave Jiang <dave.jiang@intel.com>
>Cc: Alison Schofield <alison.schofield@intel.com>
>Cc: Vishal Verma <vishal.l.verma@intel.com>
>Cc: Ira Weiny <ira.weiny@intel.com>
>Cc: Shiju Jose <shiju.jose@huawei.com>
>Acked-by: "Peter Zijlstra (Intel)" <peterz@infradead.org>
>Signed-off-by: Dan Williams <dan.j.williams@intel.com>

Hi Dan,

For changes in CXL EDAC (drivers/cxl/core/edac.c),
Tested-by: Shiju Jose <shiju.jose@huawei.com>

>---
> drivers/cxl/core/cdat.c   |   6 +-
> drivers/cxl/core/core.h   |  17 ++-
> drivers/cxl/core/edac.c   |  44 +++---
> drivers/cxl/core/hdm.c    |  41 +++---
> drivers/cxl/core/mbox.c   |   6 +-
> drivers/cxl/core/memdev.c |  50 +++----
> drivers/cxl/core/port.c   |  18 +--
> drivers/cxl/core/region.c | 295 ++++++++++++++++----------------------
> drivers/cxl/cxl.h         |  13 +-
> include/linux/rwsem.h     |   1 +
> 10 files changed, 212 insertions(+), 279 deletions(-)
>
[...]
>diff --git a/drivers/cxl/core/edac.c b/drivers/cxl/core/edac.c index
>2cbc664e5d62..f1ebdbe222c8 100644
>--- a/drivers/cxl/core/edac.c
>+++ b/drivers/cxl/core/edac.c
>@@ -115,10 +115,9 @@ static int cxl_scrub_get_attrbs(struct
>cxl_patrol_scrub_context *cxl_ps_ctx,
> 						flags, min_cycle);
> 	}
>
>-	struct rw_semaphore *region_lock __free(rwsem_read_release) =
>-		rwsem_read_intr_acquire(&cxl_region_rwsem);
>-	if (!region_lock)
>-		return -EINTR;
>+	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
>+	if ((ret = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
Checkpatch is giving error here and in other places with similar coding style,  
"ERROR: do not use assignment in if condition"

>+		return ret;
>
[...]
> 		p = &cxlr->params;
>@@ -2215,18 +2173,18 @@ int cxl_decoder_detach(struct cxl_region *cxlr,
> 	struct cxl_region *detach;
>
> 	/* when the decoder is being destroyed lock unconditionally */
>-	if (mode == DETACH_INVALIDATE)
>-		down_write(&cxl_region_rwsem);
>-	else {
>-		int rc = down_write_killable(&cxl_region_rwsem);
>+	if (mode == DETACH_INVALIDATE) {
>+		guard(rwsem_write)(&cxl_rwsem.region);
>+		detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);
>+	} else {
>+		int rc;
>
>-		if (rc)
>+		ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
>+		if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
> 			return rc;
>+		detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);
> 	}
May be detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);
add outside if ... else as before?
>
>-	detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);
>-	up_write(&cxl_region_rwsem);
>-
> 	if (detach) {
> 		device_release_driver(&detach->dev);
> 		put_device(&detach->dev);
>@@ -2234,29 +2192,35 @@ int cxl_decoder_detach(struct cxl_region *cxlr,
> 	return 0;
> }
[...]
> /*
>  * downgrade write lock to read lock
>--
>2.50.0
>

Thanks,
Shiju

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 7/8] cxl/region: Consolidate cxl_decoder_kill_region() and cxl_region_detach()
  2025-07-11 23:49 ` [PATCH v3 7/8] cxl/region: Consolidate cxl_decoder_kill_region() and cxl_region_detach() Dan Williams
@ 2025-07-14 18:17   ` Dave Jiang
  2025-07-15 17:07     ` dan.j.williams
  2025-07-14 22:09   ` Fabio M. De Francesco
  2025-07-15 15:56   ` Jonathan Cameron
  2 siblings, 1 reply; 32+ messages in thread
From: Dave Jiang @ 2025-07-14 18:17 UTC (permalink / raw)
  To: Dan Williams, linux-cxl
  Cc: linux-kernel, Davidlohr Bueso, Jonathan Cameron, Alison Schofield,
	Vishal Verma, Ira Weiny, Peter Zijlstra (Intel)



On 7/11/25 4:49 PM, Dan Williams wrote:
> Both detach_target() and cxld_unregister() want to tear down a cxl_region
> when an endpoint decoder is either detached or destroyed.
> 
> When a region is to be destroyed cxl_region_detach() releases
> cxl_region_rwsem unbinds the cxl_region driver and re-acquires the rwsem.
> 

I think this one needs some commas?

When a region is to be destroyed cxl_region_detach() releases
cxl_region_rwsem, unbinds the cxl_region driver, and re-acquires the rwsem.

> This "reverse" locking pattern is difficult to reason about, not amenable
> to scope-based cleanup, and the minor differences in the calling context of
> detach_target() and cxld_unregister() currently results in the
> cxl_decoder_kill_region() wrapper.
> 
> Introduce cxl_decoder_detach() to wrap a core __cxl_decoder_detach() that
> serves both cases. I.e. either detaching a known position in a region
> (interruptible), or detaching an endpoint decoder if it is found to be a
> member of a region (uninterruptible).
> 
> Cc: Davidlohr Bueso <dave@stgolabs.net>
> Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
> Cc: Dave Jiang <dave.jiang@intel.com>
> Cc: Alison Schofield <alison.schofield@intel.com>
> Cc: Vishal Verma <vishal.l.verma@intel.com>
> Cc: Ira Weiny <ira.weiny@intel.com>
> Acked-by: "Peter Zijlstra (Intel)" <peterz@infradead.org>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>

Reviewed-by: Dave Jiang <dave.jiang@intel.com>

> ---
>  drivers/cxl/core/core.h   |  15 +++++-
>  drivers/cxl/core/port.c   |   9 ++--
>  drivers/cxl/core/region.c | 103 ++++++++++++++++++++++----------------
>  3 files changed, 75 insertions(+), 52 deletions(-)
> 
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index 29b61828a847..2be37084409f 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -12,6 +12,11 @@ extern const struct device_type cxl_pmu_type;
>  
>  extern struct attribute_group cxl_base_attribute_group;
>  
> +enum cxl_detach_mode {
> +	DETACH_ONLY,
> +	DETACH_INVALIDATE,
> +};
> +
>  #ifdef CONFIG_CXL_REGION
>  extern struct device_attribute dev_attr_create_pmem_region;
>  extern struct device_attribute dev_attr_create_ram_region;
> @@ -20,7 +25,11 @@ extern struct device_attribute dev_attr_region;
>  extern const struct device_type cxl_pmem_region_type;
>  extern const struct device_type cxl_dax_region_type;
>  extern const struct device_type cxl_region_type;
> -void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled);
> +
> +int cxl_decoder_detach(struct cxl_region *cxlr,
> +		       struct cxl_endpoint_decoder *cxled, int pos,
> +		       enum cxl_detach_mode mode);
> +
>  #define CXL_REGION_ATTR(x) (&dev_attr_##x.attr)
>  #define CXL_REGION_TYPE(x) (&cxl_region_type)
>  #define SET_CXL_REGION_ATTR(x) (&dev_attr_##x.attr),
> @@ -48,7 +57,9 @@ static inline int cxl_get_poison_by_endpoint(struct cxl_port *port)
>  {
>  	return 0;
>  }
> -static inline void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled)
> +static inline int cxl_decoder_detach(struct cxl_region *cxlr,
> +				     struct cxl_endpoint_decoder *cxled,
> +				     int pos, enum cxl_detach_mode mode)
>  {
>  }
>  static inline int cxl_region_init(void)
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index eb46c6764d20..087a20a9ee1c 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -2001,12 +2001,9 @@ EXPORT_SYMBOL_NS_GPL(cxl_decoder_add, "CXL");
>  
>  static void cxld_unregister(void *dev)
>  {
> -	struct cxl_endpoint_decoder *cxled;
> -
> -	if (is_endpoint_decoder(dev)) {
> -		cxled = to_cxl_endpoint_decoder(dev);
> -		cxl_decoder_kill_region(cxled);
> -	}
> +	if (is_endpoint_decoder(dev))
> +		cxl_decoder_detach(NULL, to_cxl_endpoint_decoder(dev), -1,
> +				   DETACH_INVALIDATE);
>  
>  	device_unregister(dev);
>  }
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 2a97fa9a394f..4314aaed8ad8 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -2135,27 +2135,43 @@ static int cxl_region_attach(struct cxl_region *cxlr,
>  	return 0;
>  }
>  
> -static int cxl_region_detach(struct cxl_endpoint_decoder *cxled)
> +static struct cxl_region *
> +__cxl_decoder_detach(struct cxl_region *cxlr,
> +		     struct cxl_endpoint_decoder *cxled, int pos,
> +		     enum cxl_detach_mode mode)
>  {
> -	struct cxl_port *iter, *ep_port = cxled_to_port(cxled);
> -	struct cxl_region *cxlr = cxled->cxld.region;
>  	struct cxl_region_params *p;
> -	int rc = 0;
>  
>  	lockdep_assert_held_write(&cxl_region_rwsem);
>  
> -	if (!cxlr)
> -		return 0;
> +	if (!cxled) {
> +		p = &cxlr->params;
>  
> -	p = &cxlr->params;
> -	get_device(&cxlr->dev);
> +		if (pos >= p->interleave_ways) {
> +			dev_dbg(&cxlr->dev, "position %d out of range %d\n",
> +				pos, p->interleave_ways);
> +			return ERR_PTR(-ENXIO);
> +		}
> +
> +		if (!p->targets[pos])
> +			return NULL;
> +		cxled = p->targets[pos];
> +	} else {
> +		cxlr = cxled->cxld.region;
> +		if (!cxlr)
> +			return NULL;
> +		p = &cxlr->params;
> +	}
> +
> +	if (mode == DETACH_INVALIDATE)
> +		cxled->part = -1;
>  
>  	if (p->state > CXL_CONFIG_ACTIVE) {
>  		cxl_region_decode_reset(cxlr, p->interleave_ways);
>  		p->state = CXL_CONFIG_ACTIVE;
>  	}
>  
> -	for (iter = ep_port; !is_cxl_root(iter);
> +	for (struct cxl_port *iter = cxled_to_port(cxled); !is_cxl_root(iter);
>  	     iter = to_cxl_port(iter->dev.parent))
>  		cxl_port_detach_region(iter, cxlr, cxled);
>  
> @@ -2166,7 +2182,7 @@ static int cxl_region_detach(struct cxl_endpoint_decoder *cxled)
>  		dev_WARN_ONCE(&cxlr->dev, 1, "expected %s:%s at position %d\n",
>  			      dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
>  			      cxled->pos);
> -		goto out;
> +		return NULL;
>  	}
>  
>  	if (p->state == CXL_CONFIG_ACTIVE) {
> @@ -2180,21 +2196,42 @@ static int cxl_region_detach(struct cxl_endpoint_decoder *cxled)
>  		.end = -1,
>  	};
>  
> -	/* notify the region driver that one of its targets has departed */
> -	up_write(&cxl_region_rwsem);
> -	device_release_driver(&cxlr->dev);
> -	down_write(&cxl_region_rwsem);
> -out:
> -	put_device(&cxlr->dev);
> -	return rc;
> +	get_device(&cxlr->dev);
> +	return cxlr;
>  }
>  
> -void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled)
> +/*
> + * Cleanup a decoder's interest in a region. There are 2 cases to
> + * handle, removing an unknown @cxled from a known position in a region
> + * (detach_target()) or removing a known @cxled from an unknown @cxlr
> + * (cxld_unregister())
> + *
> + * When the detachment finds a region release the region driver.
> + */
> +int cxl_decoder_detach(struct cxl_region *cxlr,
> +		       struct cxl_endpoint_decoder *cxled, int pos,
> +		       enum cxl_detach_mode mode)
>  {
> -	down_write(&cxl_region_rwsem);
> -	cxled->part = -1;
> -	cxl_region_detach(cxled);
> +	struct cxl_region *detach;
> +
> +	/* when the decoder is being destroyed lock unconditionally */
> +	if (mode == DETACH_INVALIDATE)
> +		down_write(&cxl_region_rwsem);
> +	else {
> +		int rc = down_write_killable(&cxl_region_rwsem);
> +
> +		if (rc)
> +			return rc;
> +	}
> +
> +	detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);
>  	up_write(&cxl_region_rwsem);
> +
> +	if (detach) {
> +		device_release_driver(&detach->dev);
> +		put_device(&detach->dev);
> +	}
> +	return 0;
>  }
>  
>  static int attach_target(struct cxl_region *cxlr,
> @@ -2225,29 +2262,7 @@ static int attach_target(struct cxl_region *cxlr,
>  
>  static int detach_target(struct cxl_region *cxlr, int pos)
>  {
> -	struct cxl_region_params *p = &cxlr->params;
> -	int rc;
> -
> -	rc = down_write_killable(&cxl_region_rwsem);
> -	if (rc)
> -		return rc;
> -
> -	if (pos >= p->interleave_ways) {
> -		dev_dbg(&cxlr->dev, "position %d out of range %d\n", pos,
> -			p->interleave_ways);
> -		rc = -ENXIO;
> -		goto out;
> -	}
> -
> -	if (!p->targets[pos]) {
> -		rc = 0;
> -		goto out;
> -	}
> -
> -	rc = cxl_region_detach(p->targets[pos]);
> -out:
> -	up_write(&cxl_region_rwsem);
> -	return rc;
> +	return cxl_decoder_detach(cxlr, NULL, pos, DETACH_ONLY);
>  }
>  
>  static size_t store_targetN(struct cxl_region *cxlr, const char *buf, int pos,


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 8/8] cxl: Convert to ACQUIRE() for conditional rwsem locking
  2025-07-11 23:49 ` [PATCH v3 8/8] cxl: Convert to ACQUIRE() for conditional rwsem locking Dan Williams
  2025-07-14 16:28   ` Shiju Jose
@ 2025-07-14 18:39   ` Dave Jiang
  2025-07-14 22:12   ` Fabio M. De Francesco
  2025-07-15 16:20   ` Jonathan Cameron
  3 siblings, 0 replies; 32+ messages in thread
From: Dave Jiang @ 2025-07-14 18:39 UTC (permalink / raw)
  To: Dan Williams, linux-cxl
  Cc: linux-kernel, David Lechner, Peter Zijlstra, Linus Torvalds,
	Ingo Molnar, Fabio M. De Francesco, Davidlohr Bueso,
	Jonathan Cameron, Alison Schofield, Vishal Verma, Ira Weiny,
	Shiju Jose



On 7/11/25 4:49 PM, Dan Williams wrote:
> Use ACQUIRE() to cleanup conditional locking paths in the CXL driver
> The ACQUIRE() macro and its associated ACQUIRE_ERR() helpers, like
> scoped_cond_guard(), arrange for scoped-based conditional locking. Unlike
> scoped_cond_guard(), these macros arrange for an ERR_PTR() to be retrieved
> representing the state of the conditional lock.
> 
> The goal of this conversion is to complete the removal of all explicit
> unlock calls in the subsystem. I.e. the methods to acquire a lock are
> solely via guard(), scoped_guard() (for limited cases), or ACQUIRE(). All
> unlock is implicit / scope-based. In order to make sure all lock sites are
> converted, the existing rwsem's are consolidated and renamed in 'struct
> cxl_rwsem'. While that makes the patch noisier it gives a clean cut-off
> between old-world (explicit unlock allowed), and new world (explicit unlock
> deleted).
> 
> Cc: David Lechner <dlechner@baylibre.com>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: Linus Torvalds <torvalds@linux-foundation.org>
> Cc: Ingo Molnar <mingo@kernel.org>
> Cc: "Fabio M. De Francesco" <fabio.m.de.francesco@linux.intel.com>
> Cc: Davidlohr Bueso <dave@stgolabs.net>
> Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
> Cc: Dave Jiang <dave.jiang@intel.com>
> Cc: Alison Schofield <alison.schofield@intel.com>
> Cc: Vishal Verma <vishal.l.verma@intel.com>
> Cc: Ira Weiny <ira.weiny@intel.com>
> Cc: Shiju Jose <shiju.jose@huawei.com>
> Acked-by: "Peter Zijlstra (Intel)" <peterz@infradead.org>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>

Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> ---
>  drivers/cxl/core/cdat.c   |   6 +-
>  drivers/cxl/core/core.h   |  17 ++-
>  drivers/cxl/core/edac.c   |  44 +++---
>  drivers/cxl/core/hdm.c    |  41 +++---
>  drivers/cxl/core/mbox.c   |   6 +-
>  drivers/cxl/core/memdev.c |  50 +++----
>  drivers/cxl/core/port.c   |  18 +--
>  drivers/cxl/core/region.c | 295 ++++++++++++++++----------------------
>  drivers/cxl/cxl.h         |  13 +-
>  include/linux/rwsem.h     |   1 +
>  10 files changed, 212 insertions(+), 279 deletions(-)
> 
> diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
> index 0ccef2f2a26a..c0af645425f4 100644
> --- a/drivers/cxl/core/cdat.c
> +++ b/drivers/cxl/core/cdat.c
> @@ -336,7 +336,7 @@ static int match_cxlrd_hb(struct device *dev, void *data)
>  	cxlrd = to_cxl_root_decoder(dev);
>  	cxlsd = &cxlrd->cxlsd;
>  
> -	guard(rwsem_read)(&cxl_region_rwsem);
> +	guard(rwsem_read)(&cxl_rwsem.region);
>  	for (int i = 0; i < cxlsd->nr_targets; i++) {
>  		if (host_bridge == cxlsd->target[i]->dport_dev)
>  			return 1;
> @@ -987,7 +987,7 @@ void cxl_region_shared_upstream_bandwidth_update(struct cxl_region *cxlr)
>  	bool is_root;
>  	int rc;
>  
> -	lockdep_assert_held(&cxl_dpa_rwsem);
> +	lockdep_assert_held(&cxl_rwsem.dpa);
>  
>  	struct xarray *usp_xa __free(free_perf_xa) =
>  		kzalloc(sizeof(*usp_xa), GFP_KERNEL);
> @@ -1057,7 +1057,7 @@ void cxl_region_perf_data_calculate(struct cxl_region *cxlr,
>  {
>  	struct cxl_dpa_perf *perf;
>  
> -	lockdep_assert_held(&cxl_dpa_rwsem);
> +	lockdep_assert_held(&cxl_rwsem.dpa);
>  
>  	perf = cxled_get_dpa_perf(cxled);
>  	if (IS_ERR(perf))
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index 2be37084409f..f796731deedf 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -5,6 +5,7 @@
>  #define __CXL_CORE_H__
>  
>  #include <cxl/mailbox.h>
> +#include <linux/rwsem.h>
>  
>  extern const struct device_type cxl_nvdimm_bridge_type;
>  extern const struct device_type cxl_nvdimm_type;
> @@ -107,8 +108,20 @@ u16 cxl_rcrb_to_aer(struct device *dev, resource_size_t rcrb);
>  #define PCI_RCRB_CAP_HDR_NEXT_MASK	GENMASK(15, 8)
>  #define PCI_CAP_EXP_SIZEOF		0x3c
>  
> -extern struct rw_semaphore cxl_dpa_rwsem;
> -extern struct rw_semaphore cxl_region_rwsem;
> +struct cxl_rwsem {
> +	/*
> +	 * All changes to HPA (interleave configuration) occur with this
> +	 * lock held for write.
> +	 */
> +	struct rw_semaphore region;
> +	/*
> +	 * All changes to a device DPA space occur with this lock held
> +	 * for write.
> +	 */
> +	struct rw_semaphore dpa;
> +};
> +
> +extern struct cxl_rwsem cxl_rwsem;
>  
>  int cxl_memdev_init(void);
>  void cxl_memdev_exit(void);
> diff --git a/drivers/cxl/core/edac.c b/drivers/cxl/core/edac.c
> index 2cbc664e5d62..f1ebdbe222c8 100644
> --- a/drivers/cxl/core/edac.c
> +++ b/drivers/cxl/core/edac.c
> @@ -115,10 +115,9 @@ static int cxl_scrub_get_attrbs(struct cxl_patrol_scrub_context *cxl_ps_ctx,
>  						flags, min_cycle);
>  	}
>  
> -	struct rw_semaphore *region_lock __free(rwsem_read_release) =
> -		rwsem_read_intr_acquire(&cxl_region_rwsem);
> -	if (!region_lock)
> -		return -EINTR;
> +	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
> +	if ((ret = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
> +		return ret;
>  
>  	cxlr = cxl_ps_ctx->cxlr;
>  	p = &cxlr->params;
> @@ -154,10 +153,9 @@ static int cxl_scrub_set_attrbs_region(struct device *dev,
>  	struct cxl_region *cxlr;
>  	int ret, i;
>  
> -	struct rw_semaphore *region_lock __free(rwsem_read_release) =
> -		rwsem_read_intr_acquire(&cxl_region_rwsem);
> -	if (!region_lock)
> -		return -EINTR;
> +	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
> +	if ((ret = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
> +		return ret;
>  
>  	cxlr = cxl_ps_ctx->cxlr;
>  	p = &cxlr->params;
> @@ -1332,16 +1330,15 @@ cxl_mem_perform_sparing(struct device *dev,
>  	struct cxl_memdev_sparing_in_payload sparing_pi;
>  	struct cxl_event_dram *rec = NULL;
>  	u16 validity_flags = 0;
> +	int ret;
>  
> -	struct rw_semaphore *region_lock __free(rwsem_read_release) =
> -		rwsem_read_intr_acquire(&cxl_region_rwsem);
> -	if (!region_lock)
> -		return -EINTR;
> +	ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
> +	if ((ret = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
> +		return ret;
>  
> -	struct rw_semaphore *dpa_lock __free(rwsem_read_release) =
> -		rwsem_read_intr_acquire(&cxl_dpa_rwsem);
> -	if (!dpa_lock)
> -		return -EINTR;
> +	ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
> +	if ((ret = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
> +		return ret;
>  
>  	if (!cxl_sparing_ctx->cap_safe_when_in_use) {
>  		/* Memory to repair must be offline */
> @@ -1779,16 +1776,15 @@ static int cxl_mem_perform_ppr(struct cxl_ppr_context *cxl_ppr_ctx)
>  	struct cxl_memdev_ppr_maintenance_attrbs maintenance_attrbs;
>  	struct cxl_memdev *cxlmd = cxl_ppr_ctx->cxlmd;
>  	struct cxl_mem_repair_attrbs attrbs = { 0 };
> +	int ret;
>  
> -	struct rw_semaphore *region_lock __free(rwsem_read_release) =
> -		rwsem_read_intr_acquire(&cxl_region_rwsem);
> -	if (!region_lock)
> -		return -EINTR;
> +	ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
> +	if ((ret = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
> +		return ret;
>  
> -	struct rw_semaphore *dpa_lock __free(rwsem_read_release) =
> -		rwsem_read_intr_acquire(&cxl_dpa_rwsem);
> -	if (!dpa_lock)
> -		return -EINTR;
> +	ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
> +	if ((ret = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
> +		return ret;
>  
>  	if (!cxl_ppr_ctx->media_accessible || !cxl_ppr_ctx->data_retained) {
>  		/* Memory to repair must be offline */
> diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
> index e9cb34e30248..865a71bce251 100644
> --- a/drivers/cxl/core/hdm.c
> +++ b/drivers/cxl/core/hdm.c
> @@ -16,7 +16,10 @@
>   * for enumerating these registers and capabilities.
>   */
>  
> -DECLARE_RWSEM(cxl_dpa_rwsem);
> +struct cxl_rwsem cxl_rwsem = {
> +	.region = __RWSEM_INITIALIZER(cxl_rwsem.region),
> +	.dpa = __RWSEM_INITIALIZER(cxl_rwsem.dpa),
> +};
>  
>  static int add_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
>  			   int *target_map)
> @@ -214,7 +217,7 @@ void cxl_dpa_debug(struct seq_file *file, struct cxl_dev_state *cxlds)
>  {
>  	struct resource *p1, *p2;
>  
> -	guard(rwsem_read)(&cxl_dpa_rwsem);
> +	guard(rwsem_read)(&cxl_rwsem.dpa);
>  	for (p1 = cxlds->dpa_res.child; p1; p1 = p1->sibling) {
>  		__cxl_dpa_debug(file, p1, 0);
>  		for (p2 = p1->child; p2; p2 = p2->sibling)
> @@ -266,7 +269,7 @@ static void __cxl_dpa_release(struct cxl_endpoint_decoder *cxled)
>  	struct resource *res = cxled->dpa_res;
>  	resource_size_t skip_start;
>  
> -	lockdep_assert_held_write(&cxl_dpa_rwsem);
> +	lockdep_assert_held_write(&cxl_rwsem.dpa);
>  
>  	/* save @skip_start, before @res is released */
>  	skip_start = res->start - cxled->skip;
> @@ -281,7 +284,7 @@ static void __cxl_dpa_release(struct cxl_endpoint_decoder *cxled)
>  
>  static void cxl_dpa_release(void *cxled)
>  {
> -	guard(rwsem_write)(&cxl_dpa_rwsem);
> +	guard(rwsem_write)(&cxl_rwsem.dpa);
>  	__cxl_dpa_release(cxled);
>  }
>  
> @@ -293,7 +296,7 @@ static void devm_cxl_dpa_release(struct cxl_endpoint_decoder *cxled)
>  {
>  	struct cxl_port *port = cxled_to_port(cxled);
>  
> -	lockdep_assert_held_write(&cxl_dpa_rwsem);
> +	lockdep_assert_held_write(&cxl_rwsem.dpa);
>  	devm_remove_action(&port->dev, cxl_dpa_release, cxled);
>  	__cxl_dpa_release(cxled);
>  }
> @@ -361,7 +364,7 @@ static int __cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled,
>  	struct resource *res;
>  	int rc;
>  
> -	lockdep_assert_held_write(&cxl_dpa_rwsem);
> +	lockdep_assert_held_write(&cxl_rwsem.dpa);
>  
>  	if (!len) {
>  		dev_warn(dev, "decoder%d.%d: empty reservation attempted\n",
> @@ -470,7 +473,7 @@ int cxl_dpa_setup(struct cxl_dev_state *cxlds, const struct cxl_dpa_info *info)
>  {
>  	struct device *dev = cxlds->dev;
>  
> -	guard(rwsem_write)(&cxl_dpa_rwsem);
> +	guard(rwsem_write)(&cxl_rwsem.dpa);
>  
>  	if (cxlds->nr_partitions)
>  		return -EBUSY;
> @@ -516,9 +519,8 @@ int devm_cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled,
>  	struct cxl_port *port = cxled_to_port(cxled);
>  	int rc;
>  
> -	down_write(&cxl_dpa_rwsem);
> -	rc = __cxl_dpa_reserve(cxled, base, len, skipped);
> -	up_write(&cxl_dpa_rwsem);
> +	scoped_guard(rwsem_write, &cxl_rwsem.dpa)
> +		rc = __cxl_dpa_reserve(cxled, base, len, skipped);
>  
>  	if (rc)
>  		return rc;
> @@ -529,7 +531,7 @@ EXPORT_SYMBOL_NS_GPL(devm_cxl_dpa_reserve, "CXL");
>  
>  resource_size_t cxl_dpa_size(struct cxl_endpoint_decoder *cxled)
>  {
> -	guard(rwsem_read)(&cxl_dpa_rwsem);
> +	guard(rwsem_read)(&cxl_rwsem.dpa);
>  	if (cxled->dpa_res)
>  		return resource_size(cxled->dpa_res);
>  
> @@ -540,7 +542,7 @@ resource_size_t cxl_dpa_resource_start(struct cxl_endpoint_decoder *cxled)
>  {
>  	resource_size_t base = -1;
>  
> -	lockdep_assert_held(&cxl_dpa_rwsem);
> +	lockdep_assert_held(&cxl_rwsem.dpa);
>  	if (cxled->dpa_res)
>  		base = cxled->dpa_res->start;
>  
> @@ -552,7 +554,7 @@ int cxl_dpa_free(struct cxl_endpoint_decoder *cxled)
>  	struct cxl_port *port = cxled_to_port(cxled);
>  	struct device *dev = &cxled->cxld.dev;
>  
> -	guard(rwsem_write)(&cxl_dpa_rwsem);
> +	guard(rwsem_write)(&cxl_rwsem.dpa);
>  	if (!cxled->dpa_res)
>  		return 0;
>  	if (cxled->cxld.region) {
> @@ -582,7 +584,7 @@ int cxl_dpa_set_part(struct cxl_endpoint_decoder *cxled,
>  	struct device *dev = &cxled->cxld.dev;
>  	int part;
>  
> -	guard(rwsem_write)(&cxl_dpa_rwsem);
> +	guard(rwsem_write)(&cxl_rwsem.dpa);
>  	if (cxled->cxld.flags & CXL_DECODER_F_ENABLE)
>  		return -EBUSY;
>  
> @@ -614,7 +616,7 @@ static int __cxl_dpa_alloc(struct cxl_endpoint_decoder *cxled, u64 size)
>  	struct resource *p, *last;
>  	int part;
>  
> -	guard(rwsem_write)(&cxl_dpa_rwsem);
> +	guard(rwsem_write)(&cxl_rwsem.dpa);
>  	if (cxled->cxld.region) {
>  		dev_dbg(dev, "decoder attached to %s\n",
>  			dev_name(&cxled->cxld.region->dev));
> @@ -842,9 +844,8 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)
>  		}
>  	}
>  
> -	down_read(&cxl_dpa_rwsem);
> -	setup_hw_decoder(cxld, hdm);
> -	up_read(&cxl_dpa_rwsem);
> +	scoped_guard(rwsem_read, &cxl_rwsem.dpa)
> +		setup_hw_decoder(cxld, hdm);
>  
>  	port->commit_end++;
>  	rc = cxld_await_commit(hdm, cxld->id);
> @@ -882,7 +883,7 @@ void cxl_port_commit_reap(struct cxl_decoder *cxld)
>  {
>  	struct cxl_port *port = to_cxl_port(cxld->dev.parent);
>  
> -	lockdep_assert_held_write(&cxl_region_rwsem);
> +	lockdep_assert_held_write(&cxl_rwsem.region);
>  
>  	/*
>  	 * Once the highest committed decoder is disabled, free any other
> @@ -1030,7 +1031,7 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
>  		else
>  			cxld->target_type = CXL_DECODER_DEVMEM;
>  
> -		guard(rwsem_write)(&cxl_region_rwsem);
> +		guard(rwsem_write)(&cxl_rwsem.region);
>  		if (cxld->id != cxl_num_decoders_committed(port)) {
>  			dev_warn(&port->dev,
>  				 "decoder%d.%d: Committed out of order\n",
> diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> index 81b21effe8cf..92cd3cbdd8ec 100644
> --- a/drivers/cxl/core/mbox.c
> +++ b/drivers/cxl/core/mbox.c
> @@ -909,8 +909,8 @@ void cxl_event_trace_record(const struct cxl_memdev *cxlmd,
>  		 * translations. Take topology mutation locks and lookup
>  		 * { HPA, REGION } from { DPA, MEMDEV } in the event record.
>  		 */
> -		guard(rwsem_read)(&cxl_region_rwsem);
> -		guard(rwsem_read)(&cxl_dpa_rwsem);
> +		guard(rwsem_read)(&cxl_rwsem.region);
> +		guard(rwsem_read)(&cxl_rwsem.dpa);
>  
>  		dpa = le64_to_cpu(evt->media_hdr.phys_addr) & CXL_DPA_MASK;
>  		cxlr = cxl_dpa_to_region(cxlmd, dpa);
> @@ -1265,7 +1265,7 @@ int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd)
>  	/* synchronize with cxl_mem_probe() and decoder write operations */
>  	guard(device)(&cxlmd->dev);
>  	endpoint = cxlmd->endpoint;
> -	guard(rwsem_read)(&cxl_region_rwsem);
> +	guard(rwsem_read)(&cxl_rwsem.region);
>  	/*
>  	 * Require an endpoint to be safe otherwise the driver can not
>  	 * be sure that the device is unmapped.
> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> index f88a13adf7fa..f5fbd34310fd 100644
> --- a/drivers/cxl/core/memdev.c
> +++ b/drivers/cxl/core/memdev.c
> @@ -232,15 +232,13 @@ int cxl_trigger_poison_list(struct cxl_memdev *cxlmd)
>  	if (!port || !is_cxl_endpoint(port))
>  		return -EINVAL;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
>  		return rc;
>  
> -	rc = down_read_interruptible(&cxl_dpa_rwsem);
> -	if (rc) {
> -		up_read(&cxl_region_rwsem);
> +	ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
>  		return rc;
> -	}
>  
>  	if (cxl_num_decoders_committed(port) == 0) {
>  		/* No regions mapped to this memdev */
> @@ -249,8 +247,6 @@ int cxl_trigger_poison_list(struct cxl_memdev *cxlmd)
>  		/* Regions mapped, collect poison by endpoint */
>  		rc =  cxl_get_poison_by_endpoint(port);
>  	}
> -	up_read(&cxl_dpa_rwsem);
> -	up_read(&cxl_region_rwsem);
>  
>  	return rc;
>  }
> @@ -292,19 +288,17 @@ int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa)
>  	if (!IS_ENABLED(CONFIG_DEBUG_FS))
>  		return 0;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
>  		return rc;
>  
> -	rc = down_read_interruptible(&cxl_dpa_rwsem);
> -	if (rc) {
> -		up_read(&cxl_region_rwsem);
> +	ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
>  		return rc;
> -	}
>  
>  	rc = cxl_validate_poison_dpa(cxlmd, dpa);
>  	if (rc)
> -		goto out;
> +		return rc;
>  
>  	inject.address = cpu_to_le64(dpa);
>  	mbox_cmd = (struct cxl_mbox_cmd) {
> @@ -314,7 +308,7 @@ int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa)
>  	};
>  	rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd);
>  	if (rc)
> -		goto out;
> +		return rc;
>  
>  	cxlr = cxl_dpa_to_region(cxlmd, dpa);
>  	if (cxlr)
> @@ -327,11 +321,8 @@ int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa)
>  		.length = cpu_to_le32(1),
>  	};
>  	trace_cxl_poison(cxlmd, cxlr, &record, 0, 0, CXL_POISON_TRACE_INJECT);
> -out:
> -	up_read(&cxl_dpa_rwsem);
> -	up_read(&cxl_region_rwsem);
>  
> -	return rc;
> +	return 0;
>  }
>  EXPORT_SYMBOL_NS_GPL(cxl_inject_poison, "CXL");
>  
> @@ -347,19 +338,17 @@ int cxl_clear_poison(struct cxl_memdev *cxlmd, u64 dpa)
>  	if (!IS_ENABLED(CONFIG_DEBUG_FS))
>  		return 0;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
>  		return rc;
>  
> -	rc = down_read_interruptible(&cxl_dpa_rwsem);
> -	if (rc) {
> -		up_read(&cxl_region_rwsem);
> +	ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
>  		return rc;
> -	}
>  
>  	rc = cxl_validate_poison_dpa(cxlmd, dpa);
>  	if (rc)
> -		goto out;
> +		return rc;
>  
>  	/*
>  	 * In CXL 3.0 Spec 8.2.9.8.4.3, the Clear Poison mailbox command
> @@ -378,7 +367,7 @@ int cxl_clear_poison(struct cxl_memdev *cxlmd, u64 dpa)
>  
>  	rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd);
>  	if (rc)
> -		goto out;
> +		return rc;
>  
>  	cxlr = cxl_dpa_to_region(cxlmd, dpa);
>  	if (cxlr)
> @@ -391,11 +380,8 @@ int cxl_clear_poison(struct cxl_memdev *cxlmd, u64 dpa)
>  		.length = cpu_to_le32(1),
>  	};
>  	trace_cxl_poison(cxlmd, cxlr, &record, 0, 0, CXL_POISON_TRACE_CLEAR);
> -out:
> -	up_read(&cxl_dpa_rwsem);
> -	up_read(&cxl_region_rwsem);
>  
> -	return rc;
> +	return 0;
>  }
>  EXPORT_SYMBOL_NS_GPL(cxl_clear_poison, "CXL");
>  
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index 087a20a9ee1c..bacf1380dc4d 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -30,18 +30,12 @@
>   * instantiated by the core.
>   */
>  
> -/*
> - * All changes to the interleave configuration occur with this lock held
> - * for write.
> - */
> -DECLARE_RWSEM(cxl_region_rwsem);
> -
>  static DEFINE_IDA(cxl_port_ida);
>  static DEFINE_XARRAY(cxl_root_buses);
>  
>  int cxl_num_decoders_committed(struct cxl_port *port)
>  {
> -	lockdep_assert_held(&cxl_region_rwsem);
> +	lockdep_assert_held(&cxl_rwsem.region);
>  
>  	return port->commit_end + 1;
>  }
> @@ -176,7 +170,7 @@ static ssize_t target_list_show(struct device *dev,
>  	ssize_t offset;
>  	int rc;
>  
> -	guard(rwsem_read)(&cxl_region_rwsem);
> +	guard(rwsem_read)(&cxl_rwsem.region);
>  	rc = emit_target_list(cxlsd, buf);
>  	if (rc < 0)
>  		return rc;
> @@ -196,7 +190,7 @@ static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
>  	struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(dev);
>  	struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
>  	struct cxl_dev_state *cxlds = cxlmd->cxlds;
> -	/* without @cxl_dpa_rwsem, make sure @part is not reloaded */
> +	/* without @cxl_rwsem.dpa, make sure @part is not reloaded */
>  	int part = READ_ONCE(cxled->part);
>  	const char *desc;
>  
> @@ -235,7 +229,7 @@ static ssize_t dpa_resource_show(struct device *dev, struct device_attribute *at
>  {
>  	struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(dev);
>  
> -	guard(rwsem_read)(&cxl_dpa_rwsem);
> +	guard(rwsem_read)(&cxl_rwsem.dpa);
>  	return sysfs_emit(buf, "%#llx\n", (u64)cxl_dpa_resource_start(cxled));
>  }
>  static DEVICE_ATTR_RO(dpa_resource);
> @@ -560,7 +554,7 @@ static ssize_t decoders_committed_show(struct device *dev,
>  {
>  	struct cxl_port *port = to_cxl_port(dev);
>  
> -	guard(rwsem_read)(&cxl_region_rwsem);
> +	guard(rwsem_read)(&cxl_rwsem.region);
>  	return sysfs_emit(buf, "%d\n", cxl_num_decoders_committed(port));
>  }
>  
> @@ -1722,7 +1716,7 @@ static int decoder_populate_targets(struct cxl_switch_decoder *cxlsd,
>  	if (xa_empty(&port->dports))
>  		return -EINVAL;
>  
> -	guard(rwsem_write)(&cxl_region_rwsem);
> +	guard(rwsem_write)(&cxl_rwsem.region);
>  	for (i = 0; i < cxlsd->cxld.interleave_ways; i++) {
>  		struct cxl_dport *dport = find_dport(port, target_map[i]);
>  
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 4314aaed8ad8..ad60c93be803 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -141,16 +141,12 @@ static ssize_t uuid_show(struct device *dev, struct device_attribute *attr,
>  	struct cxl_region_params *p = &cxlr->params;
>  	ssize_t rc;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
>  		return rc;
>  	if (cxlr->mode != CXL_PARTMODE_PMEM)
> -		rc = sysfs_emit(buf, "\n");
> -	else
> -		rc = sysfs_emit(buf, "%pUb\n", &p->uuid);
> -	up_read(&cxl_region_rwsem);
> -
> -	return rc;
> +		return sysfs_emit(buf, "\n");
> +	return sysfs_emit(buf, "%pUb\n", &p->uuid);
>  }
>  
>  static int is_dup(struct device *match, void *data)
> @@ -162,7 +158,7 @@ static int is_dup(struct device *match, void *data)
>  	if (!is_cxl_region(match))
>  		return 0;
>  
> -	lockdep_assert_held(&cxl_region_rwsem);
> +	lockdep_assert_held(&cxl_rwsem.region);
>  	cxlr = to_cxl_region(match);
>  	p = &cxlr->params;
>  
> @@ -192,27 +188,22 @@ static ssize_t uuid_store(struct device *dev, struct device_attribute *attr,
>  	if (uuid_is_null(&temp))
>  		return -EINVAL;
>  
> -	rc = down_write_killable(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_write_kill, region_rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_write_kill, &region_rwsem)))
>  		return rc;
>  
>  	if (uuid_equal(&p->uuid, &temp))
> -		goto out;
> +		return len;
>  
> -	rc = -EBUSY;
>  	if (p->state >= CXL_CONFIG_ACTIVE)
> -		goto out;
> +		return -EBUSY;
>  
>  	rc = bus_for_each_dev(&cxl_bus_type, NULL, &temp, is_dup);
>  	if (rc < 0)
> -		goto out;
> +		return rc;
>  
>  	uuid_copy(&p->uuid, &temp);
> -out:
> -	up_write(&cxl_region_rwsem);
>  
> -	if (rc)
> -		return rc;
>  	return len;
>  }
>  static DEVICE_ATTR_RW(uuid);
> @@ -354,20 +345,17 @@ static int queue_reset(struct cxl_region *cxlr)
>  	struct cxl_region_params *p = &cxlr->params;
>  	int rc;
>  
> -	rc = down_write_killable(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
>  		return rc;
>  
>  	/* Already in the requested state? */
>  	if (p->state < CXL_CONFIG_COMMIT)
> -		goto out;
> +		return 0;
>  
>  	p->state = CXL_CONFIG_RESET_PENDING;
>  
> -out:
> -	up_write(&cxl_region_rwsem);
> -
> -	return rc;
> +	return 0;
>  }
>  
>  static int __commit(struct cxl_region *cxlr)
> @@ -375,19 +363,17 @@ static int __commit(struct cxl_region *cxlr)
>  	struct cxl_region_params *p = &cxlr->params;
>  	int rc;
>  
> -	rc = down_write_killable(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
>  		return rc;
>  
>  	/* Already in the requested state? */
>  	if (p->state >= CXL_CONFIG_COMMIT)
> -		goto out;
> +		return 0;
>  
>  	/* Not ready to commit? */
> -	if (p->state < CXL_CONFIG_ACTIVE) {
> -		rc = -ENXIO;
> -		goto out;
> -	}
> +	if (p->state < CXL_CONFIG_ACTIVE)
> +		return -ENXIO;
>  
>  	/*
>  	 * Invalidate caches before region setup to drop any speculative
> @@ -395,16 +381,15 @@ static int __commit(struct cxl_region *cxlr)
>  	 */
>  	rc = cxl_region_invalidate_memregion(cxlr);
>  	if (rc)
> -		goto out;
> +		return rc;
>  
>  	rc = cxl_region_decode_commit(cxlr);
> -	if (rc == 0)
> -		p->state = CXL_CONFIG_COMMIT;
> +	if (rc)
> +		return rc;
>  
> -out:
> -	up_write(&cxl_region_rwsem);
> +	p->state = CXL_CONFIG_COMMIT;
>  
> -	return rc;
> +	return 0;
>  }
>  
>  static ssize_t commit_store(struct device *dev, struct device_attribute *attr,
> @@ -437,10 +422,10 @@ static ssize_t commit_store(struct device *dev, struct device_attribute *attr,
>  	device_release_driver(&cxlr->dev);
>  
>  	/*
> -	 * With the reset pending take cxl_region_rwsem unconditionally
> +	 * With the reset pending take cxl_rwsem.region unconditionally
>  	 * to ensure the reset gets handled before returning.
>  	 */
> -	guard(rwsem_write)(&cxl_region_rwsem);
> +	guard(rwsem_write)(&cxl_rwsem.region);
>  
>  	/*
>  	 * Revalidate that the reset is still pending in case another
> @@ -461,13 +446,10 @@ static ssize_t commit_show(struct device *dev, struct device_attribute *attr,
>  	struct cxl_region_params *p = &cxlr->params;
>  	ssize_t rc;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
>  		return rc;
> -	rc = sysfs_emit(buf, "%d\n", p->state >= CXL_CONFIG_COMMIT);
> -	up_read(&cxl_region_rwsem);
> -
> -	return rc;
> +	return sysfs_emit(buf, "%d\n", p->state >= CXL_CONFIG_COMMIT);
>  }
>  static DEVICE_ATTR_RW(commit);
>  
> @@ -491,15 +473,12 @@ static ssize_t interleave_ways_show(struct device *dev,
>  {
>  	struct cxl_region *cxlr = to_cxl_region(dev);
>  	struct cxl_region_params *p = &cxlr->params;
> -	ssize_t rc;
> +	int rc;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
>  		return rc;
> -	rc = sysfs_emit(buf, "%d\n", p->interleave_ways);
> -	up_read(&cxl_region_rwsem);
> -
> -	return rc;
> +	return sysfs_emit(buf, "%d\n", p->interleave_ways);
>  }
>  
>  static const struct attribute_group *get_cxl_region_target_group(void);
> @@ -534,23 +513,21 @@ static ssize_t interleave_ways_store(struct device *dev,
>  		return -EINVAL;
>  	}
>  
> -	rc = down_write_killable(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
>  		return rc;
> -	if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) {
> -		rc = -EBUSY;
> -		goto out;
> -	}
> +
> +	if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE)
> +		return -EBUSY;
>  
>  	save = p->interleave_ways;
>  	p->interleave_ways = val;
>  	rc = sysfs_update_group(&cxlr->dev.kobj, get_cxl_region_target_group());
> -	if (rc)
> +	if (rc) {
>  		p->interleave_ways = save;
> -out:
> -	up_write(&cxl_region_rwsem);
> -	if (rc)
>  		return rc;
> +	}
> +
>  	return len;
>  }
>  static DEVICE_ATTR_RW(interleave_ways);
> @@ -561,15 +538,12 @@ static ssize_t interleave_granularity_show(struct device *dev,
>  {
>  	struct cxl_region *cxlr = to_cxl_region(dev);
>  	struct cxl_region_params *p = &cxlr->params;
> -	ssize_t rc;
> +	int rc;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
>  		return rc;
> -	rc = sysfs_emit(buf, "%d\n", p->interleave_granularity);
> -	up_read(&cxl_region_rwsem);
> -
> -	return rc;
> +	return sysfs_emit(buf, "%d\n", p->interleave_granularity);
>  }
>  
>  static ssize_t interleave_granularity_store(struct device *dev,
> @@ -602,19 +576,15 @@ static ssize_t interleave_granularity_store(struct device *dev,
>  	if (cxld->interleave_ways > 1 && val != cxld->interleave_granularity)
>  		return -EINVAL;
>  
> -	rc = down_write_killable(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
>  		return rc;
> -	if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) {
> -		rc = -EBUSY;
> -		goto out;
> -	}
> +
> +	if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE)
> +		return -EBUSY;
>  
>  	p->interleave_granularity = val;
> -out:
> -	up_write(&cxl_region_rwsem);
> -	if (rc)
> -		return rc;
> +
>  	return len;
>  }
>  static DEVICE_ATTR_RW(interleave_granularity);
> @@ -625,17 +595,15 @@ static ssize_t resource_show(struct device *dev, struct device_attribute *attr,
>  	struct cxl_region *cxlr = to_cxl_region(dev);
>  	struct cxl_region_params *p = &cxlr->params;
>  	u64 resource = -1ULL;
> -	ssize_t rc;
> +	int rc;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
>  		return rc;
> +
>  	if (p->res)
>  		resource = p->res->start;
> -	rc = sysfs_emit(buf, "%#llx\n", resource);
> -	up_read(&cxl_region_rwsem);
> -
> -	return rc;
> +	return sysfs_emit(buf, "%#llx\n", resource);
>  }
>  static DEVICE_ATTR_RO(resource);
>  
> @@ -663,7 +631,7 @@ static int alloc_hpa(struct cxl_region *cxlr, resource_size_t size)
>  	struct resource *res;
>  	u64 remainder = 0;
>  
> -	lockdep_assert_held_write(&cxl_region_rwsem);
> +	lockdep_assert_held_write(&cxl_rwsem.region);
>  
>  	/* Nothing to do... */
>  	if (p->res && resource_size(p->res) == size)
> @@ -705,7 +673,7 @@ static void cxl_region_iomem_release(struct cxl_region *cxlr)
>  	struct cxl_region_params *p = &cxlr->params;
>  
>  	if (device_is_registered(&cxlr->dev))
> -		lockdep_assert_held_write(&cxl_region_rwsem);
> +		lockdep_assert_held_write(&cxl_rwsem.region);
>  	if (p->res) {
>  		/*
>  		 * Autodiscovered regions may not have been able to insert their
> @@ -722,7 +690,7 @@ static int free_hpa(struct cxl_region *cxlr)
>  {
>  	struct cxl_region_params *p = &cxlr->params;
>  
> -	lockdep_assert_held_write(&cxl_region_rwsem);
> +	lockdep_assert_held_write(&cxl_rwsem.region);
>  
>  	if (!p->res)
>  		return 0;
> @@ -746,15 +714,14 @@ static ssize_t size_store(struct device *dev, struct device_attribute *attr,
>  	if (rc)
>  		return rc;
>  
> -	rc = down_write_killable(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
>  		return rc;
>  
>  	if (val)
>  		rc = alloc_hpa(cxlr, val);
>  	else
>  		rc = free_hpa(cxlr);
> -	up_write(&cxl_region_rwsem);
>  
>  	if (rc)
>  		return rc;
> @@ -770,15 +737,12 @@ static ssize_t size_show(struct device *dev, struct device_attribute *attr,
>  	u64 size = 0;
>  	ssize_t rc;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
>  		return rc;
>  	if (p->res)
>  		size = resource_size(p->res);
> -	rc = sysfs_emit(buf, "%#llx\n", size);
> -	up_read(&cxl_region_rwsem);
> -
> -	return rc;
> +	return sysfs_emit(buf, "%#llx\n", size);
>  }
>  static DEVICE_ATTR_RW(size);
>  
> @@ -804,26 +768,20 @@ static size_t show_targetN(struct cxl_region *cxlr, char *buf, int pos)
>  	struct cxl_endpoint_decoder *cxled;
>  	int rc;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
>  		return rc;
>  
>  	if (pos >= p->interleave_ways) {
>  		dev_dbg(&cxlr->dev, "position %d out of range %d\n", pos,
>  			p->interleave_ways);
> -		rc = -ENXIO;
> -		goto out;
> +		return -ENXIO;
>  	}
>  
>  	cxled = p->targets[pos];
>  	if (!cxled)
> -		rc = sysfs_emit(buf, "\n");
> -	else
> -		rc = sysfs_emit(buf, "%s\n", dev_name(&cxled->cxld.dev));
> -out:
> -	up_read(&cxl_region_rwsem);
> -
> -	return rc;
> +		return sysfs_emit(buf, "\n");
> +	return sysfs_emit(buf, "%s\n", dev_name(&cxled->cxld.dev));
>  }
>  
>  static int check_commit_order(struct device *dev, void *data)
> @@ -938,7 +896,7 @@ cxl_port_pick_region_decoder(struct cxl_port *port,
>  	/*
>  	 * This decoder is pinned registered as long as the endpoint decoder is
>  	 * registered, and endpoint decoder unregistration holds the
> -	 * cxl_region_rwsem over unregister events, so no need to hold on to
> +	 * cxl_rwsem.region over unregister events, so no need to hold on to
>  	 * this extra reference.
>  	 */
>  	put_device(dev);
> @@ -1129,7 +1087,7 @@ static int cxl_port_attach_region(struct cxl_port *port,
>  	unsigned long index;
>  	int rc = -EBUSY;
>  
> -	lockdep_assert_held_write(&cxl_region_rwsem);
> +	lockdep_assert_held_write(&cxl_rwsem.region);
>  
>  	cxl_rr = cxl_rr_load(port, cxlr);
>  	if (cxl_rr) {
> @@ -1239,7 +1197,7 @@ static void cxl_port_detach_region(struct cxl_port *port,
>  	struct cxl_region_ref *cxl_rr;
>  	struct cxl_ep *ep = NULL;
>  
> -	lockdep_assert_held_write(&cxl_region_rwsem);
> +	lockdep_assert_held_write(&cxl_rwsem.region);
>  
>  	cxl_rr = cxl_rr_load(port, cxlr);
>  	if (!cxl_rr)
> @@ -2142,7 +2100,7 @@ __cxl_decoder_detach(struct cxl_region *cxlr,
>  {
>  	struct cxl_region_params *p;
>  
> -	lockdep_assert_held_write(&cxl_region_rwsem);
> +	lockdep_assert_held_write(&cxl_rwsem.region);
>  
>  	if (!cxled) {
>  		p = &cxlr->params;
> @@ -2215,18 +2173,18 @@ int cxl_decoder_detach(struct cxl_region *cxlr,
>  	struct cxl_region *detach;
>  
>  	/* when the decoder is being destroyed lock unconditionally */
> -	if (mode == DETACH_INVALIDATE)
> -		down_write(&cxl_region_rwsem);
> -	else {
> -		int rc = down_write_killable(&cxl_region_rwsem);
> +	if (mode == DETACH_INVALIDATE) {
> +		guard(rwsem_write)(&cxl_rwsem.region);
> +		detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);
> +	} else {
> +		int rc;
>  
> -		if (rc)
> +		ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
> +		if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
>  			return rc;
> +		detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);
>  	}
>  
> -	detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);
> -	up_write(&cxl_region_rwsem);
> -
>  	if (detach) {
>  		device_release_driver(&detach->dev);
>  		put_device(&detach->dev);
> @@ -2234,29 +2192,35 @@ int cxl_decoder_detach(struct cxl_region *cxlr,
>  	return 0;
>  }
>  
> +static int __attach_target(struct cxl_region *cxlr,
> +			   struct cxl_endpoint_decoder *cxled, int pos,
> +			   unsigned int state)
> +{
> +	int rc;
> +
> +	if (state == TASK_INTERRUPTIBLE) {
> +		ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
> +		if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
> +			return rc;
> +		guard(rwsem_read)(&cxl_rwsem.dpa);
> +		return cxl_region_attach(cxlr, cxled, pos);
> +	}
> +	guard(rwsem_write)(&cxl_rwsem.region);
> +	guard(rwsem_read)(&cxl_rwsem.dpa);
> +	return cxl_region_attach(cxlr, cxled, pos);
> +}
> +
>  static int attach_target(struct cxl_region *cxlr,
>  			 struct cxl_endpoint_decoder *cxled, int pos,
>  			 unsigned int state)
>  {
> -	int rc = 0;
> -
> -	if (state == TASK_INTERRUPTIBLE)
> -		rc = down_write_killable(&cxl_region_rwsem);
> -	else
> -		down_write(&cxl_region_rwsem);
> -	if (rc)
> -		return rc;
> -
> -	down_read(&cxl_dpa_rwsem);
> -	rc = cxl_region_attach(cxlr, cxled, pos);
> -	up_read(&cxl_dpa_rwsem);
> -	up_write(&cxl_region_rwsem);
> +	int rc = __attach_target(cxlr, cxled, pos, state);
>  
> -	if (rc)
> -		dev_warn(cxled->cxld.dev.parent,
> -			"failed to attach %s to %s: %d\n",
> -			dev_name(&cxled->cxld.dev), dev_name(&cxlr->dev), rc);
> +	if (rc == 0)
> +		return 0;
>  
> +	dev_warn(cxled->cxld.dev.parent, "failed to attach %s to %s: %d\n",
> +		 dev_name(&cxled->cxld.dev), dev_name(&cxlr->dev), rc);
>  	return rc;
>  }
>  
> @@ -2516,7 +2480,7 @@ static int cxl_region_perf_attrs_callback(struct notifier_block *nb,
>  		return NOTIFY_DONE;
>  
>  	/*
> -	 * No need to hold cxl_region_rwsem; region parameters are stable
> +	 * No need to hold cxl_rwsem.region; region parameters are stable
>  	 * within the cxl_region driver.
>  	 */
>  	region_nid = phys_to_target_node(cxlr->params.res->start);
> @@ -2539,7 +2503,7 @@ static int cxl_region_calculate_adistance(struct notifier_block *nb,
>  	int region_nid;
>  
>  	/*
> -	 * No need to hold cxl_region_rwsem; region parameters are stable
> +	 * No need to hold cxl_rwsem.region; region parameters are stable
>  	 * within the cxl_region driver.
>  	 */
>  	region_nid = phys_to_target_node(cxlr->params.res->start);
> @@ -2688,17 +2652,13 @@ static ssize_t region_show(struct device *dev, struct device_attribute *attr,
>  	struct cxl_decoder *cxld = to_cxl_decoder(dev);
>  	ssize_t rc;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
>  		return rc;
>  
>  	if (cxld->region)
> -		rc = sysfs_emit(buf, "%s\n", dev_name(&cxld->region->dev));
> -	else
> -		rc = sysfs_emit(buf, "\n");
> -	up_read(&cxl_region_rwsem);
> -
> -	return rc;
> +		return sysfs_emit(buf, "%s\n", dev_name(&cxld->region->dev));
> +	return sysfs_emit(buf, "\n");
>  }
>  DEVICE_ATTR_RO(region);
>  
> @@ -3037,7 +2997,7 @@ static int cxl_pmem_region_alloc(struct cxl_region *cxlr)
>  	struct device *dev;
>  	int i;
>  
> -	guard(rwsem_read)(&cxl_region_rwsem);
> +	guard(rwsem_read)(&cxl_rwsem.region);
>  	if (p->state != CXL_CONFIG_COMMIT)
>  		return -ENXIO;
>  
> @@ -3049,7 +3009,7 @@ static int cxl_pmem_region_alloc(struct cxl_region *cxlr)
>  	cxlr_pmem->hpa_range.start = p->res->start;
>  	cxlr_pmem->hpa_range.end = p->res->end;
>  
> -	/* Snapshot the region configuration underneath the cxl_region_rwsem */
> +	/* Snapshot the region configuration underneath the cxl_rwsem.region */
>  	cxlr_pmem->nr_mappings = p->nr_targets;
>  	for (i = 0; i < p->nr_targets; i++) {
>  		struct cxl_endpoint_decoder *cxled = p->targets[i];
> @@ -3126,7 +3086,7 @@ static struct cxl_dax_region *cxl_dax_region_alloc(struct cxl_region *cxlr)
>  	struct cxl_dax_region *cxlr_dax;
>  	struct device *dev;
>  
> -	guard(rwsem_read)(&cxl_region_rwsem);
> +	guard(rwsem_read)(&cxl_rwsem.region);
>  	if (p->state != CXL_CONFIG_COMMIT)
>  		return ERR_PTR(-ENXIO);
>  
> @@ -3326,7 +3286,7 @@ static int match_region_by_range(struct device *dev, const void *data)
>  	cxlr = to_cxl_region(dev);
>  	p = &cxlr->params;
>  
> -	guard(rwsem_read)(&cxl_region_rwsem);
> +	guard(rwsem_read)(&cxl_rwsem.region);
>  	if (p->res && p->res->start == r->start && p->res->end == r->end)
>  		return 1;
>  
> @@ -3386,7 +3346,7 @@ static int __construct_region(struct cxl_region *cxlr,
>  	struct resource *res;
>  	int rc;
>  
> -	guard(rwsem_write)(&cxl_region_rwsem);
> +	guard(rwsem_write)(&cxl_rwsem.region);
>  	p = &cxlr->params;
>  	if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) {
>  		dev_err(cxlmd->dev.parent,
> @@ -3522,10 +3482,10 @@ int cxl_add_to_region(struct cxl_endpoint_decoder *cxled)
>  
>  	attach_target(cxlr, cxled, -1, TASK_UNINTERRUPTIBLE);
>  
> -	down_read(&cxl_region_rwsem);
> -	p = &cxlr->params;
> -	attach = p->state == CXL_CONFIG_COMMIT;
> -	up_read(&cxl_region_rwsem);
> +	scoped_guard(rwsem_read, &cxl_rwsem.region) {
> +		p = &cxlr->params;
> +		attach = p->state == CXL_CONFIG_COMMIT;
> +	}
>  
>  	if (attach) {
>  		/*
> @@ -3550,7 +3510,7 @@ u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, u64 spa)
>  	if (!endpoint)
>  		return ~0ULL;
>  
> -	guard(rwsem_write)(&cxl_region_rwsem);
> +	guard(rwsem_write)(&cxl_rwsem.region);
>  
>  	xa_for_each(&endpoint->regions, index, iter) {
>  		struct cxl_region_params *p = &iter->region->params;
> @@ -3592,30 +3552,23 @@ static int cxl_region_can_probe(struct cxl_region *cxlr)
>  	struct cxl_region_params *p = &cxlr->params;
>  	int rc;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc) {
> +	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem))) {
>  		dev_dbg(&cxlr->dev, "probe interrupted\n");
>  		return rc;
>  	}
>  
>  	if (p->state < CXL_CONFIG_COMMIT) {
>  		dev_dbg(&cxlr->dev, "config state: %d\n", p->state);
> -		rc = -ENXIO;
> -		goto out;
> +		return -ENXIO;
>  	}
>  
>  	if (test_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags)) {
>  		dev_err(&cxlr->dev,
>  			"failed to activate, re-commit region and retry\n");
> -		rc = -ENXIO;
> -		goto out;
> +		return -ENXIO;
>  	}
>  
> -out:
> -	up_read(&cxl_region_rwsem);
> -
> -	if (rc)
> -		return rc;
>  	return 0;
>  }
>  
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 3f1695c96abc..50799a681231 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -469,7 +469,7 @@ enum cxl_config_state {
>   * @nr_targets: number of targets
>   * @cache_size: extended linear cache size if exists, otherwise zero.
>   *
> - * State transitions are protected by the cxl_region_rwsem
> + * State transitions are protected by cxl_rwsem.region
>   */
>  struct cxl_region_params {
>  	enum cxl_config_state state;
> @@ -912,15 +912,4 @@ bool cxl_endpoint_decoder_reset_detected(struct cxl_port *port);
>  #endif
>  
>  u16 cxl_gpf_get_dvsec(struct device *dev);
> -
> -static inline struct rw_semaphore *rwsem_read_intr_acquire(struct rw_semaphore *rwsem)
> -{
> -	if (down_read_interruptible(rwsem))
> -		return NULL;
> -
> -	return rwsem;
> -}
> -
> -DEFINE_FREE(rwsem_read_release, struct rw_semaphore *, if (_T) up_read(_T))
> -
>  #endif /* __CXL_H__ */
> diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h
> index c810deb88d13..cbafdc12e743 100644
> --- a/include/linux/rwsem.h
> +++ b/include/linux/rwsem.h
> @@ -244,6 +244,7 @@ DEFINE_GUARD_COND(rwsem_read, _intr, down_read_interruptible(_T), _RET == 0)
>  
>  DEFINE_GUARD(rwsem_write, struct rw_semaphore *, down_write(_T), up_write(_T))
>  DEFINE_GUARD_COND(rwsem_write, _try, down_write_trylock(_T))
> +DEFINE_GUARD_COND(rwsem_write, _kill, down_write_killable(_T), _RET == 0)
>  
>  /*
>   * downgrade write lock to read lock


^ permalink raw reply	[flat|nested] 32+ messages in thread

* RE: [PATCH v3 8/8] cxl: Convert to ACQUIRE() for conditional rwsem locking
  2025-07-14 16:28   ` Shiju Jose
@ 2025-07-14 19:21     ` dan.j.williams
  0 siblings, 0 replies; 32+ messages in thread
From: dan.j.williams @ 2025-07-14 19:21 UTC (permalink / raw)
  To: Shiju Jose, Dan Williams, linux-cxl@vger.kernel.org
  Cc: linux-kernel@vger.kernel.org, David Lechner, Peter Zijlstra,
	Linus Torvalds, Ingo Molnar, Fabio M. De Francesco,
	Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
	Vishal Verma, Ira Weiny

Shiju Jose wrote:
> >-----Original Message-----
> >From: Dan Williams <dan.j.williams@intel.com>
> >Sent: 12 July 2025 00:50
> >To: linux-cxl@vger.kernel.org
> >Cc: linux-kernel@vger.kernel.org; David Lechner <dlechner@baylibre.com>;
> >Peter Zijlstra <peterz@infradead.org>; Linus Torvalds <torvalds@linux-
> >foundation.org>; Ingo Molnar <mingo@kernel.org>; Fabio M. De Francesco
> ><fabio.m.de.francesco@linux.intel.com>; Davidlohr Bueso <dave@stgolabs.net>;
> >Jonathan Cameron <jonathan.cameron@huawei.com>; Dave Jiang
> ><dave.jiang@intel.com>; Alison Schofield <alison.schofield@intel.com>; Vishal
> >Verma <vishal.l.verma@intel.com>; Ira Weiny <ira.weiny@intel.com>; Shiju Jose
> ><shiju.jose@huawei.com>
> >Subject: [PATCH v3 8/8] cxl: Convert to ACQUIRE() for conditional rwsem locking
> >
> >Use ACQUIRE() to cleanup conditional locking paths in the CXL driver The
> >ACQUIRE() macro and its associated ACQUIRE_ERR() helpers, like
> >scoped_cond_guard(), arrange for scoped-based conditional locking. Unlike
> >scoped_cond_guard(), these macros arrange for an ERR_PTR() to be retrieved
> >representing the state of the conditional lock.
> >
> >The goal of this conversion is to complete the removal of all explicit unlock calls
> >in the subsystem. I.e. the methods to acquire a lock are solely via guard(),
> >scoped_guard() (for limited cases), or ACQUIRE(). All unlock is implicit / scope-
> >based. In order to make sure all lock sites are converted, the existing rwsem's
> >are consolidated and renamed in 'struct cxl_rwsem'. While that makes the patch
> >noisier it gives a clean cut-off between old-world (explicit unlock allowed), and
> >new world (explicit unlock deleted).
> >
> >Cc: David Lechner <dlechner@baylibre.com>
> >Cc: Peter Zijlstra <peterz@infradead.org>
> >Cc: Linus Torvalds <torvalds@linux-foundation.org>
> >Cc: Ingo Molnar <mingo@kernel.org>
> >Cc: "Fabio M. De Francesco" <fabio.m.de.francesco@linux.intel.com>
> >Cc: Davidlohr Bueso <dave@stgolabs.net>
> >Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
> >Cc: Dave Jiang <dave.jiang@intel.com>
> >Cc: Alison Schofield <alison.schofield@intel.com>
> >Cc: Vishal Verma <vishal.l.verma@intel.com>
> >Cc: Ira Weiny <ira.weiny@intel.com>
> >Cc: Shiju Jose <shiju.jose@huawei.com>
> >Acked-by: "Peter Zijlstra (Intel)" <peterz@infradead.org>
> >Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> 
> Hi Dan,
> 
> For changes in CXL EDAC (drivers/cxl/core/edac.c),
> Tested-by: Shiju Jose <shiju.jose@huawei.com>
> 
> >---
> > drivers/cxl/core/cdat.c   |   6 +-
> > drivers/cxl/core/core.h   |  17 ++-
> > drivers/cxl/core/edac.c   |  44 +++---
> > drivers/cxl/core/hdm.c    |  41 +++---
> > drivers/cxl/core/mbox.c   |   6 +-
> > drivers/cxl/core/memdev.c |  50 +++----
> > drivers/cxl/core/port.c   |  18 +--
> > drivers/cxl/core/region.c | 295 ++++++++++++++++----------------------
> > drivers/cxl/cxl.h         |  13 +-
> > include/linux/rwsem.h     |   1 +
> > 10 files changed, 212 insertions(+), 279 deletions(-)
> >
> [...]
> >diff --git a/drivers/cxl/core/edac.c b/drivers/cxl/core/edac.c index
> >2cbc664e5d62..f1ebdbe222c8 100644
> >--- a/drivers/cxl/core/edac.c
> >+++ b/drivers/cxl/core/edac.c
> >@@ -115,10 +115,9 @@ static int cxl_scrub_get_attrbs(struct
> >cxl_patrol_scrub_context *cxl_ps_ctx,
> > 						flags, min_cycle);
> > 	}
> >
> >-	struct rw_semaphore *region_lock __free(rwsem_read_release) =
> >-		rwsem_read_intr_acquire(&cxl_region_rwsem);
> >-	if (!region_lock)
> >-		return -EINTR;
> >+	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
> >+	if ((ret = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
> Checkpatch is giving error here and in other places with similar coding style,  
> "ERROR: do not use assignment in if condition"

Yes, the proposal is that the rule be relaxed for ACQUIRE_ERR()
compactness similar to how the C99 variable declaration expectations
were relaxed for __free().

Prior discussion with Jonathan [1] and Alison [2] about this. 

[1]: http://lore.kernel.org/6870833aa1344_588c100dd@dwillia2-xfh.jf.intel.com.notmuch
[2]: http://lore.kernel.org/aGXDMZB6omShJpoj@aschofie-mobl2.lan

In general, checkpatch also needs to catch up with cleanup macros like
DEFINE_FREE. So, I think that "checkpatch updates for cleanup.h" is a
worthwhile discussion as these helpers get more use.

> [...]
> > 		p = &cxlr->params;
> >@@ -2215,18 +2173,18 @@ int cxl_decoder_detach(struct cxl_region *cxlr,
> > 	struct cxl_region *detach;
> >
> > 	/* when the decoder is being destroyed lock unconditionally */
> >-	if (mode == DETACH_INVALIDATE)
> >-		down_write(&cxl_region_rwsem);
> >-	else {
> >-		int rc = down_write_killable(&cxl_region_rwsem);
> >+	if (mode == DETACH_INVALIDATE) {
> >+		guard(rwsem_write)(&cxl_rwsem.region);
> >+		detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);
> >+	} else {
> >+		int rc;
> >
> >-		if (rc)
> >+		ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
> >+		if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
> > 			return rc;
> >+		detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);
> > 	}
> May be detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);
> add outside if ... else as before?

Recall that the lock is only held within its bracketed scope. So this...

	if (mode == DETACH_INVALIDATE) {
		guard(rwsem_write)(&cxl_rwsem.region);
	} else {
		int rc;
	
		ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
		if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
			return rc;
	}
	detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);

...would be equivalent to:

	if (mode == DETACH_INVALIDATE) {
		down_write(&cxl_rwsem.region);
		up_write(&cxl_rwsem.region);
	} else {
		int rc = down_write_killable(&cxl_rwsem.region);

		if (rc)
			return rc;
		up_write(&cxl_rwsem.region);
	}
	detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 5/8] cxl/region: Split commit_store() into __commit() and queue_reset() helpers
  2025-07-11 23:49 ` [PATCH v3 5/8] cxl/region: Split commit_store() into __commit() and queue_reset() helpers Dan Williams
@ 2025-07-14 21:49   ` Fabio M. De Francesco
  0 siblings, 0 replies; 32+ messages in thread
From: Fabio M. De Francesco @ 2025-07-14 21:49 UTC (permalink / raw)
  To: linux-cxl, Dan Williams
  Cc: linux-kernel, Davidlohr Bueso, Jonathan Cameron, Dave Jiang,
	Alison Schofield, Vishal Verma, Ira Weiny, Peter Zijlstra (Intel)

On Saturday, July 12, 2025 1:49:29 AM Central European Summer Time Dan Williams wrote:
> The complexity of dropping the lock is removed in favor of splitting commit
> operations to a helper, and leaving all the complexities of "decommit" for
> commit_store() to coordinate the different locking contexts.
> 
> The CPU cache-invalidation in the decommit path is solely handled now by
> cxl_region_decode_reset(). Previously the CPU caches were being needlessly
> flushed twice in the decommit path where the first flush had no guarantee
> that the memory would not be immediately re-dirtied.
> 
> Cc: Davidlohr Bueso <dave@stgolabs.net>
> Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
> Cc: Dave Jiang <dave.jiang@intel.com>
> Cc: Alison Schofield <alison.schofield@intel.com>
> Cc: Vishal Verma <vishal.l.verma@intel.com>
> Cc: Ira Weiny <ira.weiny@intel.com>
> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>

Reviewed-by: Fabio M. De Francesco <fabio.m.de.francesco@linux.intel.com>

> ---
>  drivers/cxl/core/region.c | 99 +++++++++++++++++++++++++++------------
>  1 file changed, 70 insertions(+), 29 deletions(-)
> 
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 6e5e1460068d..3a77aec2c447 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -349,30 +349,42 @@ static int cxl_region_decode_commit(struct cxl_region *cxlr)
>  	return rc;
>  }
>  
> -static ssize_t commit_store(struct device *dev, struct device_attribute *attr,
> -			    const char *buf, size_t len)
> +static int queue_reset(struct cxl_region *cxlr)
>  {
> -	struct cxl_region *cxlr = to_cxl_region(dev);
>  	struct cxl_region_params *p = &cxlr->params;
> -	bool commit;
> -	ssize_t rc;
> +	int rc;
>  
> -	rc = kstrtobool(buf, &commit);
> +	rc = down_write_killable(&cxl_region_rwsem);
>  	if (rc)
>  		return rc;
>  
> +	/* Already in the requested state? */
> +	if (p->state < CXL_CONFIG_COMMIT)
> +		goto out;
> +
> +	p->state = CXL_CONFIG_RESET_PENDING;
> +
> +out:
> +	up_write(&cxl_region_rwsem);
> +
> +	return rc;
> +}
> +
> +static int __commit(struct cxl_region *cxlr)
> +{
> +	struct cxl_region_params *p = &cxlr->params;
> +	int rc;
> +
>  	rc = down_write_killable(&cxl_region_rwsem);
>  	if (rc)
>  		return rc;
>  
>  	/* Already in the requested state? */
> -	if (commit && p->state >= CXL_CONFIG_COMMIT)
> -		goto out;
> -	if (!commit && p->state < CXL_CONFIG_COMMIT)
> +	if (p->state >= CXL_CONFIG_COMMIT)
>  		goto out;
>  
>  	/* Not ready to commit? */
> -	if (commit && p->state < CXL_CONFIG_ACTIVE) {
> +	if (p->state < CXL_CONFIG_ACTIVE) {
>  		rc = -ENXIO;
>  		goto out;
>  	}
> @@ -385,31 +397,60 @@ static ssize_t commit_store(struct device *dev, struct device_attribute *attr,
>  	if (rc)
>  		goto out;
>  
> -	if (commit) {
> -		rc = cxl_region_decode_commit(cxlr);
> -		if (rc == 0)
> -			p->state = CXL_CONFIG_COMMIT;
> -	} else {
> -		p->state = CXL_CONFIG_RESET_PENDING;
> -		up_write(&cxl_region_rwsem);
> -		device_release_driver(&cxlr->dev);
> -		down_write(&cxl_region_rwsem);
> -
> -		/*
> -		 * The lock was dropped, so need to revalidate that the reset is
> -		 * still pending.
> -		 */
> -		if (p->state == CXL_CONFIG_RESET_PENDING) {
> -			cxl_region_decode_reset(cxlr, p->interleave_ways);
> -			p->state = CXL_CONFIG_ACTIVE;
> -		}
> -	}
> +	rc = cxl_region_decode_commit(cxlr);
> +	if (rc == 0)
> +		p->state = CXL_CONFIG_COMMIT;
>  
>  out:
>  	up_write(&cxl_region_rwsem);
>  
> +	return rc;
> +}
> +
> +static ssize_t commit_store(struct device *dev, struct device_attribute *attr,
> +			    const char *buf, size_t len)
> +{
> +	struct cxl_region *cxlr = to_cxl_region(dev);
> +	struct cxl_region_params *p = &cxlr->params;
> +	bool commit;
> +	ssize_t rc;
> +
> +	rc = kstrtobool(buf, &commit);
>  	if (rc)
>  		return rc;
> +
> +	if (commit) {
> +		rc = __commit(cxlr);
> +		if (rc)
> +			return rc;
> +		return len;
> +	}
> +
> +	rc = queue_reset(cxlr);
> +	if (rc)
> +		return rc;
> +
> +	/*
> +	 * Unmap the region and depend the reset-pending state to ensure
> +	 * it does not go active again until post reset
> +	 */
> +	device_release_driver(&cxlr->dev);
> +
> +	/*
> +	 * With the reset pending take cxl_region_rwsem unconditionally
> +	 * to ensure the reset gets handled before returning.
> +	 */
> +	guard(rwsem_write)(&cxl_region_rwsem);
> +
> +	/*
> +	 * Revalidate that the reset is still pending in case another
> +	 * thread already handled this reset.
> +	 */
> +	if (p->state == CXL_CONFIG_RESET_PENDING) {
> +		cxl_region_decode_reset(cxlr, p->interleave_ways);
> +		p->state = CXL_CONFIG_ACTIVE;
> +	}
> +
>  	return len;
>  }
>  
> -- 
> 2.50.0
> 
> 
> 





^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 6/8] cxl/region: Move ready-to-probe state check to a helper
  2025-07-11 23:49 ` [PATCH v3 6/8] cxl/region: Move ready-to-probe state check to a helper Dan Williams
@ 2025-07-14 22:02   ` Fabio M. De Francesco
  2025-07-15 15:33   ` Jonathan Cameron
  1 sibling, 0 replies; 32+ messages in thread
From: Fabio M. De Francesco @ 2025-07-14 22:02 UTC (permalink / raw)
  To: linux-cxl, Dan Williams
  Cc: linux-kernel, Davidlohr Bueso, Jonathan Cameron, Dave Jiang,
	Alison Schofield, Vishal Verma, Ira Weiny, Peter Zijlstra (Intel)

On Saturday, July 12, 2025 1:49:30 AM Central European Summer Time Dan Williams wrote:
> Rather than unlocking the region rwsem in the middle of cxl_region_probe()
> create a helper for determining when the region is ready-to-probe.
> 
> Cc: Davidlohr Bueso <dave@stgolabs.net>
> Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
> Cc: Dave Jiang <dave.jiang@intel.com>
> Cc: Alison Schofield <alison.schofield@intel.com>
> Cc: Vishal Verma <vishal.l.verma@intel.com>
> Cc: Ira Weiny <ira.weiny@intel.com>
> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>

Reviewed-by: Fabio M. De Francesco <fabio.m.de.francesco@linux.intel.com>

> ---
>  drivers/cxl/core/region.c | 24 ++++++++++++++++++------
>  1 file changed, 18 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 3a77aec2c447..2a97fa9a394f 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -3572,9 +3572,8 @@ static void shutdown_notifiers(void *_cxlr)
>  	unregister_mt_adistance_algorithm(&cxlr->adist_notifier);
>  }
>  
> -static int cxl_region_probe(struct device *dev)
> +static int cxl_region_can_probe(struct cxl_region *cxlr)
>  {
> -	struct cxl_region *cxlr = to_cxl_region(dev);
>  	struct cxl_region_params *p = &cxlr->params;
>  	int rc;
>  
> @@ -3597,15 +3596,28 @@ static int cxl_region_probe(struct device *dev)
>  		goto out;
>  	}
>  
> -	/*
> -	 * From this point on any path that changes the region's state away from
> -	 * CXL_CONFIG_COMMIT is also responsible for releasing the driver.
> -	 */
>  out:
>  	up_read(&cxl_region_rwsem);
>  
>  	if (rc)
>  		return rc;
> +	return 0;
> +}
> +
> +static int cxl_region_probe(struct device *dev)
> +{
> +	struct cxl_region *cxlr = to_cxl_region(dev);
> +	struct cxl_region_params *p = &cxlr->params;
> +	int rc;
> +
> +	rc = cxl_region_can_probe(cxlr);
> +	if (rc)
> +		return rc;
> +
> +	/*
> +	 * From this point on any path that changes the region's state away from
> +	 * CXL_CONFIG_COMMIT is also responsible for releasing the driver.
> +	 */
>  
>  	cxlr->memory_notifier.notifier_call = cxl_region_perf_attrs_callback;
>  	cxlr->memory_notifier.priority = CXL_CALLBACK_PRI;
> -- 
> 2.50.0
> 
> 
> 





^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 7/8] cxl/region: Consolidate cxl_decoder_kill_region() and cxl_region_detach()
  2025-07-11 23:49 ` [PATCH v3 7/8] cxl/region: Consolidate cxl_decoder_kill_region() and cxl_region_detach() Dan Williams
  2025-07-14 18:17   ` Dave Jiang
@ 2025-07-14 22:09   ` Fabio M. De Francesco
  2025-07-15 15:56   ` Jonathan Cameron
  2 siblings, 0 replies; 32+ messages in thread
From: Fabio M. De Francesco @ 2025-07-14 22:09 UTC (permalink / raw)
  To: linux-cxl, Dan Williams
  Cc: linux-kernel, Davidlohr Bueso, Jonathan Cameron, Dave Jiang,
	Alison Schofield, Vishal Verma, Ira Weiny, Peter Zijlstra (Intel)

On Saturday, July 12, 2025 1:49:31 AM Central European Summer Time Dan Williams wrote:
> Both detach_target() and cxld_unregister() want to tear down a cxl_region
> when an endpoint decoder is either detached or destroyed.
> 
> When a region is to be destroyed cxl_region_detach() releases
> cxl_region_rwsem unbinds the cxl_region driver and re-acquires the rwsem.
> 
> This "reverse" locking pattern is difficult to reason about, not amenable
> to scope-based cleanup, and the minor differences in the calling context of
> detach_target() and cxld_unregister() currently results in the
> cxl_decoder_kill_region() wrapper.
> 
> Introduce cxl_decoder_detach() to wrap a core __cxl_decoder_detach() that
> serves both cases. I.e. either detaching a known position in a region
> (interruptible), or detaching an endpoint decoder if it is found to be a
> member of a region (uninterruptible).
> 
> Cc: Davidlohr Bueso <dave@stgolabs.net>
> Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
> Cc: Dave Jiang <dave.jiang@intel.com>
> Cc: Alison Schofield <alison.schofield@intel.com>
> Cc: Vishal Verma <vishal.l.verma@intel.com>
> Cc: Ira Weiny <ira.weiny@intel.com>
> Acked-by: "Peter Zijlstra (Intel)" <peterz@infradead.org>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>

Reviewed-by: Fabio M. De Francesco <fabio.m.de.francesco@linux.intel.com>

> ---
>  drivers/cxl/core/core.h   |  15 +++++-
>  drivers/cxl/core/port.c   |   9 ++--
>  drivers/cxl/core/region.c | 103 ++++++++++++++++++++++----------------
>  3 files changed, 75 insertions(+), 52 deletions(-)
> 
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index 29b61828a847..2be37084409f 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -12,6 +12,11 @@ extern const struct device_type cxl_pmu_type;
>  
>  extern struct attribute_group cxl_base_attribute_group;
>  
> +enum cxl_detach_mode {
> +	DETACH_ONLY,
> +	DETACH_INVALIDATE,
> +};
> +
>  #ifdef CONFIG_CXL_REGION
>  extern struct device_attribute dev_attr_create_pmem_region;
>  extern struct device_attribute dev_attr_create_ram_region;
> @@ -20,7 +25,11 @@ extern struct device_attribute dev_attr_region;
>  extern const struct device_type cxl_pmem_region_type;
>  extern const struct device_type cxl_dax_region_type;
>  extern const struct device_type cxl_region_type;
> -void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled);
> +
> +int cxl_decoder_detach(struct cxl_region *cxlr,
> +		       struct cxl_endpoint_decoder *cxled, int pos,
> +		       enum cxl_detach_mode mode);
> +
>  #define CXL_REGION_ATTR(x) (&dev_attr_##x.attr)
>  #define CXL_REGION_TYPE(x) (&cxl_region_type)
>  #define SET_CXL_REGION_ATTR(x) (&dev_attr_##x.attr),
> @@ -48,7 +57,9 @@ static inline int cxl_get_poison_by_endpoint(struct cxl_port *port)
>  {
>  	return 0;
>  }
> -static inline void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled)
> +static inline int cxl_decoder_detach(struct cxl_region *cxlr,
> +				     struct cxl_endpoint_decoder *cxled,
> +				     int pos, enum cxl_detach_mode mode)
>  {
>  }
>  static inline int cxl_region_init(void)
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index eb46c6764d20..087a20a9ee1c 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -2001,12 +2001,9 @@ EXPORT_SYMBOL_NS_GPL(cxl_decoder_add, "CXL");
>  
>  static void cxld_unregister(void *dev)
>  {
> -	struct cxl_endpoint_decoder *cxled;
> -
> -	if (is_endpoint_decoder(dev)) {
> -		cxled = to_cxl_endpoint_decoder(dev);
> -		cxl_decoder_kill_region(cxled);
> -	}
> +	if (is_endpoint_decoder(dev))
> +		cxl_decoder_detach(NULL, to_cxl_endpoint_decoder(dev), -1,
> +				   DETACH_INVALIDATE);
>  
>  	device_unregister(dev);
>  }
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 2a97fa9a394f..4314aaed8ad8 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -2135,27 +2135,43 @@ static int cxl_region_attach(struct cxl_region *cxlr,
>  	return 0;
>  }
>  
> -static int cxl_region_detach(struct cxl_endpoint_decoder *cxled)
> +static struct cxl_region *
> +__cxl_decoder_detach(struct cxl_region *cxlr,
> +		     struct cxl_endpoint_decoder *cxled, int pos,
> +		     enum cxl_detach_mode mode)
>  {
> -	struct cxl_port *iter, *ep_port = cxled_to_port(cxled);
> -	struct cxl_region *cxlr = cxled->cxld.region;
>  	struct cxl_region_params *p;
> -	int rc = 0;
>  
>  	lockdep_assert_held_write(&cxl_region_rwsem);
>  
> -	if (!cxlr)
> -		return 0;
> +	if (!cxled) {
> +		p = &cxlr->params;
>  
> -	p = &cxlr->params;
> -	get_device(&cxlr->dev);
> +		if (pos >= p->interleave_ways) {
> +			dev_dbg(&cxlr->dev, "position %d out of range %d\n",
> +				pos, p->interleave_ways);
> +			return ERR_PTR(-ENXIO);
> +		}
> +
> +		if (!p->targets[pos])
> +			return NULL;
> +		cxled = p->targets[pos];
> +	} else {
> +		cxlr = cxled->cxld.region;
> +		if (!cxlr)
> +			return NULL;
> +		p = &cxlr->params;
> +	}
> +
> +	if (mode == DETACH_INVALIDATE)
> +		cxled->part = -1;
>  
>  	if (p->state > CXL_CONFIG_ACTIVE) {
>  		cxl_region_decode_reset(cxlr, p->interleave_ways);
>  		p->state = CXL_CONFIG_ACTIVE;
>  	}
>  
> -	for (iter = ep_port; !is_cxl_root(iter);
> +	for (struct cxl_port *iter = cxled_to_port(cxled); !is_cxl_root(iter);
>  	     iter = to_cxl_port(iter->dev.parent))
>  		cxl_port_detach_region(iter, cxlr, cxled);
>  
> @@ -2166,7 +2182,7 @@ static int cxl_region_detach(struct cxl_endpoint_decoder *cxled)
>  		dev_WARN_ONCE(&cxlr->dev, 1, "expected %s:%s at position %d\n",
>  			      dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
>  			      cxled->pos);
> -		goto out;
> +		return NULL;
>  	}
>  
>  	if (p->state == CXL_CONFIG_ACTIVE) {
> @@ -2180,21 +2196,42 @@ static int cxl_region_detach(struct cxl_endpoint_decoder *cxled)
>  		.end = -1,
>  	};
>  
> -	/* notify the region driver that one of its targets has departed */
> -	up_write(&cxl_region_rwsem);
> -	device_release_driver(&cxlr->dev);
> -	down_write(&cxl_region_rwsem);
> -out:
> -	put_device(&cxlr->dev);
> -	return rc;
> +	get_device(&cxlr->dev);
> +	return cxlr;
>  }
>  
> -void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled)
> +/*
> + * Cleanup a decoder's interest in a region. There are 2 cases to
> + * handle, removing an unknown @cxled from a known position in a region
> + * (detach_target()) or removing a known @cxled from an unknown @cxlr
> + * (cxld_unregister())
> + *
> + * When the detachment finds a region release the region driver.
> + */
> +int cxl_decoder_detach(struct cxl_region *cxlr,
> +		       struct cxl_endpoint_decoder *cxled, int pos,
> +		       enum cxl_detach_mode mode)
>  {
> -	down_write(&cxl_region_rwsem);
> -	cxled->part = -1;
> -	cxl_region_detach(cxled);
> +	struct cxl_region *detach;
> +
> +	/* when the decoder is being destroyed lock unconditionally */
> +	if (mode == DETACH_INVALIDATE)
> +		down_write(&cxl_region_rwsem);
> +	else {
> +		int rc = down_write_killable(&cxl_region_rwsem);
> +
> +		if (rc)
> +			return rc;
> +	}
> +
> +	detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);
>  	up_write(&cxl_region_rwsem);
> +
> +	if (detach) {
> +		device_release_driver(&detach->dev);
> +		put_device(&detach->dev);
> +	}
> +	return 0;
>  }
>  
>  static int attach_target(struct cxl_region *cxlr,
> @@ -2225,29 +2262,7 @@ static int attach_target(struct cxl_region *cxlr,
>  
>  static int detach_target(struct cxl_region *cxlr, int pos)
>  {
> -	struct cxl_region_params *p = &cxlr->params;
> -	int rc;
> -
> -	rc = down_write_killable(&cxl_region_rwsem);
> -	if (rc)
> -		return rc;
> -
> -	if (pos >= p->interleave_ways) {
> -		dev_dbg(&cxlr->dev, "position %d out of range %d\n", pos,
> -			p->interleave_ways);
> -		rc = -ENXIO;
> -		goto out;
> -	}
> -
> -	if (!p->targets[pos]) {
> -		rc = 0;
> -		goto out;
> -	}
> -
> -	rc = cxl_region_detach(p->targets[pos]);
> -out:
> -	up_write(&cxl_region_rwsem);
> -	return rc;
> +	return cxl_decoder_detach(cxlr, NULL, pos, DETACH_ONLY);
>  }
>  
>  static size_t store_targetN(struct cxl_region *cxlr, const char *buf, int pos,
> -- 
> 2.50.0
> 
> 
> 





^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 8/8] cxl: Convert to ACQUIRE() for conditional rwsem locking
  2025-07-11 23:49 ` [PATCH v3 8/8] cxl: Convert to ACQUIRE() for conditional rwsem locking Dan Williams
  2025-07-14 16:28   ` Shiju Jose
  2025-07-14 18:39   ` Dave Jiang
@ 2025-07-14 22:12   ` Fabio M. De Francesco
  2025-07-15 16:20   ` Jonathan Cameron
  3 siblings, 0 replies; 32+ messages in thread
From: Fabio M. De Francesco @ 2025-07-14 22:12 UTC (permalink / raw)
  To: linux-cxl, Dan Williams
  Cc: linux-kernel, David Lechner, Peter Zijlstra, Linus Torvalds,
	Ingo Molnar, Davidlohr Bueso, Jonathan Cameron, Dave Jiang,
	Alison Schofield, Vishal Verma, Ira Weiny, Shiju Jose

On Saturday, July 12, 2025 1:49:32 AM Central European Summer Time Dan Williams wrote:
> Use ACQUIRE() to cleanup conditional locking paths in the CXL driver
> The ACQUIRE() macro and its associated ACQUIRE_ERR() helpers, like
> scoped_cond_guard(), arrange for scoped-based conditional locking. Unlike
> scoped_cond_guard(), these macros arrange for an ERR_PTR() to be retrieved
> representing the state of the conditional lock.
> 
> The goal of this conversion is to complete the removal of all explicit
> unlock calls in the subsystem. I.e. the methods to acquire a lock are
> solely via guard(), scoped_guard() (for limited cases), or ACQUIRE(). All
> unlock is implicit / scope-based. In order to make sure all lock sites are
> converted, the existing rwsem's are consolidated and renamed in 'struct
> cxl_rwsem'. While that makes the patch noisier it gives a clean cut-off
> between old-world (explicit unlock allowed), and new world (explicit unlock
> deleted).
> 
> Cc: David Lechner <dlechner@baylibre.com>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: Linus Torvalds <torvalds@linux-foundation.org>
> Cc: Ingo Molnar <mingo@kernel.org>
> Cc: "Fabio M. De Francesco" <fabio.m.de.francesco@linux.intel.com>
> Cc: Davidlohr Bueso <dave@stgolabs.net>
> Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
> Cc: Dave Jiang <dave.jiang@intel.com>
> Cc: Alison Schofield <alison.schofield@intel.com>
> Cc: Vishal Verma <vishal.l.verma@intel.com>
> Cc: Ira Weiny <ira.weiny@intel.com>
> Cc: Shiju Jose <shiju.jose@huawei.com>
> Acked-by: "Peter Zijlstra (Intel)" <peterz@infradead.org>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>

Reviewed-by: Fabio M. De Francesco <fabio.m.de.francesco@linux.intel.com>

> ---
>  drivers/cxl/core/cdat.c   |   6 +-
>  drivers/cxl/core/core.h   |  17 ++-
>  drivers/cxl/core/edac.c   |  44 +++---
>  drivers/cxl/core/hdm.c    |  41 +++---
>  drivers/cxl/core/mbox.c   |   6 +-
>  drivers/cxl/core/memdev.c |  50 +++----
>  drivers/cxl/core/port.c   |  18 +--
>  drivers/cxl/core/region.c | 295 ++++++++++++++++----------------------
>  drivers/cxl/cxl.h         |  13 +-
>  include/linux/rwsem.h     |   1 +
>  10 files changed, 212 insertions(+), 279 deletions(-)
> 
> diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
> index 0ccef2f2a26a..c0af645425f4 100644
> --- a/drivers/cxl/core/cdat.c
> +++ b/drivers/cxl/core/cdat.c
> @@ -336,7 +336,7 @@ static int match_cxlrd_hb(struct device *dev, void *data)
>  	cxlrd = to_cxl_root_decoder(dev);
>  	cxlsd = &cxlrd->cxlsd;
>  
> -	guard(rwsem_read)(&cxl_region_rwsem);
> +	guard(rwsem_read)(&cxl_rwsem.region);
>  	for (int i = 0; i < cxlsd->nr_targets; i++) {
>  		if (host_bridge == cxlsd->target[i]->dport_dev)
>  			return 1;
> @@ -987,7 +987,7 @@ void cxl_region_shared_upstream_bandwidth_update(struct cxl_region *cxlr)
>  	bool is_root;
>  	int rc;
>  
> -	lockdep_assert_held(&cxl_dpa_rwsem);
> +	lockdep_assert_held(&cxl_rwsem.dpa);
>  
>  	struct xarray *usp_xa __free(free_perf_xa) =
>  		kzalloc(sizeof(*usp_xa), GFP_KERNEL);
> @@ -1057,7 +1057,7 @@ void cxl_region_perf_data_calculate(struct cxl_region *cxlr,
>  {
>  	struct cxl_dpa_perf *perf;
>  
> -	lockdep_assert_held(&cxl_dpa_rwsem);
> +	lockdep_assert_held(&cxl_rwsem.dpa);
>  
>  	perf = cxled_get_dpa_perf(cxled);
>  	if (IS_ERR(perf))
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index 2be37084409f..f796731deedf 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -5,6 +5,7 @@
>  #define __CXL_CORE_H__
>  
>  #include <cxl/mailbox.h>
> +#include <linux/rwsem.h>
>  
>  extern const struct device_type cxl_nvdimm_bridge_type;
>  extern const struct device_type cxl_nvdimm_type;
> @@ -107,8 +108,20 @@ u16 cxl_rcrb_to_aer(struct device *dev, resource_size_t rcrb);
>  #define PCI_RCRB_CAP_HDR_NEXT_MASK	GENMASK(15, 8)
>  #define PCI_CAP_EXP_SIZEOF		0x3c
>  
> -extern struct rw_semaphore cxl_dpa_rwsem;
> -extern struct rw_semaphore cxl_region_rwsem;
> +struct cxl_rwsem {
> +	/*
> +	 * All changes to HPA (interleave configuration) occur with this
> +	 * lock held for write.
> +	 */
> +	struct rw_semaphore region;
> +	/*
> +	 * All changes to a device DPA space occur with this lock held
> +	 * for write.
> +	 */
> +	struct rw_semaphore dpa;
> +};
> +
> +extern struct cxl_rwsem cxl_rwsem;
>  
>  int cxl_memdev_init(void);
>  void cxl_memdev_exit(void);
> diff --git a/drivers/cxl/core/edac.c b/drivers/cxl/core/edac.c
> index 2cbc664e5d62..f1ebdbe222c8 100644
> --- a/drivers/cxl/core/edac.c
> +++ b/drivers/cxl/core/edac.c
> @@ -115,10 +115,9 @@ static int cxl_scrub_get_attrbs(struct cxl_patrol_scrub_context *cxl_ps_ctx,
>  						flags, min_cycle);
>  	}
>  
> -	struct rw_semaphore *region_lock __free(rwsem_read_release) =
> -		rwsem_read_intr_acquire(&cxl_region_rwsem);
> -	if (!region_lock)
> -		return -EINTR;
> +	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
> +	if ((ret = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
> +		return ret;
>  
>  	cxlr = cxl_ps_ctx->cxlr;
>  	p = &cxlr->params;
> @@ -154,10 +153,9 @@ static int cxl_scrub_set_attrbs_region(struct device *dev,
>  	struct cxl_region *cxlr;
>  	int ret, i;
>  
> -	struct rw_semaphore *region_lock __free(rwsem_read_release) =
> -		rwsem_read_intr_acquire(&cxl_region_rwsem);
> -	if (!region_lock)
> -		return -EINTR;
> +	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
> +	if ((ret = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
> +		return ret;
>  
>  	cxlr = cxl_ps_ctx->cxlr;
>  	p = &cxlr->params;
> @@ -1332,16 +1330,15 @@ cxl_mem_perform_sparing(struct device *dev,
>  	struct cxl_memdev_sparing_in_payload sparing_pi;
>  	struct cxl_event_dram *rec = NULL;
>  	u16 validity_flags = 0;
> +	int ret;
>  
> -	struct rw_semaphore *region_lock __free(rwsem_read_release) =
> -		rwsem_read_intr_acquire(&cxl_region_rwsem);
> -	if (!region_lock)
> -		return -EINTR;
> +	ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
> +	if ((ret = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
> +		return ret;
>  
> -	struct rw_semaphore *dpa_lock __free(rwsem_read_release) =
> -		rwsem_read_intr_acquire(&cxl_dpa_rwsem);
> -	if (!dpa_lock)
> -		return -EINTR;
> +	ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
> +	if ((ret = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
> +		return ret;
>  
>  	if (!cxl_sparing_ctx->cap_safe_when_in_use) {
>  		/* Memory to repair must be offline */
> @@ -1779,16 +1776,15 @@ static int cxl_mem_perform_ppr(struct cxl_ppr_context *cxl_ppr_ctx)
>  	struct cxl_memdev_ppr_maintenance_attrbs maintenance_attrbs;
>  	struct cxl_memdev *cxlmd = cxl_ppr_ctx->cxlmd;
>  	struct cxl_mem_repair_attrbs attrbs = { 0 };
> +	int ret;
>  
> -	struct rw_semaphore *region_lock __free(rwsem_read_release) =
> -		rwsem_read_intr_acquire(&cxl_region_rwsem);
> -	if (!region_lock)
> -		return -EINTR;
> +	ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
> +	if ((ret = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
> +		return ret;
>  
> -	struct rw_semaphore *dpa_lock __free(rwsem_read_release) =
> -		rwsem_read_intr_acquire(&cxl_dpa_rwsem);
> -	if (!dpa_lock)
> -		return -EINTR;
> +	ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
> +	if ((ret = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
> +		return ret;
>  
>  	if (!cxl_ppr_ctx->media_accessible || !cxl_ppr_ctx->data_retained) {
>  		/* Memory to repair must be offline */
> diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
> index e9cb34e30248..865a71bce251 100644
> --- a/drivers/cxl/core/hdm.c
> +++ b/drivers/cxl/core/hdm.c
> @@ -16,7 +16,10 @@
>   * for enumerating these registers and capabilities.
>   */
>  
> -DECLARE_RWSEM(cxl_dpa_rwsem);
> +struct cxl_rwsem cxl_rwsem = {
> +	.region = __RWSEM_INITIALIZER(cxl_rwsem.region),
> +	.dpa = __RWSEM_INITIALIZER(cxl_rwsem.dpa),
> +};
>  
>  static int add_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
>  			   int *target_map)
> @@ -214,7 +217,7 @@ void cxl_dpa_debug(struct seq_file *file, struct cxl_dev_state *cxlds)
>  {
>  	struct resource *p1, *p2;
>  
> -	guard(rwsem_read)(&cxl_dpa_rwsem);
> +	guard(rwsem_read)(&cxl_rwsem.dpa);
>  	for (p1 = cxlds->dpa_res.child; p1; p1 = p1->sibling) {
>  		__cxl_dpa_debug(file, p1, 0);
>  		for (p2 = p1->child; p2; p2 = p2->sibling)
> @@ -266,7 +269,7 @@ static void __cxl_dpa_release(struct cxl_endpoint_decoder *cxled)
>  	struct resource *res = cxled->dpa_res;
>  	resource_size_t skip_start;
>  
> -	lockdep_assert_held_write(&cxl_dpa_rwsem);
> +	lockdep_assert_held_write(&cxl_rwsem.dpa);
>  
>  	/* save @skip_start, before @res is released */
>  	skip_start = res->start - cxled->skip;
> @@ -281,7 +284,7 @@ static void __cxl_dpa_release(struct cxl_endpoint_decoder *cxled)
>  
>  static void cxl_dpa_release(void *cxled)
>  {
> -	guard(rwsem_write)(&cxl_dpa_rwsem);
> +	guard(rwsem_write)(&cxl_rwsem.dpa);
>  	__cxl_dpa_release(cxled);
>  }
>  
> @@ -293,7 +296,7 @@ static void devm_cxl_dpa_release(struct cxl_endpoint_decoder *cxled)
>  {
>  	struct cxl_port *port = cxled_to_port(cxled);
>  
> -	lockdep_assert_held_write(&cxl_dpa_rwsem);
> +	lockdep_assert_held_write(&cxl_rwsem.dpa);
>  	devm_remove_action(&port->dev, cxl_dpa_release, cxled);
>  	__cxl_dpa_release(cxled);
>  }
> @@ -361,7 +364,7 @@ static int __cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled,
>  	struct resource *res;
>  	int rc;
>  
> -	lockdep_assert_held_write(&cxl_dpa_rwsem);
> +	lockdep_assert_held_write(&cxl_rwsem.dpa);
>  
>  	if (!len) {
>  		dev_warn(dev, "decoder%d.%d: empty reservation attempted\n",
> @@ -470,7 +473,7 @@ int cxl_dpa_setup(struct cxl_dev_state *cxlds, const struct cxl_dpa_info *info)
>  {
>  	struct device *dev = cxlds->dev;
>  
> -	guard(rwsem_write)(&cxl_dpa_rwsem);
> +	guard(rwsem_write)(&cxl_rwsem.dpa);
>  
>  	if (cxlds->nr_partitions)
>  		return -EBUSY;
> @@ -516,9 +519,8 @@ int devm_cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled,
>  	struct cxl_port *port = cxled_to_port(cxled);
>  	int rc;
>  
> -	down_write(&cxl_dpa_rwsem);
> -	rc = __cxl_dpa_reserve(cxled, base, len, skipped);
> -	up_write(&cxl_dpa_rwsem);
> +	scoped_guard(rwsem_write, &cxl_rwsem.dpa)
> +		rc = __cxl_dpa_reserve(cxled, base, len, skipped);
>  
>  	if (rc)
>  		return rc;
> @@ -529,7 +531,7 @@ EXPORT_SYMBOL_NS_GPL(devm_cxl_dpa_reserve, "CXL");
>  
>  resource_size_t cxl_dpa_size(struct cxl_endpoint_decoder *cxled)
>  {
> -	guard(rwsem_read)(&cxl_dpa_rwsem);
> +	guard(rwsem_read)(&cxl_rwsem.dpa);
>  	if (cxled->dpa_res)
>  		return resource_size(cxled->dpa_res);
>  
> @@ -540,7 +542,7 @@ resource_size_t cxl_dpa_resource_start(struct cxl_endpoint_decoder *cxled)
>  {
>  	resource_size_t base = -1;
>  
> -	lockdep_assert_held(&cxl_dpa_rwsem);
> +	lockdep_assert_held(&cxl_rwsem.dpa);
>  	if (cxled->dpa_res)
>  		base = cxled->dpa_res->start;
>  
> @@ -552,7 +554,7 @@ int cxl_dpa_free(struct cxl_endpoint_decoder *cxled)
>  	struct cxl_port *port = cxled_to_port(cxled);
>  	struct device *dev = &cxled->cxld.dev;
>  
> -	guard(rwsem_write)(&cxl_dpa_rwsem);
> +	guard(rwsem_write)(&cxl_rwsem.dpa);
>  	if (!cxled->dpa_res)
>  		return 0;
>  	if (cxled->cxld.region) {
> @@ -582,7 +584,7 @@ int cxl_dpa_set_part(struct cxl_endpoint_decoder *cxled,
>  	struct device *dev = &cxled->cxld.dev;
>  	int part;
>  
> -	guard(rwsem_write)(&cxl_dpa_rwsem);
> +	guard(rwsem_write)(&cxl_rwsem.dpa);
>  	if (cxled->cxld.flags & CXL_DECODER_F_ENABLE)
>  		return -EBUSY;
>  
> @@ -614,7 +616,7 @@ static int __cxl_dpa_alloc(struct cxl_endpoint_decoder *cxled, u64 size)
>  	struct resource *p, *last;
>  	int part;
>  
> -	guard(rwsem_write)(&cxl_dpa_rwsem);
> +	guard(rwsem_write)(&cxl_rwsem.dpa);
>  	if (cxled->cxld.region) {
>  		dev_dbg(dev, "decoder attached to %s\n",
>  			dev_name(&cxled->cxld.region->dev));
> @@ -842,9 +844,8 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)
>  		}
>  	}
>  
> -	down_read(&cxl_dpa_rwsem);
> -	setup_hw_decoder(cxld, hdm);
> -	up_read(&cxl_dpa_rwsem);
> +	scoped_guard(rwsem_read, &cxl_rwsem.dpa)
> +		setup_hw_decoder(cxld, hdm);
>  
>  	port->commit_end++;
>  	rc = cxld_await_commit(hdm, cxld->id);
> @@ -882,7 +883,7 @@ void cxl_port_commit_reap(struct cxl_decoder *cxld)
>  {
>  	struct cxl_port *port = to_cxl_port(cxld->dev.parent);
>  
> -	lockdep_assert_held_write(&cxl_region_rwsem);
> +	lockdep_assert_held_write(&cxl_rwsem.region);
>  
>  	/*
>  	 * Once the highest committed decoder is disabled, free any other
> @@ -1030,7 +1031,7 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
>  		else
>  			cxld->target_type = CXL_DECODER_DEVMEM;
>  
> -		guard(rwsem_write)(&cxl_region_rwsem);
> +		guard(rwsem_write)(&cxl_rwsem.region);
>  		if (cxld->id != cxl_num_decoders_committed(port)) {
>  			dev_warn(&port->dev,
>  				 "decoder%d.%d: Committed out of order\n",
> diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> index 81b21effe8cf..92cd3cbdd8ec 100644
> --- a/drivers/cxl/core/mbox.c
> +++ b/drivers/cxl/core/mbox.c
> @@ -909,8 +909,8 @@ void cxl_event_trace_record(const struct cxl_memdev *cxlmd,
>  		 * translations. Take topology mutation locks and lookup
>  		 * { HPA, REGION } from { DPA, MEMDEV } in the event record.
>  		 */
> -		guard(rwsem_read)(&cxl_region_rwsem);
> -		guard(rwsem_read)(&cxl_dpa_rwsem);
> +		guard(rwsem_read)(&cxl_rwsem.region);
> +		guard(rwsem_read)(&cxl_rwsem.dpa);
>  
>  		dpa = le64_to_cpu(evt->media_hdr.phys_addr) & CXL_DPA_MASK;
>  		cxlr = cxl_dpa_to_region(cxlmd, dpa);
> @@ -1265,7 +1265,7 @@ int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd)
>  	/* synchronize with cxl_mem_probe() and decoder write operations */
>  	guard(device)(&cxlmd->dev);
>  	endpoint = cxlmd->endpoint;
> -	guard(rwsem_read)(&cxl_region_rwsem);
> +	guard(rwsem_read)(&cxl_rwsem.region);
>  	/*
>  	 * Require an endpoint to be safe otherwise the driver can not
>  	 * be sure that the device is unmapped.
> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> index f88a13adf7fa..f5fbd34310fd 100644
> --- a/drivers/cxl/core/memdev.c
> +++ b/drivers/cxl/core/memdev.c
> @@ -232,15 +232,13 @@ int cxl_trigger_poison_list(struct cxl_memdev *cxlmd)
>  	if (!port || !is_cxl_endpoint(port))
>  		return -EINVAL;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
>  		return rc;
>  
> -	rc = down_read_interruptible(&cxl_dpa_rwsem);
> -	if (rc) {
> -		up_read(&cxl_region_rwsem);
> +	ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
>  		return rc;
> -	}
>  
>  	if (cxl_num_decoders_committed(port) == 0) {
>  		/* No regions mapped to this memdev */
> @@ -249,8 +247,6 @@ int cxl_trigger_poison_list(struct cxl_memdev *cxlmd)
>  		/* Regions mapped, collect poison by endpoint */
>  		rc =  cxl_get_poison_by_endpoint(port);
>  	}
> -	up_read(&cxl_dpa_rwsem);
> -	up_read(&cxl_region_rwsem);
>  
>  	return rc;
>  }
> @@ -292,19 +288,17 @@ int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa)
>  	if (!IS_ENABLED(CONFIG_DEBUG_FS))
>  		return 0;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
>  		return rc;
>  
> -	rc = down_read_interruptible(&cxl_dpa_rwsem);
> -	if (rc) {
> -		up_read(&cxl_region_rwsem);
> +	ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
>  		return rc;
> -	}
>  
>  	rc = cxl_validate_poison_dpa(cxlmd, dpa);
>  	if (rc)
> -		goto out;
> +		return rc;
>  
>  	inject.address = cpu_to_le64(dpa);
>  	mbox_cmd = (struct cxl_mbox_cmd) {
> @@ -314,7 +308,7 @@ int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa)
>  	};
>  	rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd);
>  	if (rc)
> -		goto out;
> +		return rc;
>  
>  	cxlr = cxl_dpa_to_region(cxlmd, dpa);
>  	if (cxlr)
> @@ -327,11 +321,8 @@ int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa)
>  		.length = cpu_to_le32(1),
>  	};
>  	trace_cxl_poison(cxlmd, cxlr, &record, 0, 0, CXL_POISON_TRACE_INJECT);
> -out:
> -	up_read(&cxl_dpa_rwsem);
> -	up_read(&cxl_region_rwsem);
>  
> -	return rc;
> +	return 0;
>  }
>  EXPORT_SYMBOL_NS_GPL(cxl_inject_poison, "CXL");
>  
> @@ -347,19 +338,17 @@ int cxl_clear_poison(struct cxl_memdev *cxlmd, u64 dpa)
>  	if (!IS_ENABLED(CONFIG_DEBUG_FS))
>  		return 0;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
>  		return rc;
>  
> -	rc = down_read_interruptible(&cxl_dpa_rwsem);
> -	if (rc) {
> -		up_read(&cxl_region_rwsem);
> +	ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
>  		return rc;
> -	}
>  
>  	rc = cxl_validate_poison_dpa(cxlmd, dpa);
>  	if (rc)
> -		goto out;
> +		return rc;
>  
>  	/*
>  	 * In CXL 3.0 Spec 8.2.9.8.4.3, the Clear Poison mailbox command
> @@ -378,7 +367,7 @@ int cxl_clear_poison(struct cxl_memdev *cxlmd, u64 dpa)
>  
>  	rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd);
>  	if (rc)
> -		goto out;
> +		return rc;
>  
>  	cxlr = cxl_dpa_to_region(cxlmd, dpa);
>  	if (cxlr)
> @@ -391,11 +380,8 @@ int cxl_clear_poison(struct cxl_memdev *cxlmd, u64 dpa)
>  		.length = cpu_to_le32(1),
>  	};
>  	trace_cxl_poison(cxlmd, cxlr, &record, 0, 0, CXL_POISON_TRACE_CLEAR);
> -out:
> -	up_read(&cxl_dpa_rwsem);
> -	up_read(&cxl_region_rwsem);
>  
> -	return rc;
> +	return 0;
>  }
>  EXPORT_SYMBOL_NS_GPL(cxl_clear_poison, "CXL");
>  
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index 087a20a9ee1c..bacf1380dc4d 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -30,18 +30,12 @@
>   * instantiated by the core.
>   */
>  
> -/*
> - * All changes to the interleave configuration occur with this lock held
> - * for write.
> - */
> -DECLARE_RWSEM(cxl_region_rwsem);
> -
>  static DEFINE_IDA(cxl_port_ida);
>  static DEFINE_XARRAY(cxl_root_buses);
>  
>  int cxl_num_decoders_committed(struct cxl_port *port)
>  {
> -	lockdep_assert_held(&cxl_region_rwsem);
> +	lockdep_assert_held(&cxl_rwsem.region);
>  
>  	return port->commit_end + 1;
>  }
> @@ -176,7 +170,7 @@ static ssize_t target_list_show(struct device *dev,
>  	ssize_t offset;
>  	int rc;
>  
> -	guard(rwsem_read)(&cxl_region_rwsem);
> +	guard(rwsem_read)(&cxl_rwsem.region);
>  	rc = emit_target_list(cxlsd, buf);
>  	if (rc < 0)
>  		return rc;
> @@ -196,7 +190,7 @@ static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
>  	struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(dev);
>  	struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
>  	struct cxl_dev_state *cxlds = cxlmd->cxlds;
> -	/* without @cxl_dpa_rwsem, make sure @part is not reloaded */
> +	/* without @cxl_rwsem.dpa, make sure @part is not reloaded */
>  	int part = READ_ONCE(cxled->part);
>  	const char *desc;
>  
> @@ -235,7 +229,7 @@ static ssize_t dpa_resource_show(struct device *dev, struct device_attribute *at
>  {
>  	struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(dev);
>  
> -	guard(rwsem_read)(&cxl_dpa_rwsem);
> +	guard(rwsem_read)(&cxl_rwsem.dpa);
>  	return sysfs_emit(buf, "%#llx\n", (u64)cxl_dpa_resource_start(cxled));
>  }
>  static DEVICE_ATTR_RO(dpa_resource);
> @@ -560,7 +554,7 @@ static ssize_t decoders_committed_show(struct device *dev,
>  {
>  	struct cxl_port *port = to_cxl_port(dev);
>  
> -	guard(rwsem_read)(&cxl_region_rwsem);
> +	guard(rwsem_read)(&cxl_rwsem.region);
>  	return sysfs_emit(buf, "%d\n", cxl_num_decoders_committed(port));
>  }
>  
> @@ -1722,7 +1716,7 @@ static int decoder_populate_targets(struct cxl_switch_decoder *cxlsd,
>  	if (xa_empty(&port->dports))
>  		return -EINVAL;
>  
> -	guard(rwsem_write)(&cxl_region_rwsem);
> +	guard(rwsem_write)(&cxl_rwsem.region);
>  	for (i = 0; i < cxlsd->cxld.interleave_ways; i++) {
>  		struct cxl_dport *dport = find_dport(port, target_map[i]);
>  
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 4314aaed8ad8..ad60c93be803 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -141,16 +141,12 @@ static ssize_t uuid_show(struct device *dev, struct device_attribute *attr,
>  	struct cxl_region_params *p = &cxlr->params;
>  	ssize_t rc;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &region_rwsem)))
>  		return rc;
>  	if (cxlr->mode != CXL_PARTMODE_PMEM)
> -		rc = sysfs_emit(buf, "\n");
> -	else
> -		rc = sysfs_emit(buf, "%pUb\n", &p->uuid);
> -	up_read(&cxl_region_rwsem);
> -
> -	return rc;
> +		return sysfs_emit(buf, "\n");
> +	return sysfs_emit(buf, "%pUb\n", &p->uuid);
>  }
>  
>  static int is_dup(struct device *match, void *data)
> @@ -162,7 +158,7 @@ static int is_dup(struct device *match, void *data)
>  	if (!is_cxl_region(match))
>  		return 0;
>  
> -	lockdep_assert_held(&cxl_region_rwsem);
> +	lockdep_assert_held(&cxl_rwsem.region);
>  	cxlr = to_cxl_region(match);
>  	p = &cxlr->params;
>  
> @@ -192,27 +188,22 @@ static ssize_t uuid_store(struct device *dev, struct device_attribute *attr,
>  	if (uuid_is_null(&temp))
>  		return -EINVAL;
>  
> -	rc = down_write_killable(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_write_kill, region_rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_write_kill, &region_rwsem)))
>  		return rc;
>  
>  	if (uuid_equal(&p->uuid, &temp))
> -		goto out;
> +		return len;
>  
> -	rc = -EBUSY;
>  	if (p->state >= CXL_CONFIG_ACTIVE)
> -		goto out;
> +		return -EBUSY;
>  
>  	rc = bus_for_each_dev(&cxl_bus_type, NULL, &temp, is_dup);
>  	if (rc < 0)
> -		goto out;
> +		return rc;
>  
>  	uuid_copy(&p->uuid, &temp);
> -out:
> -	up_write(&cxl_region_rwsem);
>  
> -	if (rc)
> -		return rc;
>  	return len;
>  }
>  static DEVICE_ATTR_RW(uuid);
> @@ -354,20 +345,17 @@ static int queue_reset(struct cxl_region *cxlr)
>  	struct cxl_region_params *p = &cxlr->params;
>  	int rc;
>  
> -	rc = down_write_killable(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
>  		return rc;
>  
>  	/* Already in the requested state? */
>  	if (p->state < CXL_CONFIG_COMMIT)
> -		goto out;
> +		return 0;
>  
>  	p->state = CXL_CONFIG_RESET_PENDING;
>  
> -out:
> -	up_write(&cxl_region_rwsem);
> -
> -	return rc;
> +	return 0;
>  }
>  
>  static int __commit(struct cxl_region *cxlr)
> @@ -375,19 +363,17 @@ static int __commit(struct cxl_region *cxlr)
>  	struct cxl_region_params *p = &cxlr->params;
>  	int rc;
>  
> -	rc = down_write_killable(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
>  		return rc;
>  
>  	/* Already in the requested state? */
>  	if (p->state >= CXL_CONFIG_COMMIT)
> -		goto out;
> +		return 0;
>  
>  	/* Not ready to commit? */
> -	if (p->state < CXL_CONFIG_ACTIVE) {
> -		rc = -ENXIO;
> -		goto out;
> -	}
> +	if (p->state < CXL_CONFIG_ACTIVE)
> +		return -ENXIO;
>  
>  	/*
>  	 * Invalidate caches before region setup to drop any speculative
> @@ -395,16 +381,15 @@ static int __commit(struct cxl_region *cxlr)
>  	 */
>  	rc = cxl_region_invalidate_memregion(cxlr);
>  	if (rc)
> -		goto out;
> +		return rc;
>  
>  	rc = cxl_region_decode_commit(cxlr);
> -	if (rc == 0)
> -		p->state = CXL_CONFIG_COMMIT;
> +	if (rc)
> +		return rc;
>  
> -out:
> -	up_write(&cxl_region_rwsem);
> +	p->state = CXL_CONFIG_COMMIT;
>  
> -	return rc;
> +	return 0;
>  }
>  
>  static ssize_t commit_store(struct device *dev, struct device_attribute *attr,
> @@ -437,10 +422,10 @@ static ssize_t commit_store(struct device *dev, struct device_attribute *attr,
>  	device_release_driver(&cxlr->dev);
>  
>  	/*
> -	 * With the reset pending take cxl_region_rwsem unconditionally
> +	 * With the reset pending take cxl_rwsem.region unconditionally
>  	 * to ensure the reset gets handled before returning.
>  	 */
> -	guard(rwsem_write)(&cxl_region_rwsem);
> +	guard(rwsem_write)(&cxl_rwsem.region);
>  
>  	/*
>  	 * Revalidate that the reset is still pending in case another
> @@ -461,13 +446,10 @@ static ssize_t commit_show(struct device *dev, struct device_attribute *attr,
>  	struct cxl_region_params *p = &cxlr->params;
>  	ssize_t rc;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
>  		return rc;
> -	rc = sysfs_emit(buf, "%d\n", p->state >= CXL_CONFIG_COMMIT);
> -	up_read(&cxl_region_rwsem);
> -
> -	return rc;
> +	return sysfs_emit(buf, "%d\n", p->state >= CXL_CONFIG_COMMIT);
>  }
>  static DEVICE_ATTR_RW(commit);
>  
> @@ -491,15 +473,12 @@ static ssize_t interleave_ways_show(struct device *dev,
>  {
>  	struct cxl_region *cxlr = to_cxl_region(dev);
>  	struct cxl_region_params *p = &cxlr->params;
> -	ssize_t rc;
> +	int rc;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
>  		return rc;
> -	rc = sysfs_emit(buf, "%d\n", p->interleave_ways);
> -	up_read(&cxl_region_rwsem);
> -
> -	return rc;
> +	return sysfs_emit(buf, "%d\n", p->interleave_ways);
>  }
>  
>  static const struct attribute_group *get_cxl_region_target_group(void);
> @@ -534,23 +513,21 @@ static ssize_t interleave_ways_store(struct device *dev,
>  		return -EINVAL;
>  	}
>  
> -	rc = down_write_killable(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
>  		return rc;
> -	if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) {
> -		rc = -EBUSY;
> -		goto out;
> -	}
> +
> +	if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE)
> +		return -EBUSY;
>  
>  	save = p->interleave_ways;
>  	p->interleave_ways = val;
>  	rc = sysfs_update_group(&cxlr->dev.kobj, get_cxl_region_target_group());
> -	if (rc)
> +	if (rc) {
>  		p->interleave_ways = save;
> -out:
> -	up_write(&cxl_region_rwsem);
> -	if (rc)
>  		return rc;
> +	}
> +
>  	return len;
>  }
>  static DEVICE_ATTR_RW(interleave_ways);
> @@ -561,15 +538,12 @@ static ssize_t interleave_granularity_show(struct device *dev,
>  {
>  	struct cxl_region *cxlr = to_cxl_region(dev);
>  	struct cxl_region_params *p = &cxlr->params;
> -	ssize_t rc;
> +	int rc;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
>  		return rc;
> -	rc = sysfs_emit(buf, "%d\n", p->interleave_granularity);
> -	up_read(&cxl_region_rwsem);
> -
> -	return rc;
> +	return sysfs_emit(buf, "%d\n", p->interleave_granularity);
>  }
>  
>  static ssize_t interleave_granularity_store(struct device *dev,
> @@ -602,19 +576,15 @@ static ssize_t interleave_granularity_store(struct device *dev,
>  	if (cxld->interleave_ways > 1 && val != cxld->interleave_granularity)
>  		return -EINVAL;
>  
> -	rc = down_write_killable(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
>  		return rc;
> -	if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) {
> -		rc = -EBUSY;
> -		goto out;
> -	}
> +
> +	if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE)
> +		return -EBUSY;
>  
>  	p->interleave_granularity = val;
> -out:
> -	up_write(&cxl_region_rwsem);
> -	if (rc)
> -		return rc;
> +
>  	return len;
>  }
>  static DEVICE_ATTR_RW(interleave_granularity);
> @@ -625,17 +595,15 @@ static ssize_t resource_show(struct device *dev, struct device_attribute *attr,
>  	struct cxl_region *cxlr = to_cxl_region(dev);
>  	struct cxl_region_params *p = &cxlr->params;
>  	u64 resource = -1ULL;
> -	ssize_t rc;
> +	int rc;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
>  		return rc;
> +
>  	if (p->res)
>  		resource = p->res->start;
> -	rc = sysfs_emit(buf, "%#llx\n", resource);
> -	up_read(&cxl_region_rwsem);
> -
> -	return rc;
> +	return sysfs_emit(buf, "%#llx\n", resource);
>  }
>  static DEVICE_ATTR_RO(resource);
>  
> @@ -663,7 +631,7 @@ static int alloc_hpa(struct cxl_region *cxlr, resource_size_t size)
>  	struct resource *res;
>  	u64 remainder = 0;
>  
> -	lockdep_assert_held_write(&cxl_region_rwsem);
> +	lockdep_assert_held_write(&cxl_rwsem.region);
>  
>  	/* Nothing to do... */
>  	if (p->res && resource_size(p->res) == size)
> @@ -705,7 +673,7 @@ static void cxl_region_iomem_release(struct cxl_region *cxlr)
>  	struct cxl_region_params *p = &cxlr->params;
>  
>  	if (device_is_registered(&cxlr->dev))
> -		lockdep_assert_held_write(&cxl_region_rwsem);
> +		lockdep_assert_held_write(&cxl_rwsem.region);
>  	if (p->res) {
>  		/*
>  		 * Autodiscovered regions may not have been able to insert their
> @@ -722,7 +690,7 @@ static int free_hpa(struct cxl_region *cxlr)
>  {
>  	struct cxl_region_params *p = &cxlr->params;
>  
> -	lockdep_assert_held_write(&cxl_region_rwsem);
> +	lockdep_assert_held_write(&cxl_rwsem.region);
>  
>  	if (!p->res)
>  		return 0;
> @@ -746,15 +714,14 @@ static ssize_t size_store(struct device *dev, struct device_attribute *attr,
>  	if (rc)
>  		return rc;
>  
> -	rc = down_write_killable(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
>  		return rc;
>  
>  	if (val)
>  		rc = alloc_hpa(cxlr, val);
>  	else
>  		rc = free_hpa(cxlr);
> -	up_write(&cxl_region_rwsem);
>  
>  	if (rc)
>  		return rc;
> @@ -770,15 +737,12 @@ static ssize_t size_show(struct device *dev, struct device_attribute *attr,
>  	u64 size = 0;
>  	ssize_t rc;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
>  		return rc;
>  	if (p->res)
>  		size = resource_size(p->res);
> -	rc = sysfs_emit(buf, "%#llx\n", size);
> -	up_read(&cxl_region_rwsem);
> -
> -	return rc;
> +	return sysfs_emit(buf, "%#llx\n", size);
>  }
>  static DEVICE_ATTR_RW(size);
>  
> @@ -804,26 +768,20 @@ static size_t show_targetN(struct cxl_region *cxlr, char *buf, int pos)
>  	struct cxl_endpoint_decoder *cxled;
>  	int rc;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
>  		return rc;
>  
>  	if (pos >= p->interleave_ways) {
>  		dev_dbg(&cxlr->dev, "position %d out of range %d\n", pos,
>  			p->interleave_ways);
> -		rc = -ENXIO;
> -		goto out;
> +		return -ENXIO;
>  	}
>  
>  	cxled = p->targets[pos];
>  	if (!cxled)
> -		rc = sysfs_emit(buf, "\n");
> -	else
> -		rc = sysfs_emit(buf, "%s\n", dev_name(&cxled->cxld.dev));
> -out:
> -	up_read(&cxl_region_rwsem);
> -
> -	return rc;
> +		return sysfs_emit(buf, "\n");
> +	return sysfs_emit(buf, "%s\n", dev_name(&cxled->cxld.dev));
>  }
>  
>  static int check_commit_order(struct device *dev, void *data)
> @@ -938,7 +896,7 @@ cxl_port_pick_region_decoder(struct cxl_port *port,
>  	/*
>  	 * This decoder is pinned registered as long as the endpoint decoder is
>  	 * registered, and endpoint decoder unregistration holds the
> -	 * cxl_region_rwsem over unregister events, so no need to hold on to
> +	 * cxl_rwsem.region over unregister events, so no need to hold on to
>  	 * this extra reference.
>  	 */
>  	put_device(dev);
> @@ -1129,7 +1087,7 @@ static int cxl_port_attach_region(struct cxl_port *port,
>  	unsigned long index;
>  	int rc = -EBUSY;
>  
> -	lockdep_assert_held_write(&cxl_region_rwsem);
> +	lockdep_assert_held_write(&cxl_rwsem.region);
>  
>  	cxl_rr = cxl_rr_load(port, cxlr);
>  	if (cxl_rr) {
> @@ -1239,7 +1197,7 @@ static void cxl_port_detach_region(struct cxl_port *port,
>  	struct cxl_region_ref *cxl_rr;
>  	struct cxl_ep *ep = NULL;
>  
> -	lockdep_assert_held_write(&cxl_region_rwsem);
> +	lockdep_assert_held_write(&cxl_rwsem.region);
>  
>  	cxl_rr = cxl_rr_load(port, cxlr);
>  	if (!cxl_rr)
> @@ -2142,7 +2100,7 @@ __cxl_decoder_detach(struct cxl_region *cxlr,
>  {
>  	struct cxl_region_params *p;
>  
> -	lockdep_assert_held_write(&cxl_region_rwsem);
> +	lockdep_assert_held_write(&cxl_rwsem.region);
>  
>  	if (!cxled) {
>  		p = &cxlr->params;
> @@ -2215,18 +2173,18 @@ int cxl_decoder_detach(struct cxl_region *cxlr,
>  	struct cxl_region *detach;
>  
>  	/* when the decoder is being destroyed lock unconditionally */
> -	if (mode == DETACH_INVALIDATE)
> -		down_write(&cxl_region_rwsem);
> -	else {
> -		int rc = down_write_killable(&cxl_region_rwsem);
> +	if (mode == DETACH_INVALIDATE) {
> +		guard(rwsem_write)(&cxl_rwsem.region);
> +		detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);
> +	} else {
> +		int rc;
>  
> -		if (rc)
> +		ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
> +		if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
>  			return rc;
> +		detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);
>  	}
>  
> -	detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);
> -	up_write(&cxl_region_rwsem);
> -
>  	if (detach) {
>  		device_release_driver(&detach->dev);
>  		put_device(&detach->dev);
> @@ -2234,29 +2192,35 @@ int cxl_decoder_detach(struct cxl_region *cxlr,
>  	return 0;
>  }
>  
> +static int __attach_target(struct cxl_region *cxlr,
> +			   struct cxl_endpoint_decoder *cxled, int pos,
> +			   unsigned int state)
> +{
> +	int rc;
> +
> +	if (state == TASK_INTERRUPTIBLE) {
> +		ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
> +		if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
> +			return rc;
> +		guard(rwsem_read)(&cxl_rwsem.dpa);
> +		return cxl_region_attach(cxlr, cxled, pos);
> +	}
> +	guard(rwsem_write)(&cxl_rwsem.region);
> +	guard(rwsem_read)(&cxl_rwsem.dpa);
> +	return cxl_region_attach(cxlr, cxled, pos);
> +}
> +
>  static int attach_target(struct cxl_region *cxlr,
>  			 struct cxl_endpoint_decoder *cxled, int pos,
>  			 unsigned int state)
>  {
> -	int rc = 0;
> -
> -	if (state == TASK_INTERRUPTIBLE)
> -		rc = down_write_killable(&cxl_region_rwsem);
> -	else
> -		down_write(&cxl_region_rwsem);
> -	if (rc)
> -		return rc;
> -
> -	down_read(&cxl_dpa_rwsem);
> -	rc = cxl_region_attach(cxlr, cxled, pos);
> -	up_read(&cxl_dpa_rwsem);
> -	up_write(&cxl_region_rwsem);
> +	int rc = __attach_target(cxlr, cxled, pos, state);
>  
> -	if (rc)
> -		dev_warn(cxled->cxld.dev.parent,
> -			"failed to attach %s to %s: %d\n",
> -			dev_name(&cxled->cxld.dev), dev_name(&cxlr->dev), rc);
> +	if (rc == 0)
> +		return 0;
>  
> +	dev_warn(cxled->cxld.dev.parent, "failed to attach %s to %s: %d\n",
> +		 dev_name(&cxled->cxld.dev), dev_name(&cxlr->dev), rc);
>  	return rc;
>  }
>  
> @@ -2516,7 +2480,7 @@ static int cxl_region_perf_attrs_callback(struct notifier_block *nb,
>  		return NOTIFY_DONE;
>  
>  	/*
> -	 * No need to hold cxl_region_rwsem; region parameters are stable
> +	 * No need to hold cxl_rwsem.region; region parameters are stable
>  	 * within the cxl_region driver.
>  	 */
>  	region_nid = phys_to_target_node(cxlr->params.res->start);
> @@ -2539,7 +2503,7 @@ static int cxl_region_calculate_adistance(struct notifier_block *nb,
>  	int region_nid;
>  
>  	/*
> -	 * No need to hold cxl_region_rwsem; region parameters are stable
> +	 * No need to hold cxl_rwsem.region; region parameters are stable
>  	 * within the cxl_region driver.
>  	 */
>  	region_nid = phys_to_target_node(cxlr->params.res->start);
> @@ -2688,17 +2652,13 @@ static ssize_t region_show(struct device *dev, struct device_attribute *attr,
>  	struct cxl_decoder *cxld = to_cxl_decoder(dev);
>  	ssize_t rc;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc)
> +	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
>  		return rc;
>  
>  	if (cxld->region)
> -		rc = sysfs_emit(buf, "%s\n", dev_name(&cxld->region->dev));
> -	else
> -		rc = sysfs_emit(buf, "\n");
> -	up_read(&cxl_region_rwsem);
> -
> -	return rc;
> +		return sysfs_emit(buf, "%s\n", dev_name(&cxld->region->dev));
> +	return sysfs_emit(buf, "\n");
>  }
>  DEVICE_ATTR_RO(region);
>  
> @@ -3037,7 +2997,7 @@ static int cxl_pmem_region_alloc(struct cxl_region *cxlr)
>  	struct device *dev;
>  	int i;
>  
> -	guard(rwsem_read)(&cxl_region_rwsem);
> +	guard(rwsem_read)(&cxl_rwsem.region);
>  	if (p->state != CXL_CONFIG_COMMIT)
>  		return -ENXIO;
>  
> @@ -3049,7 +3009,7 @@ static int cxl_pmem_region_alloc(struct cxl_region *cxlr)
>  	cxlr_pmem->hpa_range.start = p->res->start;
>  	cxlr_pmem->hpa_range.end = p->res->end;
>  
> -	/* Snapshot the region configuration underneath the cxl_region_rwsem */
> +	/* Snapshot the region configuration underneath the cxl_rwsem.region */
>  	cxlr_pmem->nr_mappings = p->nr_targets;
>  	for (i = 0; i < p->nr_targets; i++) {
>  		struct cxl_endpoint_decoder *cxled = p->targets[i];
> @@ -3126,7 +3086,7 @@ static struct cxl_dax_region *cxl_dax_region_alloc(struct cxl_region *cxlr)
>  	struct cxl_dax_region *cxlr_dax;
>  	struct device *dev;
>  
> -	guard(rwsem_read)(&cxl_region_rwsem);
> +	guard(rwsem_read)(&cxl_rwsem.region);
>  	if (p->state != CXL_CONFIG_COMMIT)
>  		return ERR_PTR(-ENXIO);
>  
> @@ -3326,7 +3286,7 @@ static int match_region_by_range(struct device *dev, const void *data)
>  	cxlr = to_cxl_region(dev);
>  	p = &cxlr->params;
>  
> -	guard(rwsem_read)(&cxl_region_rwsem);
> +	guard(rwsem_read)(&cxl_rwsem.region);
>  	if (p->res && p->res->start == r->start && p->res->end == r->end)
>  		return 1;
>  
> @@ -3386,7 +3346,7 @@ static int __construct_region(struct cxl_region *cxlr,
>  	struct resource *res;
>  	int rc;
>  
> -	guard(rwsem_write)(&cxl_region_rwsem);
> +	guard(rwsem_write)(&cxl_rwsem.region);
>  	p = &cxlr->params;
>  	if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) {
>  		dev_err(cxlmd->dev.parent,
> @@ -3522,10 +3482,10 @@ int cxl_add_to_region(struct cxl_endpoint_decoder *cxled)
>  
>  	attach_target(cxlr, cxled, -1, TASK_UNINTERRUPTIBLE);
>  
> -	down_read(&cxl_region_rwsem);
> -	p = &cxlr->params;
> -	attach = p->state == CXL_CONFIG_COMMIT;
> -	up_read(&cxl_region_rwsem);
> +	scoped_guard(rwsem_read, &cxl_rwsem.region) {
> +		p = &cxlr->params;
> +		attach = p->state == CXL_CONFIG_COMMIT;
> +	}
>  
>  	if (attach) {
>  		/*
> @@ -3550,7 +3510,7 @@ u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, u64 spa)
>  	if (!endpoint)
>  		return ~0ULL;
>  
> -	guard(rwsem_write)(&cxl_region_rwsem);
> +	guard(rwsem_write)(&cxl_rwsem.region);
>  
>  	xa_for_each(&endpoint->regions, index, iter) {
>  		struct cxl_region_params *p = &iter->region->params;
> @@ -3592,30 +3552,23 @@ static int cxl_region_can_probe(struct cxl_region *cxlr)
>  	struct cxl_region_params *p = &cxlr->params;
>  	int rc;
>  
> -	rc = down_read_interruptible(&cxl_region_rwsem);
> -	if (rc) {
> +	ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
> +	if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem))) {
>  		dev_dbg(&cxlr->dev, "probe interrupted\n");
>  		return rc;
>  	}
>  
>  	if (p->state < CXL_CONFIG_COMMIT) {
>  		dev_dbg(&cxlr->dev, "config state: %d\n", p->state);
> -		rc = -ENXIO;
> -		goto out;
> +		return -ENXIO;
>  	}
>  
>  	if (test_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags)) {
>  		dev_err(&cxlr->dev,
>  			"failed to activate, re-commit region and retry\n");
> -		rc = -ENXIO;
> -		goto out;
> +		return -ENXIO;
>  	}
>  
> -out:
> -	up_read(&cxl_region_rwsem);
> -
> -	if (rc)
> -		return rc;
>  	return 0;
>  }
>  
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 3f1695c96abc..50799a681231 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -469,7 +469,7 @@ enum cxl_config_state {
>   * @nr_targets: number of targets
>   * @cache_size: extended linear cache size if exists, otherwise zero.
>   *
> - * State transitions are protected by the cxl_region_rwsem
> + * State transitions are protected by cxl_rwsem.region
>   */
>  struct cxl_region_params {
>  	enum cxl_config_state state;
> @@ -912,15 +912,4 @@ bool cxl_endpoint_decoder_reset_detected(struct cxl_port *port);
>  #endif
>  
>  u16 cxl_gpf_get_dvsec(struct device *dev);
> -
> -static inline struct rw_semaphore *rwsem_read_intr_acquire(struct rw_semaphore *rwsem)
> -{
> -	if (down_read_interruptible(rwsem))
> -		return NULL;
> -
> -	return rwsem;
> -}
> -
> -DEFINE_FREE(rwsem_read_release, struct rw_semaphore *, if (_T) up_read(_T))
> -
>  #endif /* __CXL_H__ */
> diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h
> index c810deb88d13..cbafdc12e743 100644
> --- a/include/linux/rwsem.h
> +++ b/include/linux/rwsem.h
> @@ -244,6 +244,7 @@ DEFINE_GUARD_COND(rwsem_read, _intr, down_read_interruptible(_T), _RET == 0)
>  
>  DEFINE_GUARD(rwsem_write, struct rw_semaphore *, down_write(_T), up_write(_T))
>  DEFINE_GUARD_COND(rwsem_write, _try, down_write_trylock(_T))
> +DEFINE_GUARD_COND(rwsem_write, _kill, down_write_killable(_T), _RET == 0)
>  
>  /*
>   * downgrade write lock to read lock
> -- 
> 2.50.0
> 
> 
> 





^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 6/8] cxl/region: Move ready-to-probe state check to a helper
  2025-07-11 23:49 ` [PATCH v3 6/8] cxl/region: Move ready-to-probe state check to a helper Dan Williams
  2025-07-14 22:02   ` Fabio M. De Francesco
@ 2025-07-15 15:33   ` Jonathan Cameron
  2025-07-15 17:08     ` dan.j.williams
  1 sibling, 1 reply; 32+ messages in thread
From: Jonathan Cameron @ 2025-07-15 15:33 UTC (permalink / raw)
  To: Dan Williams
  Cc: linux-cxl, linux-kernel, Davidlohr Bueso, Dave Jiang,
	Alison Schofield, Vishal Verma, Ira Weiny, Peter Zijlstra (Intel)

On Fri, 11 Jul 2025 16:49:30 -0700
Dan Williams <dan.j.williams@intel.com> wrote:

> Rather than unlocking the region rwsem in the middle of cxl_region_probe()
> create a helper for determining when the region is ready-to-probe.
I'd maybe mention the odd bit of 
if (rc)
	return rc;

return 0;

Will go away shortly. Or maybe that is overkill for a commit message.

Anyhow, with that in mind LGTM
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>


> 
> Cc: Davidlohr Bueso <dave@stgolabs.net>
> Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
> Cc: Dave Jiang <dave.jiang@intel.com>
> Cc: Alison Schofield <alison.schofield@intel.com>
> Cc: Vishal Verma <vishal.l.verma@intel.com>
> Cc: Ira Weiny <ira.weiny@intel.com>
> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> ---
>  drivers/cxl/core/region.c | 24 ++++++++++++++++++------
>  1 file changed, 18 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 3a77aec2c447..2a97fa9a394f 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -3572,9 +3572,8 @@ static void shutdown_notifiers(void *_cxlr)
>  	unregister_mt_adistance_algorithm(&cxlr->adist_notifier);
>  }
>  
> -static int cxl_region_probe(struct device *dev)
> +static int cxl_region_can_probe(struct cxl_region *cxlr)
>  {
> -	struct cxl_region *cxlr = to_cxl_region(dev);
>  	struct cxl_region_params *p = &cxlr->params;
>  	int rc;
>  
> @@ -3597,15 +3596,28 @@ static int cxl_region_probe(struct device *dev)
>  		goto out;
>  	}
>  
> -	/*
> -	 * From this point on any path that changes the region's state away from
> -	 * CXL_CONFIG_COMMIT is also responsible for releasing the driver.
> -	 */
>  out:
>  	up_read(&cxl_region_rwsem);
>  
>  	if (rc)
>  		return rc;
> +	return 0;

This is an odd bit of code now.  Why not just

	return rc;

Ah. Patch 8 drops the if (rc) return rc bit.



> +}
> +
> +static int cxl_region_probe(struct device *dev)
> +{
> +	struct cxl_region *cxlr = to_cxl_region(dev);
> +	struct cxl_region_params *p = &cxlr->params;
> +	int rc;
> +
> +	rc = cxl_region_can_probe(cxlr);
> +	if (rc)
> +		return rc;
> +
> +	/*
> +	 * From this point on any path that changes the region's state away from
> +	 * CXL_CONFIG_COMMIT is also responsible for releasing the driver.
> +	 */
>  
>  	cxlr->memory_notifier.notifier_call = cxl_region_perf_attrs_callback;
>  	cxlr->memory_notifier.priority = CXL_CALLBACK_PRI;


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 1/8] cleanup: Introduce ACQUIRE() and ACQUIRE_ERR() for conditional locks
  2025-07-11 23:49 ` [PATCH v3 1/8] cleanup: Introduce ACQUIRE() and ACQUIRE_ERR() " Dan Williams
@ 2025-07-15 15:34   ` Jonathan Cameron
  0 siblings, 0 replies; 32+ messages in thread
From: Jonathan Cameron @ 2025-07-15 15:34 UTC (permalink / raw)
  To: Dan Williams
  Cc: linux-cxl, linux-kernel, Peter Zijlstra, Ingo Molnar,
	Linus Torvalds, David Lechner, Fabio M. De Francesco

On Fri, 11 Jul 2025 16:49:25 -0700
Dan Williams <dan.j.williams@intel.com> wrote:

> From: Peter Zijlstra <peterz@infradead.org>
> 
> scoped_cond_guard(), automatic cleanup for conditional locks, has a couple
> pain points:
> 
> * It causes existing straight-line code to be re-indented into a new
>   bracketed scope. While this can be mitigated by a new helper function
>   to contain the scope, that is not always a comfortable conversion.
> 
> * The return code from the conditional lock is tossed in favor of a scheme
>   to pass a 'return err;' statement to the macro.
> 
> Other attempts to clean this up, to behave more like guard() [1], got hung
> up trying to both establish and evaluate the conditional lock in one
> statement.
> 
> ACQUIRE() solves this by reflecting the result of the condition in the
> automatic variable established by the lock CLASS(). The result is
> separately retrieved with the ACQUIRE_ERR() helper, effectively a PTR_ERR()
> operation.
> 
> Link: http://lore.kernel.org/all/Z1LBnX9TpZLR5Dkf@gmail.com [1]
> Link: http://patch.msgid.link/20250512105026.GP4439@noisy.programming.kicks-ass.net
> Link: http://patch.msgid.link/20250512185817.GA1808@noisy.programming.kicks-ass.net
> Cc: Ingo Molnar <mingo@kernel.org>
> Cc: Linus Torvalds <torvalds@linux-foundation.org>
> Cc: David Lechner <dlechner@baylibre.com>
> Cc: "Fabio M. De Francesco" <fabio.m.de.francesco@linux.intel.com>
> Signed-off-by: "Peter Zijlstra (Intel)" <peterz@infradead.org>
> [djbw: wrap Peter's proposal with changelog and comments]
> Co-developed-by: Dan Williams <dan.j.williams@intel.com>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>

LGTM and is definitely an improvement on all previous attempts to solve this.

Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 7/8] cxl/region: Consolidate cxl_decoder_kill_region() and cxl_region_detach()
  2025-07-11 23:49 ` [PATCH v3 7/8] cxl/region: Consolidate cxl_decoder_kill_region() and cxl_region_detach() Dan Williams
  2025-07-14 18:17   ` Dave Jiang
  2025-07-14 22:09   ` Fabio M. De Francesco
@ 2025-07-15 15:56   ` Jonathan Cameron
  2025-07-15 16:44     ` dan.j.williams
  2 siblings, 1 reply; 32+ messages in thread
From: Jonathan Cameron @ 2025-07-15 15:56 UTC (permalink / raw)
  To: Dan Williams
  Cc: linux-cxl, linux-kernel, Davidlohr Bueso, Dave Jiang,
	Alison Schofield, Vishal Verma, Ira Weiny, Peter Zijlstra (Intel)

On Fri, 11 Jul 2025 16:49:31 -0700
Dan Williams <dan.j.williams@intel.com> wrote:

> Both detach_target() and cxld_unregister() want to tear down a cxl_region
> when an endpoint decoder is either detached or destroyed.
> 
> When a region is to be destroyed cxl_region_detach() releases
> cxl_region_rwsem unbinds the cxl_region driver and re-acquires the rwsem.
> 
> This "reverse" locking pattern is difficult to reason about, not amenable
> to scope-based cleanup, and the minor differences in the calling context of
> detach_target() and cxld_unregister() currently results in the
> cxl_decoder_kill_region() wrapper.
> 
> Introduce cxl_decoder_detach() to wrap a core __cxl_decoder_detach() that
> serves both cases. I.e. either detaching a known position in a region
> (interruptible), or detaching an endpoint decoder if it is found to be a
> member of a region (uninterruptible).
> 
> Cc: Davidlohr Bueso <dave@stgolabs.net>
> Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
> Cc: Dave Jiang <dave.jiang@intel.com>
> Cc: Alison Schofield <alison.schofield@intel.com>
> Cc: Vishal Verma <vishal.l.verma@intel.com>
> Cc: Ira Weiny <ira.weiny@intel.com>
> Acked-by: "Peter Zijlstra (Intel)" <peterz@infradead.org>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
One query inline about what I think is a change in when a reference count is
held on the region device.  I'm struggling to reason about whether that change
would have always been safe or if there is another change here that makes
it fine now?

(or whether I'm just misreading the change).

Jonathan



> ---
>  drivers/cxl/core/core.h   |  15 +++++-
>  drivers/cxl/core/port.c   |   9 ++--
>  drivers/cxl/core/region.c | 103 ++++++++++++++++++++++----------------
>  3 files changed, 75 insertions(+), 52 deletions(-)
> 
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index 29b61828a847..2be37084409f 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -12,6 +12,11 @@ extern const struct device_type cxl_pmu_type;
>  
>  extern struct attribute_group cxl_base_attribute_group;
>  
> +enum cxl_detach_mode {
> +	DETACH_ONLY,
> +	DETACH_INVALIDATE,
> +};
> +
>  #ifdef CONFIG_CXL_REGION
>  extern struct device_attribute dev_attr_create_pmem_region;
>  extern struct device_attribute dev_attr_create_ram_region;
> @@ -20,7 +25,11 @@ extern struct device_attribute dev_attr_region;
>  extern const struct device_type cxl_pmem_region_type;
>  extern const struct device_type cxl_dax_region_type;
>  extern const struct device_type cxl_region_type;
> -void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled);
> +
> +int cxl_decoder_detach(struct cxl_region *cxlr,
> +		       struct cxl_endpoint_decoder *cxled, int pos,
> +		       enum cxl_detach_mode mode);
> +
>  #define CXL_REGION_ATTR(x) (&dev_attr_##x.attr)
>  #define CXL_REGION_TYPE(x) (&cxl_region_type)
>  #define SET_CXL_REGION_ATTR(x) (&dev_attr_##x.attr),
> @@ -48,7 +57,9 @@ static inline int cxl_get_poison_by_endpoint(struct cxl_port *port)
>  {
>  	return 0;
>  }
> -static inline void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled)
> +static inline int cxl_decoder_detach(struct cxl_region *cxlr,
> +				     struct cxl_endpoint_decoder *cxled,
> +				     int pos, enum cxl_detach_mode mode)
>  {
>  }
>  static inline int cxl_region_init(void)
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index eb46c6764d20..087a20a9ee1c 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -2001,12 +2001,9 @@ EXPORT_SYMBOL_NS_GPL(cxl_decoder_add, "CXL");
>  
>  static void cxld_unregister(void *dev)
>  {
> -	struct cxl_endpoint_decoder *cxled;
> -
> -	if (is_endpoint_decoder(dev)) {
> -		cxled = to_cxl_endpoint_decoder(dev);
> -		cxl_decoder_kill_region(cxled);
> -	}
> +	if (is_endpoint_decoder(dev))
> +		cxl_decoder_detach(NULL, to_cxl_endpoint_decoder(dev), -1,
> +				   DETACH_INVALIDATE);
>  
>  	device_unregister(dev);
>  }
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 2a97fa9a394f..4314aaed8ad8 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -2135,27 +2135,43 @@ static int cxl_region_attach(struct cxl_region *cxlr,
>  	return 0;
>  }
>  
> -static int cxl_region_detach(struct cxl_endpoint_decoder *cxled)
> +static struct cxl_region *
> +__cxl_decoder_detach(struct cxl_region *cxlr,
> +		     struct cxl_endpoint_decoder *cxled, int pos,
> +		     enum cxl_detach_mode mode)
>  {
> -	struct cxl_port *iter, *ep_port = cxled_to_port(cxled);
> -	struct cxl_region *cxlr = cxled->cxld.region;
>  	struct cxl_region_params *p;
> -	int rc = 0;
>  
>  	lockdep_assert_held_write(&cxl_region_rwsem);
>  
> -	if (!cxlr)
> -		return 0;
> +	if (!cxled) {
> +		p = &cxlr->params;
>  
> -	p = &cxlr->params;
> -	get_device(&cxlr->dev);

This is a fairly nasty patch to unwind and fully understand but
I'm nervous that in the existing we have a get_device(&cxlr->dev)
before potential cxl_region_decode_reset(cxlr, ...)
and now we don't.  I'm not sure if that is a real problem though,
it just makes me nervous.


> +		if (pos >= p->interleave_ways) {
> +			dev_dbg(&cxlr->dev, "position %d out of range %d\n",
> +				pos, p->interleave_ways);
> +			return ERR_PTR(-ENXIO);
> +		}
> +
> +		if (!p->targets[pos])
> +			return NULL;
> +		cxled = p->targets[pos];
> +	} else {
> +		cxlr = cxled->cxld.region;
> +		if (!cxlr)
> +			return NULL;
> +		p = &cxlr->params;
> +	}
> +
> +	if (mode == DETACH_INVALIDATE)
> +		cxled->part = -1;
>  
>  	if (p->state > CXL_CONFIG_ACTIVE) {
>  		cxl_region_decode_reset(cxlr, p->interleave_ways);
>  		p->state = CXL_CONFIG_ACTIVE;
>  	}
>  
> -	for (iter = ep_port; !is_cxl_root(iter);
> +	for (struct cxl_port *iter = cxled_to_port(cxled); !is_cxl_root(iter);
>  	     iter = to_cxl_port(iter->dev.parent))
>  		cxl_port_detach_region(iter, cxlr, cxled);
>  
> @@ -2166,7 +2182,7 @@ static int cxl_region_detach(struct cxl_endpoint_decoder *cxled)
>  		dev_WARN_ONCE(&cxlr->dev, 1, "expected %s:%s at position %d\n",
>  			      dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
>  			      cxled->pos);
> -		goto out;
> +		return NULL;
>  	}
>  
>  	if (p->state == CXL_CONFIG_ACTIVE) {
> @@ -2180,21 +2196,42 @@ static int cxl_region_detach(struct cxl_endpoint_decoder *cxled)
>  		.end = -1,
>  	};
>  
> -	/* notify the region driver that one of its targets has departed */
> -	up_write(&cxl_region_rwsem);
> -	device_release_driver(&cxlr->dev);
> -	down_write(&cxl_region_rwsem);
> -out:
> -	put_device(&cxlr->dev);
> -	return rc;
> +	get_device(&cxlr->dev);
> +	return cxlr;
>  }
>  
> -void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled)
> +/*
> + * Cleanup a decoder's interest in a region. There are 2 cases to
> + * handle, removing an unknown @cxled from a known position in a region
> + * (detach_target()) or removing a known @cxled from an unknown @cxlr
> + * (cxld_unregister())
> + *
> + * When the detachment finds a region release the region driver.
> + */
> +int cxl_decoder_detach(struct cxl_region *cxlr,
> +		       struct cxl_endpoint_decoder *cxled, int pos,
> +		       enum cxl_detach_mode mode)
>  {
> -	down_write(&cxl_region_rwsem);
> -	cxled->part = -1;
> -	cxl_region_detach(cxled);
> +	struct cxl_region *detach;
> +
> +	/* when the decoder is being destroyed lock unconditionally */
> +	if (mode == DETACH_INVALIDATE)
> +		down_write(&cxl_region_rwsem);
> +	else {
> +		int rc = down_write_killable(&cxl_region_rwsem);
> +
> +		if (rc)
> +			return rc;
> +	}
> +
> +	detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);
>  	up_write(&cxl_region_rwsem);
> +
> +	if (detach) {
> +		device_release_driver(&detach->dev);
> +		put_device(&detach->dev);
> +	}
> +	return 0;
>  }
>  
>  static int attach_target(struct cxl_region *cxlr,
> @@ -2225,29 +2262,7 @@ static int attach_target(struct cxl_region *cxlr,
>  
>  static int detach_target(struct cxl_region *cxlr, int pos)
>  {
> -	struct cxl_region_params *p = &cxlr->params;
> -	int rc;
> -
> -	rc = down_write_killable(&cxl_region_rwsem);
> -	if (rc)
> -		return rc;
> -
> -	if (pos >= p->interleave_ways) {
> -		dev_dbg(&cxlr->dev, "position %d out of range %d\n", pos,
> -			p->interleave_ways);
> -		rc = -ENXIO;
> -		goto out;
> -	}
> -
> -	if (!p->targets[pos]) {
> -		rc = 0;
> -		goto out;
> -	}
> -
> -	rc = cxl_region_detach(p->targets[pos]);
> -out:
> -	up_write(&cxl_region_rwsem);
> -	return rc;
> +	return cxl_decoder_detach(cxlr, NULL, pos, DETACH_ONLY);
>  }
>  
>  static size_t store_targetN(struct cxl_region *cxlr, const char *buf, int pos,


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 8/8] cxl: Convert to ACQUIRE() for conditional rwsem locking
  2025-07-11 23:49 ` [PATCH v3 8/8] cxl: Convert to ACQUIRE() for conditional rwsem locking Dan Williams
                     ` (2 preceding siblings ...)
  2025-07-14 22:12   ` Fabio M. De Francesco
@ 2025-07-15 16:20   ` Jonathan Cameron
  2025-07-15 17:13     ` dan.j.williams
  3 siblings, 1 reply; 32+ messages in thread
From: Jonathan Cameron @ 2025-07-15 16:20 UTC (permalink / raw)
  To: Dan Williams
  Cc: linux-cxl, linux-kernel, David Lechner, Peter Zijlstra,
	Linus Torvalds, Ingo Molnar, Fabio M. De Francesco,
	Davidlohr Bueso, Dave Jiang, Alison Schofield, Vishal Verma,
	Ira Weiny, Shiju Jose

On Fri, 11 Jul 2025 16:49:32 -0700
Dan Williams <dan.j.williams@intel.com> wrote:

> Use ACQUIRE() to cleanup conditional locking paths in the CXL driver
> The ACQUIRE() macro and its associated ACQUIRE_ERR() helpers, like
> scoped_cond_guard(), arrange for scoped-based conditional locking. Unlike
> scoped_cond_guard(), these macros arrange for an ERR_PTR() to be retrieved
> representing the state of the conditional lock.
> 
> The goal of this conversion is to complete the removal of all explicit
> unlock calls in the subsystem. I.e. the methods to acquire a lock are
> solely via guard(), scoped_guard() (for limited cases), or ACQUIRE(). All
> unlock is implicit / scope-based. In order to make sure all lock sites are
> converted, the existing rwsem's are consolidated and renamed in 'struct
> cxl_rwsem'. While that makes the patch noisier it gives a clean cut-off
> between old-world (explicit unlock allowed), and new world (explicit unlock
> deleted).
> 
> Cc: David Lechner <dlechner@baylibre.com>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: Linus Torvalds <torvalds@linux-foundation.org>
> Cc: Ingo Molnar <mingo@kernel.org>
> Cc: "Fabio M. De Francesco" <fabio.m.de.francesco@linux.intel.com>
> Cc: Davidlohr Bueso <dave@stgolabs.net>
> Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
> Cc: Dave Jiang <dave.jiang@intel.com>
> Cc: Alison Schofield <alison.schofield@intel.com>
> Cc: Vishal Verma <vishal.l.verma@intel.com>
> Cc: Ira Weiny <ira.weiny@intel.com>
> Cc: Shiju Jose <shiju.jose@huawei.com>
> Acked-by: "Peter Zijlstra (Intel)" <peterz@infradead.org>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>

Two trivial comments inline. 

Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>

> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 4314aaed8ad8..ad60c93be803 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c

>  static int attach_target(struct cxl_region *cxlr,
>  			 struct cxl_endpoint_decoder *cxled, int pos,
>  			 unsigned int state)
>  {
> -	int rc = 0;
> -
> -	if (state == TASK_INTERRUPTIBLE)
> -		rc = down_write_killable(&cxl_region_rwsem);
> -	else
> -		down_write(&cxl_region_rwsem);
> -	if (rc)
> -		return rc;
> -
> -	down_read(&cxl_dpa_rwsem);
> -	rc = cxl_region_attach(cxlr, cxled, pos);
> -	up_read(&cxl_dpa_rwsem);
> -	up_write(&cxl_region_rwsem);
> +	int rc = __attach_target(cxlr, cxled, pos, state);
>  

... (start of block for next comment)

> -	if (rc)
> -		dev_warn(cxled->cxld.dev.parent,
> -			"failed to attach %s to %s: %d\n",
> -			dev_name(&cxled->cxld.dev), dev_name(&cxlr->dev), rc);
> +	if (rc == 0)
> +		return 0;
>  
> +	dev_warn(cxled->cxld.dev.parent, "failed to attach %s to %s: %d\n",
> +		 dev_name(&cxled->cxld.dev), dev_name(&cxlr->dev), rc);

I'm not seeing a reason for this change.  I prefer the original
with the error path as the out of line case.


>  	return rc;
>  }


> @@ -3592,30 +3552,23 @@ static int cxl_region_can_probe(struct cxl_region *cxlr)
...
  
>  	if (test_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags)) {
>  		dev_err(&cxlr->dev,
>  			"failed to activate, re-commit region and retry\n");
> -		rc = -ENXIO;
> -		goto out;
> +		return -ENXIO;

		return dev_err_probe(&cxlr->dev, -ENXIO,
				     "failed to activate, re-commit region and retry\n");

perhaps. I always like the cleanup.h stuff enabling this as it improves the patch
line count no end ;)


>  	}
>  
> -out:
> -	up_read(&cxl_region_rwsem);
> -
> -	if (rc)
> -		return rc;
>  	return 0;
>  }

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 7/8] cxl/region: Consolidate cxl_decoder_kill_region() and cxl_region_detach()
  2025-07-15 15:56   ` Jonathan Cameron
@ 2025-07-15 16:44     ` dan.j.williams
  2025-07-16 10:32       ` Jonathan Cameron
  0 siblings, 1 reply; 32+ messages in thread
From: dan.j.williams @ 2025-07-15 16:44 UTC (permalink / raw)
  To: Jonathan Cameron, Dan Williams
  Cc: linux-cxl, linux-kernel, Davidlohr Bueso, Dave Jiang,
	Alison Schofield, Vishal Verma, Ira Weiny, Peter Zijlstra (Intel)

Jonathan Cameron wrote:
> On Fri, 11 Jul 2025 16:49:31 -0700
> Dan Williams <dan.j.williams@intel.com> wrote:
> 
> > Both detach_target() and cxld_unregister() want to tear down a cxl_region
> > when an endpoint decoder is either detached or destroyed.
> > 
> > When a region is to be destroyed cxl_region_detach() releases
> > cxl_region_rwsem unbinds the cxl_region driver and re-acquires the rwsem.
> > 
> > This "reverse" locking pattern is difficult to reason about, not amenable
> > to scope-based cleanup, and the minor differences in the calling context of
> > detach_target() and cxld_unregister() currently results in the
> > cxl_decoder_kill_region() wrapper.
> > 
> > Introduce cxl_decoder_detach() to wrap a core __cxl_decoder_detach() that
> > serves both cases. I.e. either detaching a known position in a region
> > (interruptible), or detaching an endpoint decoder if it is found to be a
> > member of a region (uninterruptible).
> > 
> > Cc: Davidlohr Bueso <dave@stgolabs.net>
> > Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
> > Cc: Dave Jiang <dave.jiang@intel.com>
> > Cc: Alison Schofield <alison.schofield@intel.com>
> > Cc: Vishal Verma <vishal.l.verma@intel.com>
> > Cc: Ira Weiny <ira.weiny@intel.com>
> > Acked-by: "Peter Zijlstra (Intel)" <peterz@infradead.org>
> > Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> One query inline about what I think is a change in when a reference count is
> held on the region device.  I'm struggling to reason about whether that change
> would have always been safe or if there is another change here that makes
> it fine now?
> 
> (or whether I'm just misreading the change).
> 
> Jonathan
[..]
> 
> > diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> > index eb46c6764d20..087a20a9ee1c 100644
> > --- a/drivers/cxl/core/port.c
> > +++ b/drivers/cxl/core/port.c
> > @@ -2001,12 +2001,9 @@ EXPORT_SYMBOL_NS_GPL(cxl_decoder_add, "CXL");
> >  
> >  static void cxld_unregister(void *dev)
> >  {
> > -	struct cxl_endpoint_decoder *cxled;
> > -
> > -	if (is_endpoint_decoder(dev)) {
> > -		cxled = to_cxl_endpoint_decoder(dev);
> > -		cxl_decoder_kill_region(cxled);
> > -	}
> > +	if (is_endpoint_decoder(dev))
> > +		cxl_decoder_detach(NULL, to_cxl_endpoint_decoder(dev), -1,
> > +				   DETACH_INVALIDATE);
> >  
> >  	device_unregister(dev);
> >  }
> > diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> > index 2a97fa9a394f..4314aaed8ad8 100644
> > --- a/drivers/cxl/core/region.c
> > +++ b/drivers/cxl/core/region.c
> > @@ -2135,27 +2135,43 @@ static int cxl_region_attach(struct cxl_region *cxlr,
> >  	return 0;
> >  }
> >  
> > -static int cxl_region_detach(struct cxl_endpoint_decoder *cxled)
> > +static struct cxl_region *
> > +__cxl_decoder_detach(struct cxl_region *cxlr,
> > +		     struct cxl_endpoint_decoder *cxled, int pos,
> > +		     enum cxl_detach_mode mode)
> >  {
> > -	struct cxl_port *iter, *ep_port = cxled_to_port(cxled);
> > -	struct cxl_region *cxlr = cxled->cxld.region;
> >  	struct cxl_region_params *p;
> > -	int rc = 0;
> >  
> >  	lockdep_assert_held_write(&cxl_region_rwsem);
> >  
> > -	if (!cxlr)
> > -		return 0;
> > +	if (!cxled) {
> > +		p = &cxlr->params;
> >  
> > -	p = &cxlr->params;
> > -	get_device(&cxlr->dev);
> 
> This is a fairly nasty patch to unwind and fully understand but
> I'm nervous that in the existing we have a get_device(&cxlr->dev)
> before potential cxl_region_decode_reset(cxlr, ...)
> and now we don't.  I'm not sure if that is a real problem though,
> it just makes me nervous.

The reference count is not for cxl_region_decode_reset(). The reference
count is to keep the region from being freed when the lock is dropped so
that device_release_driver() can see if it has work to do.

The lookup result from endpoint decoder to region is only stable while
the lock is held and the region could be freed at any point after that.
The pin holds that off until we are done with the potential follow-on
work from detaching a decoder.

The reference is not taken in other paths like region sysfs because
userspace activity in sysfs attributes holds off attribute
unregistration.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 7/8] cxl/region: Consolidate cxl_decoder_kill_region() and cxl_region_detach()
  2025-07-14 18:17   ` Dave Jiang
@ 2025-07-15 17:07     ` dan.j.williams
  0 siblings, 0 replies; 32+ messages in thread
From: dan.j.williams @ 2025-07-15 17:07 UTC (permalink / raw)
  To: Dave Jiang, Dan Williams, linux-cxl
  Cc: linux-kernel, Davidlohr Bueso, Jonathan Cameron, Alison Schofield,
	Vishal Verma, Ira Weiny, Peter Zijlstra (Intel)

Dave Jiang wrote:
> 
> 
> On 7/11/25 4:49 PM, Dan Williams wrote:
> > Both detach_target() and cxld_unregister() want to tear down a cxl_region
> > when an endpoint decoder is either detached or destroyed.
> > 
> > When a region is to be destroyed cxl_region_detach() releases
> > cxl_region_rwsem unbinds the cxl_region driver and re-acquires the rwsem.
> > 
> 
> I think this one needs some commas?
> 
> When a region is to be destroyed cxl_region_detach() releases
> cxl_region_rwsem, unbinds the cxl_region driver, and re-acquires the rwsem.

Yes, modulo whether you believe in that last Oxford comma.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 6/8] cxl/region: Move ready-to-probe state check to a helper
  2025-07-15 15:33   ` Jonathan Cameron
@ 2025-07-15 17:08     ` dan.j.williams
  0 siblings, 0 replies; 32+ messages in thread
From: dan.j.williams @ 2025-07-15 17:08 UTC (permalink / raw)
  To: Jonathan Cameron, Dan Williams
  Cc: linux-cxl, linux-kernel, Davidlohr Bueso, Dave Jiang,
	Alison Schofield, Vishal Verma, Ira Weiny, Peter Zijlstra (Intel)

Jonathan Cameron wrote:
> On Fri, 11 Jul 2025 16:49:30 -0700
> Dan Williams <dan.j.williams@intel.com> wrote:
> 
> > Rather than unlocking the region rwsem in the middle of cxl_region_probe()
> > create a helper for determining when the region is ready-to-probe.
> I'd maybe mention the odd bit of 
> if (rc)
> 	return rc;
> 
> return 0;
> 
> Will go away shortly. Or maybe that is overkill for a commit message.
> 
> Anyhow, with that in mind LGTM
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> 
> 
> > 
> > Cc: Davidlohr Bueso <dave@stgolabs.net>
> > Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
> > Cc: Dave Jiang <dave.jiang@intel.com>
> > Cc: Alison Schofield <alison.schofield@intel.com>
> > Cc: Vishal Verma <vishal.l.verma@intel.com>
> > Cc: Ira Weiny <ira.weiny@intel.com>
> > Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> > Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> > Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> > ---
> >  drivers/cxl/core/region.c | 24 ++++++++++++++++++------
> >  1 file changed, 18 insertions(+), 6 deletions(-)
> > 
> > diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> > index 3a77aec2c447..2a97fa9a394f 100644
> > --- a/drivers/cxl/core/region.c
> > +++ b/drivers/cxl/core/region.c
> > @@ -3572,9 +3572,8 @@ static void shutdown_notifiers(void *_cxlr)
> >  	unregister_mt_adistance_algorithm(&cxlr->adist_notifier);
> >  }
> >  
> > -static int cxl_region_probe(struct device *dev)
> > +static int cxl_region_can_probe(struct cxl_region *cxlr)
> >  {
> > -	struct cxl_region *cxlr = to_cxl_region(dev);
> >  	struct cxl_region_params *p = &cxlr->params;
> >  	int rc;
> >  
> > @@ -3597,15 +3596,28 @@ static int cxl_region_probe(struct device *dev)
> >  		goto out;
> >  	}
> >  
> > -	/*
> > -	 * From this point on any path that changes the region's state away from
> > -	 * CXL_CONFIG_COMMIT is also responsible for releasing the driver.
> > -	 */
> >  out:
> >  	up_read(&cxl_region_rwsem);
> >  
> >  	if (rc)
> >  		return rc;
> > +	return 0;
> 
> This is an odd bit of code now.  Why not just
> 
> 	return rc;
> 
> Ah. Patch 8 drops the if (rc) return rc bit.

It was an artifact of how it was developed. I am inclined to let it be
unless something else major comes up.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 8/8] cxl: Convert to ACQUIRE() for conditional rwsem locking
  2025-07-15 16:20   ` Jonathan Cameron
@ 2025-07-15 17:13     ` dan.j.williams
  2025-07-15 17:38       ` Dave Jiang
  0 siblings, 1 reply; 32+ messages in thread
From: dan.j.williams @ 2025-07-15 17:13 UTC (permalink / raw)
  To: Jonathan Cameron, Dan Williams
  Cc: linux-cxl, linux-kernel, David Lechner, Peter Zijlstra,
	Linus Torvalds, Ingo Molnar, Fabio M. De Francesco,
	Davidlohr Bueso, Dave Jiang, Alison Schofield, Vishal Verma,
	Ira Weiny, Shiju Jose

Jonathan Cameron wrote:
> On Fri, 11 Jul 2025 16:49:32 -0700
> Dan Williams <dan.j.williams@intel.com> wrote:
> 
> > Use ACQUIRE() to cleanup conditional locking paths in the CXL driver
> > The ACQUIRE() macro and its associated ACQUIRE_ERR() helpers, like
> > scoped_cond_guard(), arrange for scoped-based conditional locking. Unlike
> > scoped_cond_guard(), these macros arrange for an ERR_PTR() to be retrieved
> > representing the state of the conditional lock.
> > 
> > The goal of this conversion is to complete the removal of all explicit
> > unlock calls in the subsystem. I.e. the methods to acquire a lock are
> > solely via guard(), scoped_guard() (for limited cases), or ACQUIRE(). All
> > unlock is implicit / scope-based. In order to make sure all lock sites are
> > converted, the existing rwsem's are consolidated and renamed in 'struct
> > cxl_rwsem'. While that makes the patch noisier it gives a clean cut-off
> > between old-world (explicit unlock allowed), and new world (explicit unlock
> > deleted).
> > 
> > Cc: David Lechner <dlechner@baylibre.com>
> > Cc: Peter Zijlstra <peterz@infradead.org>
> > Cc: Linus Torvalds <torvalds@linux-foundation.org>
> > Cc: Ingo Molnar <mingo@kernel.org>
> > Cc: "Fabio M. De Francesco" <fabio.m.de.francesco@linux.intel.com>
> > Cc: Davidlohr Bueso <dave@stgolabs.net>
> > Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
> > Cc: Dave Jiang <dave.jiang@intel.com>
> > Cc: Alison Schofield <alison.schofield@intel.com>
> > Cc: Vishal Verma <vishal.l.verma@intel.com>
> > Cc: Ira Weiny <ira.weiny@intel.com>
> > Cc: Shiju Jose <shiju.jose@huawei.com>
> > Acked-by: "Peter Zijlstra (Intel)" <peterz@infradead.org>
> > Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> 
> Two trivial comments inline. 
> 
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> 
> > diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> > index 4314aaed8ad8..ad60c93be803 100644
> > --- a/drivers/cxl/core/region.c
> > +++ b/drivers/cxl/core/region.c
> 
> >  static int attach_target(struct cxl_region *cxlr,
> >  			 struct cxl_endpoint_decoder *cxled, int pos,
> >  			 unsigned int state)
> >  {
> > -	int rc = 0;
> > -
> > -	if (state == TASK_INTERRUPTIBLE)
> > -		rc = down_write_killable(&cxl_region_rwsem);
> > -	else
> > -		down_write(&cxl_region_rwsem);
> > -	if (rc)
> > -		return rc;
> > -
> > -	down_read(&cxl_dpa_rwsem);
> > -	rc = cxl_region_attach(cxlr, cxled, pos);
> > -	up_read(&cxl_dpa_rwsem);
> > -	up_write(&cxl_region_rwsem);
> > +	int rc = __attach_target(cxlr, cxled, pos, state);
> >  
> 
> ... (start of block for next comment)
> 
> > -	if (rc)
> > -		dev_warn(cxled->cxld.dev.parent,
> > -			"failed to attach %s to %s: %d\n",
> > -			dev_name(&cxled->cxld.dev), dev_name(&cxlr->dev), rc);
> > +	if (rc == 0)
> > +		return 0;
> >  
> > +	dev_warn(cxled->cxld.dev.parent, "failed to attach %s to %s: %d\n",
> > +		 dev_name(&cxled->cxld.dev), dev_name(&cxlr->dev), rc);
> 
> I'm not seeing a reason for this change.  I prefer the original
> with the error path as the out of line case.

I prefer the out-dent of the message. I'll let Dave tie-break.

> >  	return rc;
> >  }
> 
> 
> > @@ -3592,30 +3552,23 @@ static int cxl_region_can_probe(struct cxl_region *cxlr)
> ...
>   
> >  	if (test_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags)) {
> >  		dev_err(&cxlr->dev,
> >  			"failed to activate, re-commit region and retry\n");
> > -		rc = -ENXIO;
> > -		goto out;
> > +		return -ENXIO;
> 
> 		return dev_err_probe(&cxlr->dev, -ENXIO,
> 				     "failed to activate, re-commit region and retry\n");
> 
> perhaps. I always like the cleanup.h stuff enabling this as it improves the patch
> line count no end ;)

I think that looks good, but perhaps as a follow-on cleanup?

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 8/8] cxl: Convert to ACQUIRE() for conditional rwsem locking
  2025-07-15 17:13     ` dan.j.williams
@ 2025-07-15 17:38       ` Dave Jiang
  0 siblings, 0 replies; 32+ messages in thread
From: Dave Jiang @ 2025-07-15 17:38 UTC (permalink / raw)
  To: dan.j.williams, Jonathan Cameron
  Cc: linux-cxl, linux-kernel, David Lechner, Peter Zijlstra,
	Linus Torvalds, Ingo Molnar, Fabio M. De Francesco,
	Davidlohr Bueso, Alison Schofield, Vishal Verma, Ira Weiny,
	Shiju Jose



On 7/15/25 10:13 AM, dan.j.williams@intel.com wrote:
> Jonathan Cameron wrote:
>> On Fri, 11 Jul 2025 16:49:32 -0700
>> Dan Williams <dan.j.williams@intel.com> wrote:
>>
>>> Use ACQUIRE() to cleanup conditional locking paths in the CXL driver
>>> The ACQUIRE() macro and its associated ACQUIRE_ERR() helpers, like
>>> scoped_cond_guard(), arrange for scoped-based conditional locking. Unlike
>>> scoped_cond_guard(), these macros arrange for an ERR_PTR() to be retrieved
>>> representing the state of the conditional lock.
>>>
>>> The goal of this conversion is to complete the removal of all explicit
>>> unlock calls in the subsystem. I.e. the methods to acquire a lock are
>>> solely via guard(), scoped_guard() (for limited cases), or ACQUIRE(). All
>>> unlock is implicit / scope-based. In order to make sure all lock sites are
>>> converted, the existing rwsem's are consolidated and renamed in 'struct
>>> cxl_rwsem'. While that makes the patch noisier it gives a clean cut-off
>>> between old-world (explicit unlock allowed), and new world (explicit unlock
>>> deleted).
>>>
>>> Cc: David Lechner <dlechner@baylibre.com>
>>> Cc: Peter Zijlstra <peterz@infradead.org>
>>> Cc: Linus Torvalds <torvalds@linux-foundation.org>
>>> Cc: Ingo Molnar <mingo@kernel.org>
>>> Cc: "Fabio M. De Francesco" <fabio.m.de.francesco@linux.intel.com>
>>> Cc: Davidlohr Bueso <dave@stgolabs.net>
>>> Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
>>> Cc: Dave Jiang <dave.jiang@intel.com>
>>> Cc: Alison Schofield <alison.schofield@intel.com>
>>> Cc: Vishal Verma <vishal.l.verma@intel.com>
>>> Cc: Ira Weiny <ira.weiny@intel.com>
>>> Cc: Shiju Jose <shiju.jose@huawei.com>
>>> Acked-by: "Peter Zijlstra (Intel)" <peterz@infradead.org>
>>> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
>>
>> Two trivial comments inline. 
>>
>> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
>>
>>> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
>>> index 4314aaed8ad8..ad60c93be803 100644
>>> --- a/drivers/cxl/core/region.c
>>> +++ b/drivers/cxl/core/region.c
>>
>>>  static int attach_target(struct cxl_region *cxlr,
>>>  			 struct cxl_endpoint_decoder *cxled, int pos,
>>>  			 unsigned int state)
>>>  {
>>> -	int rc = 0;
>>> -
>>> -	if (state == TASK_INTERRUPTIBLE)
>>> -		rc = down_write_killable(&cxl_region_rwsem);
>>> -	else
>>> -		down_write(&cxl_region_rwsem);
>>> -	if (rc)
>>> -		return rc;
>>> -
>>> -	down_read(&cxl_dpa_rwsem);
>>> -	rc = cxl_region_attach(cxlr, cxled, pos);
>>> -	up_read(&cxl_dpa_rwsem);
>>> -	up_write(&cxl_region_rwsem);
>>> +	int rc = __attach_target(cxlr, cxled, pos, state);
>>>  
>>
>> ... (start of block for next comment)
>>
>>> -	if (rc)
>>> -		dev_warn(cxled->cxld.dev.parent,
>>> -			"failed to attach %s to %s: %d\n",
>>> -			dev_name(&cxled->cxld.dev), dev_name(&cxlr->dev), rc);
>>> +	if (rc == 0)
>>> +		return 0;
>>>  
>>> +	dev_warn(cxled->cxld.dev.parent, "failed to attach %s to %s: %d\n",
>>> +		 dev_name(&cxled->cxld.dev), dev_name(&cxlr->dev), rc);
>>
>> I'm not seeing a reason for this change.  I prefer the original
>> with the error path as the out of line case.
> 
> I prefer the out-dent of the message. I'll let Dave tie-break.

I'm leaning towards leaving it alone if there's no functional change. If there's a v4 then revert the changes, otherwise I guess just let it be.

DJ

> 
>>>  	return rc;
>>>  }
>>
>>
>>> @@ -3592,30 +3552,23 @@ static int cxl_region_can_probe(struct cxl_region *cxlr)
>> ...
>>   
>>>  	if (test_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags)) {
>>>  		dev_err(&cxlr->dev,
>>>  			"failed to activate, re-commit region and retry\n");
>>> -		rc = -ENXIO;
>>> -		goto out;
>>> +		return -ENXIO;
>>
>> 		return dev_err_probe(&cxlr->dev, -ENXIO,
>> 				     "failed to activate, re-commit region and retry\n");
>>
>> perhaps. I always like the cleanup.h stuff enabling this as it improves the patch
>> line count no end ;)
> 
> I think that looks good, but perhaps as a follow-on cleanup?


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 7/8] cxl/region: Consolidate cxl_decoder_kill_region() and cxl_region_detach()
  2025-07-15 16:44     ` dan.j.williams
@ 2025-07-16 10:32       ` Jonathan Cameron
  0 siblings, 0 replies; 32+ messages in thread
From: Jonathan Cameron @ 2025-07-16 10:32 UTC (permalink / raw)
  To: dan.j.williams
  Cc: linux-cxl, linux-kernel, Davidlohr Bueso, Dave Jiang,
	Alison Schofield, Vishal Verma, Ira Weiny, Peter Zijlstra (Intel)

On Tue, 15 Jul 2025 09:44:29 -0700
dan.j.williams@intel.com wrote:

> Jonathan Cameron wrote:
> > On Fri, 11 Jul 2025 16:49:31 -0700
> > Dan Williams <dan.j.williams@intel.com> wrote:
> >   
> > > Both detach_target() and cxld_unregister() want to tear down a cxl_region
> > > when an endpoint decoder is either detached or destroyed.
> > > 
> > > When a region is to be destroyed cxl_region_detach() releases
> > > cxl_region_rwsem unbinds the cxl_region driver and re-acquires the rwsem.
> > > 
> > > This "reverse" locking pattern is difficult to reason about, not amenable
> > > to scope-based cleanup, and the minor differences in the calling context of
> > > detach_target() and cxld_unregister() currently results in the
> > > cxl_decoder_kill_region() wrapper.
> > > 
> > > Introduce cxl_decoder_detach() to wrap a core __cxl_decoder_detach() that
> > > serves both cases. I.e. either detaching a known position in a region
> > > (interruptible), or detaching an endpoint decoder if it is found to be a
> > > member of a region (uninterruptible).
> > > 
> > > Cc: Davidlohr Bueso <dave@stgolabs.net>
> > > Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
> > > Cc: Dave Jiang <dave.jiang@intel.com>
> > > Cc: Alison Schofield <alison.schofield@intel.com>
> > > Cc: Vishal Verma <vishal.l.verma@intel.com>
> > > Cc: Ira Weiny <ira.weiny@intel.com>
> > > Acked-by: "Peter Zijlstra (Intel)" <peterz@infradead.org>
> > > Signed-off-by: Dan Williams <dan.j.williams@intel.com>  
> > One query inline about what I think is a change in when a reference count is
> > held on the region device.  I'm struggling to reason about whether that change
> > would have always been safe or if there is another change here that makes
> > it fine now?
> > 
> > (or whether I'm just misreading the change).
> > 
> > Jonathan  
> [..]
> >   
> > > diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> > > index eb46c6764d20..087a20a9ee1c 100644
> > > --- a/drivers/cxl/core/port.c
> > > +++ b/drivers/cxl/core/port.c
> > > @@ -2001,12 +2001,9 @@ EXPORT_SYMBOL_NS_GPL(cxl_decoder_add, "CXL");
> > >  
> > >  static void cxld_unregister(void *dev)
> > >  {
> > > -	struct cxl_endpoint_decoder *cxled;
> > > -
> > > -	if (is_endpoint_decoder(dev)) {
> > > -		cxled = to_cxl_endpoint_decoder(dev);
> > > -		cxl_decoder_kill_region(cxled);
> > > -	}
> > > +	if (is_endpoint_decoder(dev))
> > > +		cxl_decoder_detach(NULL, to_cxl_endpoint_decoder(dev), -1,
> > > +				   DETACH_INVALIDATE);
> > >  
> > >  	device_unregister(dev);
> > >  }
> > > diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> > > index 2a97fa9a394f..4314aaed8ad8 100644
> > > --- a/drivers/cxl/core/region.c
> > > +++ b/drivers/cxl/core/region.c
> > > @@ -2135,27 +2135,43 @@ static int cxl_region_attach(struct cxl_region *cxlr,
> > >  	return 0;
> > >  }
> > >  
> > > -static int cxl_region_detach(struct cxl_endpoint_decoder *cxled)
> > > +static struct cxl_region *
> > > +__cxl_decoder_detach(struct cxl_region *cxlr,
> > > +		     struct cxl_endpoint_decoder *cxled, int pos,
> > > +		     enum cxl_detach_mode mode)
> > >  {
> > > -	struct cxl_port *iter, *ep_port = cxled_to_port(cxled);
> > > -	struct cxl_region *cxlr = cxled->cxld.region;
> > >  	struct cxl_region_params *p;
> > > -	int rc = 0;
> > >  
> > >  	lockdep_assert_held_write(&cxl_region_rwsem);
> > >  
> > > -	if (!cxlr)
> > > -		return 0;
> > > +	if (!cxled) {
> > > +		p = &cxlr->params;
> > >  
> > > -	p = &cxlr->params;
> > > -	get_device(&cxlr->dev);  
> > 
> > This is a fairly nasty patch to unwind and fully understand but
> > I'm nervous that in the existing we have a get_device(&cxlr->dev)
> > before potential cxl_region_decode_reset(cxlr, ...)
> > and now we don't.  I'm not sure if that is a real problem though,
> > it just makes me nervous.  
> 
> The reference count is not for cxl_region_decode_reset(). The reference
> count is to keep the region from being freed when the lock is dropped so
> that device_release_driver() can see if it has work to do.
> 
> The lookup result from endpoint decoder to region is only stable while
> the lock is held and the region could be freed at any point after that.
> The pin holds that off until we are done with the potential follow-on
> work from detaching a decoder.
> 
> The reference is not taken in other paths like region sysfs because
> userspace activity in sysfs attributes holds off attribute
> unregistration.
Fair enough and thanks for the explanation.
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>

> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 0/8] cleanup: Introduce ACQUIRE(), a guard() for conditional locks
  2025-07-11 23:49 [PATCH v3 0/8] cleanup: Introduce ACQUIRE(), a guard() for conditional locks Dan Williams
                   ` (7 preceding siblings ...)
  2025-07-11 23:49 ` [PATCH v3 8/8] cxl: Convert to ACQUIRE() for conditional rwsem locking Dan Williams
@ 2025-07-16 20:52 ` Dave Jiang
  2025-07-30 15:10 ` Andy Shevchenko
  9 siblings, 0 replies; 32+ messages in thread
From: Dave Jiang @ 2025-07-16 20:52 UTC (permalink / raw)
  To: Dan Williams, linux-cxl
  Cc: linux-kernel, Alison Schofield, David Lechner, Davidlohr Bueso,
	Fabio M. De Francesco, Ingo Molnar, Ira Weiny, Jonathan Cameron,
	Linus Torvalds, Peter Zijlstra (Intel), Shiju Jose, Vishal Verma



On 7/11/25 4:49 PM, Dan Williams wrote:
> Changes since v2 [1]:
> - Pick up Acks and Reviews
> - Whitespace fixups for cleanup.h changes (Jonathan)
> - Use consistent local variable style for ACQUIRE_ERR() (Jonathan)
>   - Not addressed: switch to less compact style ACQUIRE_ERR()
> - Not addressed: pickup checkpatch change for ACQUIRE_ERR() style in
>   this series (Alison)
> - Drop the cxl_decoder_detach() CLASS() and convert to a helper function (Jonathan)
> - Refactor attach_target() to make it easier to read (Jonathan)

Applied to cxl/next
b873adfddeeb337fa8e9f381fd35eb94f7887f2f


> 
> [1]: http://lore.kernel.org/20250619050416.782871-1-dan.j.williams@intel.com
> 
> For those new to this set, the motivation for this work is that the CXL
> subsystem adopted scope-based-cleanup helpers and achieved some decent
> cleanups. However, that work stalled with conditional locks. It stalled
> due to the pain points of scoped_cond_guard(). See patch1.
> 
> In the interim, approaches like rwsem_read_intr_acquire() attempted to
> workaround the pain points, but started a "parallel universe" of helpers
> that is not sustainable.
> 
>     0c6e6f1357cb cxl/edac: Add CXL memory device patrol scrub control feature
> 
> Peter fixed all of this up in a manner consistent with existing guards.
> Take that proposal and run with it to unblock further cleanups of "goto"
> in unwind paths in the CXL subsystem.
> 
> Potential follow-on work identified by this effort:
> 
> - __GUARD_IS_ERR() asm helper [2]
> - Checkpatch fixups for proposed ACQUIRE_ERR() style [3]
> 
> [2]: http://lore.kernel.org/20250514064624.GA24938@noisy.programming.kicks-ass.net
> [3]: http://lore.kernel.org/aGXDMZB6omShJpoj@aschofie-mobl2.lan
> 
> Dan Williams (7):
>   cxl/mbox: Convert poison list mutex to ACQUIRE()
>   cxl/decoder: Move decoder register programming to a helper
>   cxl/decoder: Drop pointless locking
>   cxl/region: Split commit_store() into __commit() and queue_reset()
>     helpers
>   cxl/region: Move ready-to-probe state check to a helper
>   cxl/region: Consolidate cxl_decoder_kill_region() and
>     cxl_region_detach()
>   cxl: Convert to ACQUIRE() for conditional rwsem locking
> 
> Peter Zijlstra (1):
>   cleanup: Introduce ACQUIRE() and ACQUIRE_ERR() for conditional locks
> 
>  drivers/cxl/core/cdat.c   |   6 +-
>  drivers/cxl/core/core.h   |  32 ++-
>  drivers/cxl/core/edac.c   |  44 ++--
>  drivers/cxl/core/hdm.c    | 118 +++++-----
>  drivers/cxl/core/mbox.c   |  13 +-
>  drivers/cxl/core/memdev.c |  50 ++--
>  drivers/cxl/core/port.c   |  27 +--
>  drivers/cxl/core/region.c | 473 ++++++++++++++++++++------------------
>  drivers/cxl/cxl.h         |  13 +-
>  drivers/cxl/cxlmem.h      |   4 +-
>  include/linux/cleanup.h   |  95 ++++++--
>  include/linux/mutex.h     |   2 +-
>  include/linux/rwsem.h     |   3 +-
>  13 files changed, 480 insertions(+), 400 deletions(-)
> 
> 
> base-commit: e04c78d86a9699d136910cfc0bdcf01087e3267e


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 0/8] cleanup: Introduce ACQUIRE(), a guard() for conditional locks
  2025-07-11 23:49 [PATCH v3 0/8] cleanup: Introduce ACQUIRE(), a guard() for conditional locks Dan Williams
                   ` (8 preceding siblings ...)
  2025-07-16 20:52 ` [PATCH v3 0/8] cleanup: Introduce ACQUIRE(), a guard() for conditional locks Dave Jiang
@ 2025-07-30 15:10 ` Andy Shevchenko
  2025-08-01 18:49   ` dan.j.williams
  9 siblings, 1 reply; 32+ messages in thread
From: Andy Shevchenko @ 2025-07-30 15:10 UTC (permalink / raw)
  To: Dan Williams, Nathan Chancellor
  Cc: linux-cxl, linux-kernel, Alison Schofield, Dave Jiang,
	David Lechner, Davidlohr Bueso, Fabio M. De Francesco,
	Ingo Molnar, Ira Weiny, Jonathan Cameron, Linus Torvalds,
	Peter Zijlstra (Intel), Shiju Jose, Vishal Verma

On Fri, Jul 11, 2025 at 04:49:24PM -0700, Dan Williams wrote:
> Changes since v2 [1]:
> - Pick up Acks and Reviews
> - Whitespace fixups for cleanup.h changes (Jonathan)
> - Use consistent local variable style for ACQUIRE_ERR() (Jonathan)
>   - Not addressed: switch to less compact style ACQUIRE_ERR()
> - Not addressed: pickup checkpatch change for ACQUIRE_ERR() style in
>   this series (Alison)
> - Drop the cxl_decoder_detach() CLASS() and convert to a helper function (Jonathan)
> - Refactor attach_target() to make it easier to read (Jonathan)
> 
> [1]: http://lore.kernel.org/20250619050416.782871-1-dan.j.williams@intel.com
> 
> For those new to this set, the motivation for this work is that the CXL
> subsystem adopted scope-based-cleanup helpers and achieved some decent
> cleanups. However, that work stalled with conditional locks. It stalled
> due to the pain points of scoped_cond_guard(). See patch1.
> 
> In the interim, approaches like rwsem_read_intr_acquire() attempted to
> workaround the pain points, but started a "parallel universe" of helpers
> that is not sustainable.
> 
>     0c6e6f1357cb cxl/edac: Add CXL memory device patrol scrub control feature
> 
> Peter fixed all of this up in a manner consistent with existing guards.
> Take that proposal and run with it to unblock further cleanups of "goto"
> in unwind paths in the CXL subsystem.
> 
> Potential follow-on work identified by this effort:
> 
> - __GUARD_IS_ERR() asm helper [2]
> - Checkpatch fixups for proposed ACQUIRE_ERR() style [3]
> 
> [2]: http://lore.kernel.org/20250514064624.GA24938@noisy.programming.kicks-ass.net
> [3]: http://lore.kernel.org/aGXDMZB6omShJpoj@aschofie-mobl2.lan

This series broke `make W=1` build vor clang. +Cc Nathan.

Par exemple:

/kernel/time/posix-timers.c:89:1: error: unused function 'class_lock_timer_lock_err' [-Werror,-Wunused-function]
   89 | DEFINE_CLASS_IS_COND_GUARD(lock_timer);
      | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/include/linux/cleanup.h:376:2: note: expanded from macro 'DEFINE_CLASS_IS_COND_GUARD'
  376 |         __DEFINE_GUARD_LOCK_PTR(_name, _T)
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/include/linux/cleanup.h:358:20: note: expanded from macro '__DEFINE_GUARD_LOCK_PTR'
  358 |         static inline int class_##_name##_lock_err(class_##_name##_t *_T)   \
      |                           ^~~~~~~~~~~~~~~~~~~~~~~~
<scratch space>:24:1: note: expanded from here
   24 | class_lock_timer_lock_err
      | ^~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 0/8] cleanup: Introduce ACQUIRE(), a guard() for conditional locks
  2025-07-30 15:10 ` Andy Shevchenko
@ 2025-08-01 18:49   ` dan.j.williams
  2025-08-01 19:02     ` Nathan Chancellor
  0 siblings, 1 reply; 32+ messages in thread
From: dan.j.williams @ 2025-08-01 18:49 UTC (permalink / raw)
  To: Andy Shevchenko, Dan Williams, Nathan Chancellor
  Cc: linux-cxl, linux-kernel, Alison Schofield, Dave Jiang,
	David Lechner, Davidlohr Bueso, Fabio M. De Francesco,
	Ingo Molnar, Ira Weiny, Jonathan Cameron, Linus Torvalds,
	Peter Zijlstra (Intel), Shiju Jose, Vishal Verma

Andy Shevchenko wrote:
> On Fri, Jul 11, 2025 at 04:49:24PM -0700, Dan Williams wrote:
> > Changes since v2 [1]:
> > - Pick up Acks and Reviews
> > - Whitespace fixups for cleanup.h changes (Jonathan)
> > - Use consistent local variable style for ACQUIRE_ERR() (Jonathan)
> >   - Not addressed: switch to less compact style ACQUIRE_ERR()
> > - Not addressed: pickup checkpatch change for ACQUIRE_ERR() style in
> >   this series (Alison)
> > - Drop the cxl_decoder_detach() CLASS() and convert to a helper function (Jonathan)
> > - Refactor attach_target() to make it easier to read (Jonathan)
> > 
> > [1]: http://lore.kernel.org/20250619050416.782871-1-dan.j.williams@intel.com
> > 
> > For those new to this set, the motivation for this work is that the CXL
> > subsystem adopted scope-based-cleanup helpers and achieved some decent
> > cleanups. However, that work stalled with conditional locks. It stalled
> > due to the pain points of scoped_cond_guard(). See patch1.
> > 
> > In the interim, approaches like rwsem_read_intr_acquire() attempted to
> > workaround the pain points, but started a "parallel universe" of helpers
> > that is not sustainable.
> > 
> >     0c6e6f1357cb cxl/edac: Add CXL memory device patrol scrub control feature
> > 
> > Peter fixed all of this up in a manner consistent with existing guards.
> > Take that proposal and run with it to unblock further cleanups of "goto"
> > in unwind paths in the CXL subsystem.
> > 
> > Potential follow-on work identified by this effort:
> > 
> > - __GUARD_IS_ERR() asm helper [2]
> > - Checkpatch fixups for proposed ACQUIRE_ERR() style [3]
> > 
> > [2]: http://lore.kernel.org/20250514064624.GA24938@noisy.programming.kicks-ass.net
> > [3]: http://lore.kernel.org/aGXDMZB6omShJpoj@aschofie-mobl2.lan
> 
> This series broke `make W=1` build vor clang. +Cc Nathan.
> 
> Par exemple:
> 
> /kernel/time/posix-timers.c:89:1: error: unused function 'class_lock_timer_lock_err' [-Werror,-Wunused-function]
>    89 | DEFINE_CLASS_IS_COND_GUARD(lock_timer);
>       | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> /include/linux/cleanup.h:376:2: note: expanded from macro 'DEFINE_CLASS_IS_COND_GUARD'
>   376 |         __DEFINE_GUARD_LOCK_PTR(_name, _T)
>       |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> /include/linux/cleanup.h:358:20: note: expanded from macro '__DEFINE_GUARD_LOCK_PTR'
>   358 |         static inline int class_##_name##_lock_err(class_##_name##_t *_T)   \
>       |                           ^~~~~~~~~~~~~~~~~~~~~~~~
> <scratch space>:24:1: note: expanded from here
>    24 | class_lock_timer_lock_err
>       | ^~~~~~~~~~~~~~~~~~~~~~~~~
> 1 error generated.

A few observations:

- This is odd, the inline should have been compiled away if not used.
- __always_inline does not help
- Seems to go away with __maybe_unused, but that seems more like a
  compiler band-aid than a fix
- This locking pattern is not immediately amenable to the ACQUIRE_ERR()
  approach because the unlock object is the return code from the
  constructor.

Given all that, and that an ACQUIRE_ERR() would end up being messier
than the scoped_timer_get_or_fail() approach, I think the best fix is to
quiet the warning, but maybe Peter and Nathan have other ideas?

-- 8< --
diff --git a/include/linux/cleanup.h b/include/linux/cleanup.h
index 4eb83dd71cfe..0dc7148d1b88 100644
--- a/include/linux/cleanup.h
+++ b/include/linux/cleanup.h
@@ -348,7 +348,8 @@ static __maybe_unused const bool class_##_name##_is_conditional = _is_cond
 		}                                                           \
 		return _ptr;                                                \
 	}                                                                   \
-	static inline int class_##_name##_lock_err(class_##_name##_t *_T)   \
+	static __maybe_unused inline int class_##_name##_lock_err(          \
+		class_##_name##_t *_T)                                      \
 	{                                                                   \
 		long _rc = (__force unsigned long)*(_exp);                  \
 		if (!_rc) {                                                 \

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 0/8] cleanup: Introduce ACQUIRE(), a guard() for conditional locks
  2025-08-01 18:49   ` dan.j.williams
@ 2025-08-01 19:02     ` Nathan Chancellor
  2025-08-01 23:51       ` dan.j.williams
  0 siblings, 1 reply; 32+ messages in thread
From: Nathan Chancellor @ 2025-08-01 19:02 UTC (permalink / raw)
  To: dan.j.williams
  Cc: Andy Shevchenko, linux-cxl, linux-kernel, Alison Schofield,
	Dave Jiang, David Lechner, Davidlohr Bueso, Fabio M. De Francesco,
	Ingo Molnar, Ira Weiny, Jonathan Cameron, Linus Torvalds,
	Peter Zijlstra (Intel), Shiju Jose, Vishal Verma

On Fri, Aug 01, 2025 at 11:49:22AM -0700, dan.j.williams@intel.com wrote:
> Andy Shevchenko wrote:
> > This series broke `make W=1` build vor clang. +Cc Nathan.
> > 
> > Par exemple:
> > 
> > /kernel/time/posix-timers.c:89:1: error: unused function 'class_lock_timer_lock_err' [-Werror,-Wunused-function]
> >    89 | DEFINE_CLASS_IS_COND_GUARD(lock_timer);
> >       | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > /include/linux/cleanup.h:376:2: note: expanded from macro 'DEFINE_CLASS_IS_COND_GUARD'
> >   376 |         __DEFINE_GUARD_LOCK_PTR(_name, _T)
> >       |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > /include/linux/cleanup.h:358:20: note: expanded from macro '__DEFINE_GUARD_LOCK_PTR'
> >   358 |         static inline int class_##_name##_lock_err(class_##_name##_t *_T)   \
> >       |                           ^~~~~~~~~~~~~~~~~~~~~~~~
> > <scratch space>:24:1: note: expanded from here
> >    24 | class_lock_timer_lock_err
> >       | ^~~~~~~~~~~~~~~~~~~~~~~~~
> > 1 error generated.
> 
> A few observations:
> 
> - This is odd, the inline should have been compiled away if not used.
> - __always_inline does not help
> - Seems to go away with __maybe_unused, but that seems more like a
>   compiler band-aid than a fix

See commit 6863f5643dd7 ("kbuild: allow Clang to find unused static
inline functions for W=1 build") for more information on the difference
between GCC and Clang when it comes to how 'static inline' functions
behave with -Wunused-function, namely that Clang will warn for functions
defined in .c files (but not .h files), whereas GCC will not warn for
either.

> - This locking pattern is not immediately amenable to the ACQUIRE_ERR()
>   approach because the unlock object is the return code from the
>   constructor.
> 
> Given all that, and that an ACQUIRE_ERR() would end up being messier
> than the scoped_timer_get_or_fail() approach, I think the best fix is to
> quiet the warning, but maybe Peter and Nathan have other ideas?

Yes, this is what I would recommend, as we never care if this function
is unused, right? You could probably outright substitute
'__maybe_unused' for 'inline' in this case, since the compiler is
already free to ignore it and the attribute takes care of any potential
unused warnings, which I think 'inline' is primarily used for nowadays.

Cheers,
Nathan

> -- 8< --
> diff --git a/include/linux/cleanup.h b/include/linux/cleanup.h
> index 4eb83dd71cfe..0dc7148d1b88 100644
> --- a/include/linux/cleanup.h
> +++ b/include/linux/cleanup.h
> @@ -348,7 +348,8 @@ static __maybe_unused const bool class_##_name##_is_conditional = _is_cond
>  		}                                                           \
>  		return _ptr;                                                \
>  	}                                                                   \
> -	static inline int class_##_name##_lock_err(class_##_name##_t *_T)   \
> +	static __maybe_unused inline int class_##_name##_lock_err(          \
> +		class_##_name##_t *_T)                                      \
>  	{                                                                   \
>  		long _rc = (__force unsigned long)*(_exp);                  \
>  		if (!_rc) {                                                 \

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 0/8] cleanup: Introduce ACQUIRE(), a guard() for conditional locks
  2025-08-01 19:02     ` Nathan Chancellor
@ 2025-08-01 23:51       ` dan.j.williams
  0 siblings, 0 replies; 32+ messages in thread
From: dan.j.williams @ 2025-08-01 23:51 UTC (permalink / raw)
  To: Nathan Chancellor, dan.j.williams
  Cc: Andy Shevchenko, linux-cxl, linux-kernel, Alison Schofield,
	Dave Jiang, David Lechner, Davidlohr Bueso, Fabio M. De Francesco,
	Ingo Molnar, Ira Weiny, Jonathan Cameron, Linus Torvalds,
	Peter Zijlstra (Intel), Shiju Jose, Vishal Verma

Nathan Chancellor wrote:
> On Fri, Aug 01, 2025 at 11:49:22AM -0700, dan.j.williams@intel.com wrote:
> > Andy Shevchenko wrote:
> > > This series broke `make W=1` build vor clang. +Cc Nathan.
> > > 
> > > Par exemple:
> > > 
> > > /kernel/time/posix-timers.c:89:1: error: unused function 'class_lock_timer_lock_err' [-Werror,-Wunused-function]
> > >    89 | DEFINE_CLASS_IS_COND_GUARD(lock_timer);
> > >       | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > > /include/linux/cleanup.h:376:2: note: expanded from macro 'DEFINE_CLASS_IS_COND_GUARD'
> > >   376 |         __DEFINE_GUARD_LOCK_PTR(_name, _T)
> > >       |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > > /include/linux/cleanup.h:358:20: note: expanded from macro '__DEFINE_GUARD_LOCK_PTR'
> > >   358 |         static inline int class_##_name##_lock_err(class_##_name##_t *_T)   \
> > >       |                           ^~~~~~~~~~~~~~~~~~~~~~~~
> > > <scratch space>:24:1: note: expanded from here
> > >    24 | class_lock_timer_lock_err
> > >       | ^~~~~~~~~~~~~~~~~~~~~~~~~
> > > 1 error generated.
> > 
> > A few observations:
> > 
> > - This is odd, the inline should have been compiled away if not used.
> > - __always_inline does not help
> > - Seems to go away with __maybe_unused, but that seems more like a
> >   compiler band-aid than a fix
> 
> See commit 6863f5643dd7 ("kbuild: allow Clang to find unused static
> inline functions for W=1 build") for more information on the difference
> between GCC and Clang when it comes to how 'static inline' functions
> behave with -Wunused-function, namely that Clang will warn for functions
> defined in .c files (but not .h files), whereas GCC will not warn for
> either.

Ah, thanks!

> > - This locking pattern is not immediately amenable to the ACQUIRE_ERR()
> >   approach because the unlock object is the return code from the
> >   constructor.
> > 
> > Given all that, and that an ACQUIRE_ERR() would end up being messier
> > than the scoped_timer_get_or_fail() approach, I think the best fix is to
> > quiet the warning, but maybe Peter and Nathan have other ideas?
> 
> Yes, this is what I would recommend, as we never care if this function
> is unused, right?

Hmm, it would be nice to care. For example, if someone did something
like use ACQUIRE() without a corresponding ACQUIRE_ERR(). It would be
nice to catch that, but that warning would only fire for clang builds
where the lock guard is defined in a C file.

> You could probably outright substitute '__maybe_unused' for 'inline'
> in this case, since the compiler is already free to ignore it and the
> attribute takes care of any potential unused warnings, which I think
> 'inline' is primarily used for nowadays.

Oh, true, good point.

Another option is make scoped_cond_guard() use the lock error helper.
The compiler should always elide that __guard_err() call, and in the
meantime maybe catch a true positive unused symbol for
class_##_name##_lock_err.

-- 8< --
diff --git a/include/linux/cleanup.h b/include/linux/cleanup.h
index 4eb83dd71cfe..534723b14d92 100644
--- a/include/linux/cleanup.h
+++ b/include/linux/cleanup.h
@@ -421,13 +421,14 @@ _label:									\
 #define scoped_guard(_name, args...)	\
 	__scoped_guard(_name, __UNIQUE_ID(label), args)
 
-#define __scoped_cond_guard(_name, _fail, _label, args...)		\
-	for (CLASS(_name, scope)(args); true; ({ goto _label; }))	\
-		if (!__guard_ptr(_name)(&scope)) {			\
-			BUILD_BUG_ON(!__is_cond_ptr(_name));		\
-			_fail;						\
-_label:									\
-			break;						\
+#define __scoped_cond_guard(_name, _fail, _label, args...)        \
+	for (CLASS(_name, scope)(args); true; ({ goto _label; })) \
+		if (!__guard_ptr(_name)(&scope) ||                \
+		    __guard_err(_name)(&scope)) {                 \
+			BUILD_BUG_ON(!__is_cond_ptr(_name));      \
+			_fail;                                    \
+_label:                                                           \
+			break;                                    \
 		} else
 
 #define scoped_cond_guard(_name, _fail, args...)	\

^ permalink raw reply related	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2025-08-01 23:51 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-11 23:49 [PATCH v3 0/8] cleanup: Introduce ACQUIRE(), a guard() for conditional locks Dan Williams
2025-07-11 23:49 ` [PATCH v3 1/8] cleanup: Introduce ACQUIRE() and ACQUIRE_ERR() " Dan Williams
2025-07-15 15:34   ` Jonathan Cameron
2025-07-11 23:49 ` [PATCH v3 2/8] cxl/mbox: Convert poison list mutex to ACQUIRE() Dan Williams
2025-07-11 23:49 ` [PATCH v3 3/8] cxl/decoder: Move decoder register programming to a helper Dan Williams
2025-07-11 23:49 ` [PATCH v3 4/8] cxl/decoder: Drop pointless locking Dan Williams
2025-07-11 23:49 ` [PATCH v3 5/8] cxl/region: Split commit_store() into __commit() and queue_reset() helpers Dan Williams
2025-07-14 21:49   ` Fabio M. De Francesco
2025-07-11 23:49 ` [PATCH v3 6/8] cxl/region: Move ready-to-probe state check to a helper Dan Williams
2025-07-14 22:02   ` Fabio M. De Francesco
2025-07-15 15:33   ` Jonathan Cameron
2025-07-15 17:08     ` dan.j.williams
2025-07-11 23:49 ` [PATCH v3 7/8] cxl/region: Consolidate cxl_decoder_kill_region() and cxl_region_detach() Dan Williams
2025-07-14 18:17   ` Dave Jiang
2025-07-15 17:07     ` dan.j.williams
2025-07-14 22:09   ` Fabio M. De Francesco
2025-07-15 15:56   ` Jonathan Cameron
2025-07-15 16:44     ` dan.j.williams
2025-07-16 10:32       ` Jonathan Cameron
2025-07-11 23:49 ` [PATCH v3 8/8] cxl: Convert to ACQUIRE() for conditional rwsem locking Dan Williams
2025-07-14 16:28   ` Shiju Jose
2025-07-14 19:21     ` dan.j.williams
2025-07-14 18:39   ` Dave Jiang
2025-07-14 22:12   ` Fabio M. De Francesco
2025-07-15 16:20   ` Jonathan Cameron
2025-07-15 17:13     ` dan.j.williams
2025-07-15 17:38       ` Dave Jiang
2025-07-16 20:52 ` [PATCH v3 0/8] cleanup: Introduce ACQUIRE(), a guard() for conditional locks Dave Jiang
2025-07-30 15:10 ` Andy Shevchenko
2025-08-01 18:49   ` dan.j.williams
2025-08-01 19:02     ` Nathan Chancellor
2025-08-01 23:51       ` dan.j.williams

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).