Module Name:    src
Committed By:   knakahara
Date:           Mon Apr 27 06:42:53 UTC 2015

Modified Files:
        src/distrib/sets/lists/comp: mi
        src/share/man/man9: Makefile
        src/sys/arch/x86/include: intr.h pci_machdep.h pci_machdep_common.h
        src/sys/arch/x86/pci: pci_intr_machdep.c
        src/sys/arch/x86/x86: intr.c
        src/sys/kern: kern_stub.c
        src/sys/sys: intr.h
Added Files:
        src/share/man/man9: pci_intr_distribute.9
        src/sys/arch/x86/include: intr_distribute.h

Log Message:
add pci_intr_distribute(9) for x86.


To generate a diff of this commit:
cvs rdiff -u -r1.1954 -r1.1955 src/distrib/sets/lists/comp/mi
cvs rdiff -u -r1.384 -r1.385 src/share/man/man9/Makefile
cvs rdiff -u -r0 -r1.1 src/share/man/man9/pci_intr_distribute.9
cvs rdiff -u -r1.45 -r1.46 src/sys/arch/x86/include/intr.h
cvs rdiff -u -r0 -r1.1 src/sys/arch/x86/include/intr_distribute.h
cvs rdiff -u -r1.15 -r1.16 src/sys/arch/x86/include/pci_machdep.h
cvs rdiff -u -r1.13 -r1.14 src/sys/arch/x86/include/pci_machdep_common.h
cvs rdiff -u -r1.27 -r1.28 src/sys/arch/x86/pci/pci_intr_machdep.c
cvs rdiff -u -r1.78 -r1.79 src/sys/arch/x86/x86/intr.c
cvs rdiff -u -r1.38 -r1.39 src/sys/kern/kern_stub.c
cvs rdiff -u -r1.17 -r1.18 src/sys/sys/intr.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/distrib/sets/lists/comp/mi
diff -u src/distrib/sets/lists/comp/mi:1.1954 src/distrib/sets/lists/comp/mi:1.1955
--- src/distrib/sets/lists/comp/mi:1.1954	Tue Apr 21 11:10:29 2015
+++ src/distrib/sets/lists/comp/mi	Mon Apr 27 06:42:52 2015
@@ -1,4 +1,4 @@
-#	$NetBSD: mi,v 1.1954 2015/04/21 11:10:29 pooka Exp $
+#	$NetBSD: mi,v 1.1955 2015/04/27 06:42:52 knakahara Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -17131,6 +17131,7 @@
 ./usr/share/man/html9/in_getifa.html		comp-sys-htmlman	html
 ./usr/share/man/html9/incore.html		comp-sys-htmlman	html
 ./usr/share/man/html9/inittodr.html		comp-sys-htmlman	html
+./usr/share/man/html9/pci_intr_distribute.html	comp-sys-htmlman	html
 ./usr/share/man/html9/intro.html		comp-sys-htmlman	html
 ./usr/share/man/html9/ioasic.html		comp-sys-htmlman	html
 ./usr/share/man/html9/ioasic_attach_devs.html	comp-sys-htmlman	html
@@ -24063,6 +24064,7 @@
 ./usr/share/man/man9/in_getifa.9		comp-sys-man		.man
 ./usr/share/man/man9/incore.9			comp-sys-man		.man
 ./usr/share/man/man9/inittodr.9			comp-sys-man		.man
+./usr/share/man/man9/pci_intr_distribute.9	comp-sys-man		.man
 ./usr/share/man/man9/intro.9			comp-sys-man		.man
 ./usr/share/man/man9/ioasic.9			comp-sys-man		.man
 ./usr/share/man/man9/ioasic_attach_devs.9	comp-sys-man		.man

Index: src/share/man/man9/Makefile
diff -u src/share/man/man9/Makefile:1.384 src/share/man/man9/Makefile:1.385
--- src/share/man/man9/Makefile:1.384	Tue Apr 21 11:10:29 2015
+++ src/share/man/man9/Makefile	Mon Apr 27 06:42:52 2015
@@ -1,4 +1,4 @@
-#       $NetBSD: Makefile,v 1.384 2015/04/21 11:10:29 pooka Exp $
+#       $NetBSD: Makefile,v 1.385 2015/04/27 06:42:52 knakahara Exp $
 
 #	Makefile for section 9 (kernel function and variable) manual pages.
 
@@ -39,10 +39,11 @@ MAN=	accept_filter.9 accf_data.9 accf_ht
 	microseq.9 microtime.9 microuptime.9 mi_switch.9 module.9 \
 	mstohz.9 mutex.9 m_tag.9 namecache.9 \
 	namei.9 nullop.9 opencrypto.9 optstr.9 \
-	panic.9 pathbuf.9 pci.9 pci_configure_bus.9 pci_intr.9 pckbport.9 \
-	pcmcia.9 pcq.9 pcu.9 percpu.9 pfil.9 physio.9 pmap.9 pmatch.9 \
-	pmc.9 pmf.9 pool.9 pool_cache.9 powerhook_establish.9 ppi.9 \
-	ppsratecheck.9 preempt.9 proc_find.9 pserialize.9 putter.9 \
+	panic.9 pathbuf.9 pci.9 pci_configure_bus.9 pci_intr.9 \
+	pci_intr_distribute.9 pckbport.9 pcmcia.9 pcq.9 pcu.9 \
+	percpu.9 pfil.9 physio.9 pmap.9 pmatch.9 pmc.9 pmf.9 pool.9 \
+	pool_cache.9 powerhook_establish.9 ppi.9 ppsratecheck.9 preempt.9 \
+	proc_find.9 pserialize.9 putter.9 \
 	radio.9 ras.9 rasops.9 ratecheck.9 resettodr.9 rnd.9 rndsink.9 \
 	roundup.9 rssadapt.9 rt_timer.9 rwlock.9 RUN_ONCE.9 STACK.9 \
 	scanc.9 \

