linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [patch V2 00/10] genirq/msi: Spring cleaning
@ 2025-03-13 13:03 Thomas Gleixner
  2025-03-13 13:03 ` [patch V2 01/10] cleanup: Provide retain_ptr() Thomas Gleixner
                   ` (9 more replies)
  0 siblings, 10 replies; 20+ messages in thread
From: Thomas Gleixner @ 2025-03-13 13:03 UTC (permalink / raw)
  To: LKML
  Cc: Marc Zyngier, Peter Zijlstra, Nishanth Menon, Jonathan Cameron,
	Dhruva Gole, Tero Kristo, Santosh Shilimkar, Logan Gunthorpe,
	Dave Jiang, Jon Mason, Allen Hubbe, ntb, Bjorn Helgaas, linux-pci,
	Michael Kelley, Wei Liu, Haiyang Zhang, linux-hyperv, Wei Huang,
	Manivannan Sadhasivam, James E.J. Bottomley, Martin K. Petersen,
	linux-scsi, Jonathan Cameron

This is version 2 of the cleanup work. The previous version can be found
here:

   https://lore.kernel.org/all/20250309083453.900516105@linutronix.de

While converting the MSI descriptor locking to a lock guard() I stumbled
over various abuse of MSI descriptors (again).

The following series cleans up the offending code and converts the MSI
descriptor locking over to lock guards.

Changes vs. V1:

   - Introduce retain_ptr() to allow using __free() when the allocation is
     consumed by a called function (on success) and therefore no_free_ptr()
     can't be used.

   - Rework the PCI/MSI changes to avoid gotos in guard sections

   - Drop patch 1 as it's already applied

   - Collect Reviewed/Tested/Acked-by tags where appropriate

Patches 3,4,6-10 are unmodifed.

The series applies on:

    git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/msi

and is available from git:

    git://git.kernel.org/pub/scm/linux/kernel/git/tglx/devel.git irq/msi

Thanks,

	tglx
---
 drivers/ntb/msi.c                   |   22 +---
 drivers/pci/controller/pci-hyperv.c |   14 ---
 drivers/pci/msi/api.c               |    6 -
 drivers/pci/msi/msi.c               |  168 ++++++++++++++++++++++--------------
 drivers/pci/pci.h                   |    9 +
 drivers/pci/tph.c                   |   44 ---------
 drivers/soc/ti/ti_sci_inta_msi.c    |   10 --
 drivers/ufs/host/ufs-qcom.c         |   75 ++++++++--------
 include/linux/cleanup.h             |   17 +++
 include/linux/irqdomain.h           |    2 
 include/linux/msi.h                 |    7 +
 kernel/irq/msi.c                    |  125 ++++++++++----------------
 12 files changed, 247 insertions(+), 252 deletions(-)



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

* [patch V2 01/10] cleanup: Provide retain_ptr()
  2025-03-13 13:03 [patch V2 00/10] genirq/msi: Spring cleaning Thomas Gleixner
@ 2025-03-13 13:03 ` Thomas Gleixner
  2025-03-13 15:24   ` Jonathan Cameron
                     ` (2 more replies)
  2025-03-13 13:03 ` [patch V2 02/10] genirq/msi: Use lock guards for MSI descriptor locking Thomas Gleixner
                   ` (8 subsequent siblings)
  9 siblings, 3 replies; 20+ messages in thread
From: Thomas Gleixner @ 2025-03-13 13:03 UTC (permalink / raw)
  To: LKML
  Cc: Marc Zyngier, Peter Zijlstra, Nishanth Menon, Jonathan Cameron,
	Dhruva Gole, Tero Kristo, Santosh Shilimkar, Logan Gunthorpe,
	Dave Jiang, Jon Mason, Allen Hubbe, ntb, Bjorn Helgaas, linux-pci,
	Michael Kelley, Wei Liu, Haiyang Zhang, linux-hyperv, Wei Huang,
	Manivannan Sadhasivam, James E.J. Bottomley, Martin K. Petersen,
	linux-scsi, Jonathan Cameron

In cases where an allocation is consumed by another function, the
allocation needs to be retained on success or freed on failure. The code
pattern is usually:

	struct foo *f = kzalloc(sizeof(*f), GFP_KERNEL);
	struct bar *b;

	,,,
	// Initialize f
	...
	if (ret)
		goto free;
        ...
	bar = bar_create(f);
	if (!bar) {
		ret = -ENOMEM;
	   	goto free;
	}
	...
	return 0;
free:
	kfree(f);
	return ret;

This prevents using __free(kfree) on @f because there is no canonical way
to tell the cleanup code that the allocation should not be freed.

Abusing no_free_ptr() by force ignoring the return value is not really a
sensible option either.

Provide an explicit macro retain_ptr(), which NULLs the cleanup
pointer. That makes it easy to analyze and reason about.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Peter Zijlstra <peterz@infradead.org>
---
 include/linux/cleanup.h |   17 +++++++++++++++++
 1 file changed, 17 insertions(+)

--- a/include/linux/cleanup.h
+++ b/include/linux/cleanup.h
@@ -216,6 +216,23 @@ const volatile void * __must_check_fn(co
 
 #define return_ptr(p)	return no_free_ptr(p)
 
+/*
+ * Only for situations where an allocation is handed in to another function
+ * and consumed by that function on success.
+ *
+ *	struct foo *f __free(kfree) = kzalloc(sizeof(*f), GFP_KERNEL);
+ *
+ *	setup(f);
+ *	if (some_condition)
+ *		return -EINVAL;
+ *	....
+ *	ret = bar(f);
+ *	if (!ret)
+ *		retain_ptr(f);
+ *	return ret;
+ */
+#define retain_ptr(p)				\
+	__get_and_null(p, NULL)
 
 /*
  * DEFINE_CLASS(name, type, exit, init, init_args...):


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

* [patch V2 02/10] genirq/msi: Use lock guards for MSI descriptor locking
  2025-03-13 13:03 [patch V2 00/10] genirq/msi: Spring cleaning Thomas Gleixner
  2025-03-13 13:03 ` [patch V2 01/10] cleanup: Provide retain_ptr() Thomas Gleixner
