Changeset View
Standalone View
sys/dev/pci/pci.c
Show First 20 Lines • Show All 119 Lines • ▼ Show 20 Lines | |||||
static int pci_msi_blacklisted(void); | static int pci_msi_blacklisted(void); | ||||
static int pci_msix_blacklisted(void); | static int pci_msix_blacklisted(void); | ||||
static void pci_resume_msi(device_t dev); | static void pci_resume_msi(device_t dev); | ||||
static void pci_resume_msix(device_t dev); | static void pci_resume_msix(device_t dev); | ||||
static int pci_remap_intr_method(device_t bus, device_t dev, | static int pci_remap_intr_method(device_t bus, device_t dev, | ||||
u_int irq); | u_int irq); | ||||
static void pci_hint_device_unit(device_t acdev, device_t child, | static void pci_hint_device_unit(device_t acdev, device_t child, | ||||
const char *name, int *unitp); | const char *name, int *unitp); | ||||
static int pci_reset_post(device_t dev, device_t child); | |||||
static int pci_reset_prepare(device_t dev, device_t child); | |||||
static int pci_reset(device_t dev, device_t child, int flags); | |||||
static int pci_get_id_method(device_t dev, device_t child, | static int pci_get_id_method(device_t dev, device_t child, | ||||
enum pci_id_type type, uintptr_t *rid); | enum pci_id_type type, uintptr_t *rid); | ||||
static struct pci_devinfo * pci_fill_devinfo(device_t pcib, device_t bus, int d, | static struct pci_devinfo * pci_fill_devinfo(device_t pcib, device_t bus, int d, | ||||
int b, int s, int f, uint16_t vid, uint16_t did); | int b, int s, int f, uint16_t vid, uint16_t did); | ||||
static device_method_t pci_methods[] = { | static device_method_t pci_methods[] = { | ||||
/* Device interface */ | /* Device interface */ | ||||
DEVMETHOD(device_probe, pci_probe), | DEVMETHOD(device_probe, pci_probe), | ||||
DEVMETHOD(device_attach, pci_attach), | DEVMETHOD(device_attach, pci_attach), | ||||
DEVMETHOD(device_detach, pci_detach), | DEVMETHOD(device_detach, pci_detach), | ||||
DEVMETHOD(device_shutdown, bus_generic_shutdown), | DEVMETHOD(device_shutdown, bus_generic_shutdown), | ||||
DEVMETHOD(device_suspend, bus_generic_suspend), | DEVMETHOD(device_suspend, bus_generic_suspend), | ||||
DEVMETHOD(device_resume, pci_resume), | DEVMETHOD(device_resume, pci_resume), | ||||
/* Bus interface */ | /* Bus interface */ | ||||
DEVMETHOD(bus_print_child, pci_print_child), | DEVMETHOD(bus_print_child, pci_print_child), | ||||
DEVMETHOD(bus_probe_nomatch, pci_probe_nomatch), | DEVMETHOD(bus_probe_nomatch, pci_probe_nomatch), | ||||
DEVMETHOD(bus_read_ivar, pci_read_ivar), | DEVMETHOD(bus_read_ivar, pci_read_ivar), | ||||
DEVMETHOD(bus_write_ivar, pci_write_ivar), | DEVMETHOD(bus_write_ivar, pci_write_ivar), | ||||
DEVMETHOD(bus_driver_added, pci_driver_added), | DEVMETHOD(bus_driver_added, pci_driver_added), | ||||
DEVMETHOD(bus_setup_intr, pci_setup_intr), | DEVMETHOD(bus_setup_intr, pci_setup_intr), | ||||
DEVMETHOD(bus_teardown_intr, pci_teardown_intr), | DEVMETHOD(bus_teardown_intr, pci_teardown_intr), | ||||
DEVMETHOD(bus_reset_prepare, pci_reset_prepare), | |||||
DEVMETHOD(bus_reset_post, pci_reset_post), | |||||
DEVMETHOD(bus_reset, pci_reset), | |||||
DEVMETHOD(bus_get_dma_tag, pci_get_dma_tag), | DEVMETHOD(bus_get_dma_tag, pci_get_dma_tag), | ||||
DEVMETHOD(bus_get_resource_list,pci_get_resource_list), | DEVMETHOD(bus_get_resource_list,pci_get_resource_list), | ||||
DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), | DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), | ||||
DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), | DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), | ||||
DEVMETHOD(bus_delete_resource, pci_delete_resource), | DEVMETHOD(bus_delete_resource, pci_delete_resource), | ||||
DEVMETHOD(bus_alloc_resource, pci_alloc_resource), | DEVMETHOD(bus_alloc_resource, pci_alloc_resource), | ||||
DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), | DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), | ||||
▲ Show 20 Lines • Show All 6,198 Lines • ▼ Show 20 Lines | pcie_flr(device_t dev, u_int max_delay, bool force) | ||||
/* Wait for 100ms. */ | /* Wait for 100ms. */ | ||||
pause_sbt("pcieflr", (100 + compl_delay) * SBT_1MS, 0, C_HARDCLOCK); | pause_sbt("pcieflr", (100 + compl_delay) * SBT_1MS, 0, C_HARDCLOCK); | ||||
if (pci_read_config(dev, cap + PCIER_DEVICE_STA, 2) & | if (pci_read_config(dev, cap + PCIER_DEVICE_STA, 2) & | ||||
PCIEM_STA_TRANSACTION_PND) | PCIEM_STA_TRANSACTION_PND) | ||||
pci_printf(&dinfo->cfg, "Transactions pending after FLR!\n"); | pci_printf(&dinfo->cfg, "Transactions pending after FLR!\n"); | ||||
return (true); | return (true); | ||||
} | |||||
int | |||||
pci_power_reset(device_t dev) | |||||
{ | |||||
int ps; | |||||
ps = pci_get_powerstate(dev); | |||||
if (ps != PCI_POWERSTATE_D0 && ps != PCI_POWERSTATE_D3) | |||||
pci_set_powerstate(dev, PCI_POWERSTATE_D0); | |||||
pci_set_powerstate(dev, PCI_POWERSTATE_D3); | |||||
pci_set_powerstate(dev, ps); | |||||
return (0); | |||||
} | |||||
/* | |||||
* Try link drop and retrain of the downstream port of upstream | |||||
* switch, for PCIe. According to the PCIe 3.0 spec 6.6.1, this must | |||||
jhb: Has to be 'device_get_devclass(device_get_parent(port))' | |||||
Done Inline ActionsParent of the port is pcib, not pci class ? I never can understand this two-device_t per device stuff. kib: Parent of the port is pcib, not pci class ? I never can understand this two-device_t per… | |||||
* cause Conventional Hot reset of the device in the slot. | |||||
* Alternative, for PCIe, could be the secondary bus reset initiatied | |||||
* on the upstream switch PCIR_BRIDGECTL_1, bit 6. | |||||
*/ | |||||
int | |||||
pcie_link_reset(device_t port, int pcie_location) | |||||
{ | |||||
uint16_t v; | |||||
v = pci_read_config(port, pcie_location + PCIER_LINK_CTL, 2); | |||||
v |= PCIEM_LINK_CTL_LINK_DIS; | |||||
pci_write_config(port, pcie_location + PCIER_LINK_CTL, v, 2); | |||||
pause_sbt("pcier1", mstosbt(20), 0, 0); | |||||
v &= ~PCIEM_LINK_CTL_LINK_DIS; | |||||
v |= PCIEM_LINK_CTL_RETRAIN_LINK; | |||||
pci_write_config(port, pcie_location + PCIER_LINK_CTL, v, 2); | |||||
pause_sbt("pcier2", mstosbt(100), 0, 0); /* 100 ms */ | |||||
v = pci_read_config(port, pcie_location + PCIER_LINK_STA, 2); | |||||
return ((v & PCIEM_LINK_STA_TRAINING) != 0 ? ETIMEDOUT : 0); | |||||
} | |||||
static int | |||||
pci_reset_post(device_t dev, device_t child) | |||||
{ | |||||
if (dev == device_get_parent(child)) | |||||
pci_restore_state(child); | |||||
Not Done Inline Actionsdo you need to guard against hz < 50 here? Do we have a lower bound of HZ we support? imp: do you need to guard against hz < 50 here? Do we have a lower bound of HZ we support? | |||||
return (0); | |||||
} | |||||
static int | |||||
Not Done Inline ActionsDo we need to loop here to make sure that the link retraining finished? IIRC, the standard wants you to read the status register and wait for PCIE_LINK_STA_TRAINING to clear. imp: Do we need to loop here to make sure that the link retraining finished? IIRC, the standard… | |||||
pci_reset_prepare(device_t dev, device_t child) | |||||
{ | |||||
if (dev == device_get_parent(child)) | |||||
Not Done Inline ActionsOne issue with doing a link reset first is that it resets all functions on a multifunction device. In theory the D0->D3->D0 only affects a single function whereas a link reset affects everything downstream. Certainly I've passed through individual functions to a VM via ppt (e.g. VFs) while leaving the other functions on the host. It would be surprising to have ppt reset all of the functions which is what your change would do. jhb: One issue with doing a link reset first is that it resets all functions on a multifunction… | |||||
Done Inline ActionsIf pci_hard_reset() is not useful for ppt(4), then I do not see much point in bringing it in. kib: If pci_hard_reset() is not useful for ppt(4), then I do not see much point in bringing it in. | |||||
pci_save_state(child); | |||||
Not Done Inline ActionsShouldn't this be 'device_get_devclass(device_get_parent(dev))'? The 'dev' here is a leaf device like em0, so it's declass won't be "pci". jhb: Shouldn't this be 'device_get_devclass(device_get_parent(dev))'? The 'dev' here is a leaf… | |||||
return (0); | |||||
} | |||||
static int | |||||
pci_reset(device_t dev, device_t child, int flags) | |||||
Not Done Inline ActionsGiven that pcie_link_reset() already does checks, I would replace this entire block with just 'error = pcie_link_reset(device_get_parent(device_get_parent(dev));' jhb: Given that pcie_link_reset() already does checks, I would replace this entire block with just… | |||||
{ | |||||
int error; | |||||
Not Done Inline ActionsThis check then becomes redundant, and it would have to be. You've already assumed the ivars of 'dev' are PCI ivars, so that means the devclass of dev's parent has to be pci. jhb: This check then becomes redundant, and it would have to be. You've already assumed the ivars… | |||||
if (dev == NULL || device_get_parent(child) != dev) | |||||
return (0); | |||||
if ((flags & DEVF_RESET_DETACH) != 0) { | |||||
error = device_get_state(child) == DS_ATTACHED ? | |||||
device_detach(child) : 0; | |||||
} else { | |||||
error = BUS_SUSPEND_CHILD(dev, child); | |||||
} | |||||
if (error == 0) { | |||||
if (!pcie_flr(child, 1000, false)) { | |||||
error = BUS_RESET_PREPARE(dev, child); | |||||
if (error == 0) | |||||
pci_power_reset(child); | |||||
BUS_RESET_POST(dev, child); | |||||
} | |||||
if ((flags & DEVF_RESET_DETACH) != 0) | |||||
device_probe_and_attach(child); | |||||
else | |||||
BUS_RESUME_CHILD(dev, child); | |||||
} | |||||
return (error); | |||||
} | } | ||||
const struct pci_device_table * | const struct pci_device_table * | ||||
pci_match_device(device_t child, const struct pci_device_table *id, size_t nelt) | pci_match_device(device_t child, const struct pci_device_table *id, size_t nelt) | ||||
{ | { | ||||
bool match; | bool match; | ||||
uint16_t vendor, device, subvendor, subdevice, class, subclass, revid; | uint16_t vendor, device, subvendor, subdevice, class, subclass, revid; | ||||
Show All 23 Lines | while (nelt-- > 0) { | ||||
if (match) | if (match) | ||||
return (id); | return (id); | ||||
id++; | id++; | ||||
} | } | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
static void | static void | ||||
pci_print_faulted_dev_name(const struct pci_devinfo *dinfo) | pci_print_faulted_dev_name(const struct pci_devinfo *dinfo) | ||||
Not Done Inline ActionsWhy not just suspend the devices? You could just use 'bus_generic_suspend()' I think without needing device_get_children, etc.? jhb: Why not just suspend the devices? You could just use 'bus_generic_suspend()' I think without… | |||||
Done Inline ActionsSuspend is nop for significant number of devices, in particular for mlx5(4). Detach/reattach works for all of them. kib: Suspend is nop for significant number of devices, in particular for mlx5(4). Detach/reattach… | |||||
Not Done Inline Actionsdetach / attach destroys all saved state for the device. Is that OK? imp: detach / attach destroys all saved state for the device. Is that OK? | |||||
{ | { | ||||
const char *dev_name; | const char *dev_name; | ||||
device_t dev; | device_t dev; | ||||
dev = dinfo->cfg.dev; | dev = dinfo->cfg.dev; | ||||
printf("pci%d:%d:%d:%d", dinfo->cfg.domain, dinfo->cfg.bus, | printf("pci%d:%d:%d:%d", dinfo->cfg.domain, dinfo->cfg.bus, | ||||
dinfo->cfg.slot, dinfo->cfg.func); | dinfo->cfg.slot, dinfo->cfg.func); | ||||
dev_name = device_get_name(dev); | dev_name = device_get_name(dev); | ||||
▲ Show 20 Lines • Show All 114 Lines • Show Last 20 Lines |
Has to be 'device_get_devclass(device_get_parent(port))'