All of lore.kernel.org
 help / color / mirror / Atom feed
From: Prarit Bhargava <prarit@sgi.com>
To: linux-ia64@vger.kernel.org
Subject: [PATCH 1/6]: SN Hotplug Driver - SN IRQ fixes
Date: Mon, 02 May 2005 12:46:54 +0000	[thread overview]
Message-ID: <4276213E.4060201@sgi.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 0 bytes --]



[-- Attachment #2: sn_fixup22.patch-1 --]
[-- Type: text/plain, Size: 15808 bytes --]

This patch  fixes the SN IRQ code such that cpu affinity and
Hotplug can modify IRQ values.  The sn_irq_info structures are now locked
using a RCU lock mechanism to avoid lock contention in the lost interrupt
WAR code.

Signed-off-by: Prarit Bhargava <prarit@sgi.com>

 arch/ia64/sn/include/pci/pcidev.h |    3 
 arch/ia64/sn/kernel/io_init.c     |   37 +++--
 arch/ia64/sn/kernel/irq.c         |  246 ++++++++++++++++++++------------------
 include/asm-ia64/sn/intr.h        |    6 
 4 files changed, 158 insertions(+), 134 deletions(-)

diff -urNp linux-2.5-pristine/arch/ia64/sn/include/pci/pcidev.h sn_fixup/arch/ia64/sn/include/pci/pcidev.h
--- linux-2.5-pristine/arch/ia64/sn/include/pci/pcidev.h	2005-04-06 16:43:32.000000000 -0400
+++ sn_fixup/arch/ia64/sn/include/pci/pcidev.h	2027-04-28 13:06:34.000000000 -0400
@@ -10,8 +10,6 @@
 
 #include <linux/pci.h>
 
-extern struct sn_irq_info **sn_irq;
-
 #define SN_PCIDEV_INFO(pci_dev) \
         ((struct pcidev_info *)(pci_dev)->sysdata)
 
@@ -51,4 +49,5 @@ struct pcidev_info {
 extern void sn_irq_fixup(struct pci_dev *pci_dev,
 			 struct sn_irq_info *sn_irq_info);
 
+extern void sn_irq_lh_init(void);
 #endif				/* _ASM_IA64_SN_PCI_PCIDEV_H */
diff -urNp linux-2.5-pristine/arch/ia64/sn/kernel/io_init.c sn_fixup/arch/ia64/sn/kernel/io_init.c
--- linux-2.5-pristine/arch/ia64/sn/kernel/io_init.c	2005-04-06 16:43:32.000000000 -0400
+++ sn_fixup/arch/ia64/sn/kernel/io_init.c	2027-04-28 13:06:34.000000000 -0400
@@ -20,7 +20,6 @@
 #include <asm/sn/io.h>
 #include <asm/sn/simulator.h>
 
-char master_baseio_wid;
 nasid_t master_nasid = INVALID_NASID;	/* Partition Master */
 
 struct slab_info {
@@ -197,10 +196,12 @@ static void sn_pci_fixup_slot(struct pci
 {
 	int idx;
 	int segment = 0;
-	uint64_t size;
-	struct sn_irq_info *sn_irq_info;
-	struct pci_dev *host_pci_dev;
 	int status = 0;
+	struct pci_bus *host_pci_bus;
+	struct pci_dev *host_pci_dev;
+	struct sn_irq_info *sn_irq_info;
+	unsigned long size;
+	unsigned int bus_no, devfn;
 
 	dev->sysdata = kmalloc(sizeof(struct pcidev_info), GFP_KERNEL);
 	if (SN_PCIDEV_INFO(dev) <= 0)
@@ -218,7 +219,7 @@ static void sn_pci_fixup_slot(struct pci
 				     (u64) __pa(SN_PCIDEV_INFO(dev)),
 				     (u64) __pa(sn_irq_info));
 	if (status)
-		BUG();		/* Cannot get platform pci device information information */
+		BUG(); /* Cannot get platform pci device information */
 
 	/* Copy over PIO Mapped Addresses */
 	for (idx = 0; idx <= PCI_ROM_RESOURCE; idx++) {
@@ -240,13 +241,18 @@ static void sn_pci_fixup_slot(struct pci
 			dev->resource[idx].parent = &iomem_resource;
 	}
 
-	/* set up host bus linkages */
-	host_pci_dev =
-	    pci_find_slot(SN_PCIDEV_INFO(dev)->pdi_slot_host_handle >> 32,
-			  SN_PCIDEV_INFO(dev)->
-			  pdi_slot_host_handle & 0xffffffff);
+	/* Using the PROMs values for the PCI host bus, get the Linux
+	 * PCI host_pci_dev struct and set up host bus linkages
+	 */
+
+	bus_no = SN_PCIDEV_INFO(dev)->pdi_slot_host_handle >> 32;
+	devfn = SN_PCIDEV_INFO(dev)->pdi_slot_host_handle & 0xffffffff;
+	host_pci_bus = pci_find_bus(pci_domain_nr(dev->bus), bus_no);
+	host_pci_dev = pci_get_slot(host_pci_bus, devfn);
+
+	SN_PCIDEV_INFO(dev)->host_pci_dev = host_pci_dev;
 	SN_PCIDEV_INFO(dev)->pdi_host_pcidev_info =
-	    SN_PCIDEV_INFO(host_pci_dev);
+						SN_PCIDEV_INFO(host_pci_dev);
 	SN_PCIDEV_INFO(dev)->pdi_linux_pcidev = dev;
 	SN_PCIDEV_INFO(dev)->pdi_pcibus_info = SN_PCIBUS_BUSSOFT(dev->bus);
 
@@ -255,6 +261,9 @@ static void sn_pci_fixup_slot(struct pci
 		SN_PCIDEV_INFO(dev)->pdi_sn_irq_info = sn_irq_info;
 		dev->irq = SN_PCIDEV_INFO(dev)->pdi_sn_irq_info->irq_irq;
 		sn_irq_fixup(dev, sn_irq_info);
+	} else {
+		SN_PCIDEV_INFO(dev)->pdi_sn_irq_info = NULL;
+		kfree(sn_irq_info);
 	}
 }
 
@@ -343,11 +352,7 @@ static int __init sn_pci_init(void)
 	 */
 	ia64_max_iommu_merge_mask = ~PAGE_MASK;
 	sn_fixup_ionodes();
-	sn_irq = kmalloc(sizeof(struct sn_irq_info *) * NR_IRQS, GFP_KERNEL);
-	if (sn_irq <= 0)
-		BUG();		/* Canno afford to run out of memory. */
-	memset(sn_irq, 0, sizeof(struct sn_irq_info *) * NR_IRQS);
-
+	sn_irq_lh_init();
 	sn_init_cpei_timer();
 
 #ifdef CONFIG_PROC_FS
diff -urNp linux-2.5-pristine/arch/ia64/sn/kernel/irq.c sn_fixup/arch/ia64/sn/kernel/irq.c
--- linux-2.5-pristine/arch/ia64/sn/kernel/irq.c	2005-04-06 16:43:32.000000000 -0400
+++ sn_fixup/arch/ia64/sn/kernel/irq.c	2027-04-28 13:06:34.000000000 -0400
@@ -9,6 +9,7 @@
  */
 
 #include <linux/irq.h>
+#include <linux/spinlock.h>
 #include <asm/sn/intr.h>
 #include <asm/sn/addrs.h>
 #include <asm/sn/arch.h>
@@ -25,7 +26,8 @@ static void unregister_intr_pda(struct s
 
 extern int sn_force_interrupt_flag;
 extern int sn_ioif_inited;
-struct sn_irq_info **sn_irq;
+static struct list_head **sn_irq_lh;
+static spinlock_t sn_irq_info_lock = SPIN_LOCK_UNLOCKED; /* non-IRQ lock */
 
 static inline uint64_t sn_intr_alloc(nasid_t local_nasid, int local_widget,
 				     u64 sn_irq_info,
@@ -112,7 +114,7 @@ static void sn_end_irq(unsigned int irq)
 		nasid = get_nasid();
 		event_occurred = HUB_L((uint64_t *) GLOBAL_MMR_ADDR
 				       (nasid, SH_EVENT_OCCURRED));
-		/* If the UART bit is set here, we may have received an 
+		/* If the UART bit is set here, we may have received an
 		 * interrupt from the UART that the driver missed.  To
 		 * make sure, we IPI ourselves to force us to look again.
 		 */
@@ -126,82 +128,84 @@ static void sn_end_irq(unsigned int irq)
 		force_interrupt(irq);
 }
 
+static void sn_irq_info_free(struct rcu_head *head);
+
 static void sn_set_affinity_irq(unsigned int irq, cpumask_t mask)
 {
-	struct sn_irq_info *sn_irq_info = sn_irq[irq];
-	struct sn_irq_info *tmp_sn_irq_info;
+	struct sn_irq_info *sn_irq_info, *sn_irq_info_safe;
 	int cpuid, cpuphys;
-	nasid_t t_nasid;	/* nasid to target */
-	int t_slice;		/* slice to target */
-
-	/* allocate a temp sn_irq_info struct to get new target info */
-	tmp_sn_irq_info = kmalloc(sizeof(*tmp_sn_irq_info), GFP_KERNEL);
-	if (!tmp_sn_irq_info)
-		return;
 
 	cpuid = first_cpu(mask);
 	cpuphys = cpu_physical_id(cpuid);
-	t_nasid = cpuid_to_nasid(cpuid);
-	t_slice = cpuid_to_slice(cpuid);
 
-	while (sn_irq_info) {
-		int status;
-		int local_widget;
-		uint64_t bridge = (uint64_t) sn_irq_info->irq_bridge;
-		nasid_t local_nasid = NASID_GET(bridge);
+	list_for_each_entry_safe(sn_irq_info, sn_irq_info_safe,
+				 sn_irq_lh[irq], list) {
+		uint64_t bridge;
+		int local_widget, status;
+		nasid_t local_nasid;
+		struct sn_irq_info *new_irq_info;
+
+		new_irq_info = kmalloc(sizeof(struct sn_irq_info), GFP_ATOMIC);
+		if (new_irq_info == NULL)
+			break;
+		memcpy(new_irq_info, sn_irq_info, sizeof(struct sn_irq_info));
+
+		bridge = (uint64_t) new_irq_info->irq_bridge;
+		if (!bridge) {
+			kfree(new_irq_info);
+			break; /* irq is not a device interrupt */
+		}
 
-		if (!bridge)
-			break;	/* irq is not a device interrupt */
+		local_nasid = NASID_GET(bridge);
 
 		if (local_nasid & 1)
 			local_widget = TIO_SWIN_WIDGETNUM(bridge);
 		else
 			local_widget = SWIN_WIDGETNUM(bridge);
 
-		/* Free the old PROM sn_irq_info structure */
-		sn_intr_free(local_nasid, local_widget, sn_irq_info);
+		/* Free the old PROM new_irq_info structure */
+		sn_intr_free(local_nasid, local_widget, new_irq_info);
+		/* Update kernels new_irq_info with new target info */
+		unregister_intr_pda(new_irq_info);
 
-		/* allocate a new PROM sn_irq_info struct */
+		/* allocate a new PROM new_irq_info struct */
 		status = sn_intr_alloc(local_nasid, local_widget,
-				       __pa(tmp_sn_irq_info), irq, t_nasid,
-				       t_slice);
+				       __pa(new_irq_info), irq,
+				       cpuid_to_nasid(cpuid),
+				       cpuid_to_slice(cpuid));
+
+		/* SAL call failed */
+		if (status) {
+			kfree(new_irq_info);
+			break;
+		}
 
-		if (status == 0) {
-			/* Update kernels sn_irq_info with new target info */
-			unregister_intr_pda(sn_irq_info);
-			sn_irq_info->irq_cpuid = cpuid;
-			sn_irq_info->irq_nasid = t_nasid;
-			sn_irq_info->irq_slice = t_slice;
-			sn_irq_info->irq_xtalkaddr =
-			    tmp_sn_irq_info->irq_xtalkaddr;
-			sn_irq_info->irq_cookie = tmp_sn_irq_info->irq_cookie;
-			register_intr_pda(sn_irq_info);
+		new_irq_info->irq_cpuid = cpuid;
+		register_intr_pda(new_irq_info);
 
-			if (IS_PCI_BRIDGE_ASIC(sn_irq_info->irq_bridge_type)) {
-				pcibr_change_devices_irq(sn_irq_info);
-			}
+		if (IS_PCI_BRIDGE_ASIC(new_irq_info->irq_bridge_type))
+			pcibr_change_devices_irq(new_irq_info);
 
-			sn_irq_info = sn_irq_info->irq_next;
+		spin_lock(&sn_irq_info_lock);
+		list_replace_rcu(&sn_irq_info->list, &new_irq_info->list);
+		spin_unlock(&sn_irq_info_lock);
+		call_rcu(&sn_irq_info->rcu, sn_irq_info_free);
 
 #ifdef CONFIG_SMP
-			set_irq_affinity_info((irq & 0xff), cpuphys, 0);
+		set_irq_affinity_info((irq & 0xff), cpuphys, 0);
 #endif
-		} else {
-			break;	/* snp_affinity failed the intr_alloc */
-		}
 	}
-	kfree(tmp_sn_irq_info);
 }
 
 struct hw_interrupt_type irq_type_sn = {
-	"SN hub",
-	sn_startup_irq,
-	sn_shutdown_irq,
-	sn_enable_irq,
-	sn_disable_irq,
-	sn_ack_irq,
-	sn_end_irq,
-	sn_set_affinity_irq
+	.typename	= "SN hub",
+	.startup	= sn_startup_irq,
+	.shutdown	= sn_shutdown_irq,
+	.enable		= sn_enable_irq,
+	.disable	= sn_disable_irq,
+	.ack		= sn_ack_irq,
+	.end		= sn_end_irq,
+	.set_affinity	= sn_set_affinity_irq
 };
 
 unsigned int sn_local_vector_to_irq(u8 vector)
@@ -242,19 +246,18 @@ static void unregister_intr_pda(struct s
 	struct sn_irq_info *tmp_irq_info;
 	int i, foundmatch;
 
+	rcu_read_lock();
 	if (pdacpu(cpu)->sn_last_irq == irq) {
 		foundmatch = 0;
-		for (i = pdacpu(cpu)->sn_last_irq - 1; i; i--) {
-			tmp_irq_info = sn_irq[i];
-			while (tmp_irq_info) {
+		for (i = pdacpu(cpu)->sn_last_irq - 1;
+		     i && !foundmatch; i--) {
+			list_for_each_entry_rcu(tmp_irq_info,
+						sn_irq_lh[i],
+						list) {
 				if (tmp_irq_info->irq_cpuid == cpu) {
-					foundmatch++;
+					foundmatch = 1;
 					break;
 				}
-				tmp_irq_info = tmp_irq_info->irq_next;
-			}
-			if (foundmatch) {
-				break;
 			}
 		}
 		pdacpu(cpu)->sn_last_irq = i;
@@ -262,60 +265,27 @@ static void unregister_intr_pda(struct s
 
 	if (pdacpu(cpu)->sn_first_irq == irq) {
 		foundmatch = 0;
-		for (i = pdacpu(cpu)->sn_first_irq + 1; i < NR_IRQS; i++) {
-			tmp_irq_info = sn_irq[i];
-			while (tmp_irq_info) {
+		for (i = pdacpu(cpu)->sn_first_irq + 1;
+		     i < NR_IRQS && !foundmatch; i++) {
+			list_for_each_entry_rcu(tmp_irq_info,
+						sn_irq_lh[i],
+						list) {
 				if (tmp_irq_info->irq_cpuid == cpu) {
-					foundmatch++;
+					foundmatch = 1;
 					break;
 				}
-				tmp_irq_info = tmp_irq_info->irq_next;
-			}
-			if (foundmatch) {
-				break;
 			}
 		}
 		pdacpu(cpu)->sn_first_irq = ((i == NR_IRQS) ? 0 : i);
 	}
+	rcu_read_unlock();
 }
 
-struct sn_irq_info *sn_irq_alloc(nasid_t local_nasid, int local_widget, int irq,
-				 nasid_t nasid, int slice)
+static void sn_irq_info_free(struct rcu_head *head)
 {
 	struct sn_irq_info *sn_irq_info;
-	int status;
-
-	sn_irq_info = kmalloc(sizeof(*sn_irq_info), GFP_KERNEL);
-	if (sn_irq_info == NULL)
-		return NULL;
-
-	memset(sn_irq_info, 0x0, sizeof(*sn_irq_info));
-
-	status =
-	    sn_intr_alloc(local_nasid, local_widget, __pa(sn_irq_info), irq,
-			  nasid, slice);
-
-	if (status) {
-		kfree(sn_irq_info);
-		return NULL;
-	} else {
-		return sn_irq_info;
-	}
-}
-
-void sn_irq_free(struct sn_irq_info *sn_irq_info)
-{
-	uint64_t bridge = (uint64_t) sn_irq_info->irq_bridge;
-	nasid_t local_nasid = NASID_GET(bridge);
-	int local_widget;
-
-	if (local_nasid & 1)	/* tio check */
-		local_widget = TIO_SWIN_WIDGETNUM(bridge);
-	else
-		local_widget = SWIN_WIDGETNUM(bridge);
-
-	sn_intr_free(local_nasid, local_widget, sn_irq_info);
 
+	sn_irq_info = container_of(head, struct sn_irq_info, rcu);
 	kfree(sn_irq_info);
 }
 
@@ -325,30 +295,54 @@ void sn_irq_fixup(struct pci_dev *pci_de
 	int slice = sn_irq_info->irq_slice;
 	int cpu = nasid_slice_to_cpuid(nasid, slice);
 
+	pci_dev_get(pci_dev);
+
 	sn_irq_info->irq_cpuid = cpu;
 	sn_irq_info->irq_pciioinfo = SN_PCIDEV_INFO(pci_dev);
 
 	/* link it into the sn_irq[irq] list */
-	sn_irq_info->irq_next = sn_irq[sn_irq_info->irq_irq];
-	sn_irq[sn_irq_info->irq_irq] = sn_irq_info;
+	spin_lock(&sn_irq_info_lock);
+	list_add_rcu(&sn_irq_info->list, sn_irq_lh[sn_irq_info->irq_irq]);
+	spin_unlock(&sn_irq_info_lock);
 
 	(void)register_intr_pda(sn_irq_info);
 }
 
+void sn_irq_unfixup(struct pci_dev *pci_dev)
+{
+	struct sn_irq_info *sn_irq_info;
+
+	/* Only cleanup IRQ stuff if this device has a host bus context */
+	if (!SN_PCIDEV_BUSSOFT(pci_dev))
+		return;
+
+	sn_irq_info = SN_PCIDEV_INFO(pci_dev)->pdi_sn_irq_info;
+	if (!sn_irq_info || !sn_irq_info->irq_irq)
+		return;
+
+	unregister_intr_pda(sn_irq_info);
+	spin_lock(&sn_irq_info_lock);
+	list_del_rcu(&sn_irq_info->list);
+	spin_unlock(&sn_irq_info_lock);
+	call_rcu(&sn_irq_info->rcu, sn_irq_info_free);
+
+	pci_dev_put(pci_dev);
+}
+
 static void force_interrupt(int irq)
 {
 	struct sn_irq_info *sn_irq_info;
 
 	if (!sn_ioif_inited)
 		return;
-	sn_irq_info = sn_irq[irq];
-	while (sn_irq_info) {
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(sn_irq_info, sn_irq_lh[irq], list) {
 		if (IS_PCI_BRIDGE_ASIC(sn_irq_info->irq_bridge_type) &&
-		    (sn_irq_info->irq_bridge != NULL)) {
+		    (sn_irq_info->irq_bridge != NULL))
 			pcibr_force_interrupt(sn_irq_info);
-		}
-		sn_irq_info = sn_irq_info->irq_next;
 	}
+	rcu_read_unlock();
 }
 
 /*
@@ -413,19 +407,41 @@ static void sn_check_intr(int irq, struc
 
 void sn_lb_int_war_check(void)
 {
+	struct sn_irq_info *sn_irq_info;
 	int i;
 
 	if (!sn_ioif_inited || pda->sn_first_irq == 0)
 		return;
+
+	rcu_read_lock();
 	for (i = pda->sn_first_irq; i <= pda->sn_last_irq; i++) {
-		struct sn_irq_info *sn_irq_info = sn_irq[i];
-		while (sn_irq_info) {
-			/* Only call for PCI bridges that are fully initialized. */
+		list_for_each_entry_rcu(sn_irq_info, sn_irq_lh[i], list) {
+			/*
+			 * Only call for PCI bridges that are fully
+			 * initialized.
+			 */
 			if (IS_PCI_BRIDGE_ASIC(sn_irq_info->irq_bridge_type) &&
-			    (sn_irq_info->irq_bridge != NULL)) {
+			    (sn_irq_info->irq_bridge != NULL))
 				sn_check_intr(i, sn_irq_info);
-			}
-			sn_irq_info = sn_irq_info->irq_next;
 		}
 	}
+	rcu_read_unlock();
+}
+
+void sn_irq_lh_init(void)
+{
+	int i;
+
+	sn_irq_lh = kmalloc(sizeof(struct list_head *) * NR_IRQS, GFP_KERNEL);
+	if (!sn_irq_lh)
+		panic("SN PCI INIT: Failed to allocate memory for PCI init\n");
+
+	for (i = 0; i < NR_IRQS; i++) {
+		sn_irq_lh[i] = kmalloc(sizeof(struct list_head), GFP_KERNEL);
+		if (!sn_irq_lh[i])
+			panic("SN PCI INIT: Failed IRQ memory allocation\n");
+
+		INIT_LIST_HEAD(sn_irq_lh[i]);
+	}
+
 }
diff -urNp linux-2.5-pristine/include/asm-ia64/sn/intr.h sn_fixup/include/asm-ia64/sn/intr.h
--- linux-2.5-pristine/include/asm-ia64/sn/intr.h	2005-04-06 16:43:31.000000000 -0400
+++ sn_fixup/include/asm-ia64/sn/intr.h	2027-04-28 13:06:34.000000000 -0400
@@ -9,6 +9,8 @@
 #ifndef _ASM_IA64_SN_INTR_H
 #define _ASM_IA64_SN_INTR_H
 
+#include <linux/rcupdate.h>
+
 #define SGI_UART_VECTOR		(0xe9)
 #define SGI_PCIBR_ERROR		(0x33)
 
@@ -33,7 +35,7 @@
 
 // The SN PROM irq struct
 struct sn_irq_info {
-	struct sn_irq_info *irq_next;	/* sharing irq list	     */
+	struct sn_irq_info *irq_next;	/* deprecated DO NOT USE     */
 	short		irq_nasid;	/* Nasid IRQ is assigned to  */
 	int		irq_slice;	/* slice IRQ is assigned to  */
 	int		irq_cpuid;	/* kernel logical cpuid	     */
@@ -47,6 +49,8 @@ struct sn_irq_info {
 	int		irq_cookie;	/* unique cookie 	     */
 	int		irq_flags;	/* flags */
 	int		irq_share_cnt;	/* num devices sharing IRQ   */
+	struct list_head	list;	/* list of sn_irq_info structs */
+	struct rcu_head		rcu;	/* rcu callback list */
 };
 
 extern void sn_send_IPI_phys(int, long, int, int);

                 reply	other threads:[~2005-05-02 12:46 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4276213E.4060201@sgi.com \
    --to=prarit@sgi.com \
    --cc=linux-ia64@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.