@ 2025-03-13 13:03 ` Thomas Gleixner
  2025-03-13 15:26   ` Jonathan Cameron
  2025-03-13 13:03 ` [patch V2 03/10] soc: ti: ti_sci_inta_msi: Switch MSI descriptor locking to guard() Thomas Gleixner
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 20+ messages in thread
From: Thomas Gleixner @ 2025-03-13 13:03 UTC (permalink / raw)
  To: LKML
  Cc: Marc Zyngier, Peter Zijlstra, Nishanth Menon, Jonathan Cameron,
	Dhruva Gole, Tero Kristo, Santosh Shilimkar, Logan Gunthorpe,
	Dave Jiang, Jon Mason, Allen Hubbe, ntb, Bjorn Helgaas, linux-pci,
	Michael Kelley, Wei Liu, Haiyang Zhang, linux-hyperv, Wei Huang,
	Manivannan Sadhasivam, James E.J. Bottomley, Martin K. Petersen,
	linux-scsi, Jonathan Cameron

Provide a lock guard for MSI descriptor locking and update the core code
accordingly.

No functional change intended.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
V2: Remove the gotos - Jonathan
---
 include/linux/irqdomain.h |    2 
 include/linux/msi.h       |    3 +
 kernel/irq/msi.c          |  109 ++++++++++++++++------------------------------
 3 files changed, 45 insertions(+), 69 deletions(-)

--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -281,6 +281,8 @@ static inline struct fwnode_handle *irq_
 
 void irq_domain_free_fwnode(struct fwnode_handle *fwnode);
 
+DEFINE_FREE(irq_domain_free_fwnode, struct fwnode_handle *, if (_T) irq_domain_free_fwnode(_T))
+
 struct irq_domain_chip_generic_info;
 
 /**
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -227,6 +227,9 @@ int msi_setup_device_data(struct device
 void msi_lock_descs(struct device *dev);
 void msi_unlock_descs(struct device *dev);
 
+DEFINE_LOCK_GUARD_1(msi_descs_lock, struct device, msi_lock_descs(_T->lock),
+		    msi_unlock_descs(_T->lock));
+
 struct msi_desc *msi_domain_first_desc(struct device *dev, unsigned int domid,
 				       enum msi_desc_filter filter);
 
--- a/kernel/irq/msi.c
+++ b/kernel/irq/msi.c
@@ -443,7 +443,6 @@ EXPORT_SYMBOL_GPL(msi_next_desc);
 unsigned int msi_domain_get_virq(struct device *dev, unsigned int domid, unsigned int index)
 {
 	struct msi_desc *desc;
-	unsigned int ret = 0;
 	bool pcimsi = false;
 	struct xarray *xa;
 
@@ -457,7 +456,7 @@ unsigned int msi_domain_get_virq(struct
 	if (dev_is_pci(dev) && domid == MSI_DEFAULT_DOMAIN)
 		pcimsi = to_pci_dev(dev)->msi_enabled;
 
-	msi_lock_descs(dev);
+	guard(msi_descs_lock)(dev);
 	xa = &dev->msi.data->__domains[domid].store;
 	desc = xa_load(xa, pcimsi ? 0 : index);
 	if (desc && desc->irq) {
@@ -466,16 +465,12 @@ unsigned int msi_domain_get_virq(struct
 		 * PCI-MSIX and platform MSI use a descriptor per
 		 * interrupt.
 		 */
-		if (pcimsi) {
-			if (index < desc->nvec_used)
-				ret = desc->irq + index;
-		} else {
-			ret = desc->irq;
-		}
+		if (!pcimsi)
+			return desc->irq;
+		if (index < desc->nvec_used)
+			return desc->irq + index;
 	}
-
-	msi_unlock_descs(dev);
-	return ret;
+	return 0;
 }
 EXPORT_SYMBOL_GPL(msi_domain_get_virq);
 
@@ -993,9 +988,8 @@ bool msi_create_device_irq_domain(struct
 				  void *chip_data)
 {
 	struct irq_domain *domain, *parent = dev->msi.domain;
-	struct fwnode_handle *fwnode, *fwnalloced = NULL;
-	struct msi_domain_template *bundle;
 	const struct msi_parent_ops *pops;
+	struct fwnode_handle *fwnode;
 
 	if (!irq_domain_is_msi_parent(parent))
 		return false;
@@ -1003,7 +997,8 @@ bool msi_create_device_irq_domain(struct
 	if (domid >= MSI_MAX_DEVICE_IRQDOMAINS)
 		return false;
 
-	bundle = kmemdup(template, sizeof(*bundle), GFP_KERNEL);
+	struct msi_domain_template *bundle __free(kfree) =
+		bundle = kmemdup(template, sizeof(*bundle), GFP_KERNEL);
 	if (!bundle)
 		return false;
 
@@ -1026,41 +1021,36 @@ bool msi_create_device_irq_domain(struct
 	 * node as they are not guaranteed to have a fwnode. They are never
 	 * looked up and always handled in the context of the device.
 	 */
-	if (bundle->info.flags & MSI_FLAG_USE_DEV_FWNODE)
-		fwnode = dev->fwnode;
+	struct fwnode_handle *fwnode_alloced __free(irq_domain_free_fwnode) = NULL;
+
+	if (!(bundle->info.flags & MSI_FLAG_USE_DEV_FWNODE))
+		fwnode = fwnode_alloced = irq_domain_alloc_named_fwnode(bundle->name);
 	else
-		fwnode = fwnalloced = irq_domain_alloc_named_fwnode(bundle->name);
+		fwnode = dev->fwnode;
 
 	if (!fwnode)
-		goto free_bundle;
+		return false;
 
 	if (msi_setup_device_data(dev))
-		goto free_fwnode;
-
-	msi_lock_descs(dev);
+		return false;
 
+	guard(msi_descs_lock)(dev);
 	if (WARN_ON_ONCE(msi_get_device_domain(dev, domid)))
-		goto fail;
+		return false;
 
 	if (!pops->init_dev_msi_info(dev, parent, parent, &bundle->info))
-		goto fail;
+		return false;
 
 	domain = __msi_create_irq_domain(fwnode, &bundle->info, IRQ_DOMAIN_FLAG_MSI_DEVICE, parent);
 	if (!domain)
-		goto fail;
+		return false;
 
+	/* @bundle and @fwnode_alloced are now in use. Prevent cleanup */
+	retain_ptr(bundle);
+	retain_ptr(fwnode_alloced);
 	domain->dev = dev;
 	dev->msi.data->__domains[domid].domain = domain;
-	msi_unlock_descs(dev);
 	return true;
-
-fail:
-	msi_unlock_descs(dev);
-free_fwnode:
-	irq_domain_free_fwnode(fwnalloced);
-free_bundle:
-	kfree(bundle);
-	return false;
 }
 
 /**
@@ -1074,12 +1064,10 @@ void msi_remove_device_irq_domain(struct
 	struct msi_domain_info *info;
 	struct irq_domain *domain;
 
-	msi_lock_descs(dev);
-
+	guard(msi_descs_lock)(dev);
 	domain = msi_get_device_domain(dev, domid);
-
 	if (!domain || !irq_domain_is_msi_device(domain))
-		goto unlock;
+		return;
 
 	dev->msi.data->__domains[domid].domain = NULL;
 	info = domain->host_data;
@@ -1088,9 +1076,6 @@ void msi_remove_device_irq_domain(struct
 	irq_domain_remove(domain);
 	irq_domain_free_fwnode(fwnode);
 	kfree(container_of(info, struct msi_domain_template, info));
-
-unlock:
-	msi_unlock_descs(dev);
 }
 
 /**
@@ -1106,16 +1091,14 @@ bool msi_match_device_irq_domain(struct
 {
 	struct msi_domain_info *info;
 	struct irq_domain *domain;
-	bool ret = false;
 
-	msi_lock_descs(dev);
+	guard(msi_descs_lock)(dev);
 	domain = msi_get_device_domain(dev, domid);
 	if (domain && irq_domain_is_msi_device(domain)) {
 		info = domain->host_data;
-		ret = info->bus_token == bus_token;
+		return info->bus_token == bus_token;
 	}
-	msi_unlock_descs(dev);
-	return ret;
+	return false;
 }
 
 static int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev,
@@ -1365,12 +1348,9 @@ int msi_domain_alloc_irqs_range(struct d
 		.last	= last,
 		.nirqs	= last + 1 - first,
 	};
-	int ret;
 
-	msi_lock_descs(dev);
-	ret = msi_domain_alloc_locked(dev, &ctrl);
-	msi_unlock_descs(dev);
-	return ret;
+	guard(msi_descs_lock)(dev);
+	return msi_domain_alloc_locked(dev, &ctrl);
 }
 EXPORT_SYMBOL_GPL(msi_domain_alloc_irqs_range);
 
@@ -1474,12 +1454,8 @@ struct msi_map msi_domain_alloc_irq_at(s
 				       const struct irq_affinity_desc *affdesc,
 				       union msi_instance_cookie *icookie)
 {
-	struct msi_map map;
-
-	msi_lock_descs(dev);
-	map = __msi_domain_alloc_irq_at(dev, domid, index, affdesc, icookie);
-	msi_unlock_descs(dev);
-	return map;
+	guard(msi_descs_lock)(dev);
+	return __msi_domain_alloc_irq_at(dev, domid, index, affdesc, icookie);
 }
 
 /**
@@ -1516,13 +1492,11 @@ int msi_device_domain_alloc_wired(struct
 
 	icookie.value = ((u64)type << 32) | hwirq;
 
-	msi_lock_descs(dev);
+	guard(msi_descs_lock)(dev);
 	if (WARN_ON_ONCE(msi_get_device_domain(dev, domid) != domain))
 		map.index = -EINVAL;
 	else
 		map = __msi_domain_alloc_irq_at(dev, domid, MSI_ANY_INDEX, NULL, &icookie);
-	msi_unlock_descs(dev);
-
 	return map.index >= 0 ? map.virq : map.index;
 }
 
@@ -1615,9 +1589,8 @@ static void msi_domain_free_irqs_range_l
 void msi_domain_free_irqs_range(struct device *dev, unsigned int domid,
 				unsigned int first, unsigned int last)
 {
-	msi_lock_descs(dev);
+	guard(msi_descs_lock)(dev);
 	msi_domain_free_irqs_range_locked(dev, domid, first, last);
-	msi_unlock_descs(dev);
 }
 EXPORT_SYMBOL_GPL(msi_domain_free_irqs_all);
 
@@ -1647,9 +1620,8 @@ void msi_domain_free_irqs_all_locked(str
  */
 void msi_domain_free_irqs_all(struct device *dev, unsigned int domid)
 {
-	msi_lock_descs(dev);
+	guard(msi_descs_lock)(dev);
 	msi_domain_free_irqs_all_locked(dev, domid);
-	msi_unlock_descs(dev);
 }
 
 /**
@@ -1668,12 +1640,11 @@ void msi_device_domain_free_wired(struct
 	if (WARN_ON_ONCE(!dev || !desc || domain->bus_token != DOMAIN_BUS_WIRED_TO_MSI))
 		return;
 
-	msi_lock_descs(dev);
-	if (!WARN_ON_ONCE(msi_get_device_domain(dev, MSI_DEFAULT_DOMAIN) != domain)) {
-		msi_domain_free_irqs_range_locked(dev, MSI_DEFAULT_DOMAIN, desc->msi_index,
-						  desc->msi_index);
-	}
-	msi_unlock_descs(dev);
+	guard(msi_descs_lock)(dev);
+	if (WARN_ON_ONCE(msi_get_device_domain(dev, MSI_DEFAULT_DOMAIN) != domain))
+		return;
+	msi_domain_free_irqs_range_locked(dev, MSI_DEFAULT_DOMAIN, desc->msi_index,
+					  desc->msi_index);
 }
 
 /**


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

* [patch V2 03/10] soc: ti: ti_sci_inta_msi: Switch MSI descriptor locking to guard()
  2025-03-13 13:03 [patch V2 00/10] genirq/msi: Spring cleaning Thomas Gleixner
  2025-03-13 13:03 ` [patch V2 01/10] cleanup: Provide retain_ptr() Thomas Gleixner
  2025-03-13 13:03 ` [patch V2 02/10] genirq/msi: Use lock guards for MSI descriptor locking Thomas Gleixner
@ 2025-03-13 13:03 ` Thomas Gleixner
  2025-03-13 13:03 ` [patch V2 04/10] NTB/msi: Switch MSI descriptor locking to lock guard() Thomas Gleixner
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Thomas Gleixner @ 2025-03-13 13:03 UTC (permalink / raw)
  To: LKML
  Cc: Marc Zyngier, Nishanth Menon, Jonathan Cameron, Dhruva Gole,
	Tero Kristo, Santosh Shilimkar, Peter Zijlstra, Logan Gunthorpe,
	Dave Jiang, Jon Mason, Allen Hubbe, ntb, Bjorn Helgaas, linux-pci,
	Michael Kelley, Wei Liu, Haiyang Zhang, linux-hyperv, Wei Huang,
	Manivannan Sadhasivam, James E.J. Bottomley, Martin K. Petersen,
	linux-scsi, Jonathan Cameron

Convert the code to use the new guard(msi_descs_lock).

No functional change intended.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Nishanth Menon <nm@ti.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Dhruva Gole <d-gole@ti.com>
Cc: Tero Kristo <kristo@kernel.org>
Cc: Santosh Shilimkar <ssantosh@kernel.org>
---
 drivers/soc/ti/ti_sci_inta_msi.c |   10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

--- a/drivers/soc/ti/ti_sci_inta_msi.c
+++ b/drivers/soc/ti/ti_sci_inta_msi.c
@@ -103,19 +103,15 @@ int ti_sci_inta_msi_domain_alloc_irqs(st
 	if (ret)
 		return ret;
 
-	msi_lock_descs(dev);
+	guard(msi_descs_lock)(dev);
 	nvec = ti_sci_inta_msi_alloc_descs(dev, res);
-	if (nvec <= 0) {
-		ret = nvec;
-		goto unlock;
-	}
+	if (nvec <= 0)
+		return nvec;
 
 	/* Use alloc ALL as it's unclear whether there are gaps in the indices */
 	ret = msi_domain_alloc_irqs_all_locked(dev, MSI_DEFAULT_DOMAIN, nvec);
 	if (ret)
 		dev_err(dev, "Failed to allocate IRQs %d\n", ret);
-unlock:
-	msi_unlock_descs(dev);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(ti_sci_inta_msi_domain_alloc_irqs);




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

* [patch V2 04/10] NTB/msi: Switch MSI descriptor locking to lock guard()
  2025-03-13 13:03 [patch V2 00/10] genirq/msi: Spring cleaning Thomas Gleixner
                   ` (2 preceding siblings ...)
  2025-03-13 13:03 ` [patch V2 03/10] soc: ti: ti_sci_inta_msi: Switch MSI descriptor locking to guard() Thomas Gleixner
@ 2025-03-13 13:03 ` Thomas Gleixner
  2025-03-13 13:03 ` [patch V2 05/10] PCI/MSI: Switch to MSI descriptor locking to guard() Thomas Gleixner
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Thomas Gleixner @ 2025-03-13 13:03 UTC (permalink / raw)
  To: LKML
  Cc: Marc Zyngier, Jonathan Cameron, Logan Gunthorpe, Dave Jiang,
	Jon Mason, Allen Hubbe, ntb, Peter Zijlstra, Nishanth Menon,
	Dhruva Gole, Tero Kristo, Santosh Shilimkar, Bjorn Helgaas,
	linux-pci, Michael Kelley, Wei Liu, Haiyang Zhang, linux-hyperv,
	Wei Huang, Manivannan Sadhasivam, James E.J. Bottomley,
	Martin K. Petersen, linux-scsi, Jonathan Cameron

Convert the code to use the new guard(msi_descs_lock).

No functional change intended.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
Acked-by: Dave Jiang <dave.jiang@intel.com>
Cc: Jon Mason <jdmason@kudzu.us>
Cc: Allen Hubbe <allenbh@gmail.com>
Cc: ntb@lists.linux.dev

---
 drivers/ntb/msi.c |   22 ++++++++--------------
 1 file changed, 8 insertions(+), 14 deletions(-)

--- a/drivers/ntb/msi.c
+++ b/drivers/ntb/msi.c
@@ -106,10 +106,10 @@ int ntb_msi_setup_mws(struct ntb_dev *nt
 	if (!ntb->msi)
 		return -EINVAL;
 
-	msi_lock_descs(&ntb->pdev->dev);
-	desc = msi_first_desc(&ntb->pdev->dev, MSI_DESC_ASSOCIATED);
-	addr = desc->msg.address_lo + ((uint64_t)desc->msg.address_hi << 32);
-	msi_unlock_descs(&ntb->pdev->dev);
+	scoped_guard (msi_descs_lock, &ntb->pdev->dev) {
+		desc = msi_first_desc(&ntb->pdev->dev, MSI_DESC_ASSOCIATED);
+		addr = desc->msg.address_lo + ((uint64_t)desc->msg.address_hi << 32);
+	}
 
 	for (peer = 0; peer < ntb_peer_port_count(ntb); peer++) {
 		peer_widx = ntb_peer_highest_mw_idx(ntb, peer);
@@ -289,7 +289,7 @@ int ntbm_msi_request_threaded_irq(struct
 	if (!ntb->msi)
 		return -EINVAL;
 
-	msi_lock_descs(dev);
+	guard(msi_descs_lock)(dev);
 	msi_for_each_desc(entry, dev, MSI_DESC_ASSOCIATED) {
 		if (irq_has_action(entry->irq))
 			continue;
@@ -307,17 +307,11 @@ int ntbm_msi_request_threaded_irq(struct
 		ret = ntbm_msi_setup_callback(ntb, entry, msi_desc);
 		if (ret) {
 			devm_free_irq(&ntb->dev, entry->irq, dev_id);
-			goto unlock;
+			return ret;
 		}
-
-		ret = entry->irq;
-		goto unlock;
+		return entry->irq;
 	}
-	ret = -ENODEV;
-
-unlock:
-	msi_unlock_descs(dev);
-	return ret;
+	return -ENODEV;
 }
 EXPORT_SYMBOL(ntbm_msi_request_threaded_irq);
 




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

* [patch V2 05/10] PCI/MSI: Switch to MSI descriptor locking to guard()
  2025-03-13 13:03 [patch V2 00/10] genirq/msi: Spring cleaning Thomas Gleixner
                   ` (3 preceding siblings ...)
  2025-03-13 13:03 ` [patch V2 04/10] NTB/msi: Switch MSI descriptor locking to lock guard() Thomas Gleixner
@ 2025-03-13 13:03 ` Thomas Gleixner
  2025-03-13 15:50   ` Jonathan Cameron
  2025-03-13 13:03 ` [patch V2 06/10] PCI: hv: Switch " Thomas Gleixner
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 20+ messages in thread
From: Thomas Gleixner @ 2025-03-13 13:03 UTC (permalink / raw)
  To: LKML
  Cc: Marc Zyngier, Bjorn Helgaas, linux-pci, Peter Zijlstra,
	Nishanth Menon, Jonathan Cameron, Dhruva Gole, Tero Kristo,
	Santosh Shilimkar, Logan Gunthorpe, Dave Jiang, Jon Mason,
	Allen Hubbe, ntb, Michael Kelley, Wei Liu, Haiyang Zhang,
	linux-hyperv, Wei Huang, Manivannan Sadhasivam,
	James E.J. Bottomley, Martin K. Petersen, linux-scsi,
	Jonathan Cameron

