aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Baldwin <jhb@FreeBSD.org>2007-01-22 21:48:44 +0000
committerJohn Baldwin <jhb@FreeBSD.org>2007-01-22 21:48:44 +0000
commit5fe82bca57a1dbad1538d277602bb047c78a73fc (patch)
treee54f3042911f50266b958edf09a9fe2525a0987a
parent7c32173ba888e963a1d352e14915f5527915a388 (diff)
downloadsrc-5fe82bca57a1dbad1538d277602bb047c78a73fc.tar.gz
src-5fe82bca57a1dbad1538d277602bb047c78a73fc.zip
Expand the MSI/MSI-X API to address some deficiencies in the MSI-X support.
- First off, device drivers really do need to know if they are allocating MSI or MSI-X messages. MSI requires allocating powerof2() messages for example where MSI-X does not. To address this, split out the MSI-X support from pci_msi_count() and pci_alloc_msi() into new driver-visible functions pci_msix_count() and pci_alloc_msix(). As a result, pci_msi_count() now just returns a count of the max supported MSI messages for the device, and pci_alloc_msi() only tries to allocate MSI messages. To get a count of the max supported MSI-X messages, use pci_msix_count(). To allocate MSI-X messages, use pci_alloc_msix(). pci_release_msi() still handles both MSI and MSI-X messages, however. As a result of this change, drivers using the existing API will only use MSI messages and will no longer try to use MSI-X messages. - Because MSI-X allows for each message to have its own data and address values (and thus does not require all of the messages to have their MD vectors allocated as a group), some devices allow for "sparse" use of MSI-X message slots. For example, if a device supports 8 messages but the OS is only able to allocate 2 messages, the device may make the best use of 2 IRQs if it enables the messages at slots 1 and 4 rather than default of using the first N slots (or indicies) at 1 and 2. To support this, add a new pci_remap_msix() function that a driver may call after a successful pci_alloc_msix() (but before allocating any of the SYS_RES_IRQ resources) to allow the allocated IRQ resources to be assigned to different message indices. For example, from the earlier example, after pci_alloc_msix() returned a value of 2, the driver would call pci_remap_msix() passing in array of integers { 1, 4 } as the new message indices to use. The rid's for the SYS_RES_IRQ resources will always match the message indices. Thus, after the call to pci_remap_msix() the driver would be able to access the first message in slot 1 at SYS_RES_IRQ rid 1, and the second message at slot 4 at SYS_RES_IRQ rid 4. Note that the message slots/indices are 1-based rather than 0-based so that they will always correspond to the rid values (SYS_RES_IRQ rid 0 is reserved for the legacy INTx interrupt). To support this API, a new PCIB_REMAP_MSIX() method was added to the pcib interface to change the message index for a single IRQ. Tested by: scottl
Notes
Notes: svn path=/head/; revision=166176
-rw-r--r--sys/amd64/amd64/mptable_pci.c2
-rw-r--r--sys/amd64/amd64/msi.c24
-rw-r--r--sys/amd64/amd64/nexus.c9
-rw-r--r--sys/amd64/include/intr_machdep.h1
-rw-r--r--sys/amd64/pci/pci_bus.c1
-rw-r--r--sys/dev/acpica/acpi_pcib_acpi.c1
-rw-r--r--sys/dev/acpica/acpi_pcib_pci.c1
-rw-r--r--sys/dev/pci/pci.c170
-rw-r--r--sys/dev/pci/pci_if.m17
-rw-r--r--sys/dev/pci/pci_pci.c11
-rw-r--r--sys/dev/pci/pci_private.h4
-rw-r--r--sys/dev/pci/pcib_if.m10
-rw-r--r--sys/dev/pci/pcib_private.h1
-rw-r--r--sys/dev/pci/pcivar.h18
-rw-r--r--sys/i386/i386/mptable_pci.c2
-rw-r--r--sys/i386/i386/msi.c24
-rw-r--r--sys/i386/i386/nexus.c9
-rw-r--r--sys/i386/include/intr_machdep.h1
-rw-r--r--sys/i386/pci/pci_bus.c2
19 files changed, 281 insertions, 27 deletions
diff --git a/sys/amd64/amd64/mptable_pci.c b/sys/amd64/amd64/mptable_pci.c
index 851b8270e052..d8ed06987317 100644
--- a/sys/amd64/amd64/mptable_pci.c
+++ b/sys/amd64/amd64/mptable_pci.c
@@ -120,6 +120,7 @@ static device_method_t mptable_hostb_methods[] = {
DEVMETHOD(pcib_alloc_msi, mptable_hostb_alloc_msi),
DEVMETHOD(pcib_release_msi, pcib_release_msi),
DEVMETHOD(pcib_alloc_msix, mptable_hostb_alloc_msix),
+ DEVMETHOD(pcib_remap_msix, pcib_remap_msix),
DEVMETHOD(pcib_release_msix, pcib_release_msix),
{ 0, 0 }
@@ -176,6 +177,7 @@ static device_method_t mptable_pcib_pci_methods[] = {
DEVMETHOD(pcib_alloc_msi, pcib_alloc_msi),
DEVMETHOD(pcib_release_msi, pcib_release_msi),
DEVMETHOD(pcib_alloc_msix, pcib_alloc_msix),
+ DEVMETHOD(pcib_remap_msix, pcib_remap_msix),
DEVMETHOD(pcib_release_msix, pcib_release_msix),
{0, 0}
diff --git a/sys/amd64/amd64/msi.c b/sys/amd64/amd64/msi.c
index 7be702a861a1..321078c638e1 100644
--- a/sys/amd64/amd64/msi.c
+++ b/sys/amd64/amd64/msi.c
@@ -480,6 +480,30 @@ msix_alloc(device_t dev, int index, int *irq, int *new)
}
int
+msix_remap(int index, int irq)
+{
+ struct msi_intsrc *msi;
+
+ sx_xlock(&msi_sx);
+ msi = (struct msi_intsrc *)intr_lookup_source(irq);
+ if (msi == NULL) {
+ sx_xunlock(&msi_sx);
+ return (ENOENT);
+ }
+
+ /* Make sure this is an MSI-X message. */
+ if (!msi->msi_msix) {
+ sx_xunlock(&msi_sx);
+ return (EINVAL);
+ }
+
+ KASSERT(msi->msi_dev != NULL, ("unowned message"));
+ msi->msi_index = index;
+ sx_xunlock(&msi_sx);
+ return (0);
+}
+
+int
msix_release(int irq)
{
struct msi_intsrc *msi;
diff --git a/sys/amd64/amd64/nexus.c b/sys/amd64/amd64/nexus.c
index c06f6298a7e2..01e9f4261067 100644
--- a/sys/amd64/amd64/nexus.c
+++ b/sys/amd64/amd64/nexus.c
@@ -105,6 +105,7 @@ static void nexus_delete_resource(device_t, device_t, int, int);
static int nexus_alloc_msi(device_t pcib, device_t dev, int count, int maxcount, int *irqs);
static int nexus_release_msi(device_t pcib, device_t dev, int count, int *irqs);
static int nexus_alloc_msix(device_t pcib, device_t dev, int index, int *irq);
+static int nexus_remap_msix(device_t pcib, device_t dev, int index, int irq);
static int nexus_release_msix(device_t pcib, device_t dev, int irq);
static device_method_t nexus_methods[] = {
@@ -135,6 +136,7 @@ static device_method_t nexus_methods[] = {
DEVMETHOD(pcib_alloc_msi, nexus_alloc_msi),
DEVMETHOD(pcib_release_msi, nexus_release_msi),
DEVMETHOD(pcib_alloc_msix, nexus_alloc_msix),
+ DEVMETHOD(pcib_remap_msix, nexus_remap_msix),
DEVMETHOD(pcib_release_msix, nexus_release_msix),
{ 0, 0 }
@@ -510,6 +512,13 @@ nexus_alloc_msix(device_t pcib, device_t dev, int index, int *irq)
}
static int
+nexus_remap_msix(device_t pcib, device_t dev, int index, int irq)
+{
+
+ return (msix_remap(index, irq));
+}
+
+static int
nexus_release_msix(device_t pcib, device_t dev, int irq)
{
diff --git a/sys/amd64/include/intr_machdep.h b/sys/amd64/include/intr_machdep.h
index 1dca25b451ba..c0054b138bb6 100644
--- a/sys/amd64/include/intr_machdep.h
+++ b/sys/amd64/include/intr_machdep.h
@@ -152,6 +152,7 @@ int msi_alloc(device_t dev, int count, int maxcount, int *irqs, int *newirq,
void msi_init(void);
int msi_release(int *irqs, int count);
int msix_alloc(device_t dev, int index, int *irq, int *new);
+int msix_remap(int index, int irq);
int msix_release(int irq);
#endif /* !LOCORE */
diff --git a/sys/amd64/pci/pci_bus.c b/sys/amd64/pci/pci_bus.c
index 5384831d5217..37a4c6a6c4ec 100644
--- a/sys/amd64/pci/pci_bus.c
+++ b/sys/amd64/pci/pci_bus.c
@@ -347,6 +347,7 @@ static device_method_t legacy_pcib_methods[] = {
DEVMETHOD(pcib_alloc_msi, legacy_pcib_alloc_msi),
DEVMETHOD(pcib_release_msi, pcib_release_msi),
DEVMETHOD(pcib_alloc_msix, legacy_pcib_alloc_msix),
+ DEVMETHOD(pcib_remap_msix, pcib_remap_msix),
DEVMETHOD(pcib_release_msix, pcib_release_msix),
{ 0, 0 }
diff --git a/sys/dev/acpica/acpi_pcib_acpi.c b/sys/dev/acpica/acpi_pcib_acpi.c
index 5fe703f82394..0cf1208e7548 100644
--- a/sys/dev/acpica/acpi_pcib_acpi.c
+++ b/sys/dev/acpica/acpi_pcib_acpi.c
@@ -110,6 +110,7 @@ static device_method_t acpi_pcib_acpi_methods[] = {
DEVMETHOD(pcib_alloc_msi, acpi_pcib_alloc_msi),
DEVMETHOD(pcib_release_msi, pcib_release_msi),
DEVMETHOD(pcib_alloc_msix, acpi_pcib_alloc_msix),
+ DEVMETHOD(pcib_remap_msix, pcib_remap_msix),
DEVMETHOD(pcib_release_msix, pcib_release_msix),
{0, 0}
diff --git a/sys/dev/acpica/acpi_pcib_pci.c b/sys/dev/acpica/acpi_pcib_pci.c
index 36a36a5c599c..1b4d6a8cb1fd 100644
--- a/sys/dev/acpica/acpi_pcib_pci.c
+++ b/sys/dev/acpica/acpi_pcib_pci.c
@@ -96,6 +96,7 @@ static device_method_t acpi_pcib_pci_methods[] = {
DEVMETHOD(pcib_alloc_msi, pcib_alloc_msi),
DEVMETHOD(pcib_release_msi, pcib_release_msi),
DEVMETHOD(pcib_alloc_msix, pcib_alloc_msix),
+ DEVMETHOD(pcib_remap_msix, pcib_remap_msix),
DEVMETHOD(pcib_release_msix, pcib_release_msix),
{0, 0}
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index dc930c1aa5c4..7ef07e2ced8f 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -101,6 +101,7 @@ static void pci_write_vpd_reg(device_t pcib, pcicfgregs *cfg,
int reg, uint32_t data);
#endif
static void pci_read_vpd(device_t pcib, pcicfgregs *cfg);
+static int pci_msi_blacklisted(void);
static device_method_t pci_methods[] = {
/* Device interface */
@@ -145,8 +146,11 @@ static device_method_t pci_methods[] = {
DEVMETHOD(pci_assign_interrupt, pci_assign_interrupt_method),
DEVMETHOD(pci_find_extcap, pci_find_extcap_method),
DEVMETHOD(pci_alloc_msi, pci_alloc_msi_method),
+ DEVMETHOD(pci_alloc_msix, pci_alloc_msix_method),
+ DEVMETHOD(pci_remap_msix, pci_remap_msix_method),
DEVMETHOD(pci_release_msi, pci_release_msi_method),
DEVMETHOD(pci_msi_count, pci_msi_count_method),
+ DEVMETHOD(pci_msix_count, pci_msix_count_method),
{ 0, 0 }
};
@@ -1024,14 +1028,36 @@ pci_pending_msix(device_t dev, u_int index)
return (bus_read_4(cfg->msix.msix_pba_res, offset) & bit);
}
-static int
-pci_alloc_msix(device_t dev, device_t child, int *count)
+/*
+ * Attempt to allocate *count MSI-X messages. The actual number allocated is
+ * returned in *count. After this function returns, each message will be
+ * available to the driver as SYS_RES_IRQ resources starting at rid 1.
+ */
+int
+pci_alloc_msix_method(device_t dev, device_t child, int *count)
{
struct pci_devinfo *dinfo = device_get_ivars(child);
pcicfgregs *cfg = &dinfo->cfg;
struct resource_list_entry *rle;
int actual, error, i, irq, max;
+ /* Don't let count == 0 get us into trouble. */
+ if (*count == 0)
+ return (EINVAL);
+
+ /* If rid 0 is allocated, then fail. */
+ rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, 0);
+ if (rle != NULL && rle->res != NULL)
+ return (ENXIO);
+
+ /* Already have allocated messages? */
+ if (cfg->msi.msi_alloc != 0 || cfg->msix.msix_alloc != 0)
+ return (ENXIO);
+
+ /* If MSI is blacklisted for this system, fail. */
+ if (pci_msi_blacklisted())
+ return (ENXIO);
+
/* MSI-X capability present? */
if (cfg->msix.msix_location == 0 || !pci_do_msix)
return (ENODEV);
@@ -1052,10 +1078,6 @@ pci_alloc_msix(device_t dev, device_t child, int *count)
}
cfg->msix.msix_pba_res = rle->res;
- /* Already have allocated messages? */
- if (cfg->msix.msix_alloc != 0)
- return (ENXIO);
-
if (bootverbose)
device_printf(child,
"attempting to allocate %d MSI-X vectors (%d supported)\n",
@@ -1132,24 +1154,105 @@ pci_alloc_msix(device_t dev, device_t child, int *count)
return (0);
}
+/*
+ * By default, pci_alloc_msix() will assign the allocated IRQ resources to
+ * the first N messages in the MSI-X table. However, device drivers may
+ * want to use different layouts in the case that they do not allocate a
+ * full table. This method allows the driver to specify what layout it
+ * wants. It must be called after a successful pci_alloc_msix() but
+ * before any of the associated SYS_RES_IRQ resources are allocated via
+ * bus_alloc_resource(). The 'indices' array contains N (where N equals
+ * the 'count' returned from pci_alloc_msix()) message indices. The
+ * indices are 1-based (meaning the first message is at index 1). On
+ * successful return, each of the messages in the 'indices' array will
+ * have an associated SYS_RES_IRQ whose rid is equal to the index. Thus,
+ * if indices contains { 2, 4 }, then upon successful return, the 'child'
+ * device will have two SYS_RES_IRQ resources available at rids 2 and 4.
+ */
+int
+pci_remap_msix_method(device_t dev, device_t child, u_int *indices)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(child);
+ pcicfgregs *cfg = &dinfo->cfg;
+ struct resource_list_entry *rle;
+ int count, error, i, j, *irqs;
+
+ /* Sanity check the indices. */
+ for (i = 0; i < cfg->msix.msix_alloc; i++)
+ if (indices[i] == 0 || indices[i] > cfg->msix.msix_msgnum)
+ return (EINVAL);
+
+ /* Check for duplicates. */
+ for (i = 0; i < cfg->msix.msix_alloc; i++)
+ for (j = i + 1; j < cfg->msix.msix_alloc; j++)
+ if (indices[i] == indices[j])
+ return (EINVAL);
+
+ /* Make sure none of the resources are allocated. */
+ for (i = 1, count = 0; count < cfg->msix.msix_alloc; i++) {
+ rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i);
+ if (rle == NULL)
+ continue;
+ if (rle->res != NULL)
+ return (EBUSY);
+ count++;
+ }
+
+ /* Save the IRQ values and free the existing resources. */
+ irqs = malloc(sizeof(int) * cfg->msix.msix_alloc, M_TEMP, M_WAITOK);
+ for (i = 1, count = 0; count < cfg->msix.msix_alloc; i++) {
+ rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i);
+ if (rle == NULL)
+ continue;
+ irqs[count] = rle->start;
+ resource_list_delete(&dinfo->resources, SYS_RES_IRQ, i);
+ count++;
+ }
+
+ /* Map the IRQ values to the new message indices and rids. */
+ for (i = 0; i < cfg->msix.msix_alloc; i++) {
+ resource_list_add(&dinfo->resources, SYS_RES_IRQ, indices[i],
+ irqs[i], irqs[i], 1);
+ error = PCIB_REMAP_MSIX(device_get_parent(dev), child,
+ indices[i], irqs[i]);
+ KASSERT(error == 0, ("Failed to remap MSI-X message"));
+ }
+ if (bootverbose) {
+ if (cfg->msix.msix_alloc == 1)
+ device_printf(child,
+ "Remapped MSI-X IRQ to index %d\n", indices[0]);
+ else {
+ device_printf(child, "Remapped MSI-X IRQs to indices");
+ for (i = 0; i < cfg->msix.msix_alloc - 1; i++)
+ printf(" %d,", indices[i]);
+ printf(" %d\n", indices[cfg->msix.msix_alloc - 1]);
+ }
+ }
+ free(irqs, M_TEMP);
+
+ return (0);
+}
+
static int
pci_release_msix(device_t dev, device_t child)
{
struct pci_devinfo *dinfo = device_get_ivars(child);
pcicfgregs *cfg = &dinfo->cfg;
struct resource_list_entry *rle;
- int i;
+ int count, i;
/* Do we have any messages to release? */
if (cfg->msix.msix_alloc == 0)
return (ENODEV);
/* Make sure none of the resources are allocated. */
- for (i = 0; i < cfg->msix.msix_alloc; i++) {
- rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i + 1);
- KASSERT(rle != NULL, ("missing MSI resource"));
+ for (i = 1, count = 0; count < cfg->msix.msix_alloc; i++) {
+ rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i);
+ if (rle == NULL)
+ continue;
if (rle->res != NULL)
return (EBUSY);
+ count++;
}
/* Update control register with to disable MSI-X. */
@@ -1158,11 +1261,14 @@ pci_release_msix(device_t dev, device_t child)
cfg->msix.msix_ctrl, 2);
/* Release the messages. */
- for (i = 0; i < cfg->msix.msix_alloc; i++) {
- rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i + 1);
+ for (i = 1, count = 0; count < cfg->msix.msix_alloc; i++) {
+ rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i);
+ if (rle == NULL)
+ continue;
PCIB_RELEASE_MSIX(device_get_parent(dev), child,
rle->start);
- resource_list_delete(&dinfo->resources, SYS_RES_IRQ, i + 1);
+ resource_list_delete(&dinfo->resources, SYS_RES_IRQ, i);
+ count++;
}
/* Update alloc count. */
@@ -1171,6 +1277,23 @@ pci_release_msix(device_t dev, device_t child)
}
/*
+ * Return the max supported MSI-X messages this device supports.
+ * Basically, assuming the MD code can alloc messages, this function
+ * should return the maximum value that pci_alloc_msix() can return.
+ * Thus, it is subject to the tunables, etc.
+ */
+int
+pci_msix_count_method(device_t dev, device_t child)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(child);
+ pcicfgregs *cfg = &dinfo->cfg;
+
+ if (pci_do_msix && cfg->msix.msix_location != 0)
+ return (cfg->msix.msix_msgnum);
+ return (0);
+}
+
+/*
* Support for MSI message signalled interrupts.
*/
void
@@ -1294,23 +1417,18 @@ pci_alloc_msi_method(device_t dev, device_t child, int *count)
if (rle != NULL && rle->res != NULL)
return (ENXIO);
+ /* Already have allocated messages? */
+ if (cfg->msi.msi_alloc != 0 || cfg->msix.msix_alloc != 0)
+ return (ENXIO);
+
/* If MSI is blacklisted for this system, fail. */
if (pci_msi_blacklisted())
return (ENXIO);
- /* Try MSI-X first. */
- error = pci_alloc_msix(dev, child, count);
- if (error != ENODEV)
- return (error);
-
/* MSI capability present? */
if (cfg->msi.msi_location == 0 || !pci_do_msi)
return (ENODEV);
- /* Already have allocated messages? */
- if (cfg->msi.msi_alloc != 0)
- return (ENXIO);
-
if (bootverbose)
device_printf(child,
"attempting to allocate %d MSI vectors (%d supported)\n",
@@ -1444,10 +1562,10 @@ pci_release_msi_method(device_t dev, device_t child)
}
/*
- * Return the max supported MSI or MSI-X messages this device supports.
+ * Return the max supported MSI messages this device supports.
* Basically, assuming the MD code can alloc messages, this function
- * should return the maximum value that pci_alloc_msi() can return. Thus,
- * it is subject to the tunables, etc.
+ * should return the maximum value that pci_alloc_msi() can return.
+ * Thus, it is subject to the tunables, etc.
*/
int
pci_msi_count_method(device_t dev, device_t child)
@@ -1455,8 +1573,6 @@ pci_msi_count_method(device_t dev, device_t child)
struct pci_devinfo *dinfo = device_get_ivars(child);
pcicfgregs *cfg = &dinfo->cfg;
- if (pci_do_msix && cfg->msix.msix_location != 0)
- return (cfg->msix.msix_msgnum);
if (pci_do_msi && cfg->msi.msi_location != 0)
return (cfg->msi.msi_msgnum);
return (0);
diff --git a/sys/dev/pci/pci_if.m b/sys/dev/pci/pci_if.m
index ba6041520b01..266ec34d10f9 100644
--- a/sys/dev/pci/pci_if.m
+++ b/sys/dev/pci/pci_if.m
@@ -118,6 +118,18 @@ METHOD int alloc_msi {
int *count;
};
+METHOD int alloc_msix {
+ device_t dev;
+ device_t child;
+ int *count;
+};
+
+METHOD int remap_msix {
+ device_t dev;
+ device_t child;
+ u_int *indices;
+};
+
METHOD int release_msi {
device_t dev;
device_t child;
@@ -127,3 +139,8 @@ METHOD int msi_count {
device_t dev;
device_t child;
} DEFAULT null_msi_count;
+
+METHOD int msix_count {
+ device_t dev;
+ device_t child;
+} DEFAULT null_msi_count;
diff --git a/sys/dev/pci/pci_pci.c b/sys/dev/pci/pci_pci.c
index 8accc42033cb..6e947981d8b1 100644
--- a/sys/dev/pci/pci_pci.c
+++ b/sys/dev/pci/pci_pci.c
@@ -82,6 +82,7 @@ static device_method_t pcib_methods[] = {
DEVMETHOD(pcib_alloc_msi, pcib_alloc_msi),
DEVMETHOD(pcib_release_msi, pcib_release_msi),
DEVMETHOD(pcib_alloc_msix, pcib_alloc_msix),
+ DEVMETHOD(pcib_remap_msix, pcib_remap_msix),
DEVMETHOD(pcib_release_msix, pcib_release_msix),
{ 0, 0 }
@@ -583,6 +584,16 @@ pcib_alloc_msix(device_t pcib, device_t dev, int index, int *irq)
return (PCIB_ALLOC_MSIX(device_get_parent(bus), dev, index, irq));
}
+/* Pass request to remap an MSI-X message up to the parent bridge. */
+int
+pcib_remap_msix(device_t pcib, device_t dev, int index, int irq)
+{
+ device_t bus;
+
+ bus = device_get_parent(pcib);
+ return (PCIB_REMAP_MSIX(device_get_parent(bus), dev, index, irq));
+}
+
/* Pass request to release an MSI-X message up to the parent bridge. */
int
pcib_release_msix(device_t pcib, device_t dev, int irq)
diff --git a/sys/dev/pci/pci_private.h b/sys/dev/pci/pci_private.h
index 9aea42d4bae4..32be4aa59fa8 100644
--- a/sys/dev/pci/pci_private.h
+++ b/sys/dev/pci/pci_private.h
@@ -67,8 +67,12 @@ int pci_disable_io_method(device_t dev, device_t child, int space);
int pci_find_extcap_method(device_t dev, device_t child,
int capability, int *capreg);
int pci_alloc_msi_method(device_t dev, device_t child, int *count);
+int pci_alloc_msix_method(device_t dev, device_t child, int *count);
+int pci_remap_msix_method(device_t dev, device_t child,
+ u_int *indices);
int pci_release_msi_method(device_t dev, device_t child);
int pci_msi_count_method(device_t dev, device_t child);
+int pci_msix_count_method(device_t dev, device_t child);
struct resource *pci_alloc_resource(device_t dev, device_t child,
int type, int *rid, u_long start, u_long end, u_long count,
u_int flags);
diff --git a/sys/dev/pci/pcib_if.m b/sys/dev/pci/pcib_if.m
index 0e12c0038504..18755faf6539 100644
--- a/sys/dev/pci/pcib_if.m
+++ b/sys/dev/pci/pcib_if.m
@@ -126,6 +126,16 @@ METHOD int alloc_msix {
};
#
+# Remap a single MSI-X message to a different index.
+#
+METHOD int remap_msix {
+ device_t pcib;
+ device_t dev;
+ int index;
+ int irq;
+};
+
+#
# Release a single MSI-X message mapped onto 'irq'.
#
METHOD int release_msix {
diff --git a/sys/dev/pci/pcib_private.h b/sys/dev/pci/pcib_private.h
index 9575df2b22db..a571578e1155 100644
--- a/sys/dev/pci/pcib_private.h
+++ b/sys/dev/pci/pcib_private.h
@@ -78,6 +78,7 @@ int pcib_route_interrupt(device_t pcib, device_t dev, int pin);
int pcib_alloc_msi(device_t pcib, device_t dev, int count, int maxcount, int *irqs);
int pcib_release_msi(device_t pcib, device_t dev, int count, int *irqs);
int pcib_alloc_msix(device_t pcib, device_t dev, int index, int *irq);
+int pcib_remap_msix(device_t pcib, device_t dev, int index, int irq);
int pcib_release_msix(device_t pcib, device_t dev, int irq);
#endif
diff --git a/sys/dev/pci/pcivar.h b/sys/dev/pci/pcivar.h
index eca13efaeb1f..a0757454c14c 100644
--- a/sys/dev/pci/pcivar.h
+++ b/sys/dev/pci/pcivar.h
@@ -396,6 +396,18 @@ pci_alloc_msi(device_t dev, int *count)
}
static __inline int
+pci_alloc_msix(device_t dev, int *count)
+{
+ return (PCI_ALLOC_MSIX(device_get_parent(dev), dev, count));
+}
+
+static __inline int
+pci_remap_msix(device_t dev, u_int *indices)
+{
+ return (PCI_REMAP_MSIX(device_get_parent(dev), dev, indices));
+}
+
+static __inline int
pci_release_msi(device_t dev)
{
return (PCI_RELEASE_MSI(device_get_parent(dev), dev));
@@ -407,6 +419,12 @@ pci_msi_count(device_t dev)
return (PCI_MSI_COUNT(device_get_parent(dev), dev));
}
+static __inline int
+pci_msix_count(device_t dev)
+{
+ return (PCI_MSIX_COUNT(device_get_parent(dev), dev));
+}
+
device_t pci_find_bsf(uint8_t, uint8_t, uint8_t);
device_t pci_find_device(uint16_t, uint16_t);
diff --git a/sys/i386/i386/mptable_pci.c b/sys/i386/i386/mptable_pci.c
index 851b8270e052..d8ed06987317 100644
--- a/sys/i386/i386/mptable_pci.c
+++ b/sys/i386/i386/mptable_pci.c
@@ -120,6 +120,7 @@ static device_method_t mptable_hostb_methods[] = {
DEVMETHOD(pcib_alloc_msi, mptable_hostb_alloc_msi),
DEVMETHOD(pcib_release_msi, pcib_release_msi),
DEVMETHOD(pcib_alloc_msix, mptable_hostb_alloc_msix),
+ DEVMETHOD(pcib_remap_msix, pcib_remap_msix),
DEVMETHOD(pcib_release_msix, pcib_release_msix),
{ 0, 0 }
@@ -176,6 +177,7 @@ static device_method_t mptable_pcib_pci_methods[] = {
DEVMETHOD(pcib_alloc_msi, pcib_alloc_msi),
DEVMETHOD(pcib_release_msi, pcib_release_msi),
DEVMETHOD(pcib_alloc_msix, pcib_alloc_msix),
+ DEVMETHOD(pcib_remap_msix, pcib_remap_msix),
DEVMETHOD(pcib_release_msix, pcib_release_msix),
{0, 0}
diff --git a/sys/i386/i386/msi.c b/sys/i386/i386/msi.c
index 7be702a861a1..321078c638e1 100644
--- a/sys/i386/i386/msi.c
+++ b/sys/i386/i386/msi.c
@@ -480,6 +480,30 @@ msix_alloc(device_t dev, int index, int *irq, int *new)
}
int
+msix_remap(int index, int irq)
+{
+ struct msi_intsrc *msi;
+
+ sx_xlock(&msi_sx);
+ msi = (struct msi_intsrc *)intr_lookup_source(irq);
+ if (msi == NULL) {
+ sx_xunlock(&msi_sx);
+ return (ENOENT);
+ }
+
+ /* Make sure this is an MSI-X message. */
+ if (!msi->msi_msix) {
+ sx_xunlock(&msi_sx);
+ return (EINVAL);
+ }
+
+ KASSERT(msi->msi_dev != NULL, ("unowned message"));
+ msi->msi_index = index;
+ sx_xunlock(&msi_sx);
+ return (0);
+}
+
+int
msix_release(int irq)
{
struct msi_intsrc *msi;
diff --git a/sys/i386/i386/nexus.c b/sys/i386/i386/nexus.c
index 43b0c906cb16..5012fc27427d 100644
--- a/sys/i386/i386/nexus.c
+++ b/sys/i386/i386/nexus.c
@@ -113,6 +113,7 @@ static void nexus_delete_resource(device_t, device_t, int, int);
static int nexus_alloc_msi(device_t pcib, device_t dev, int count, int maxcount, int *irqs);
static int nexus_release_msi(device_t pcib, device_t dev, int count, int *irqs);
static int nexus_alloc_msix(device_t pcib, device_t dev, int index, int *irq);
+static int nexus_remap_msix(device_t pcib, device_t dev, int index, int irq);
static int nexus_release_msix(device_t pcib, device_t dev, int irq);
#endif
@@ -145,6 +146,7 @@ static device_method_t nexus_methods[] = {
DEVMETHOD(pcib_alloc_msi, nexus_alloc_msi),
DEVMETHOD(pcib_release_msi, nexus_release_msi),
DEVMETHOD(pcib_alloc_msix, nexus_alloc_msix),
+ DEVMETHOD(pcib_remap_msix, nexus_remap_msix),
DEVMETHOD(pcib_release_msix, nexus_release_msix),
#endif
@@ -566,6 +568,13 @@ nexus_alloc_msix(device_t pcib, device_t dev, int index, int *irq)
}
static int
+nexus_remap_msix(device_t pcib, device_t dev, int index, int irq)
+{
+
+ return (msix_remap(index, irq));
+}
+
+static int
nexus_release_msix(device_t pcib, device_t dev, int irq)
{
diff --git a/sys/i386/include/intr_machdep.h b/sys/i386/include/intr_machdep.h
index 9bc48d9e639f..aa37fdf009c8 100644
--- a/sys/i386/include/intr_machdep.h
+++ b/sys/i386/include/intr_machdep.h
@@ -149,6 +149,7 @@ int msi_alloc(device_t dev, int count, int maxcount, int *irqs, int *newirq,
void msi_init(void);
int msi_release(int* irqs, int count);
int msix_alloc(device_t dev, int index, int *irq, int *new);
+int msix_remap(int index, int irq);
int msix_release(int irq);
#endif /* !LOCORE */
diff --git a/sys/i386/pci/pci_bus.c b/sys/i386/pci/pci_bus.c
index 8c1a53207ada..03663b239b05 100644
--- a/sys/i386/pci/pci_bus.c
+++ b/sys/i386/pci/pci_bus.c
@@ -559,6 +559,7 @@ static device_method_t legacy_pcib_methods[] = {
DEVMETHOD(pcib_alloc_msi, legacy_pcib_alloc_msi),
DEVMETHOD(pcib_release_msi, pcib_release_msi),
DEVMETHOD(pcib_alloc_msix, legacy_pcib_alloc_msix),
+ DEVMETHOD(pcib_remap_msix, pcib_remap_msix),
DEVMETHOD(pcib_release_msix, pcib_release_msix),
{ 0, 0 }
@@ -652,6 +653,7 @@ static device_method_t pcibios_pcib_pci_methods[] = {
DEVMETHOD(pcib_alloc_msi, pcib_alloc_msi),
DEVMETHOD(pcib_release_msi, pcib_release_msi),
DEVMETHOD(pcib_alloc_msix, pcib_alloc_msix),
+ DEVMETHOD(pcib_remap_msix, pcib_remap_msix),
DEVMETHOD(pcib_release_msix, pcib_release_msix),
{0, 0}