diff options
author | John Baldwin <jhb@FreeBSD.org> | 2007-01-22 21:48:44 +0000 |
---|---|---|
committer | John Baldwin <jhb@FreeBSD.org> | 2007-01-22 21:48:44 +0000 |
commit | 5fe82bca57a1dbad1538d277602bb047c78a73fc (patch) | |
tree | e54f3042911f50266b958edf09a9fe2525a0987a | |
parent | 7c32173ba888e963a1d352e14915f5527915a388 (diff) | |
download | src-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.c | 2 | ||||
-rw-r--r-- | sys/amd64/amd64/msi.c | 24 | ||||
-rw-r--r-- | sys/amd64/amd64/nexus.c | 9 | ||||
-rw-r--r-- | sys/amd64/include/intr_machdep.h | 1 | ||||
-rw-r--r-- | sys/amd64/pci/pci_bus.c | 1 | ||||
-rw-r--r-- | sys/dev/acpica/acpi_pcib_acpi.c | 1 | ||||
-rw-r--r-- | sys/dev/acpica/acpi_pcib_pci.c | 1 | ||||
-rw-r--r-- | sys/dev/pci/pci.c | 170 | ||||
-rw-r--r-- | sys/dev/pci/pci_if.m | 17 | ||||
-rw-r--r-- | sys/dev/pci/pci_pci.c | 11 | ||||
-rw-r--r-- | sys/dev/pci/pci_private.h | 4 | ||||
-rw-r--r-- | sys/dev/pci/pcib_if.m | 10 | ||||
-rw-r--r-- | sys/dev/pci/pcib_private.h | 1 | ||||
-rw-r--r-- | sys/dev/pci/pcivar.h | 18 | ||||
-rw-r--r-- | sys/i386/i386/mptable_pci.c | 2 | ||||
-rw-r--r-- | sys/i386/i386/msi.c | 24 | ||||
-rw-r--r-- | sys/i386/i386/nexus.c | 9 | ||||
-rw-r--r-- | sys/i386/include/intr_machdep.h | 1 | ||||
-rw-r--r-- | sys/i386/pci/pci_bus.c | 2 |
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} |