Convert the code to use the new guard(msi_descs_lock).

No functional change intended.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: linux-pci@vger.kernel.org
---
V2: Remove the gotos - Jonathan
---
 drivers/pci/msi/api.c |    6 --
 drivers/pci/msi/msi.c |  121 ++++++++++++++++++++++++--------------------------
 2 files changed, 62 insertions(+), 65 deletions(-)

--- a/drivers/pci/msi/api.c
+++ b/drivers/pci/msi/api.c
@@ -53,10 +53,9 @@ void pci_disable_msi(struct pci_dev *dev
 	if (!pci_msi_enabled() || !dev || !dev->msi_enabled)
 		return;
 
-	msi_lock_descs(&dev->dev);
+	guard(msi_descs_lock)(&dev->dev);
 	pci_msi_shutdown(dev);
 	pci_free_msi_irqs(dev);
-	msi_unlock_descs(&dev->dev);
 }
 EXPORT_SYMBOL(pci_disable_msi);
 
@@ -196,10 +195,9 @@ void pci_disable_msix(struct pci_dev *de
 	if (!pci_msi_enabled() || !dev || !dev->msix_enabled)
 		return;
 
-	msi_lock_descs(&dev->dev);
+	guard(msi_descs_lock)(&dev->dev);
 	pci_msix_shutdown(dev);
 	pci_free_msi_irqs(dev);
-	msi_unlock_descs(&dev->dev);
 }
 EXPORT_SYMBOL(pci_disable_msix);
 
--- a/drivers/pci/msi/msi.c
+++ b/drivers/pci/msi/msi.c
@@ -336,41 +336,11 @@ static int msi_verify_entries(struct pci
 	return !entry ? 0 : -EIO;
 }
 
-/**
- * msi_capability_init - configure device's MSI capability structure
- * @dev: pointer to the pci_dev data structure of MSI device function
- * @nvec: number of interrupts to allocate
- * @affd: description of automatic IRQ affinity assignments (may be %NULL)
- *
- * Setup the MSI capability structure of the device with the requested
- * number of interrupts.  A return value of zero indicates the successful
- * setup of an entry with the new MSI IRQ.  A negative return value indicates
- * an error, and a positive return value indicates the number of interrupts
- * which could have been allocated.
- */
-static int msi_capability_init(struct pci_dev *dev, int nvec,
-			       struct irq_affinity *affd)
+static int __msi_capability_init(struct pci_dev *dev, int nvec, struct irq_affinity_desc *masks)
 {
-	struct irq_affinity_desc *masks = NULL;
+	int ret = msi_setup_msi_desc(dev, nvec, masks);
 	struct msi_desc *entry, desc;
-	int ret;
-
-	/* Reject multi-MSI early on irq domain enabled architectures */
-	if (nvec > 1 && !pci_msi_domain_supports(dev, MSI_FLAG_MULTI_PCI_MSI, ALLOW_LEGACY))
-		return 1;
-
-	/*
-	 * Disable MSI during setup in the hardware, but mark it enabled
-	 * so that setup code can evaluate it.
-	 */
-	pci_msi_set_enable(dev, 0);
-	dev->msi_enabled = 1;
-
-	if (affd)
-		masks = irq_create_affinity_masks(nvec, affd);
 
-	msi_lock_descs(&dev->dev);
-	ret = msi_setup_msi_desc(dev, nvec, masks);
 	if (ret)
 		goto fail;
 
@@ -399,19 +369,48 @@ static int msi_capability_init(struct pc
 
 	pcibios_free_irq(dev);
 	dev->irq = entry->irq;
-	goto unlock;
-
+	return 0;
 err:
 	pci_msi_unmask(&desc, msi_multi_mask(&desc));
 	pci_free_msi_irqs(dev);
 fail:
 	dev->msi_enabled = 0;
-unlock:
-	msi_unlock_descs(&dev->dev);
-	kfree(masks);
 	return ret;
 }
 
+/**
+ * msi_capability_init - configure device's MSI capability structure
+ * @dev: pointer to the pci_dev data structure of MSI device function
+ * @nvec: number of interrupts to allocate
+ * @affd: description of automatic IRQ affinity assignments (may be %NULL)
+ *
+ * Setup the MSI capability structure of the device with the requested
+ * number of interrupts.  A return value of zero indicates the successful
+ * setup of an entry with the new MSI IRQ.  A negative return value indicates
+ * an error, and a positive return value indicates the number of interrupts
+ * which could have been allocated.
+ */
+static int msi_capability_init(struct pci_dev *dev, int nvec,
+			       struct irq_affinity *affd)
+{
+	/* Reject multi-MSI early on irq domain enabled architectures */
+	if (nvec > 1 && !pci_msi_domain_supports(dev, MSI_FLAG_MULTI_PCI_MSI, ALLOW_LEGACY))
+		return 1;
+
+	/*
+	 * Disable MSI during setup in the hardware, but mark it enabled
+	 * so that setup code can evaluate it.
+	 */
+	pci_msi_set_enable(dev, 0);
+	dev->msi_enabled = 1;
+
+	struct irq_affinity_desc *masks __free(kfree) =
+		affd ? irq_create_affinity_masks(nvec, affd) : NULL;
+
+	guard(msi_descs_lock)(&dev->dev);
+	return __msi_capability_init(dev, nvec, masks);
+}
+
 int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec,
 			   struct irq_affinity *affd)
 {
@@ -666,37 +665,37 @@ static void msix_mask_all(void __iomem *
 		writel(ctrl, base + PCI_MSIX_ENTRY_VECTOR_CTRL);
 }
 
-static int msix_setup_interrupts(struct pci_dev *dev, struct msix_entry *entries,
-				 int nvec, struct irq_affinity *affd)
+static int __msix_setup_interrupts(struct pci_dev *dev, struct msix_entry *entries,
+				   int nvec, struct irq_affinity_desc *masks)
 {
-	struct irq_affinity_desc *masks = NULL;
-	int ret;
+	int ret = msix_setup_msi_descs(dev, entries, nvec, masks);
 
-	if (affd)
-		masks = irq_create_affinity_masks(nvec, affd);
-
-	msi_lock_descs(&dev->dev);
-	ret = msix_setup_msi_descs(dev, entries, nvec, masks);
 	if (ret)
-		goto out_free;
+		return ret;
 
 	ret = pci_msi_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX);
 	if (ret)
-		goto out_free;
+		return ret;
 
 	/* Check if all MSI entries honor device restrictions */
 	ret = msi_verify_entries(dev);
 	if (ret)
-		goto out_free;
+		return ret;
 
 	msix_update_entries(dev, entries);
-	goto out_unlock;
+	return 0;
+}
 
