Skip to contentSkip to conversation

take2: [PATCH] Vector sharing (Large I/O system support)

PATCH Kenji Kaneshige 3 participants 8 messages Jul 21, 2004 – Jul 26, 2004

lore
Share & tools
lore
#0 lore raw

This is the updated patch for vector sharing. Summary of changes:
- Changed the name of some data structures.
- Removed '__cacheline_aligned' from iosapic_intr_info

Thanks,
Kenji Kaneshige

Current ia64 linux cannot handle greater than 184 interrupt sources because of the lack of vectors. The following patch enables ia64 linux to handle greater than 184 interrupt sources by allowing the same vector number to be shared by multiple IOSAPIC's RTEs.

Even if you don't have a large I/O system, you can see the behavior of vector sharing by changing IOSAPIC_LAST_DEVICE_VECTOR to fewer value.

 arch/ia64/kernel/iosapic.c  |  230 ++++++++++++++++++++++++++++++++++----------
 arch/ia64/kernel/irq.c      |    5 
 arch/ia64/kernel/irq_ia64.c |   37 ++++++-
 include/asm-ia64/hw_irq.h   |    3 
 include/asm-ia64/iosapic.h  |    6 +
 include/asm-ia64/irq.h      |    2 
 6 files changed, 229 insertions(+), 54 deletions(-)
arch/ia64/kernel/iosapic.c +180 -50
diff -Naurp linux-2.6.8-rc1/arch/ia64/kernel/iosapic.c linux-2.6.8-rc1-changed/arch/ia64/kernel/iosapic.c
@@ -103,14 +103,22 @@ static spinlock_t iosapic_lock = SPIN_LO
 
 /* These tables map IA-64 vectors to the IOSAPIC pin that generates this vector. */
 