Index: src/sys/arch/x86/include/intr.h
diff -u src/sys/arch/x86/include/intr.h:1.45 src/sys/arch/x86/include/intr.h:1.46
--- src/sys/arch/x86/include/intr.h:1.45	Sun Jul 20 15:46:34 2014
+++ src/sys/arch/x86/include/intr.h	Mon Apr 27 06:42:52 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: intr.h,v 1.45 2014/07/20 15:46:34 uebayasi Exp $	*/
+/*	$NetBSD: intr.h,v 1.46 2015/04/27 06:42:52 knakahara Exp $	*/
 
 /*-
  * Copyright (c) 1998, 2001, 2006, 2007, 2008 The NetBSD Foundation, Inc.
@@ -42,6 +42,7 @@
 #endif
 
 #include <sys/evcnt.h>
+#include <sys/queue.h>
 #include <machine/intrdefs.h>
 
 #ifndef _LOCORE
@@ -71,6 +72,11 @@ struct intrstub {
 	void *ist_resume;
 };
 
+struct percpu_evcnt {
+	cpuid_t cpuid;
+	uint64_t count;
+};
+
 struct intrsource {
 	int is_maxlevel;		/* max. IPL for this source */
 	int is_pin;			/* IRQ for legacy; pin for IO APIC,
@@ -86,6 +92,10 @@ struct intrsource {
 	int is_idtvec;
 	int is_minlevel;
 	char is_evname[32];		/* event counter name */
+	char is_intrid[INTRIDBUF];	/* intrid created by create_intrid() */
+	cpuid_t is_active_cpu;		/* active cpuid */
+	struct percpu_evcnt *is_saved_evcnt;	/* interrupt count of deactivated cpus */
+	SIMPLEQ_ENTRY(intrsource) is_list;	/* link of intrsources */
 };
 
 #define IS_LEGACY	0x0001		/* legacy ISA irq source */
@@ -182,6 +192,9 @@ int intr_find_mpmapping(int, int, int *)
 struct pic *intr_findpic(int);
 void intr_printconfig(void);
 
+struct intrsource *intr_allocate_io_intrsource(const char *);
+void intr_free_io_intrsource(const char *);
+
 int x86_send_ipi(struct cpu_info *, int);
 void x86_broadcast_ipi(int);
 void x86_ipi_handler(void);

Index: src/sys/arch/x86/include/pci_machdep.h
diff -u src/sys/arch/x86/include/pci_machdep.h:1.15 src/sys/arch/x86/include/pci_machdep.h:1.16
--- src/sys/arch/x86/include/pci_machdep.h:1.15	Wed Mar  4 05:35:50 2015
+++ src/sys/arch/x86/include/pci_machdep.h	Mon Apr 27 06:42:52 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: pci_machdep.h,v 1.15 2015/03/04 05:35:50 knakahara Exp $	*/
+/*	$NetBSD: pci_machdep.h,v 1.16 2015/04/27 06:42:52 knakahara Exp $	*/
 
 /*
  * Copyright (c) 1996 Christopher G. Demetriou.  All rights reserved.
@@ -33,6 +33,8 @@
 #ifndef _X86_PCI_MACHDEP_H_
 #define _X86_PCI_MACHDEP_H_
 
+#include <x86/intr_distribute.h>
+
 /*
  * Types provided to machine-independent PCI code
  * See also i82093var.h to find out pci_intr_handle_t's bitfield.

Index: src/sys/arch/x86/include/pci_machdep_common.h
diff -u src/sys/arch/x86/include/pci_machdep_common.h:1.13 src/sys/arch/x86/include/pci_machdep_common.h:1.14
--- src/sys/arch/x86/include/pci_machdep_common.h:1.13	Sat Mar 29 19:28:30 2014
+++ src/sys/arch/x86/include/pci_machdep_common.h	Mon Apr 27 06:42:52 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: pci_machdep_common.h,v 1.13 2014/03/29 19:28:30 christos Exp $	*/
+/*	$NetBSD: pci_machdep_common.h,v 1.14 2015/04/27 06:42:52 knakahara Exp $	*/
 
 /*
  * Copyright (c) 1996 Christopher G. Demetriou.  All rights reserved.
@@ -41,6 +41,8 @@
 #define	__HAVE_PCIIDE_MACHDEP_COMPAT_INTR_DISESTABLISH
 #endif
 
+#include <sys/kcpuset.h>
+
 /*
  * x86-specific PCI structure and type definitions.
  * NOT TO BE USED DIRECTLY BY MACHINE INDEPENDENT CODE.
@@ -116,6 +118,7 @@ const struct evcnt *pci_intr_evcnt(pci_c
 void		*pci_intr_establish(pci_chipset_tag_t, pci_intr_handle_t,
 		    int, int (*)(void *), void *);
 void		pci_intr_disestablish(pci_chipset_tag_t, void *);
+int		pci_intr_distribute(void *, const kcpuset_t *, kcpuset_t *);
 
 /* experimental MSI support */
 void *pci_msi_establish(struct pci_attach_args *, int, int (*)(void *), void *);