-out_free:
-	pci_free_msi_irqs(dev);
-out_unlock:
-	msi_unlock_descs(&dev->dev);
-	kfree(masks);
+static int msix_setup_interrupts(struct pci_dev *dev, struct msix_entry *entries,
+				 int nvec, struct irq_affinity *affd)
+{
+	struct irq_affinity_desc *masks __free(kfree) =
+		affd ? irq_create_affinity_masks(nvec, affd) : NULL;
+
+	guard(msi_descs_lock)(&dev->dev);
+	int ret = __msix_setup_interrupts(dev, entries, nvec, masks);
+	if (ret)
+		pci_free_msi_irqs(dev);
 	return ret;
 }
 
@@ -871,13 +870,13 @@ void __pci_restore_msix_state(struct pci
 
 	write_msg = arch_restore_msi_irqs(dev);
 
-	msi_lock_descs(&dev->dev);
-	msi_for_each_desc(entry, &dev->dev, MSI_DESC_ALL) {
-		if (write_msg)
-			__pci_write_msi_msg(entry, &entry->msg);
-		pci_msix_write_vector_ctrl(entry, entry->pci.msix_ctrl);
+	scoped_guard (msi_descs_lock, &dev->dev) {
+		msi_for_each_desc(entry, &dev->dev, MSI_DESC_ALL) {
+			if (write_msg)
+				__pci_write_msi_msg(entry, &entry->msg);
+			pci_msix_write_vector_ctrl(entry, entry->pci.msix_ctrl);
+		}
 	}
-	msi_unlock_descs(&dev->dev);
 
 	pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0);
 }


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

* [patch V2 06/10] PCI: hv: Switch MSI descriptor locking to guard()
  2025-03-13 13:03 [patch V2 00/10] genirq/msi: Spring cleaning Thomas Gleixner
                   ` (4 preceding siblings ...)
  2025-03-13 13:03 ` [patch V2 05/10] PCI/MSI: Switch to MSI descriptor locking to guard() Thomas Gleixner
@ 2025-03-13 13:03 ` Thomas Gleixner
  2025-03-13 13:03 ` [patch V2 07/10] PCI/MSI: Provide a sane mechanism for TPH Thomas Gleixner
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Thomas Gleixner @ 2025-03-13 13:03 UTC (permalink / raw)
  To: LKML
  Cc: Marc Zyngier, Jonathan Cameron, Michael Kelley, Wei Liu,
	Bjorn Helgaas, Haiyang Zhang, linux-hyperv, linux-pci,
	Peter Zijlstra, Nishanth Menon, Dhruva Gole, Tero Kristo,
	Santosh Shilimkar, Logan Gunthorpe, Dave Jiang, Jon Mason,
	Allen Hubbe, ntb, Wei Huang, Manivannan Sadhasivam,
	James E.J. Bottomley, Martin K. Petersen, linux-scsi,
	Jonathan Cameron

Convert the code to use the new guard(msi_descs_lock).

No functional change intended.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Michael Kelley <mhklinux@outlook.com>
Acked-by: Wei Liu <wei.liu@kernel.org>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: Haiyang Zhang <haiyangz@microsoft.com>
Cc: linux-hyperv@vger.kernel.org
Cc: linux-pci@vger.kernel.org
---
 drivers/pci/controller/pci-hyperv.c |   14 ++++----------
 1 file changed, 4 insertions(+), 10 deletions(-)

--- a/drivers/pci/controller/pci-hyperv.c
+++ b/drivers/pci/controller/pci-hyperv.c
@@ -3976,24 +3976,18 @@ static int hv_pci_restore_msi_msg(struct
 {
 	struct irq_data *irq_data;
 	struct msi_desc *entry;
-	int ret = 0;
 
 	if (!pdev->msi_enabled && !pdev->msix_enabled)
 		return 0;
 
-	msi_lock_descs(&pdev->dev);
+	guard(msi_descs_lock)(&pdev->dev);
 	msi_for_each_desc(entry, &pdev->dev, MSI_DESC_ASSOCIATED) {
 		irq_data = irq_get_irq_data(entry->irq);
-		if (WARN_ON_ONCE(!irq_data)) {
-			ret = -EINVAL;
-			break;
-		}
-
+		if (WARN_ON_ONCE(!irq_data))
+			return -EINVAL;
 		hv_compose_msi_msg(irq_data, &entry->msg);
 	}
-	msi_unlock_descs(&pdev->dev);
-
-	return ret;
+	return 0;
 }
 
 /*




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

* [patch V2 07/10] PCI/MSI: Provide a sane mechanism for TPH
  2025-03-13 13:03 [patch V2 00/10] genirq/msi: Spring cleaning Thomas Gleixner
                   ` (5 preceding siblings ...)
  2025-03-13 13:03 ` [patch V2 06/10] PCI: hv: Switch " Thomas Gleixner
@ 2025-03-13 13:03 ` Thomas Gleixner
  2025-03-13 13:03 ` [patch V2 08/10] PCI/TPH: Replace the broken MSI-X control word update Thomas Gleixner
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Thomas Gleixner @ 2025-03-13 13:03 UTC (permalink / raw)
  To: LKML
  Cc: Marc Zyngier, Bjorn Helgaas, Wei Huang, linux-pci, Peter Zijlstra,
	Nishanth Menon, Jonathan Cameron, Dhruva Gole, Tero Kristo,
	Santosh Shilimkar, Logan Gunthorpe, Dave Jiang, Jon Mason,
	Allen Hubbe, ntb, Michael Kelley, Wei Liu, Haiyang Zhang,
	linux-hyperv, Manivannan Sadhasivam, James E.J. Bottomley,
	Martin K. Petersen, linux-scsi, Jonathan Cameron

The PCI/TPH driver fiddles with the MSI-X control word of an active
interrupt completely unserialized against concurrent operations issued
from the interrupt core. It also brings the PCI/MSI-X internal cached
control word out of sync.

Provide a function, which has the required serialization and keeps the
control word cache in sync.

Unfortunately this requires to look up and lock the interrupt descriptor,
which should be only done in the interrupt core code. But confining this
particular oddity in the PCI/MSI core is the lesser of all evil. A
interrupt core implementation would require a larger pile of infrastructure
and indirections for dubious value.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: Wei Huang <wei.huang2@amd.com>
Cc: linux-pci@vger.kernel.org

---
 drivers/pci/msi/msi.c |   47 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/pci/pci.h     |    9 +++++++++
 2 files changed, 56 insertions(+)

--- a/drivers/pci/msi/msi.c
+++ b/drivers/pci/msi/msi.c
@@ -910,6 +910,53 @@ void pci_free_msi_irqs(struct pci_dev *d
 	}
 }
 
+#ifdef CONFIG_PCIE_TPH
+/**
+ * pci_msix_write_tph_tag - Update the TPH tag for a given MSI-X vector
+ * @pdev:	The PCIe device to update
+ * @index:	The MSI-X index to update
+ * @tag:	The tag to write
+ *
+ * Returns: 0 on success, error code on failure
+ */
+int pci_msix_write_tph_tag(struct pci_dev *pdev, unsigned int index, u16 tag)
+{
+	struct msi_desc *msi_desc;
+	struct irq_desc *irq_desc;
+	unsigned int virq;
+
+	if (!pdev->msix_enabled)
+		return -ENXIO;
+
+	guard(msi_descs_lock)(&pdev->dev);
+	virq = msi_get_virq(&pdev->dev, index);
+	if (!virq)
+		return -ENXIO;
+	/*
+	 * This is a horrible hack, but short of implementing a PCI
+	 * specific interrupt chip callback and a huge pile of
+	 * infrastructure, this is the minor nuissance. It provides the
+	 * protection against concurrent operations on this entry and keeps
+	 * the control word cache in sync.
+	 */
+	irq_desc = irq_to_desc(virq);
+	if (!irq_desc)
+		return -ENXIO;
+
+	guard(raw_spinlock_irq)(&irq_desc->lock);
+	msi_desc = irq_data_get_msi_desc(&irq_desc->irq_data);
+	if (!msi_desc || msi_desc->pci.msi_attrib.is_virtual)
+		return -ENXIO;
+
+	msi_desc->pci.msix_ctrl &= ~PCI_MSIX_ENTRY_CTRL_ST;
+	msi_desc->pci.msix_ctrl |= FIELD_PREP(PCI_MSIX_ENTRY_CTRL_ST, tag);
+	pci_msix_write_vector_ctrl(msi_desc, msi_desc->pci.msix_ctrl);
+	/* Flush the write */
+	readl(pci_msix_desc_addr(msi_desc));
+	return 0;
+}
+#endif
+
 /* Misc. infrastructure */
 
 struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc)
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -989,6 +989,15 @@ int pcim_request_region_exclusive(struct
 				  const char *name);
 void pcim_release_region(struct pci_dev *pdev, int bar);
 
