diff --git a/share/man/man9/pci_msi.9 b/share/man/man9/pci_msi.9 index c40f9dd..37228f3 100644 --- a/share/man/man9/pci_msi.9 +++ b/share/man/man9/pci_msi.9 @@ -24,7 +24,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd May 11, 2015 +.Dd Jul 15, 2015 .Dt PCI_MSI 9 .Os .Sh NAME @@ -38,6 +38,7 @@ .Nm pci_msix_alloc_exact , .Nm pci_msix_alloc_map , .Nm pci_intx_alloc , +.Nm pci_intr_alloc , .Nm pci_intr_release .Nd PCI MSI{,-X} manipulation functions .Sh SYNOPSIS @@ -63,9 +64,15 @@ .Ft int .Fn pci_intx_alloc "struct pci_attach_args *pa" \ "pci_intr_handle_t **ihp" +.Ft int +.Fn pci_intr_alloc "struct pci_attach_args *pa" \ +"pci_intr_handle_t **ihp" "pci_intr_type_t *counts" \ +"pci_intr_type_t max_type" .Ft void .Fn pci_intr_release "pci_chipset_tag_t pc" \ "pci_intr_handle_t *pih" "int count" +.Ft pci_intr_type_t +.Fn pci_intr_type "pci_intr_handle_t ih" .Sh DESCRIPTION The .Nm @@ -185,5 +192,107 @@ in contrast, and .Fn pci_msix_alloc have (the functions allocate memory for interrupt handlers). +.Pp +.Fn pci_intr_alloc +is wrapper function which select and automatically fallback +allocation functions according to the argument +.Fa counts . +The elements of +.Fa counts +array means each required interrupt count for INTx, MSI, and MSI-X. +The index count of +.Fa counts +must be +.Dv PCI_INTR_TYPE_SIZE . +.Fa max_type +does not mean array index counts of +.Fa counts +, it means "max" interrupt type such as INTx, MSI, and MSI-X. +I.e., if the driver wants to allocate int the following way: +.Bd -literal + 5 MSI-X + 1 MSI (if MSI-X allocation failed) + INTx (if MSI allocation failed either) +.Ed +the driver should call +.Fn pci_intr_alloc +in the following way: +.Bd -literal + int counts[PCI_INTR_TYPE_SIZE]; + counts[PCI_INTR_TYPE_MSIX] = 5; + counts[PCI_INTR_TYPE_MSI] = 1; + counts[PCI_INTR_TYPE_INTX] = 1; + error = pci_intr_alloc(pa, ihps, counts, + PCI_INTR_TYPE_MSIX); +.Ed +If the driver wants to allocate int the following way: +.Bd -literal + hardware max number MSI-X + 1 MSI (if MSI-X allocation failed) +.Ed +that is, the driver does not use INTx, the driver should call +.Fn pci_intr_alloc +in the following way: +.Bd -literal + int counts[PCI_INTR_TYPE_SIZE]; + counts[PCI_INTR_TYPE_MSIX] = -1; /* -1 means max */ + counts[PCI_INTR_TYPE_MSI] = 1; + counts[PCI_INTR_TYPE_INTX] = 0; /* 0 means not use */ + error = pci_intr_alloc(pa, ihps, counts, + PCI_INTR_TYPE_MSIX); +.Ed +If the driver wants to allocate int the following way: +.Bd -literal + 3 MSI + INTx (if MSI allocation failed) +.Ed +that is, the driver does not use MSI-X, the driver should call +.Fn pci_intr_alloc +in the following way: +.Bd -literal + int counts[PCI_INTR_TYPE_SIZE]; + counts[PCI_INTR_TYPE_MSIX] = 0; /* 0 means not use */ + counts[PCI_INTR_TYPE_MSI] = 3; + counts[PCI_INTR_TYPE_INTX] = 1; + error = pci_intr_alloc(pa, ihps, counts, + PCI_INTR_TYPE_MSI); +.Ed +If the driver wants to allocate int the following way: +.Bd -literal + 1 MSI + INTx (if MSI allocation failed) +.Ed +that is, general usage, the driver should call simply +.Fn pci_intr_alloc +in the following way: +.Bd -literal + error = pci_intr_alloc(pa, ihps, NULL, 0); +.Ed +.Fa max_type +is ignored in this case. +.Fn pci_intr_alloc +return zero on any allocation function success, and nonzero on +all allocation functions failure. On success, +.Fa counts +is overwritten by a really allocated count. +I.e, if 5 MSI-X is allocated, +.Fa counts +is +.Bd -literal + counts[PCI_INTR_TYPE_MSIX] == 5 + counts[PCI_INTR_TYPE_MSI] == 0 + counts[PCI_INTR_TYPE_INTX] == 0 +.Ed +on return. +.Ft pci_intr_type_t +return the interrupt type of +.Fa ih . +The return value is +.Dv PCI_INTR_TYPE_MSIX +for MSI-X, +.Dv PCI_INTR_TYPE_MSI +for MSI +.Dv PCI_INTR_TYPE_INTX +for others. .Sh SEE ALSO .Xr pci_intr 9 diff --git a/sys/arch/x86/include/pci_machdep_common.h b/sys/arch/x86/include/pci_machdep_common.h index 521635b..e943c69 100644 --- a/sys/arch/x86/include/pci_machdep_common.h +++ b/sys/arch/x86/include/pci_machdep_common.h @@ -120,6 +120,15 @@ void *pci_intr_establish(pci_chipset_tag_t, pci_intr_handle_t, void pci_intr_disestablish(pci_chipset_tag_t, void *); int pci_intr_distribute(void *, const kcpuset_t *, kcpuset_t *); +typedef enum { + PCI_INTR_TYPE_INTX = 0, + PCI_INTR_TYPE_MSI, + PCI_INTR_TYPE_MSIX, + PCI_INTR_TYPE_SIZE, +} pci_intr_type_t; + +pci_intr_type_t pci_intr_type(pci_intr_handle_t); + /* * If device drivers use MSI/MSI-X, they should use these API for INTx * instead of pci_intr_map(), because of conforming the pci_intr_handle @@ -128,6 +137,13 @@ int pci_intr_distribute(void *, const kcpuset_t *, kcpuset_t *); int pci_intx_alloc(const struct pci_attach_args *, pci_intr_handle_t **); +/* + * Wrapper function for generally unitied allocation to fallback MSI-X/MSI/INTx + * automatically. + */ +int pci_intr_alloc(const struct pci_attach_args *pa, + pci_intr_handle_t **, pci_intr_type_t *, pci_intr_type_t); + /* experimental MSI support */ int pci_msi_count(const struct pci_attach_args *); int pci_msi_alloc(const struct pci_attach_args *, diff --git a/sys/arch/x86/pci/pci_intr_machdep.c b/sys/arch/x86/pci/pci_intr_machdep.c index 14e5e0f..3702747 100644 --- a/sys/arch/x86/pci/pci_intr_machdep.c +++ b/sys/arch/x86/pci/pci_intr_machdep.c @@ -350,6 +350,20 @@ pci_intr_distribute(void *cookie, const kcpuset_t *newset, kcpuset_t *oldset) } #if NIOAPIC > 0 +pci_intr_type_t +pci_intr_type(pci_intr_handle_t ih) +{ + + if (INT_VIA_MSI(ih)) { + if (MSI_INT_IS_MSIX(ih)) + return PCI_INTR_TYPE_MSIX; + else + return PCI_INTR_TYPE_MSI; + } else { + return PCI_INTR_TYPE_INTX; + } +} + static void x86_pci_intx_release(pci_chipset_tag_t pc, pci_intr_handle_t *pih) { @@ -404,6 +418,107 @@ error: return error; } +/* + * Generally interrupt handler allocation wrapper function. + * pa : pci_attach_args + * ihps : interrupt handlers + * counts : The array of number of interrupt handlers. + * And overwritten allocated the number of handlers. + * CAUTION: The size of counts[] must be PCI_INTR_TYPE_SIZE. + * max_type : "max" type of using interrupts. See below. + * e.g.: + * If you want to use 5 MSI-X, 1 MSI, or INTx, you use "counts" as + * int counts[PCI_INTR_TYPE_SIZE]; + * counts[PCI_INTR_TYPE_MSIX] = 5; + * counts[PCI_INTR_TYPE_MSI] = 1; + * counts[PCI_INTR_TYPE_INTX] = 1; + * error = pci_intr_alloc(pa, ihps, counts, PCI_INTR_TYPE_MSIX); + * + * If you want to use hardware max number MSI-X or 1 MSI, + * and not to use INTx, you use "counts" as + * int counts[PCI_INTR_TYPE_SIZE]; + * counts[PCI_INTR_TYPE_MSIX] = -1; + * counts[PCI_INTR_TYPE_MSI] = 1; + * counts[PCI_INTR_TYPE_INTX] = 0; + * error = pci_intr_alloc(pa, ihps, counts, PCI_INTR_TYPE_MSIX); + * + * If you want to use 3 MSI or INTx, you can simply use this API like below + * int counts[PCI_INTR_TYPE_SIZE]; + * counts[PCI_INTR_TYPE_MSI] = 3; + * counts[PCI_INTR_TYPE_INTX] = 1; + * error = pci_intr_alloc(pa, ihps, counts, PCI_INTR_TYPE_MSI); + * + * If you want to use 1 MSI or INTx, you can simply use this API like below + * error = pci_intr_alloc(pa, ihps, NULL, 0); + * ^ ignored + */ +int +pci_intr_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, + pci_intr_type_t *counts, pci_intr_type_t max_type) +{ + int error; + int intx_count, msi_count, msix_count; + + intx_count = msi_count = msix_count = 0; + if (counts == NULL) { /* simple pattern */ + msi_count = 1; + intx_count = 1; + } else { + switch(max_type) { + case PCI_INTR_TYPE_MSIX: + msix_count = counts[PCI_INTR_TYPE_MSIX]; + /* FALLTHROUGH */ + case PCI_INTR_TYPE_MSI: + msi_count = counts[PCI_INTR_TYPE_MSI]; + /* FALLTHROUGH */ + case PCI_INTR_TYPE_INTX: + intx_count = counts[PCI_INTR_TYPE_INTX]; + break; + default: + return EINVAL; + } + } + + memset(counts, 0, sizeof(counts[0]) * PCI_INTR_TYPE_SIZE); + error = EINVAL; + + /* try MSI-X */ + if (msix_count == -1) /* use hardware max */ + msix_count = pci_msix_count(pa); + if (msix_count > 0) { + error = pci_msix_alloc_exact(pa, ihps, msix_count); + if (error == 0) { + counts[PCI_INTR_TYPE_MSIX] = msix_count; + goto out; + } + } + + /* try MSI */ + if (msi_count == -1) /* use hardware max */ + msi_count = pci_msi_count(pa); + if (msi_count > 0) { + error = pci_msi_alloc_exact(pa, ihps, msi_count); + if (error == 0) { + if (counts != NULL) { + counts[PCI_INTR_TYPE_MSI] = msi_count; + goto out; + } + } + } + + /* try INTx */ + if (intx_count != 0) { /* The number of INTx is always 1. */ + error = pci_intx_alloc(pa, ihps); + if (error == 0) { + if (counts != NULL) + counts[PCI_INTR_TYPE_INTX] = 1; + } + } + + out: + return error; +} + void pci_intr_release(pci_chipset_tag_t pc, pci_intr_handle_t *pih, int count) {