-static struct iosapic_intr_info {
+struct iosapic_rte {
+	struct list_head rte_list;	/* IOSAPIC RTEs which share the same vector */
 	char		*addr;		/* base address of IOSAPIC */
-	u32		low32;		/* current value of low word of Redirection table entry */
 	unsigned int	gsi_base;	/* first GSI assigned to this IOSAPIC */
-	char		rte_index;	/* IOSAPIC RTE index (-1 => not an IOSAPIC interrupt) */
+	char		rte_index;	/* IOSAPIC RTE index */
+};
+
+static struct iosapic_intr_info {
+	struct list_head rte_head;	/* List head of rte_list*/
+	struct iosapic_rte rte;		/* First entry of rte_list */
+	int		count;		/* # of entries on rte_list (0 => not an IOSAPIC interrupt) */
+	u32		low32;		/* current value of low word of Redirection table entry */
 	unsigned char	dmode	: 3;	/* delivery mode (see iosapic.h) */
 	unsigned char 	polarity: 1;	/* interrupt polarity (see iosapic.h) */
 	unsigned char	trigger	: 1;	/* trigger mode (see iosapic.h) */
+	unsigned char	type	: 1;	/* Vector type */
 } iosapic_intr_info[IA64_NUM_VECTORS];
 
 static struct iosapic {
@@ -144,10 +152,14 @@ static inline int
 _gsi_to_vector (unsigned int gsi)
 {
 	struct iosapic_intr_info *info;
+	struct iosapic_rte *rte;
 
-	for (info = iosapic_intr_info; info < iosapic_intr_info + IA64_NUM_VECTORS; ++info)
-		if (info->gsi_base + info->rte_index = gsi)
-			return info - iosapic_intr_info;
+	for (info = iosapic_intr_info; info < iosapic_intr_info + IA64_NUM_VECTORS; ++info) {
+		list_for_each_entry(rte, &info->rte_head, rte_list) {
+			if (rte->gsi_base + rte->rte_index = gsi)
+				return info - iosapic_intr_info;
+		}
+	}
 	return -1;
 }
 
@@ -171,22 +183,33 @@ gsi_to_irq (unsigned int gsi)
 	return _gsi_to_vector(gsi);
 }
 
+static inline struct iosapic_rte *
+gsi_vector_to_rte (unsigned int gsi, unsigned int vector)
+{
+	struct iosapic_rte *rte;
+
+	list_for_each_entry(rte, &iosapic_intr_info[vector].rte_head, rte_list) {
+		if (rte->gsi_base + rte->rte_index = gsi)
+			return rte;
+	}
+	return NULL;
+}
+
 static void
-set_rte (unsigned int vector, unsigned int dest, int mask)
+set_rte (unsigned int gsi, unsigned int vector, unsigned int dest, int mask)
 {
 	unsigned long pol, trigger, dmode, flags;
 	u32 low32, high32;
 	char *addr;
 	int rte_index;
 	char redir;
+	struct iosapic_rte *rte;
 
 	DBG(KERN_DEBUG"IOSAPIC: routing vector %d to 0x%x\n", vector, dest);
 
-	rte_index = iosapic_intr_info[vector].rte_index;
-	if (rte_index < 0)
+	if (!iosapic_intr_info[vector].count)
 		return;		/* not an IOSAPIC interrupt */
 
-	addr    = iosapic_intr_info[vector].addr;
 	pol     = iosapic_intr_info[vector].polarity;
 	trigger = iosapic_intr_info[vector].trigger;
 	dmode   = iosapic_intr_info[vector].dmode;
@@ -217,6 +240,17 @@ set_rte (unsigned int vector, unsigned i
 
 	spin_lock_irqsave(&iosapic_lock, flags);
 	{
+		if (!(iosapic_intr_info[vector].low32 & IOSAPIC_MASK))
+			low32 &= ~IOSAPIC_MASK;
+
+		rte = gsi_vector_to_rte(gsi, vector);
+		if (!rte) {
+			spin_unlock_irqrestore(&iosapic_lock, flags);
+			return;
+		}
+		rte_index = rte->rte_index;
+		addr    = rte->addr;
+
 		iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), high32);
 		iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
 		iosapic_intr_info[vector].low32 = low32;
@@ -238,18 +272,20 @@ mask_irq (unsigned int irq)
 	u32 low32;
 	int rte_index;
 	ia64_vector vec = irq_to_vector(irq);
-
-	addr = iosapic_intr_info[vec].addr;
-	rte_index = iosapic_intr_info[vec].rte_index;
-
-	if (rte_index < 0)
+	struct iosapic_rte *rte;
+  
+	if (!iosapic_intr_info[vec].count)
 		return;			/* not an IOSAPIC interrupt! */
 
 	spin_lock_irqsave(&iosapic_lock, flags);
 	{
 		/* set only the mask bit */
 		low32 = iosapic_intr_info[vec].low32 |= IOSAPIC_MASK;
-		iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
+		list_for_each_entry(rte, &iosapic_intr_info[vec].rte_head, rte_list) {
+			addr = rte->addr;
+			rte_index = rte->rte_index;
+			iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
+		}
 	}
 	spin_unlock_irqrestore(&iosapic_lock, flags);
 }
@@ -262,16 +298,19 @@ unmask_irq (unsigned int irq)
 	u32 low32;
 	int rte_index;
 	ia64_vector vec = irq_to_vector(irq);
+	struct iosapic_rte *rte;
 
-	addr = iosapic_intr_info[vec].addr;
-	rte_index = iosapic_intr_info[vec].rte_index;
-	if (rte_index < 0)
+	if (!iosapic_intr_info[vec].count)
 		return;			/* not an IOSAPIC interrupt! */
 
 	spin_lock_irqsave(&iosapic_lock, flags);
 	{
 		low32 = iosapic_intr_info[vec].low32 &= ~IOSAPIC_MASK;
-		iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
+		list_for_each_entry(rte, &iosapic_intr_info[vec].rte_head, rte_list) {
+			addr = rte->addr;
+			rte_index = rte->rte_index;
+			iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
+		}
 	}
 	spin_unlock_irqrestore(&iosapic_lock, flags);
 }