+#ifdef CONFIG_PCI_MSI
+int pci_msix_write_tph_tag(struct pci_dev *pdev, unsigned int index, u16 tag);
+#else
+static inline int pci_msix_write_tph_tag(struct pci_dev *pdev, unsigned int index, u16 tag)
+{
+	return -ENODEV;
+}
+#endif
+
 /*
  * Config Address for PCI Configuration Mechanism #1
  *




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

* [patch V2 08/10] PCI/TPH: Replace the broken MSI-X control word update
  2025-03-13 13:03 [patch V2 00/10] genirq/msi: Spring cleaning Thomas Gleixner
                   ` (6 preceding siblings ...)
  2025-03-13 13:03 ` [patch V2 07/10] PCI/MSI: Provide a sane mechanism for TPH Thomas Gleixner
@ 2025-03-13 13:03 ` Thomas Gleixner
  2025-03-13 13:03 ` [patch V2 09/10] scsi: ufs: qcom: Remove the MSI descriptor abuse Thomas Gleixner
  2025-03-13 13:03 ` [patch V2 10/10] genirq/msi: Rename msi_[un]lock_descs() Thomas Gleixner
  9 siblings, 0 replies; 20+ messages in thread
From: Thomas Gleixner @ 2025-03-13 13:03 UTC (permalink / raw)
  To: LKML
  Cc: Marc Zyngier, Bjorn Helgaas, Wei Huang, linux-pci, Peter Zijlstra,
	Nishanth Menon, Jonathan Cameron, Dhruva Gole, Tero Kristo,
	Santosh Shilimkar, Logan Gunthorpe, Dave Jiang, Jon Mason,
	Allen Hubbe, ntb, Michael Kelley, Wei Liu, Haiyang Zhang,
	linux-hyperv, Manivannan Sadhasivam, James E.J. Bottomley,
	Martin K. Petersen, linux-scsi, Jonathan Cameron

The driver walks the MSI descriptors to test whether a descriptor exists
for a given index. That's just abuse of the MSI internals.

The same test can be done with a single function call by looking up whether
there is a Linux interrupt number assigned at the index.

What's worse is that the function is completely unserialized against
modifications of the MSI-X control by operations issued from the interrupt
core. It also brings the PCI/MSI-X internal cached control word out of
sync.

Remove the trainwreck and invoke the function provided by the PCI/MSI core
to update it.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: Wei Huang <wei.huang2@amd.com>
Cc: linux-pci@vger.kernel.org

---
 drivers/pci/tph.c |   44 +-------------------------------------------
 1 file changed, 1 insertion(+), 43 deletions(-)

--- a/drivers/pci/tph.c
+++ b/drivers/pci/tph.c
@@ -204,48 +204,6 @@ static u8 get_rp_completer_type(struct p
 	return FIELD_GET(PCI_EXP_DEVCAP2_TPH_COMP_MASK, reg);
 }
 
-/* Write ST to MSI-X vector control reg - Return 0 if OK, otherwise -errno */
-static int write_tag_to_msix(struct pci_dev *pdev, int msix_idx, u16 tag)
-{
-#ifdef CONFIG_PCI_MSI
-	struct msi_desc *msi_desc = NULL;
-	void __iomem *vec_ctrl;
-	u32 val;
-	int err = 0;
-
-	msi_lock_descs(&pdev->dev);
-
-	/* Find the msi_desc entry with matching msix_idx */
-	msi_for_each_desc(msi_desc, &pdev->dev, MSI_DESC_ASSOCIATED) {
-		if (msi_desc->msi_index == msix_idx)
-			break;
-	}
-
-	if (!msi_desc) {
-		err = -ENXIO;
-		goto err_out;
-	}
-
-	/* Get the vector control register (offset 0xc) pointed by msix_idx */
-	vec_ctrl = pdev->msix_base + msix_idx * PCI_MSIX_ENTRY_SIZE;
-	vec_ctrl += PCI_MSIX_ENTRY_VECTOR_CTRL;
-
-	val = readl(vec_ctrl);
-	val &= ~PCI_MSIX_ENTRY_CTRL_ST;
-	val |= FIELD_PREP(PCI_MSIX_ENTRY_CTRL_ST, tag);
-	writel(val, vec_ctrl);
-
-	/* Read back to flush the update */
-	val = readl(vec_ctrl);
-
-err_out:
-	msi_unlock_descs(&pdev->dev);
-	return err;
-#else
-	return -ENODEV;
-#endif
-}
-
 /* Write tag to ST table - Return 0 if OK, otherwise -errno */
 static int write_tag_to_st_table(struct pci_dev *pdev, int index, u16 tag)
 {
@@ -346,7 +304,7 @@ int pcie_tph_set_st_entry(struct pci_dev
 
 	switch (loc) {
 	case PCI_TPH_LOC_MSIX:
-		err = write_tag_to_msix(pdev, index, tag);
+		err = pci_msix_write_tph_tag(pdev, index, tag);
 		break;
 	case PCI_TPH_LOC_CAP:
 		err = write_tag_to_st_table(pdev, index, tag);




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

* [patch V2 09/10] scsi: ufs: qcom: Remove the MSI descriptor abuse
  2025-03-13 13:03 [patch V2 00/10] genirq/msi: Spring cleaning Thomas Gleixner
                   ` (7 preceding siblings ...)
  2025-03-13 13:03 ` [patch V2 08/10] PCI/TPH: Replace the broken MSI-X control word update Thomas Gleixner
@ 2025-03-13 13:03 ` Thomas Gleixner
  2025-03-28 10:00   ` Dan Carpenter
  2025-03-13 13:03 ` [patch V2 10/10] genirq/msi: Rename msi_[un]lock_descs() Thomas Gleixner
  9 siblings, 1 reply; 20+ messages in thread
From: Thomas Gleixner @ 2025-03-13 13:03 UTC (permalink / raw)
  To: LKML
  Cc: Marc Zyngier, Manivannan Sadhasivam, James E.J. Bottomley,
	Martin K. Petersen, linux-scsi, Peter Zijlstra, Nishanth Menon,
	Jonathan Cameron, Dhruva Gole, Tero Kristo, Santosh Shilimkar,
	Logan Gunthorpe, Dave Jiang, Jon Mason, Allen Hubbe, ntb,
	Bjorn Helgaas, linux-pci, Michael Kelley, Wei Liu, Haiyang Zhang,
	linux-hyperv, Wei Huang, Jonathan Cameron

The driver abuses the MSI descriptors for internal purposes. Aside of core
code and MSI providers nothing has to care about their existence. They have
been encapsulated with a lot of effort because this kind of abuse caused
all sorts of issues including a maintainability nightmare.

Rewrite the code so it uses dedicated storage to hand the required
information to the interrupt handler.

No functional change intended.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Cc: "James E.J. Bottomley" <James.Bottomley@HansenPartnership.com>
Cc: "Martin K. Petersen" <martin.petersen@oracle.com>
Cc: linux-scsi@vger.kernel.org

---
 drivers/ufs/host/ufs-qcom.c |   77 ++++++++++++++++++++++----------------------
 1 file changed, 40 insertions(+), 37 deletions(-)

--- a/drivers/ufs/host/ufs-qcom.c
+++ b/drivers/ufs/host/ufs-qcom.c
@@ -1782,15 +1782,19 @@ static void ufs_qcom_write_msi_msg(struc
 	ufshcd_mcq_config_esi(hba, msg);
 }
 
+struct ufs_qcom_irq {
+	unsigned int		irq;
+	unsigned int		idx;
+	struct ufs_hba		*hba;
+};
+
 static irqreturn_t ufs_qcom_mcq_esi_handler(int irq, void *data)
 {
-	struct msi_desc *desc = data;
-	struct device *dev = msi_desc_to_dev(desc);
-	struct ufs_hba *hba = dev_get_drvdata(dev);
-	u32 id = desc->msi_index;
-	struct ufs_hw_queue *hwq = &hba->uhq[id];
+	struct ufs_qcom_irq *qi = data;
+	struct ufs_hba *hba = qi->hba;
+	struct ufs_hw_queue *hwq = &hba->uhq[qi->idx];
 
-	ufshcd_mcq_write_cqis(hba, 0x1, id);
+	ufshcd_mcq_write_cqis(hba, 0x1, qi->idx);
 	ufshcd_mcq_poll_cqe_lock(hba, hwq);
 
 	return IRQ_HANDLED;
@@ -1799,8 +1803,7 @@ static irqreturn_t ufs_qcom_mcq_esi_hand
 static int ufs_qcom_config_esi(struct ufs_hba *hba)
 {
 	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
-	struct msi_desc *desc;
-	struct msi_desc *failed_desc = NULL;
+	struct ufs_qcom_irq *qi;
 	int nr_irqs, ret;
 
 	if (host->esi_enabled)
@@ -1811,47 +1814,47 @@ static int ufs_qcom_config_esi(struct uf
 	 * 2. Poll queues do not need ESI.
 	 */
 	nr_irqs = hba->nr_hw_queues - hba->nr_queues[HCTX_TYPE_POLL];
+	qi = devm_kcalloc(hba->dev, nr_irqs, sizeof(*qi), GFP_KERNEL);
+	if (qi)
+		return -ENOMEM;
+
 	ret = platform_device_msi_init_and_alloc_irqs(hba->dev, nr_irqs,
 						      ufs_qcom_write_msi_msg);
 	if (ret) {
 		dev_err(hba->dev, "Failed to request Platform MSI %d\n", ret);
-		return ret;
+		goto cleanup;
 	}
 
-	msi_lock_descs(hba->dev);
-	msi_for_each_desc(desc, hba->dev, MSI_DESC_ALL) {
-		ret = devm_request_irq(hba->dev, desc->irq,
-				       ufs_qcom_mcq_esi_handler,
-				       IRQF_SHARED, "qcom-mcq-esi", desc);
+	for (int idx = 0; idx < nr_irqs; idx++) {
+		qi[idx].irq = msi_get_virq(hba->dev, idx);
+		qi[idx].idx = idx;
+		qi[idx].hba = hba;
+
+		ret = devm_request_irq(hba->dev, qi[idx].irq, ufs_qcom_mcq_esi_handler,
+				       IRQF_SHARED, "qcom-mcq-esi", qi + idx);
 		if (ret) {
 			dev_err(hba->dev, "%s: Fail to request IRQ for %d, err = %d\n",
-				__func__, desc->irq, ret);
-			failed_desc = desc;
-			break;
+				__func__, qi[idx].irq, ret);
+			qi[idx].irq = 0;
+			goto cleanup;
 		}
 	}
-	msi_unlock_descs(hba->dev);
 
-	if (ret) {
-		/* Rewind */
-		msi_lock_descs(hba->dev);
-		msi_for_each_desc(desc, hba->dev, MSI_DESC_ALL) {
-			if (desc == failed_desc)
-				break;
-			devm_free_irq(hba->dev, desc->irq, hba);
-		}
-		msi_unlock_descs(hba->dev);
-		platform_device_msi_free_irqs_all(hba->dev);
-	} else {
-		if (host->hw_ver.major == 6 && host->hw_ver.minor == 0 &&
-		    host->hw_ver.step == 0)
-			ufshcd_rmwl(hba, ESI_VEC_MASK,
-				    FIELD_PREP(ESI_VEC_MASK, MAX_ESI_VEC - 1),
-				    REG_UFS_CFG3);
-		ufshcd_mcq_enable_esi(hba);
-		host->esi_enabled = true;
+	if (host->hw_ver.major == 6 && host->hw_ver.minor == 0 &&
+	    host->hw_ver.step == 0) {
+		ufshcd_rmwl(hba, ESI_VEC_MASK,
+			    FIELD_PREP(ESI_VEC_MASK, MAX_ESI_VEC - 1),
+			    REG_UFS_CFG3);
 	}
-
+	ufshcd_mcq_enable_esi(hba);
+	host->esi_enabled = true;
+	return 0;
+
+cleanup:
+	for (int idx = 0; qi[idx].irq; idx++)
+		devm_free_irq(hba->dev, qi[idx].irq, hba);
+	platform_device_msi_free_irqs_all(hba->dev);
+	devm_kfree(hba->dev, qi);
 	return ret;
 }
 




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

* [patch V2 10/10] genirq/msi: Rename msi_[un]lock_descs()
  2025-03-13 13:03 [patch V2 00/10] genirq/msi: Spring cleaning Thomas Gleixner
                   ` (8 preceding siblings ...)
  2025-03-13 13:03 ` [patch V2 09/10] scsi: ufs: qcom: Remove the MSI descriptor abuse Thomas Gleixner
@ 2025-03-13 13:03 ` Thomas Gleixner
  9 siblings, 0 replies; 20+ messages in thread
From: Thomas Gleixner @ 2025-03-13 13:03 UTC (permalink / raw)
  To: LKML
  Cc: Marc Zyngier, Jonathan Cameron, Peter Zijlstra, Nishanth Menon,
	Jonathan Cameron, Dhruva Gole, Tero Kristo, Santosh Shilimkar,
	Logan Gunthorpe, Dave Jiang, Jon Mason, Allen Hubbe, ntb,
	Bjorn Helgaas, linux-pci, Michael Kelley, Wei Liu, Haiyang Zhang,
	linux-hyperv, Wei Huang, Manivannan Sadhasivam,
	James E.J. Bottomley, Martin K. Petersen, linux-scsi

Now that all abuse is gone and the legit users are converted to
guard(msi_descs_lock), rename the lock functions and document them as
internal.

No functional change.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huwei.com>
---
 include/linux/msi.h |    8 ++++----
 kernel/irq/msi.c    |   16 ++++++++++------
 2 files changed, 14 insertions(+), 10 deletions(-)

--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -224,11 +224,11 @@ struct msi_dev_domain {
 
 int msi_setup_device_data(struct device *dev);
 
-void msi_lock_descs(struct device *dev);
-void msi_unlock_descs(struct device *dev);
+void __msi_lock_descs(struct device *dev);
+void __msi_unlock_descs(struct device *dev);
 
-DEFINE_LOCK_GUARD_1(msi_descs_lock, struct device, msi_lock_descs(_T->lock),
-		    msi_unlock_descs(_T->lock));
+DEFINE_LOCK_GUARD_1(msi_descs_lock, struct device, __msi_lock_descs(_T->lock),
+		    __msi_unlock_descs(_T->lock));
 
 struct msi_desc *msi_domain_first_desc(struct device *dev, unsigned int domid,
 				       enum msi_desc_filter filter);
--- a/kernel/irq/msi.c
+++ b/kernel/irq/msi.c
@@ -337,26 +337,30 @@ int msi_setup_device_data(struct device
 }
 
 /**
- * msi_lock_descs - Lock the MSI descriptor storage of a device
+ * __msi_lock_descs - Lock the MSI descriptor storage of a device
  * @dev:	Device to operate on
+ *
+ * Internal function for guard(msi_descs_lock). Don't use in code.
  */
-void msi_lock_descs(struct device *dev)
+void __msi_lock_descs(struct device *dev)
 {
 	mutex_lock(&dev->msi.data->mutex);
 }
-EXPORT_SYMBOL_GPL(msi_lock_descs);
+EXPORT_SYMBOL_GPL(__msi_lock_descs);
 
 /**
- * msi_unlock_descs - Unlock the MSI descriptor storage of a device
+ * __msi_unlock_descs - Unlock the MSI descriptor storage of a device
  * @dev:	Device to operate on
+ *
+ * Internal function for guard(msi_descs_lock). Don't use in code.
  */
-void msi_unlock_descs(struct device *dev)
+void __msi_unlock_descs(struct device *dev)
 {
 	/* Invalidate the index which was cached by the iterator */
 	dev->msi.data->__iter_idx = MSI_XA_MAX_INDEX;
 	mutex_unlock(&dev->msi.data->mutex);
 }
-EXPORT_SYMBOL_GPL(msi_unlock_descs);
+EXPORT_SYMBOL_GPL(__msi_unlock_descs);
 
 static struct msi_desc *msi_find_desc(struct msi_device_data *md, unsigned int domid,
 				      enum msi_desc_filter filter)




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

* Re: [patch V2 01/10] cleanup: Provide retain_ptr()
  2025-03-13 13:03 ` [patch V2 01/10] cleanup: Provide retain_ptr() Thomas Gleixner
@ 2025-03-13 15:24   ` Jonathan Cameron
  2025-03-14  9:37   ` Peter Zijlstra
  2025-03-14 14:04   ` Frank Li
  2 siblings, 0 replies; 20+ messages in thread
From: Jonathan Cameron @ 2025-03-13 15:24 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: LKML, Marc Zyngier, Peter Zijlstra, Nishanth Menon, Dhruva Gole,
	Tero Kristo, Santosh Shilimkar, Logan Gunthorpe, Dave Jiang,
	Jon Mason, Allen Hubbe, ntb, Bjorn Helgaas, linux-pci,
	Michael Kelley, Wei Liu, Haiyang Zhang, linux-hyperv, Wei Huang,
	Manivannan Sadhasivam, James E.J. Bottomley, Martin K. Petersen,
	linux-scsi, Jonathan Cameron

On Thu, 13 Mar 2025 14:03:38 +0100 (CET)
Thomas Gleixner <tglx@linutronix.de> wrote:

> In cases where an allocation is consumed by another function, the
> allocation needs to be retained on success or freed on failure. The code
> pattern is usually:
> 
> 	struct foo *f = kzalloc(sizeof(*f), GFP_KERNEL);
> 	struct bar *b;
> 
> 	,,,
> 	// Initialize f
> 	...
> 	if (ret)
> 		goto free;
>         ...
> 	bar = bar_create(f);
> 	if (!bar) {
> 		ret = -ENOMEM;
> 	   	goto free;
> 	}
> 	...
> 	return 0;
> free:
> 	kfree(f);
> 	return ret;
> 
> This prevents using __free(kfree) on @f because there is no canonical way
> to tell the cleanup code that the allocation should not be freed.
> 
> Abusing no_free_ptr() by force ignoring the return value is not really a
> sensible option either.
> 
> Provide an explicit macro retain_ptr(), which NULLs the cleanup
> pointer. That makes it easy to analyze and reason about.
> 
> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
> Cc: Peter Zijlstra <peterz@infradead.org>

Seems sensible to me and the resulting code is reasonably easy to
follow / contained in a small region.

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

> ---
>  include/linux/cleanup.h |   17 +++++++++++++++++
>  1 file changed, 17 insertions(+)
> 
> --- a/include/linux/cleanup.h
> +++ b/include/linux/cleanup.h
> @@ -216,6 +216,23 @@ const volatile void * __must_check_fn(co
>  
>  #define return_ptr(p)	return no_free_ptr(p)
>  
> +/*
> + * Only for situations where an allocation is handed in to another function
> + * and consumed by that function on success.
> + *
> + *	struct foo *f __free(kfree) = kzalloc(sizeof(*f), GFP_KERNEL);
> + *
> + *	setup(f);
> + *	if (some_condition)
> + *		return -EINVAL;
> + *	....
> + *	ret = bar(f);
> + *	if (!ret)
> + *		retain_ptr(f);
> + *	return ret;
> + */
> +#define retain_ptr(p)				\
> +	__get_and_null(p, NULL)
>  
>  /*
>   * DEFINE_CLASS(name, type, exit, init, init_args...):
> 


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

* Re: [patch V2 02/10] genirq/msi: Use lock guards for MSI descriptor locking
  2025-03-13 13:03 ` [patch V2 02/10] genirq/msi: Use lock guards for MSI descriptor locking Thomas Gleixner
@ 2025-03-13 15:26   ` Jonathan Cameron
  0 siblings, 0 replies; 20+ messages in thread
From: Jonathan Cameron @ 2025-03-13 15:26 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: LKML, Marc Zyngier, Peter Zijlstra, Nishanth Menon, Dhruva Gole,
	Tero Kristo, Santosh Shilimkar, Logan Gunthorpe, Dave Jiang,
	Jon Mason, Allen Hubbe, ntb, Bjorn Helgaas, linux-pci,
	Michael Kelley, Wei Liu, Haiyang Zhang, linux-hyperv, Wei Huang,
	Manivannan Sadhasivam, James E.J. Bottomley, Martin K. Petersen,
	linux-scsi, Jonathan Cameron

On Thu, 13 Mar 2025 14:03:39 +0100 (CET)
Thomas Gleixner <tglx@linutronix.de> wrote:

> Provide a lock guard for MSI descriptor locking and update the core code
> accordingly.
> 
> No functional change intended.
> 
> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>


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

* Re: [patch V2 05/10] PCI/MSI: Switch to MSI descriptor locking to guard()
  2025-03-13 13:03 ` [patch V2 05/10] PCI/MSI: Switch to MSI descriptor locking to guard() Thomas Gleixner
@ 2025-03-13 15:50   ` Jonathan Cameron
  2025-03-13 17:55     ` Thomas Gleixner
  0 siblings, 1 reply; 20+ messages in thread
From: Jonathan Cameron @ 2025-03-13 15:50 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: LKML, Marc Zyngier, Bjorn Helgaas, linux-pci, Peter Zijlstra,
	Nishanth Menon, Dhruva Gole, Tero Kristo, Santosh Shilimkar,
	Logan Gunthorpe, Dave Jiang, Jon Mason, Allen Hubbe, ntb,
	Michael Kelley, Wei Liu, Haiyang Zhang, linux-hyperv, Wei Huang,
	Manivannan Sadhasivam, James E.J. Bottomley, Martin K. Petersen,
	linux-scsi, Jonathan Cameron

On Thu, 13 Mar 2025 14:03:44 +0100 (CET)
Thomas Gleixner <tglx@linutronix.de> wrote:

> Convert the code to use the new guard(msi_descs_lock).
> 
> No functional change intended.
> 
> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
> Cc: Bjorn Helgaas <bhelgaas@google.com>
> Cc: linux-pci@vger.kernel.org
> ---
> V2: Remove the gotos - Jonathan
Hi Thomas,


There is a bit of the original code that is carried forwards here
that superficially seemed overly complex.  However as far as I can tell
this is functionally the same as you intended.  So with that in mind
if my question isn't complete garbage, maybe a readability issue for
another day.

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

> --- a/drivers/pci/msi/msi.c
> +++ b/drivers/pci/msi/msi.c



> +static int msix_setup_interrupts(struct pci_dev *dev, struct msix_entry *entries,
> +				 int nvec, struct irq_affinity *affd)
> +{
> +	struct irq_affinity_desc *masks __free(kfree) =
> +		affd ? irq_create_affinity_masks(nvec, affd) : NULL;
> +
> +	guard(msi_descs_lock)(&dev->dev);
> +	int ret = __msix_setup_interrupts(dev, entries, nvec, masks);
> +	if (ret)
> +		pci_free_msi_irqs(dev);

It's not immediately obvious what this is undoing (i.e. where the alloc
is).  I think it is at least mostly the pci_msi_setup_msi_irqs in
__msix_setup_interrupts

Why not handle the error in __msix_setup_interrupts and make that function
side effect free.  Does require gotos but in a function that isn't
doing any cleanup magic so should be fine.

Mind you I'm not following the logic in msix_setup_interrupts()
before this series either. i.e. why doesn't msix_setup_msi_descs()
clean up after itself on failure (i.e. undo loop iterations that
weren't failures) as that at least superficially looks like it
would give more readable code.

So this is the same as current and as such the patch is fine I think.

>  	return ret;
>  }
>  



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

* Re: [patch V2 05/10] PCI/MSI: Switch to MSI descriptor locking to guard()
  2025-03-13 15:50   ` Jonathan Cameron
@ 2025-03-13 17:55     ` Thomas Gleixner
  2025-03-14  9:42       ` Peter Zijlstra
  0 siblings, 1 reply; 20+ messages in thread
From: Thomas Gleixner @ 2025-03-13 17:55 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: LKML, Marc Zyngier, Bjorn Helgaas, linux-pci, Peter Zijlstra,
	Nishanth Menon, Dhruva Gole, Tero Kristo, Santosh Shilimkar,
	Logan Gunthorpe, Dave Jiang, Jon Mason, Allen Hubbe, ntb,
	Michael Kelley, Wei Liu, Haiyang Zhang, linux-hyperv, Wei Huang,
	Manivannan Sadhasivam, James E.J. Bottomley, Martin K. Petersen,
	linux-scsi, Jonathan Cameron

On Thu, Mar 13 2025 at 15:50, Jonathan Cameron wrote:
>> +	guard(msi_descs_lock)(&dev->dev);
>> +	int ret = __msix_setup_interrupts(dev, entries, nvec, masks);
>> +	if (ret)
>> +		pci_free_msi_irqs(dev);
>
> It's not immediately obvious what this is undoing (i.e. where the alloc
> is).  I think it is at least mostly the pci_msi_setup_msi_irqs in
> __msix_setup_interrupts

It's a universal cleanup for all possible error cases.

> Why not handle the error in __msix_setup_interrupts and make that function
> side effect free.  Does require gotos but in a function that isn't
> doing any cleanup magic so should be fine.

I had the gotos first and then hated them. But you are right, it's better
to have them than having the magic clean up at the call site.

I'll fold the delta patch below.

Thanks,

        tglx
---
--- a/drivers/pci/msi/msi.c
+++ b/drivers/pci/msi/msi.c
@@ -671,19 +671,23 @@ static int __msix_setup_interrupts(struc
 	int ret = msix_setup_msi_descs(dev, entries, nvec, masks);
 
 	if (ret)
-		return ret;
+		goto fail;
 
 	ret = pci_msi_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX);
 	if (ret)
-		return ret;
+		goto fail;
 
 	/* Check if all MSI entries honor device restrictions */
 	ret = msi_verify_entries(dev);
 	if (ret)
-		return ret;
+		goto fail;
 
 	msix_update_entries(dev, entries);
 	return 0;
+
+fail:
+	pci_free_msi_irqs(dev);
+	return ret;
 }
 
 static int msix_setup_interrupts(struct pci_dev *dev, struct msix_entry *entries,
@@ -693,10 +697,7 @@ static int msix_setup_interrupts(struct
 		affd ? irq_create_affinity_masks(nvec, affd) : NULL;
 
 	guard(msi_descs_lock)(&dev->dev);
-	int ret = __msix_setup_interrupts(dev, entries, nvec, masks);
-	if (ret)
-		pci_free_msi_irqs(dev);
-	return ret;
+	return __msix_setup_interrupts(dev, entries, nvec, masks);
 }
 
 /**

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

* Re: [patch V2 01/10] cleanup: Provide retain_ptr()
  2025-03-13 13:03 ` [patch V2 01/10] cleanup: Provide retain_ptr() Thomas Gleixner
  2025-03-13 15:24   ` Jonathan Cameron
@ 2025-03-14  9:37   ` Peter Zijlstra
  2025-03-14 14:04   ` Frank Li
  2 siblings, 0 replies; 20+ messages in thread
From: Peter Zijlstra @ 2025-03-14  9:37 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: LKML, Marc Zyngier, Nishanth Menon, Jonathan Cameron, Dhruva Gole,
	Tero Kristo, Santosh Shilimkar, Logan Gunthorpe, Dave Jiang,
	Jon Mason, Allen Hubbe, ntb, Bjorn Helgaas, linux-pci,
	Michael Kelley, Wei Liu, Haiyang Zhang, linux-hyperv, Wei Huang,
	Manivannan Sadhasivam, James E.J. Bottomley, Martin K. Petersen,
	linux-scsi, Jonathan Cameron

On Thu, Mar 13, 2025 at 02:03:38PM +0100, Thomas Gleixner wrote:
> In cases where an allocation is consumed by another function, the
> allocation needs to be retained on success or freed on failure. The code
> pattern is usually:
> 
> 	struct foo *f = kzalloc(sizeof(*f), GFP_KERNEL);
> 	struct bar *b;
> 
> 	,,,
> 	// Initialize f
> 	...
> 	if (ret)
> 		goto free;
>         ...
> 	bar = bar_create(f);
> 	if (!bar) {
> 		ret = -ENOMEM;
> 	   	goto free;
> 	}
> 	...
> 	return 0;
> free:
> 	kfree(f);
> 	return ret;
> 
> This prevents using __free(kfree) on @f because there is no canonical way
> to tell the cleanup code that the allocation should not be freed.
> 
> Abusing no_free_ptr() by force ignoring the return value is not really a
> sensible option either.
> 
> Provide an explicit macro retain_ptr(), which NULLs the cleanup
> pointer. That makes it easy to analyze and reason about.

So no objection per se, but one way to solve this is by handing
ownership to bar_create(), such that it is responsible for freeing f on
failure.

Anyway, I suspect the __must_check came from Linus, OTOH take_fd(), the
equivalent for file descriptors	also don't have that __must_check. So
clearly we have precedent here.

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

* Re: [patch V2 05/10] PCI/MSI: Switch to MSI descriptor locking to guard()
  2025-03-13 17:55     ` Thomas Gleixner
@ 2025-03-14  9:42       ` Peter Zijlstra
  0 siblings, 0 replies; 20+ messages in thread
From: Peter Zijlstra @ 2025-03-14  9:42 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Jonathan Cameron, LKML, Marc Zyngier, Bjorn Helgaas, linux-pci,
	Nishanth Menon, Dhruva Gole, Tero Kristo, Santosh Shilimkar,
	Logan Gunthorpe, Dave Jiang, Jon Mason, Allen Hubbe, ntb,
	Michael Kelley, Wei Liu, Haiyang Zhang, linux-hyperv, Wei Huang,
	Manivannan Sadhasivam, James E.J. Bottomley, Martin K. Petersen,
	linux-scsi, Jonathan Cameron

On Thu, Mar 13, 2025 at 06:55:12PM +0100, Thomas Gleixner wrote:
> On Thu, Mar 13 2025 at 15:50, Jonathan Cameron wrote:
> >> +	guard(msi_descs_lock)(&dev->dev);
> >> +	int ret = __msix_setup_interrupts(dev, entries, nvec, masks);
> >> +	if (ret)
> >> +		pci_free_msi_irqs(dev);
> >
> > It's not immediately obvious what this is undoing (i.e. where the alloc
> > is).  I think it is at least mostly the pci_msi_setup_msi_irqs in
> > __msix_setup_interrupts
> 
> It's a universal cleanup for all possible error cases.
> 
> > Why not handle the error in __msix_setup_interrupts and make that function
> > side effect free.  Does require gotos but in a function that isn't
> > doing any cleanup magic so should be fine.
> 
> I had the gotos first and then hated them. But you are right, it's better
> to have them than having the magic clean up at the call site.
> 
> I'll fold the delta patch below.
> 
> Thanks,
> 
>         tglx
> ---
> --- a/drivers/pci/msi/msi.c
> +++ b/drivers/pci/msi/msi.c
> @@ -671,19 +671,23 @@ static int __msix_setup_interrupts(struc
>  	int ret = msix_setup_msi_descs(dev, entries, nvec, masks);
>  
>  	if (ret)
> -		return ret;
> +		goto fail;
>  
>  	ret = pci_msi_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX);
>  	if (ret)
> -		return ret;
> +		goto fail;
>  
>  	/* Check if all MSI entries honor device restrictions */
>  	ret = msi_verify_entries(dev);
>  	if (ret)
> -		return ret;
> +		goto fail;
>  
>  	msix_update_entries(dev, entries);
>  	return 0;
> +
> +fail:
> +	pci_free_msi_irqs(dev);
> +	return ret;
>  }

How about something like:

int __msix_setup_interrupts(struct device *_dev,... )
{
	struct device *dev __free(msi_irqs) = _dev;

	int ret = msix_setup_msi_descs(dev, entries, nvec, masks);
	if (ret)
		return ret;

	ret = pci_msi_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX);
	if (ret)
		return ret;

	/* Check if all MSI entries honor device restrictions */
	ret = msi_verify_entries(dev);
	if (ret)
		return ret;

	retain_ptr(dev);
	msix_update_entries(dev, entries);
	return 0;
}

?

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

* Re: [patch V2 01/10] cleanup: Provide retain_ptr()
  2025-03-13 13:03 ` [patch V2 01/10] cleanup: Provide retain_ptr() Thomas Gleixner
  2025-03-13 15:24   ` Jonathan Cameron
  2025-03-14  9:37   ` Peter Zijlstra
@ 2025-03-14 14:04   ` Frank Li
  2 siblings, 0 replies; 20+ messages in thread
From: Frank Li @ 2025-03-14 14:04 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: LKML, Marc Zyngier, Peter Zijlstra, Nishanth Menon,
	Jonathan Cameron, Dhruva Gole, Tero Kristo, Santosh Shilimkar,
	Logan Gunthorpe, Dave Jiang, Jon Mason, Allen Hubbe, ntb,
	Bjorn Helgaas, linux-pci, Michael Kelley, Wei Liu, Haiyang Zhang,
	linux-hyperv, Wei Huang, Manivannan Sadhasivam,
	James E.J. Bottomley, Martin K. Petersen, linux-scsi,
	Jonathan Cameron

On Thu, Mar 13, 2025 at 02:03:38PM +0100, Thomas Gleixner wrote:
> In cases where an allocation is consumed by another function, the
> allocation needs to be retained on success or freed on failure. The code
> pattern is usually:
>
> 	struct foo *f = kzalloc(sizeof(*f), GFP_KERNEL);
> 	struct bar *b;
>
> 	,,,
> 	// Initialize f
> 	...
> 	if (ret)
> 		goto free;
>         ...
> 	bar = bar_create(f);
> 	if (!bar) {
> 		ret = -ENOMEM;
> 	   	goto free;
> 	}
> 	...
> 	return 0;
> free:
> 	kfree(f);
> 	return ret;
>
> This prevents using __free(kfree) on @f because there is no canonical way
> to tell the cleanup code that the allocation should not be freed.
>
> Abusing no_free_ptr() by force ignoring the return value is not really a
> sensible option either.
>
> Provide an explicit macro retain_ptr(), which NULLs the cleanup
> pointer. That makes it easy to analyze and reason about.
>
> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
> Cc: Peter Zijlstra <peterz@infradead.org>
> ---
>  include/linux/cleanup.h |   17 +++++++++++++++++
>  1 file changed, 17 insertions(+)
>
> --- a/include/linux/cleanup.h
> +++ b/include/linux/cleanup.h
> @@ -216,6 +216,23 @@ const volatile void * __must_check_fn(co
>
>  #define return_ptr(p)	return no_free_ptr(p)
>
> +/*
> + * Only for situations where an allocation is handed in to another function
> + * and consumed by that function on success.
> + *
> + *	struct foo *f __free(kfree) = kzalloc(sizeof(*f), GFP_KERNEL);
> + *
> + *	setup(f);
> + *	if (some_condition)
> + *		return -EINVAL;
> + *	....
> + *	ret = bar(f);
> + *	if (!ret)
> + *		retain_ptr(f);
> + *	return ret;

Is it better like

	ret = bar(f);
	if (ret)
		return ret;

	retain_ptr(f);
	return 0;

If there are more than one f, like f1, f2, f3....

	ret= bar(f1, f2, ....)
	if (ret)
		return ret;

	retain_ptr(f1);
	retain_ptr(f2);
	...

	return 0;


Or define a macro like
#defne no_free_ptr_on_ok(ret, p) ret ? ret : __get_and_null(p, NULL), 0

	ret = bar (f);
	return no_free_ptr_on_ok(ret, f);

Frank

> + */
> +#define retain_ptr(p)				\
> +	__get_and_null(p, NULL)
>
>  /*
>   * DEFINE_CLASS(name, type, exit, init, init_args...):
>

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

* Re: [patch V2 09/10] scsi: ufs: qcom: Remove the MSI descriptor abuse
  2025-03-13 13:03 ` [patch V2 09/10] scsi: ufs: qcom: Remove the MSI descriptor abuse Thomas Gleixner
@ 2025-03-28 10:00   ` Dan Carpenter
  2025-03-28 14:05     ` Thomas Gleixner
  0 siblings, 1 reply; 20+ messages in thread
From: Dan Carpenter @ 2025-03-28 10:00 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: LKML, Marc Zyngier, Manivannan Sadhasivam, James E.J. Bottomley,
	Martin K. Petersen, linux-scsi, Peter Zijlstra, Nishanth Menon,
	Jonathan Cameron, Dhruva Gole, Tero Kristo, Santosh Shilimkar,
	Logan Gunthorpe, Dave Jiang, Jon Mason, Allen Hubbe, ntb,
	Bjorn Helgaas, linux-pci, Michael Kelley, Wei Liu, Haiyang Zhang,
	linux-hyperv, Wei Huang, Jonathan Cameron

On Thu, Mar 13, 2025 at 02:03:51PM +0100, Thomas Gleixner wrote:
> @@ -1799,8 +1803,7 @@ static irqreturn_t ufs_qcom_mcq_esi_hand
>  static int ufs_qcom_config_esi(struct ufs_hba *hba)
>  {
>  	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
> -	struct msi_desc *desc;
> -	struct msi_desc *failed_desc = NULL;
> +	struct ufs_qcom_irq *qi;
>  	int nr_irqs, ret;
>  
>  	if (host->esi_enabled)
> @@ -1811,47 +1814,47 @@ static int ufs_qcom_config_esi(struct uf
>  	 * 2. Poll queues do not need ESI.
>  	 */
>  	nr_irqs = hba->nr_hw_queues - hba->nr_queues[HCTX_TYPE_POLL];
> +	qi = devm_kcalloc(hba->dev, nr_irqs, sizeof(*qi), GFP_KERNEL);
> +	if (qi)

This NULL check is reversed.  Missing !.

regards,
dan carpenter

> +		return -ENOMEM;
> +
>  	ret = platform_device_msi_init_and_alloc_irqs(hba->dev, nr_irqs,
>  						      ufs_qcom_write_msi_msg);


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

* Re: [patch V2 09/10] scsi: ufs: qcom: Remove the MSI descriptor abuse
  2025-03-28 10:00   ` Dan Carpenter
@ 2025-03-28 14:05     ` Thomas Gleixner
  0 siblings, 0 replies; 20+ messages in thread
From: Thomas Gleixner @ 2025-03-28 14:05 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: LKML, Marc Zyngier, Manivannan Sadhasivam, James E.J. Bottomley,
	Martin K. Petersen, linux-scsi, Peter Zijlstra, Nishanth Menon,
	Jonathan Cameron, Dhruva Gole, Tero Kristo, Santosh Shilimkar,
	Logan Gunthorpe, Dave Jiang, Jon Mason, Allen Hubbe, ntb,
	Bjorn Helgaas, linux-pci, Michael Kelley, Wei Liu, Haiyang Zhang,
	linux-hyperv, Wei Huang, Jonathan Cameron

On Fri, Mar 28 2025 at 13:00, Dan Carpenter wrote:
> On Thu, Mar 13, 2025 at 02:03:51PM +0100, Thomas Gleixner wrote:
>> @@ -1799,8 +1803,7 @@ static irqreturn_t ufs_qcom_mcq_esi_hand
>>  static int ufs_qcom_config_esi(struct ufs_hba *hba)
>>  {
>>  	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
>> -	struct msi_desc *desc;
>> -	struct msi_desc *failed_desc = NULL;
>> +	struct ufs_qcom_irq *qi;
>>  	int nr_irqs, ret;
>>  
>>  	if (host->esi_enabled)
>> @@ -1811,47 +1814,47 @@ static int ufs_qcom_config_esi(struct uf
>>  	 * 2. Poll queues do not need ESI.
>>  	 */
>>  	nr_irqs = hba->nr_hw_queues - hba->nr_queues[HCTX_TYPE_POLL];
>> +	qi = devm_kcalloc(hba->dev, nr_irqs, sizeof(*qi), GFP_KERNEL);
>> +	if (qi)
>
> This NULL check is reversed.  Missing !.

Duh. I'm sure I've fixed that before.

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

end of thread, other threads:[~2025-03-28 14:05 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-03-13 13:03 [patch V2 00/10] genirq/msi: Spring cleaning Thomas Gleixner
2025-03-13 13:03 ` [patch V2 01/10] cleanup: Provide retain_ptr() Thomas Gleixner
2025-03-13 15:24   ` Jonathan Cameron
2025-03-14  9:37   ` Peter Zijlstra
2025-03-14 14:04   ` Frank Li
2025-03-13 13:03 ` [patch V2 02/10] genirq/msi: Use lock guards for MSI descriptor locking Thomas Gleixner
2025-03-13 15:26   ` Jonathan Cameron
2025-03-13 13:03 ` [patch V2 03/10] soc: ti: ti_sci_inta_msi: Switch MSI descriptor locking to guard() Thomas Gleixner
2025-03-13 13:03 ` [patch V2 04/10] NTB/msi: Switch MSI descriptor locking to lock guard() Thomas Gleixner
2025-03-13 13:03 ` [patch V2 05/10] PCI/MSI: Switch to MSI descriptor locking to guard() Thomas Gleixner
2025-03-13 15:50   ` Jonathan Cameron
2025-03-13 17:55     ` Thomas Gleixner
2025-03-14  9:42       ` Peter Zijlstra
2025-03-13 13:03 ` [patch V2 06/10] PCI: hv: Switch " Thomas Gleixner
2025-03-13 13:03 ` [patch V2 07/10] PCI/MSI: Provide a sane mechanism for TPH Thomas Gleixner
2025-03-13 13:03 ` [patch V2 08/10] PCI/TPH: Replace the broken MSI-X control word update Thomas Gleixner
2025-03-13 13:03 ` [patch V2 09/10] scsi: ufs: qcom: Remove the MSI descriptor abuse Thomas Gleixner
2025-03-28 10:00   ` Dan Carpenter
2025-03-28 14:05     ` Thomas Gleixner
2025-03-13 13:03 ` [patch V2 10/10] genirq/msi: Rename msi_[un]lock_descs() Thomas Gleixner

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).