Index: src/sys/arch/x86/pci/pci_intr_machdep.c
diff -u src/sys/arch/x86/pci/pci_intr_machdep.c:1.27 src/sys/arch/x86/pci/pci_intr_machdep.c:1.28
--- src/sys/arch/x86/pci/pci_intr_machdep.c:1.27	Sat Mar 29 19:28:30 2014
+++ src/sys/arch/x86/pci/pci_intr_machdep.c	Mon Apr 27 06:42:52 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: pci_intr_machdep.c,v 1.27 2014/03/29 19:28:30 christos Exp $	*/
+/*	$NetBSD: pci_intr_machdep.c,v 1.28 2015/04/27 06:42:52 knakahara Exp $	*/
 
 /*-
  * Copyright (c) 1997, 1998, 2009 The NetBSD Foundation, Inc.
@@ -73,15 +73,17 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: pci_intr_machdep.c,v 1.27 2014/03/29 19:28:30 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: pci_intr_machdep.c,v 1.28 2015/04/27 06:42:52 knakahara Exp $");
 
 #include <sys/types.h>
 #include <sys/param.h>
 #include <sys/time.h>
 #include <sys/systm.h>
+#include <sys/cpu.h>
 #include <sys/errno.h>
 #include <sys/device.h>
 #include <sys/intr.h>
+#include <sys/kmem.h>
 #include <sys/malloc.h>
 
 #include <dev/pci/pcivar.h>
@@ -328,6 +330,15 @@ pci_intr_disestablish(pci_chipset_tag_t 
 	intr_disestablish(cookie);
 }
 
+int
+pci_intr_distribute(void *cookie, const kcpuset_t *newset, kcpuset_t *oldset)
+{
+
+	/* XXX Is pc_ov->ov_intr_distribute required? */
+
+	return intr_distribute(cookie, newset, oldset);
+}
+
 #if NIOAPIC > 0
 /*
  * experimental support for MSI, does support a single vector,

Index: src/sys/arch/x86/x86/intr.c
diff -u src/sys/arch/x86/x86/intr.c:1.78 src/sys/arch/x86/x86/intr.c:1.79
--- src/sys/arch/x86/x86/intr.c:1.78	Wed Apr  8 05:52:41 2015
+++ src/sys/arch/x86/x86/intr.c	Mon Apr 27 06:42:52 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: intr.c,v 1.78 2015/04/08 05:52:41 knakahara Exp $	*/
+/*	$NetBSD: intr.c,v 1.79 2015/04/27 06:42:52 knakahara Exp $	*/
 
 /*-
  * Copyright (c) 2007, 2008, 2009 The NetBSD Foundation, Inc.
@@ -133,7 +133,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.78 2015/04/08 05:52:41 knakahara Exp $");
+__KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.79 2015/04/27 06:42:52 knakahara Exp $");
 
 #include "opt_intrdebug.h"
 #include "opt_multiprocessor.h"
@@ -180,6 +180,12 @@ __KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.7
 #include <ddb/db_output.h>
 #endif
 
+#ifdef INTRDEBUG
+#define DPRINTF(msg) printf msg
+#else
+#define DPRINTF(msg)
+#endif
+
 struct pic softintr_pic = {
 	.pic_name = "softintr_fakepic",
 	.pic_type = PIC_SOFT,
@@ -190,6 +196,9 @@ struct pic softintr_pic = {
 
 static void intr_calculatemasks(struct cpu_info *);
 
+static SIMPLEQ_HEAD(, intrsource) io_interrupt_sources =
+	SIMPLEQ_HEAD_INITIALIZER(io_interrupt_sources);
+
 #if NIOAPIC > 0 || NACPICA > 0
 static int intr_scan_bus(int, int, int *);
 #if NPCI > 0
@@ -197,9 +206,11 @@ static int intr_find_pcibridge(int, pcit
 #endif
 #endif
 
-static int intr_allocate_slot_cpu(struct cpu_info *, struct pic *, int, int *);
+static int intr_allocate_slot_cpu(struct cpu_info *, struct pic *, int, int *,
+				  struct intrsource *);
 static int __noinline intr_allocate_slot(struct pic *, int, int,
-					 struct cpu_info **, int *, int *);
+					 struct cpu_info **, int *, int *,
+					 struct intrsource *);
 
 static void intr_source_free(struct cpu_info *, int, struct pic *, int);
 
@@ -214,6 +225,14 @@ static void intr_redistribute_xc_s1(void
 static void intr_redistribute_xc_s2(void *, void *);
 static bool intr_redistribute(struct cpu_info *);
 
+static const char *legacy_intr_string(int, char *, size_t, struct pic *);
+
+static int intr_find_unused_slot(struct cpu_info *, int *);
+static void intr_activate_xcall(void *, void *);
+static void intr_deactivate_xcall(void *, void *);
+static void intr_get_affinity(struct intrsource *, kcpuset_t *);
+static int intr_set_affinity(struct intrsource *, const kcpuset_t *);
+
 /*
  * Fill in default interrupt table (in case of spurious interrupt
  * during configuration of kernel), setup interrupt control unit
@@ -432,9 +451,135 @@ intr_scan_bus(int bus, int pin, int *han
 }
 #endif
 
+/*
+ * Create an interrupt id such as "ioapic0 pin 9". This interrupt id is used
+ * by MI code and intrctl(8).
+ */
+static const char *
+create_intrid(int legacy_irq, struct pic *pic, int pin, char *buf, size_t len)
+{
+	int ih;
+
+	/*
+	 * If the device is pci, "legacy_irq" is alway -1. Least 8 bit of "ih"
+	 * is only used in intr_string() to show the irq number.
+	 * If the device is "legacy"(such as floppy), it should not use
+	 * intr_string().
+	 */
+	if (pic->pic_type == PIC_I8259) {
+		ih = legacy_irq;
+		return legacy_intr_string(ih, buf, len, pic);
+	} else {
+		ih = ((pic->pic_apicid << APIC_INT_APIC_SHIFT) & APIC_INT_APIC_MASK) |
+			((pin << APIC_INT_PIN_SHIFT) & APIC_INT_PIN_MASK);
+		if (pic->pic_type == PIC_IOAPIC) {
+			ih |= APIC_INT_VIA_APIC;
+		}
+		ih |= pin;
+		return intr_string(ih, buf, len);
+	}
+}
+
+/*
+ * Find intrsource from io_interrupt_sources list.
+ */
+static struct intrsource *
+intr_get_io_intrsource(const char *intrid)
+{
+	struct intrsource *isp;
+
+	KASSERT(mutex_owned(&cpu_lock));
+
+	SIMPLEQ_FOREACH(isp, &io_interrupt_sources, is_list) {
+		KASSERT(isp->is_intrid != NULL);
+		if (strncmp(intrid, isp->is_intrid, INTRIDBUF - 1) == 0) {
+			return isp;
+		}
+	}
+	return NULL;
+}
+
+/*
+ * Allocate intrsource and add to io_interrupt_sources list.
+ */
+struct intrsource *
+intr_allocate_io_intrsource(const char *intrid)
+{
+	CPU_INFO_ITERATOR cii;
+	struct cpu_info *ci;
+	struct intrsource *isp;
+	struct percpu_evcnt *pep;
+
+	KASSERT(mutex_owned(&cpu_lock));
+
+	if (intrid == NULL)
+		return NULL;
+
+	isp = kmem_zalloc(sizeof(*isp), KM_SLEEP);
+	if (isp == NULL) {
+		return NULL;
+	}
+
+	pep = kmem_zalloc(sizeof(*pep) * ncpu, KM_SLEEP);
+	if (pep == NULL) {
+		kmem_free(isp, sizeof(*isp));
+		return NULL;
+	}
+	isp->is_saved_evcnt = pep;
+	for (CPU_INFO_FOREACH(cii, ci)) {
+		pep->cpuid = ci->ci_cpuid;
+		pep++;
+	}
+	strncpy(isp->is_intrid, intrid, sizeof(isp->is_intrid));
+
+	SIMPLEQ_INSERT_TAIL(&io_interrupt_sources, isp, is_list);
+
+	return isp;
+}
+
+/*
+ * Remove from io_interrupt_sources list and free by the intrsource pointer.
+ */
+static void
+intr_free_io_intrsource_direct(struct intrsource *isp)
+{
+	KASSERT(mutex_owned(&cpu_lock));
+
+	SIMPLEQ_REMOVE(&io_interrupt_sources, isp, intrsource, is_list);
+
+	/* Is this interrupt established? */
+	if (isp->is_evname != '\0')
+		evcnt_detach(&isp->is_evcnt);
+
+	kmem_free(isp->is_saved_evcnt,
+	    sizeof(*(isp->is_saved_evcnt)) * ncpu);
+	kmem_free(isp, sizeof(*isp));
+}
+
+/*
+ * Remove from io_interrupt_sources list and free by the interrupt id.
+ * This function can be used by MI code.
+ */
+void
+intr_free_io_intrsource(const char *intrid)
+{
+	struct intrsource *isp;
+
+	KASSERT(mutex_owned(&cpu_lock));
+
+	if (intrid == NULL)
+		return;
+
+	if ((isp = intr_get_io_intrsource(intrid)) == NULL) {
+		return;
+	}
+
+	intr_free_io_intrsource_direct(isp);
+}
+
 static int
 intr_allocate_slot_cpu(struct cpu_info *ci, struct pic *pic, int pin,
-		       int *index)
+		       int *index, struct intrsource *chained)
 {
 	int slot, i;
 	struct intrsource *isp;
@@ -464,14 +609,13 @@ intr_allocate_slot_cpu(struct cpu_info *
 
 	isp = ci->ci_isources[slot];
 	if (isp == NULL) {
-		isp = kmem_zalloc(sizeof(*isp), KM_SLEEP);
-		if (isp == NULL) {
-			return ENOMEM;
-		}
+		isp = chained;
+		KASSERT(isp != NULL);
 		snprintf(isp->is_evname, sizeof (isp->is_evname),
 		    "pin %d", pin);
 		evcnt_attach_dynamic(&isp->is_evcnt, EVCNT_TYPE_INTR, NULL,
 		    pic->pic_name, isp->is_evname);
+		isp->is_active_cpu = ci->ci_cpuid;
 		ci->ci_isources[slot] = isp;
 	}
 
@@ -484,7 +628,8 @@ intr_allocate_slot_cpu(struct cpu_info *
  */
 static int __noinline
 intr_allocate_slot(struct pic *pic, int pin, int level,
-		   struct cpu_info **cip, int *index, int *idt_slot)
+		   struct cpu_info **cip, int *index, int *idt_slot,
+		   struct intrsource *chained)
 {
 	CPU_INFO_ITERATOR cii;
 	struct cpu_info *ci, *lci;
@@ -523,7 +668,7 @@ intr_allocate_slot(struct pic *pic, int 
 		 * Must be directed to BP.
 		 */
 		ci = &cpu_info_primary;
-		error = intr_allocate_slot_cpu(ci, pic, pin, &slot);
+		error = intr_allocate_slot_cpu(ci, pic, pin, &slot, chained);
 	} else {
 		/*
 		 * Find least loaded AP/BP and try to allocate there.
@@ -543,7 +688,7 @@ intr_allocate_slot(struct pic *pic, int 
 #endif
 		}
 		KASSERT(ci != NULL);
-		error = intr_allocate_slot_cpu(ci, pic, pin, &slot);
+		error = intr_allocate_slot_cpu(ci, pic, pin, &slot, chained);
 
 		/*
 		 * If that did not work, allocate anywhere.
@@ -555,7 +700,7 @@ intr_allocate_slot(struct pic *pic, int 
 					continue;
 				}
 				error = intr_allocate_slot_cpu(ci, pic,
-				    pin, &slot);
+				    pin, &slot, chained);
 				if (error == 0) {
 					break;
 				}
@@ -578,7 +723,6 @@ intr_allocate_slot(struct pic *pic, int 
 	}
 	if (idtvec == 0) {
 		evcnt_detach(&ci->ci_isources[slot]->is_evcnt);
-		kmem_free(ci->ci_isources[slot], sizeof(*(ci->ci_isources[slot])));
 		ci->ci_isources[slot] = NULL;
 		return EBUSY;
 	}
@@ -599,9 +743,6 @@ intr_source_free(struct cpu_info *ci, in
 	if (isp->is_handlers != NULL)
 		return;
 	ci->ci_isources[slot] = NULL;
-	evcnt_detach(&isp->is_evcnt);
-	kmem_free(isp, sizeof(*isp));
-	ci->ci_isources[slot] = NULL;
 	if (pic != &i8259_pic)
 		idt_vec_free(idtvec);
 }
@@ -705,11 +846,13 @@ intr_establish(int legacy_irq, struct pi
 	struct intrhand **p, *q, *ih;
 	struct cpu_info *ci;
 	int slot, error, idt_vec;
-	struct intrsource *source;
+	struct intrsource *chained, *source;
 #ifdef MULTIPROCESSOR
 	bool mpsafe = (known_mpsafe || level != IPL_VM);
 #endif /* MULTIPROCESSOR */
 	uint64_t where;
+	const char *intrstr;
+	char intrstr_buf[INTRIDBUF];
 
 #ifdef DIAGNOSTIC
 	if (legacy_irq != -1 && (legacy_irq < 0 || legacy_irq > 15))
@@ -725,9 +868,27 @@ intr_establish(int legacy_irq, struct pi
 		return NULL;
 	}
 
+	intrstr = create_intrid(legacy_irq, pic, pin, intrstr_buf,
+	    sizeof(intrstr_buf));
+	KASSERT(intrstr != NULL);
+
 	mutex_enter(&cpu_lock);
-	error = intr_allocate_slot(pic, pin, level, &ci, &slot, &idt_vec);
+
+	/* allocate intrsource pool, if not yet. */
+	chained = intr_get_io_intrsource(intrstr);
+	if (chained == NULL) {
+		chained = intr_allocate_io_intrsource(intrstr);
+		if (chained == NULL) {
+			mutex_exit(&cpu_lock);
+			printf("%s: can't allocate io_intersource\n", __func__);
+			return NULL;
+		}
+	}
+
+	error = intr_allocate_slot(pic, pin, level, &ci, &slot, &idt_vec,
+	    chained);
 	if (error != 0) {
+		intr_free_io_intrsource_direct(chained);
 		mutex_exit(&cpu_lock);
 		kmem_free(ih, sizeof(*ih));
 		printf("failed to allocate interrupt slot for PIC %s pin %d\n",
@@ -739,6 +900,7 @@ intr_establish(int legacy_irq, struct pi
 
 	if (source->is_handlers != NULL &&
 	    source->is_pic->pic_type != pic->pic_type) {
+		intr_free_io_intrsource_direct(chained);
 		mutex_exit(&cpu_lock);
 		kmem_free(ih, sizeof(*ih));
 		printf("%s: can't share intr source between "
@@ -761,9 +923,10 @@ intr_establish(int legacy_irq, struct pi
 		/* FALLTHROUGH */
 	case IST_PULSE:
 		if (type != IST_NONE) {
+			intr_source_free(ci, slot, pic, idt_vec);
+			intr_free_io_intrsource_direct(chained);
 			mutex_exit(&cpu_lock);
 			kmem_free(ih, sizeof(*ih));
-			intr_source_free(ci, slot, pic, idt_vec);
 			printf("%s: pic %s pin %d: can't share "
 			       "type %d with %d\n",
 				__func__, pic->pic_name, pin,
@@ -902,6 +1065,19 @@ intr_disestablish_xcall(void *arg1, void
 #endif
 }
 
+static int
+intr_num_handlers(struct intrsource *isp)
+{
+	struct intrhand *ih;
+	int num;
+
+	num = 0;
+	for (ih = isp->is_handlers; ih != NULL; ih = ih->ih_next)
+		num++;
+
+	return num;
+}
+
 /*
  * Deregister an interrupt handler.
  */
@@ -909,6 +1085,7 @@ void
 intr_disestablish(struct intrhand *ih)
 {
 	struct cpu_info *ci;
+	struct intrsource *isp;
 	uint64_t where;
 
 	/*
@@ -920,16 +1097,39 @@ intr_disestablish(struct intrhand *ih)
 	ci = ih->ih_cpu;
 	(ci->ci_nintrhand)--;
 	KASSERT(ci->ci_nintrhand >= 0);
+	isp = ci->ci_isources[ih->ih_slot];
 	if (ci == curcpu() || !mp_online) {
 		intr_disestablish_xcall(ih, NULL);
 	} else {
 		where = xc_unicast(0, intr_disestablish_xcall, ih, NULL, ci);
 		xc_wait(where);
 	}	
+
+	if (intr_num_handlers(isp) < 1) {
+		intr_free_io_intrsource_direct(isp);
+	}
+
 	mutex_exit(&cpu_lock);
+
 	kmem_free(ih, sizeof(*ih));
 }
 
+static const char *
+legacy_intr_string(int ih, char *buf, size_t len, struct pic *pic)
+{
+	int legacy_irq;
+
+	KASSERT(pic->pic_type == PIC_I8259);
+	KASSERT(APIC_IRQ_ISLEGACY(ih));
+
+	legacy_irq = APIC_IRQ_LEGACY_IRQ(ih);
+	KASSERT(legacy_irq >= 0 && legacy_irq < 16);
+
+	snprintf(buf, len, "%s pin %d", pic->pic_name, legacy_irq);
+
+	return buf;
+}
+
 const char *
 intr_string(int ih, char *buf, size_t len)
 {
@@ -940,7 +1140,6 @@ intr_string(int ih, char *buf, size_t le
 	if (ih == 0)
 		panic("%s: bogus handle 0x%x", __func__, ih);
 
-
 #if NIOAPIC > 0
 	if (ih & APIC_INT_VIA_APIC) {
 		pic = ioapic_find(APIC_IRQ_APIC(ih));
@@ -1179,6 +1378,46 @@ softint_init_md(lwp_t *l, u_int level, u
 	intr_calculatemasks(ci);
 }
 
+/*
+ * Save current affinitied cpu's interrupt count.
+ */
+static void
+intr_save_evcnt(struct intrsource *source, cpuid_t cpuid)
+{
+	struct percpu_evcnt *pep;
+	uint64_t curcnt;
+	int i;
+
+	curcnt = source->is_evcnt.ev_count;
+	pep = source->is_saved_evcnt;
+
+	for (i = 0; i < ncpu; i++) {
+		if (pep[i].cpuid == cpuid) {
+			pep[i].count = curcnt;
+			break;
+		}
+	}
+}
+
+/*
+ * Restore current affinitied cpu's interrupt count.
+ */
+static void
+intr_restore_evcnt(struct intrsource *source, cpuid_t cpuid)
+{
+	struct percpu_evcnt *pep;
+	int i;
+
+	pep = source->is_saved_evcnt;
+
+	for (i = 0; i < ncpu; i++) {
+		if (pep[i].cpuid == cpuid) {
+			source->is_evcnt.ev_count = pep[i].count;
+			break;
+		}
+	}
+}
+
 static void
 intr_redistribute_xc_t(void *arg1, void *arg2)
 {
@@ -1352,6 +1591,9 @@ intr_redistribute(struct cpu_info *oci)
 		nci->ci_nintrhand++;
 		ih->ih_cpu = nci;
 	}
+	intr_save_evcnt(isp, oci->ci_cpuid);
+	intr_restore_evcnt(isp, nci->ci_cpuid);
+	isp->is_active_cpu = nci->ci_cpuid;
 
 	return true;
 }
@@ -1386,3 +1628,263 @@ cpu_intr_count(struct cpu_info *ci)
 
 	return ci->ci_nintrhand;
 }
+
+static int
+intr_find_unused_slot(struct cpu_info *ci, int *index)
+{
+	int slot, i;
+
+	KASSERT(mutex_owned(&cpu_lock));
+
+	slot = -1;
+	for (i = 0; i < MAX_INTR_SOURCES ; i++) {
+		if (ci->ci_isources[i] == NULL) {
+			slot = i;
+			break;
+		}
+	}
+	if (slot == -1) {
+		DPRINTF(("cannot allocate ci_isources\n"));
+		return EBUSY;
+	}
+
+	*index = slot;
+	return 0;
+}
+
+/*
+ * Let cpu_info ready to accept the interrupt.
+ */
+static void
+intr_activate_xcall(void *arg1, void *arg2)
+{
+	struct cpu_info *ci;
+	struct intrsource *source;
+	struct intrstub *stubp;
+	struct intrhand *ih;
+	u_long psl;
+	int idt_vec;
+	int slot;
+
+	ih = arg1;
+
+	kpreempt_disable();
+
+	KASSERT(ih->ih_cpu == curcpu() || !mp_online);
+
+	ci = ih->ih_cpu;
+	slot = ih->ih_slot;
+	source = ci->ci_isources[slot];
+	idt_vec = source->is_idtvec;
+
+	psl = x86_read_psl();
+	x86_disable_intr();
+
+	intr_calculatemasks(ci);
+
+	if (source->is_type == IST_LEVEL) {
+		stubp = &source->is_pic->pic_level_stubs[slot];
+	} else {
+		stubp = &source->is_pic->pic_edge_stubs[slot];
+	}
+	source->is_resume = stubp->ist_resume;
+	source->is_recurse = stubp->ist_recurse;
+	setgate(&idt[idt_vec], stubp->ist_entry, 0, SDT_SYS386IGT,
+	    SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+
+	x86_write_psl(psl);
+
+	kpreempt_enable();
+}
+
+/*
+ * Let cpu_info not accept the interrupt.
+ */
+static void
+intr_deactivate_xcall(void *arg1, void *arg2)
+{
+	struct cpu_info *ci;
+	struct intrhand *ih, *lih;
+	u_long psl;
+	int slot;
+
+	ih = arg1;
+
+	kpreempt_disable();
+
+	KASSERT(ih->ih_cpu == curcpu() || !mp_online);
+
+	ci = ih->ih_cpu;
+	slot = ih->ih_slot;
+
+	psl = x86_read_psl();
+	x86_disable_intr();
+
+	/* Move all devices sharing IRQ number. */
+	ci->ci_isources[slot] = NULL;
+	for (lih = ih; lih != NULL; lih = lih->ih_next) {
+		ci->ci_nintrhand--;
+	}
+
+	intr_calculatemasks(ci);
+
+	/*
+	 * Skip unsetgate(), because the same itd[] entry is overwritten in
+	 * intr_activate_xcall().
+	 */
+
+	x86_write_psl(psl);
+
+	kpreempt_enable();
+}
+
+static void
+intr_get_affinity(struct intrsource *isp, kcpuset_t *cpuset)
+{
+	struct cpu_info *ci;
+
+	KASSERT(mutex_owned(&cpu_lock));
+
+	if (isp == NULL) {
+		kcpuset_zero(cpuset);
+		return;
+	}
+
+	ci = isp->is_handlers->ih_cpu;
+	if (ci == NULL) {
+		kcpuset_zero(cpuset);
+		return;
+	}
+
+	kcpuset_set(cpuset, cpu_index(ci));
+	return;
+}
+
+static int
+intr_set_affinity(struct intrsource *isp, const kcpuset_t *cpuset)
+{
+	struct cpu_info *oldci, *newci;
+	struct intrhand *ih, *lih;
+	struct pic *pic;
+	u_int cpu_idx;
+	int idt_vec;
+	int oldslot, newslot;
+	int err;
+	int pin;
+
+	KASSERT(mutex_owned(&cpu_lock));
+
+	/* XXX
+	 * logical destination mode is not supported, use lowest index cpu.
+	 */
+	cpu_idx = kcpuset_ffs(cpuset) - 1;
+	newci = cpu_lookup(cpu_idx);
+	if (newci == NULL) {
+		DPRINTF(("invalid cpu index: %u\n", cpu_idx));
+		return EINVAL;
+	}
+	if ((newci->ci_schedstate.spc_flags & SPCF_NOINTR) != 0) {
+		DPRINTF(("the cpu is set nointr shield. index:%u\n", cpu_idx));
+		return EINVAL;
+	}
+
+	if (isp == NULL) {
+		DPRINTF(("invalid intrctl handler\n"));
+		return EINVAL;
+	}
+
+	/* i8259_pic supports only primary cpu, see i8259.c. */
+	pic = isp->is_pic;
+	if (pic == &i8259_pic) {
+		DPRINTF(("i8259 pic does not support set_affinity\n"));
+		return ENOTSUP;
+	}
+
+	ih = isp->is_handlers;
+	oldci = ih->ih_cpu;
+	if (newci == oldci) /* nothing to do */
+		return 0;
+
+	oldslot = ih->ih_slot;
+	idt_vec = isp->is_idtvec;
+
+	err = intr_find_unused_slot(newci, &newslot);
+	if (err) {
+		DPRINTF(("failed to allocate interrupt slot for PIC %s intrid %s\n",
+			isp->is_pic->pic_name, isp->is_intrid));
+		return err;
+	}
+
+	pin = isp->is_pin;
+	(*pic->pic_hwmask)(pic, pin); /* for ci_ipending check */
+	if (oldci->ci_ipending & (1 << oldslot)) {
+		(*pic->pic_hwunmask)(pic, pin);
+		DPRINTF(("pin %d on cpuid %ld has pending interrupts.\n",
+			pin, oldci->ci_cpuid));
+		return EBUSY;
+	}
+
+	kpreempt_disable();
+
+	/* deactivate old interrupt setting */
+	if (oldci == curcpu() || !mp_online) {
+		intr_deactivate_xcall(ih, NULL);
+	} else {
+		uint64_t where;
+		where = xc_unicast(0, intr_deactivate_xcall, ih,
+				   NULL, oldci);
+		xc_wait(where);
+	}
+	intr_save_evcnt(isp, oldci->ci_cpuid);
+	(*pic->pic_delroute)(pic, oldci, pin, idt_vec, isp->is_type);
+
+	/* activate new interrupt setting */
+	newci->ci_isources[newslot] = isp;
+	for (lih = ih; lih != NULL; lih = lih->ih_next) {
+		newci->ci_nintrhand++;
+		lih->ih_cpu = newci;
+		lih->ih_slot = newslot;
+	}
+	if (newci == curcpu() || !mp_online) {
+		intr_activate_xcall(ih, NULL);
+	} else {
+		uint64_t where;
+		where = xc_unicast(0, intr_activate_xcall, ih,
+				   NULL, newci);
+		xc_wait(where);
+	}
+	intr_restore_evcnt(isp, newci->ci_cpuid);
+	isp->is_active_cpu = newci->ci_cpuid;
+	(*pic->pic_addroute)(pic, newci, pin, idt_vec, isp->is_type);
+
+	kpreempt_enable();
+
+	(*pic->pic_hwunmask)(pic, pin);
+
+	return err;
+}
+
+int
+intr_distribute(struct intrhand *ih, const kcpuset_t *newset, kcpuset_t *oldset)
+{
+	struct intrsource *isp;
+	int ret, slot;
+
+	if (ih == NULL)
+		return EINVAL;
+
+	mutex_enter(&cpu_lock);
+
+	slot = ih->ih_slot;
+	isp = ih->ih_cpu->ci_isources[slot];
+	KASSERT(isp != NULL);
+
+	if (oldset != NULL)
+		intr_get_affinity(isp, oldset);
+
+	ret = intr_set_affinity(isp, newset);
+
+	mutex_exit(&cpu_lock);
+
+	return ret;
+}

Index: src/sys/kern/kern_stub.c
diff -u src/sys/kern/kern_stub.c:1.38 src/sys/kern/kern_stub.c:1.39
--- src/sys/kern/kern_stub.c:1.38	Mon Dec  9 18:06:27 2013
+++ src/sys/kern/kern_stub.c	Mon Apr 27 06:42:53 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: kern_stub.c,v 1.38 2013/12/09 18:06:27 pooka Exp $	*/
+/*	$NetBSD: kern_stub.c,v 1.39 2015/04/27 06:42:53 knakahara Exp $	*/
 
 /*-
  * Copyright (c) 2007, 2008 The NetBSD Foundation, Inc.
@@ -62,7 +62,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_stub.c,v 1.38 2013/12/09 18:06:27 pooka Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_stub.c,v 1.39 2015/04/27 06:42:53 knakahara Exp $");
 
 #include "opt_ptrace.h"
 #include "opt_ktrace.h"
@@ -147,6 +147,8 @@ __weak_alias(userconf_prompt, voidop);
 
 __weak_alias(kobj_renamespace, nullop);
 
+__weak_alias(pci_intr_distribute, eopnotsupp);
+
 /*
  * Scheduler activations system calls.  These need to remain until libc's
  * major version is bumped.

Index: src/sys/sys/intr.h
diff -u src/sys/sys/intr.h:1.17 src/sys/sys/intr.h:1.18
--- src/sys/sys/intr.h:1.17	Sun May 25 15:42:01 2014
+++ src/sys/sys/intr.h	Mon Apr 27 06:42:53 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: intr.h,v 1.17 2014/05/25 15:42:01 rmind Exp $	*/
+/*	$NetBSD: intr.h,v 1.18 2015/04/27 06:42:53 knakahara Exp $	*/
 
 /*-
  * Copyright (c) 2007 The NetBSD Foundation, Inc.
@@ -32,6 +32,8 @@
 #ifndef _SYS_INTR_H_
 #define	_SYS_INTR_H_
 
+#define INTRIDBUF 64
+
 #ifdef _KERNEL
 
 #include <sys/types.h>

Added files:

Index: src/share/man/man9/pci_intr_distribute.9
diff -u /dev/null src/share/man/man9/pci_intr_distribute.9:1.1
--- /dev/null	Mon Apr 27 06:42:53 2015
+++ src/share/man/man9/pci_intr_distribute.9	Mon Apr 27 06:42:52 2015
@@ -0,0 +1,53 @@
+.\" $NetBSD: pci_intr_distribute.9,v 1.1 2015/04/27 06:42:52 knakahara Exp $
+.\"
+.\" Copyright (c) 2015 Internet Initiative Japan Inc.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd April 8, 2015
+.Dt PCI_INTR_DISTRIBUTE 9
+.Os
+.Sh NAME
+.Nm pci_intr_distribute
+.Sh SYNOPSIS
+.In dev/pci/pcivar.h
+.Ft int
+.Fn pci_intr_distribute "void *ich" "const kcpuset_t *newset" \
+"kcpuset_t *oldset"
+.Sh DESCRIPTION
+The
+.Nm
+functions exist to assing an interrupt to CPU.
+.Pp
+If a driver (or the other kernel component) wishes to assign an
+interrupt to CPU, it should pass the return value of
+.Fn pci_intr_establish
+to the
+.Ft ich .
+And it should set kcpuset which want to assign to
+.Ft newset .
+If it want to get the assignment before changing, it should be
+pass non-NULL value to
+.Ft oldset .
+If not, it should set NULL to
+.Ft oldset .

Index: src/sys/arch/x86/include/intr_distribute.h
diff -u /dev/null src/sys/arch/x86/include/intr_distribute.h:1.1
--- /dev/null	Mon Apr 27 06:42:53 2015
+++ src/sys/arch/x86/include/intr_distribute.h	Mon Apr 27 06:42:52 2015
@@ -0,0 +1,40 @@
+/*	$NetBSD: intr_distribute.h,v 1.1 2015/04/27 06:42:52 knakahara Exp $	*/
+
+/*
+ * Copyright (c) 2015 Internet Initiative Japan Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _X86_INTR_DISTRIBUTE_H_
+#define _X86_INTR_DISTRIBUTE_H_
+
+#ifdef _KERNEL
+
+#include <sys/kcpuset.h>
+
+int intr_distribute(struct intrhand *, const kcpuset_t *, kcpuset_t *);
+
+#endif /* _KERNEL */
+
+#endif /* !_X86_INTR_DISTRIBUTE_H_ */

Reply via email to