@@ -287,6 +326,7 @@ iosapic_set_affinity (unsigned int irq, 
 	char *addr;
 	int redir = (irq & IA64_IRQ_REDIRECTED) ? 1 : 0;
 	ia64_vector vec;
+	struct iosapic_rte *rte;
 
 	irq &= (~IA64_IRQ_REDIRECTED);
 	vec = irq_to_vector(irq);
@@ -296,10 +336,7 @@ iosapic_set_affinity (unsigned int irq, 
 
 	dest = cpu_physical_id(first_cpu(mask));
 
-	rte_index = iosapic_intr_info[vec].rte_index;
-	addr = iosapic_intr_info[vec].addr;
-
-	if (rte_index < 0)
+	if (!iosapic_intr_info[vec].count)
 		return;			/* not an IOSAPIC interrupt */
 
 	set_irq_affinity_info(irq, dest, redir);
@@ -312,15 +349,19 @@ iosapic_set_affinity (unsigned int irq, 
 		low32 = iosapic_intr_info[vec].low32 & ~(7 << IOSAPIC_DELIVERY_SHIFT);
 
 		if (redir)
-		        /* change delivery mode to lowest priority */
+			/* change delivery mode to lowest priority */
 			low32 |= (IOSAPIC_LOWEST_PRIORITY << IOSAPIC_DELIVERY_SHIFT);
 		else
-		        /* change delivery mode to fixed */
+			/* change delivery mode to fixed */
 			low32 |= (IOSAPIC_FIXED << IOSAPIC_DELIVERY_SHIFT);
 
 		iosapic_intr_info[vec].low32 = low32;
-		iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), high32);
-		iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
+		list_for_each_entry(rte, &iosapic_intr_info[vec].rte_head, rte_list) {
+			rte_index = rte->rte_index;
+			addr = rte->addr;
+			iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), high32);
+			iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
+		}
 	}
 	spin_unlock_irqrestore(&iosapic_lock, flags);
 #endif
@@ -341,9 +382,11 @@ static void
 iosapic_end_level_irq (unsigned int irq)
 {
 	ia64_vector vec = irq_to_vector(irq);
+	struct iosapic_rte *rte;
 
 	move_irq(irq);
-	iosapic_eoi(iosapic_intr_info[vec].addr, vec);
+	list_for_each_entry(rte, &iosapic_intr_info[vec].rte_head, rte_list)
+		iosapic_eoi(rte->addr, vec);
 }
 
 #define iosapic_shutdown_level_irq	mask_irq
@@ -424,6 +467,30 @@ iosapic_version (char *addr)
 }
 
 /*
+ * Find a sharable vector.
+ */
+static int
+iosapic_find_sharable_vector (unsigned long trigger, unsigned long polarity)
+{
+	int i;
+	static int next_vector = IA64_FIRST_DEVICE_VECTOR;
+
+	for (i = 0; i < IA64_NUM_DEVICE_VECTORS; i++) {
+		if (next_vector > IA64_LAST_DEVICE_VECTOR)
+			next_vector = IA64_FIRST_DEVICE_VECTOR;
+
+		if (iosapic_intr_info[next_vector].type = IOSAPIC_VECTOR_SHARABLE &&
+		    iosapic_intr_info[next_vector].trigger = trigger &&
+		    iosapic_intr_info[next_vector].polarity = polarity)
+			return next_vector++;
+
+		next_vector++;
+	}
+
+	return -1;
+}
+
+/*
  * if the given vector is already owned by other,
  *  assign a new vector for the other and make the vector available
  */
