Add a first pass at a way to blacklist MSI on systems where it doesn't

work:
- A new PCI quirk (PCI_QUIRK_DISABLE_MSI) is added to the quirk table.
- A new pci_msi_device_blacklisted() determines if a passed in device
  matches an MSI quirk in the quirk table.  This can be overridden (all
  quirks ignored) by setting the hw.pci.honor_msi_blacklist to 0.
- A global blacklist check is performed in the MI PCI bus code by checking
  to see if the device at 0:0:0 is blacklisted.

Tested by:	jdp
This commit is contained in:
John Baldwin 2006-12-14 19:57:06 +00:00
parent 89273e7b6c
commit e31182d9ca
2 changed files with 52 additions and 0 deletions

View File

@ -165,6 +165,7 @@ struct pci_quirk {
uint32_t devid; /* Vendor/device of the card */
int type;
#define PCI_QUIRK_MAP_REG 1 /* PCI map register in weird place */
#define PCI_QUIRK_DISABLE_MSI 2 /* MSI/MSI-X doesn't work */
int arg1;
int arg2;
};
@ -224,6 +225,11 @@ TUNABLE_INT("hw.pci.enable_msix", &pci_do_msix);
SYSCTL_INT(_hw_pci, OID_AUTO, enable_msix, CTLFLAG_RW, &pci_do_msix, 1,
"Enable support for MSI-X interrupts");
static int pci_honor_msi_blacklist = 1;
TUNABLE_INT("hw.pci.honor_msi_blacklist", &pci_honor_msi_blacklist);
SYSCTL_INT(_hw_pci, OID_AUTO, honor_msi_blacklist, CTLFLAG_RD,
&pci_honor_msi_blacklist, 1, "Honor chipset blacklist for MSI");
/* Find a device_t by bus/slot/function */
device_t
@ -1194,6 +1200,47 @@ pci_resume_msi(device_t dev)
cfg->msi.msi_ctrl, 2);
}
/*
* Returns true if the specified device is blacklisted because MSI
* doesn't work.
*/
int
pci_msi_device_blacklisted(device_t dev)
{
struct pci_quirk *q;
if (!pci_honor_msi_blacklist)
return (0);
for (q = &pci_quirks[0]; q->devid; q++) {
if (q->devid == pci_get_devid(dev) &&
q->type == PCI_QUIRK_DISABLE_MSI)
return (1);
}
return (0);
}
/*
* Determine if MSI is blacklisted globally on this sytem. Currently,
* we just check for blacklisted chipsets as represented by the
* host-PCI bridge at device 0:0:0. In the future, it may become
* necessary to check other system attributes, such as the kenv values
* that give the motherboard manufacturer and model number.
*/
static int
pci_msi_blacklisted(void)
{
device_t dev;
if (!pci_honor_msi_blacklist)
return (0);
dev = pci_find_bsf(0, 0, 0);
if (dev != NULL)
return (pci_msi_device_blacklisted(dev));
return (0);
}
/*
* Attempt to allocate *count MSI messages. The actual number allocated is
* returned in *count. After this function returns, each message will be
@ -1217,6 +1264,10 @@ pci_alloc_msi_method(device_t dev, device_t child, int *count)
if (rle != NULL && rle->res != NULL)
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)

View File

@ -417,6 +417,7 @@ void pci_enable_msix(device_t dev, u_int index, uint64_t address,
void pci_mask_msix(device_t dev, u_int index);
int pci_pending_msix(device_t dev, u_int index);
void pci_unmask_msix(device_t dev, u_int index);
int pci_msi_device_blacklisted(device_t dev);
#endif /* _SYS_BUS_H_ */