@@ -432,22 +499,22 @@ iosapic_reassign_vector (int vector)
 {
 	int new_vector;
 
-	if (iosapic_intr_info[vector].rte_index >= 0 || iosapic_intr_info[vector].addr
-	    || iosapic_intr_info[vector].gsi_base || iosapic_intr_info[vector].dmode
-	    || iosapic_intr_info[vector].polarity || iosapic_intr_info[vector].trigger)
-	{
+	if (iosapic_intr_info[vector].count > 0) {
 		new_vector = assign_irq_vector(AUTO_ASSIGN);
 		printk(KERN_INFO "Reassigning vector %d to %d\n", vector, new_vector);
 		memcpy(&iosapic_intr_info[new_vector], &iosapic_intr_info[vector],
 		       sizeof(struct iosapic_intr_info));
+		INIT_LIST_HEAD(&iosapic_intr_info[new_vector].rte_head);
+		list_add(&iosapic_intr_info[new_vector].rte.rte_list,
+			 &iosapic_intr_info[new_vector].rte_head);
 		memset(&iosapic_intr_info[vector], 0, sizeof(struct iosapic_intr_info));
-		iosapic_intr_info[vector].rte_index = -1;
+		INIT_LIST_HEAD(&iosapic_intr_info[vector].rte_head);
 	}
 }
 
 static void
 register_intr (unsigned int gsi, int vector, unsigned char delivery,
-	       unsigned long polarity, unsigned long trigger)
+	       unsigned long polarity, unsigned long trigger, unsigned long type)
 {
 	irq_desc_t *idesc;
 	struct hw_interrupt_type *irq_type;
@@ -455,6 +522,7 @@ register_intr (unsigned int gsi, int vec
 	int index;
 	unsigned long gsi_base;
 	char *iosapic_address;
+	struct iosapic_rte *rte;
 
 	index = find_iosapic(gsi);
 	if (index < 0) {
@@ -465,13 +533,39 @@ register_intr (unsigned int gsi, int vec
 	iosapic_address = iosapic_lists[index].addr;
 	gsi_base = iosapic_lists[index].gsi_base;
 
+	rte = gsi_vector_to_rte(gsi, vector);
+	if (!rte) {	/* Register a new interrupt */
+		if (!iosapic_intr_info[vector].count) {
+			rte = &iosapic_intr_info[vector].rte;
+			iosapic_intr_info[vector].low32 = IOSAPIC_MASK;
+		}
+		else if ((rte = kmalloc(sizeof(struct iosapic_rte), GFP_KERNEL)) = NULL) {
+			printk (KERN_WARNING "%s: cannot allocate memory\n", __FUNCTION__);
+			return;
+		}
+		list_add_tail(&rte->rte_list, &iosapic_intr_info[vector].rte_head);
+		iosapic_intr_info[vector].count++;
+	} else {	/* Override an existing interrupt */
+		if (iosapic_intr_info[vector].count > 1) {
+			if (iosapic_intr_info[vector].trigger != trigger ||
+			    iosapic_intr_info[vector].polarity != polarity ||
+			    type = IOSAPIC_VECTOR_EXCLUSIVE)
+			{
+				printk(KERN_WARNING "%s: cannot override an interrupt\n",
+				       __FUNCTION__);
+				return;
+			}
+		}
+	}
+
 	rte_index = gsi - gsi_base;
-	iosapic_intr_info[vector].rte_index = rte_index;
+	rte->rte_index = rte_index;
+	rte->addr     = iosapic_address;
+	rte->gsi_base = gsi_base;
 	iosapic_intr_info[vector].polarity = polarity;
 	iosapic_intr_info[vector].dmode    = delivery;
-	iosapic_intr_info[vector].addr     = iosapic_address;
-	iosapic_intr_info[vector].gsi_base = gsi_base;
 	iosapic_intr_info[vector].trigger  = trigger;
+	iosapic_intr_info[vector].type     = type;
 
 	if (trigger = IOSAPIC_EDGE)
 		irq_type = &irq_type_iosapic_edge;
@@ -488,10 +582,23 @@ register_intr (unsigned int gsi, int vec
 }
 
 static unsigned int
-get_target_cpu (void)
+get_target_cpu (int vector)
 {
 #ifdef CONFIG_SMP
 	static int cpu = -1;
+	int irq;
+	cpumask_t cpumask, cpumask_all = CPU_MASK_ALL;
+
+	/*
+	 * if this vector already has its destination CPU, use the
+	 * same destination CPU.
+	 */
+	for (irq = 0; irq < NR_IRQS; ++irq)
+		if (irq_to_vector(irq) = vector) {
+			cpumask = get_irq_affinity_info(irq);
+			if (!cpus_equal(cpumask, cpumask_all))
+				return cpu_physical_id(first_cpu(cpumask));
+		}
 
 	/*
 	 * If the platform supports redirection via XTP, let it
@@ -549,19 +656,40 @@ iosapic_register_intr (unsigned int gsi,
 			return vector;
 		}
 
-		vector = assign_irq_vector(AUTO_ASSIGN);
-		dest = get_target_cpu();
-		register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY,
-			polarity, trigger);
+		vector = assign_irq_vector_nopanic(AUTO_ASSIGN);
+		if (vector < 0)
+			vector = iosapic_find_sharable_vector(trigger, polarity);
+		if (vector < 0)
+			panic("%s: out of interrupt vectors!\n", __FUNCTION__);
 	}
 	spin_unlock_irqrestore(&iosapic_lock, flags);
 
+	spin_lock_irqsave(&irq_descp(vector)->lock, flags);
+	spin_lock(&iosapic_lock);
+	{
+		int tmp_vector = gsi_to_vector(gsi);
+		if (tmp_vector > 0) {
+			if (!iosapic_intr_info[vector].count)
+				free_irq_vector(vector);
+			spin_unlock(&iosapic_lock);
+			spin_unlock_irqrestore(&irq_descp(vector)->lock, flags);
+			return tmp_vector;
+		}
+		dest = get_target_cpu(vector);
+		register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY,
+			polarity, trigger, trigger = IOSAPIC_EDGE ?
+			IOSAPIC_VECTOR_EXCLUSIVE : IOSAPIC_VECTOR_SHARABLE);
+	}
+	spin_unlock(&iosapic_lock);
+
 	printk(KERN_INFO "GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d\n",
 	       gsi, (trigger = IOSAPIC_EDGE ? "edge" : "level"),
 	       (polarity = IOSAPIC_POL_HIGH ? "high" : "low"),
 	       cpu_logical_id(dest), dest, vector);
 
-	set_rte(vector, dest, 1);
+	set_rte(gsi, vector, dest, 1);
+
+	spin_unlock_irqrestore(&irq_descp(vector)->lock, flags);
 	return vector;
 }
 
@@ -603,7 +731,7 @@ iosapic_register_platform_intr (u32 int_
 		return -1;
 	}
 
-	register_intr(gsi, vector, delivery, polarity, trigger);
+	register_intr(gsi, vector, delivery, polarity, trigger, IOSAPIC_VECTOR_EXCLUSIVE);
 
 	printk(KERN_INFO "PLATFORM int %s (0x%x): GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d\n",
 	       int_type < ARRAY_SIZE(name) ? name[int_type] : "unknown",
@@ -611,7 +739,7 @@ iosapic_register_platform_intr (u32 int_
 	       (polarity = IOSAPIC_POL_HIGH ? "high" : "low"),
 	       cpu_logical_id(dest), dest, vector);
 
-	set_rte(vector, dest, mask);
+	set_rte(gsi, vector, dest, mask);
 	return vector;
 }
 
@@ -630,14 +758,14 @@ iosapic_override_isa_irq (unsigned int i
 
 	vector = isa_irq_to_vector(isa_irq);
 
-	register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY, polarity, trigger);
+	register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY, polarity, trigger, IOSAPIC_VECTOR_EXCLUSIVE);
 
 	DBG("ISA: IRQ %u -> GSI %u (%s,%s) -> CPU %d (0x%04x) vector %d\n",
 	    isa_irq, gsi, trigger = IOSAPIC_EDGE ? "edge" : "level",
 	    polarity = IOSAPIC_POL_HIGH ? "high" : "low",
 	    cpu_logical_id(dest), dest, vector);
 
-	set_rte(vector, dest, 1);
+	set_rte(gsi, vector, dest, 1);
 }
 
 void __init
@@ -645,8 +773,10 @@ iosapic_system_init (int system_pcat_com
 {
 	int vector;
 
-	for (vector = 0; vector < IA64_NUM_VECTORS; ++vector)
-		iosapic_intr_info[vector].rte_index = -1;	/* mark as unused */
+	for (vector = 0; vector < IA64_NUM_VECTORS; ++vector) {
+		iosapic_intr_info[vector].count = 0;	/* mark as unused */
+		INIT_LIST_HEAD(&iosapic_intr_info[vector].rte_head);
+	}
 
 	pcat_compat = system_pcat_compat;
 	if (pcat_compat) {
arch/ia64/kernel/irq.c +5 -0
diff -Naurp linux-2.6.8-rc1/arch/ia64/kernel/irq.c linux-2.6.8-rc1-changed/arch/ia64/kernel/irq.c
@@ -949,6 +949,11 @@ void set_irq_affinity_info (unsigned int
 	}
 }
 
+cpumask_t get_irq_affinity_info (unsigned int irq)
+{
+	return irq_affinity[irq];
+}
+
 static int irq_affinity_read_proc (char *page, char **start, off_t off,
 			int count, int *eof, void *data)
 {
arch/ia64/kernel/irq_ia64.c +33 -4
diff -Naurp linux-2.6.8-rc1/arch/ia64/kernel/irq_ia64.c linux-2.6.8-rc1-changed/arch/ia64/kernel/irq_ia64.c
@@ -74,15 +74,44 @@ irq_exit (void)
 	preempt_enable_no_resched();
 }
 
+static unsigned long ia64_vector_mask[BITS_TO_LONGS(IA64_NUM_DEVICE_VECTORS)];
+
+int
+assign_irq_vector_nopanic (int irq)
+{
+	int pos, vector;
+ again:
+	pos = find_first_zero_bit(ia64_vector_mask, IA64_NUM_DEVICE_VECTORS);
+	vector = IA64_FIRST_DEVICE_VECTOR + pos;
+	if (vector > IA64_LAST_DEVICE_VECTOR)
+		return -1;
+	if (test_and_set_bit(pos, ia64_vector_mask))
+		goto again;
+	return vector;
+}
+
 int
 assign_irq_vector (int irq)
 {
-	static int next_vector = IA64_FIRST_DEVICE_VECTOR;
+	int vector = assign_irq_vector_nopanic(irq);
 
-	if (next_vector > IA64_LAST_DEVICE_VECTOR)
-		/* XXX could look for sharable vectors instead of panic'ing... */
+	if (vector = -1)
 		panic("assign_irq_vector: out of interrupt vectors!");
-	return next_vector++;
+	return vector;
+}
+
+void
+free_irq_vector (int vector)
+{
+	int pos;
+
+	if (vector < IA64_FIRST_DEVICE_VECTOR || vector > IA64_LAST_DEVICE_VECTOR) {
+		printk(KERN_WARNING "%s: wrong device vector!\n", __FUNCTION__);
+		return;
+	}
+	pos = vector - IA64_FIRST_DEVICE_VECTOR;
+	if (!test_and_clear_bit(pos, ia64_vector_mask))
+		printk(KERN_WARNING "%s: double free!\n", __FUNCTION__);
 }
 
 extern unsigned int do_IRQ(unsigned long irq, struct pt_regs *regs);
include/asm-ia64/hw_irq.h +3 -0
diff -Naurp linux-2.6.8-rc1/include/asm-ia64/hw_irq.h linux-2.6.8-rc1-changed/include/asm-ia64/hw_irq.h
@@ -50,6 +50,7 @@ typedef u8 ia64_vector;
  */
 #define IA64_FIRST_DEVICE_VECTOR	0x30
 #define IA64_LAST_DEVICE_VECTOR		0xe7
+#define IA64_NUM_DEVICE_VECTORS		(IA64_LAST_DEVICE_VECTOR - IA64_FIRST_DEVICE_VECTOR + 1)
 
 #define IA64_MCA_RENDEZ_VECTOR		0xe8	/* MCA rendez interrupt */
 #define IA64_PERFMON_VECTOR		0xee	/* performanc monitor interrupt vector */
@@ -82,7 +83,9 @@ extern unsigned long ipi_base_addr;
 
 extern struct hw_interrupt_type irq_type_ia64_lsapic;	/* CPU-internal interrupt controller */
 
+extern int assign_irq_vector_nopanic (int irq);
 extern int assign_irq_vector (int irq);	/* allocate a free vector */
+extern void free_irq_vector (int vector);
 extern void ia64_send_ipi (int cpu, int vector, int delivery_mode, int redirect);
 extern void register_percpu_irq (ia64_vector vec, struct irqaction *action);
 
include/asm-ia64/iosapic.h +6 -0
diff -Naurp linux-2.6.8-rc1/include/asm-ia64/iosapic.h linux-2.6.8-rc1-changed/include/asm-ia64/iosapic.h
@@ -47,6 +47,12 @@
 #define	IOSAPIC_MASK_SHIFT		16
 #define	IOSAPIC_MASK			(1<<IOSAPIC_MASK_SHIFT)
 
+/*
+ * Vector type
+ */
+#define IOSAPIC_VECTOR_EXCLUSIVE	0
+#define IOSAPIC_VECTOR_SHARABLE		1
+
 #ifndef __ASSEMBLY__
 
 #ifdef CONFIG_IOSAPIC
include/asm-ia64/irq.h +2 -0
diff -Naurp linux-2.6.8-rc1/include/asm-ia64/irq.h linux-2.6.8-rc1-changed/include/asm-ia64/irq.h
@@ -31,6 +31,8 @@ extern void enable_irq (unsigned int);
 extern void set_irq_affinity_info (unsigned int irq, int dest, int redir);
 
 #ifdef CONFIG_SMP
+#include <linux/cpumask.h>
+extern cpumask_t get_irq_affinity_info (unsigned int irq);
 extern void move_irq(int irq);
 #else
 #define move_irq(irq)
#1 lore raw
Quoted 5 lines · Kenji Kaneshige

Current ia64 linux cannot handle greater than 184 interrupt sources because of the lack of vectors. The following patch enables ia64 linux to handle greater than 184 interrupt sources by allowing the same vector number to be shared by multiple IOSAPIC's RTEs.

Kenji,
Inside HP I've suggested linux use multiple vector domains (one CPU could belong in only one domain). Would that sufficiently solve this same problem or is someone hard coded to share a particular vector?

thanks,
grant

#2 lore raw
In reply to Kenji Kaneshige
Quoted 5 lines

Kenji,
Inside HP I've suggested linux use multiple vector domains (one CPU could belong in only one domain). Would that sufficiently solve this same problem or is someone hard coded to share a particular vector?

Hi Grant,

As far as I know, there are three methods to handle many interrupt sources:

(1) Share a single RTE with multiple level-triggered interrupts (e.g. multiple PCI devices share the same interrupt line). Of course, current linux can handle it. Whether to use this method depend on hardware design.

(2) Share a single vector with multiple RTEs. This is what my vector sharing patch is doing.

(3) Use multiple vector domains on the multi-node system. This method strongly depends on hardware design, and it will be implemented in the architecture specific code. I guess SGI machine is using this method.

I guess the method you mentioned is extension of method (3), which use multiple vector domains on a generic SMP machine (I'll call this "method (4)" below). Is it correct?

I think method (4) is interesting and it would be able to solve the same problem. I have discussed it a little with Bjorn Helgaas before. (please see http://www.gelato.unsw.edu.au/linux-ia64/0404/9363.html)

However, method (4) will not work if system has only a few CPUs (or it is an UP machine). So vector sharing is still needed. In addition, though I have not investigated it much, I think there would be a lot of issues need to be considered for method (4).
For example:

o How to separate CPUs and devices into multiple vector domains
o How to associate vector number with IRQ number
o How to prepare multiple 'irq_desc' arrays for each vector domains
o How to install interrupt handlers into each 'irq_desc'
o Need to consider the case some CPUs are hot-removed
o How to display the IRQ infomation through /proc filesystem

and so on...

Most of those need a much time and a lot of changes to kernel.

After all, I think the good way is to implement vector sharing first, and then consider the method (4) next. I beleave vector sharing and method (4) can work together.

By the way, do you have specific reasons to suggest method (4)? Performance issue?
And do you already have a patch for method (4)? If so, can I see it?

Thanks,
Kenji Kaneshige

#3 lore raw
In reply to Kenji Kaneshige

Kenji,
thank you. I'll try answer your questions below.

On Thu, Jul 22, 2004 at 03:16:17PM +0900, Kenji Kaneshige wrote:
...

Quoted 7 lines

(3) Use multiple vector domains on the multi-node system. This method strongly depends on hardware design, and it will be implemented in the architecture specific code. I guess SGI machine is using this method.

I guess the method you mentioned is extension of method (3), which use multiple vector domains on a generic SMP machine (I'll call this "method (4)" below). Is it correct?

Yes.

Quoted 6 lines

I think method (4) is interesting and it would be able to solve the same problem. I have discussed it a little with Bjorn Helgaas before. (please see http://www.gelato.unsw.edu.au/linux-ia64/0404/9363.html)

However, method (4) will not work if system has only a few CPUs (or it is an UP machine).

I'll argue that a system with 1 CPU and is consuming 256 vectors is just not going to work well. The patch you submitted just enables this to work, but poorly (your patch is fine, just the result it enables is not IMHO). I personally don't agree we need to support such a poor configuration. If david thinks we should, then I'm not going to argue.

Quoted 6 lines

So vector sharing is still needed. In addition,
though I have not investigated it much, I think there would be a lot of issues need to be considered for method (4).
For example:

o How to separate CPUs and devices into multiple vector domains

We assume one domain now and assign devices to CPU in round robin. No different for multiple vector domains.

Quoted 1 line

o How to associate vector number with IRQ number

ditto.

Quoted 1 line

o How to prepare multiple 'irq_desc' arrays for each vector domains

No sure what this means offhand...but assume it's just more code.

Quoted 1 line

o How to install interrupt handlers into each 'irq_desc'

Same as now (via request_irq())

Quoted 1 line

o Need to consider the case some CPUs are hot-removed

Yes - vector domain need to go away when last CPU is removed from it.

Quoted 1 line

o How to display the IRQ infomation through /proc filesystem

Same as now (ie global IRQ #)

Quoted 4 lines

and so on...

Most of those need a much time and a lot of changes to kernel.

That's probably true. But the advantage is simpler and shorter code path when handling interrupts on large configs. I would think this is more important than all the trouble the setup causes.

Quoted 4 lines

After all, I think the good way is to implement vector sharing first, and then consider the method (4) next. I beleave vector sharing and method (4) can work together.

I agree.

Quoted 2 lines

By the way, do you have specific reasons to suggest method (4)? Performance issue?

Yes.

Quoted 2 lines

And do you already have a patch for method (4)? If so, can I see it?

I don't. It will have to wait until OLS2005 (or some other conf that my management endorses). But I've played enough with IRQ code on parisc (both HPUX and parisc-linux) to understand the code path pretty well.

A few years ago, I shortened the interrupt code pathes in HPUX by removing a switch statement and one/two if () tests. netperf TCP_RR test improved ~20%. (One interrupt/packet at the time, IIRC).

Interrupt mitigation helps avoid this cost (as does NAPI) and I'm aware most workloads attempt to avoid interrupts when possible. But I still believe there are workloads that will generate lots of interrupts - eg 10GiGE - and are latency sensitive.

thanks,
grant

Quoted 2 lines

Thanks,
Kenji Kaneshige

#4 lore raw
In reply to Kenji Kaneshige
Quoted 4 lines
Quoted 1 line

o How to prepare multiple 'irq_desc' arrays for each vector domains

No sure what this means offhand...but assume it's just more code.

Sorry... I was a little confused.
What we need to prepare is vector spaces for each domain. So what we need to change is 'iosapic_intr_info' array, assign_irq_vector(), and so on..

Thanks,
Kenji Kaneshige

#5 lore raw
In reply to Kenji Kaneshige

Kenji> This is the updated patch for vector sharing. Summary of Kenji> changes: - Changed the name of some data structures. - Kenji> Removed '__cacheline_aligned' from iosapic_intr_info

I'm mostly OK with the patch, but I don't like the list handling very much. For example, maintaining a separate "count" field for the list just seems unnecessary and error-prone. It would be nicer if you could restructure the code such that you can use list_empty() instead.

	--david
#6 lore raw
In reply to Kenji Kaneshige
Quoted 3 lines · Kenji Kaneshige

What we need to prepare is vector spaces for each domain. So what we need to change is 'iosapic_intr_info' array, assign_irq_vector(), and so on..

Yes - I agree multi-vector domanins is more work and more code change. But I'm very inclined to believe the benefit is greater too based on past work I've done.

thanks,
grant

#7 lore raw
In reply to Kenji Kaneshige
Quoted 8 lines

Kenji> This is the updated patch for vector sharing. Summary of Kenji> changes: - Changed the name of some data structures. - Kenji> Removed '__cacheline_aligned' from iosapic_intr_info

I'm mostly OK with the patch, but I don't like the list handling very much. For example, maintaining a separate "count" field for the list just seems unnecessary and error-prone. It would be nicer if you could restructure the code such that you can use list_empty() instead.

Thanks for comments.
I'll restructure the code and post it again.

Thanks,
Kenji Kaneshige