Index: lib/libdevctl/devctl.h =================================================================== --- lib/libdevctl/devctl.h +++ lib/libdevctl/devctl.h @@ -30,6 +30,7 @@ #include +__BEGIN_DECLS int devctl_attach(const char *device); int devctl_detach(const char *device, bool force); int devctl_enable(const char *device); @@ -42,5 +43,7 @@ int devctl_delete(const char *device, bool force); int devctl_freeze(void); int devctl_thaw(void); +int devctl_reset(const char *device, bool detach); +__END_DECLS #endif /* !__DEVCTL_H__ */ Index: lib/libdevctl/devctl.c =================================================================== --- lib/libdevctl/devctl.c +++ lib/libdevctl/devctl.c @@ -158,3 +158,11 @@ return (devctl_simple_request(DEV_THAW, "", 0)); } + +int +devctl_reset(const char *device, bool detach) +{ + + return (devctl_simple_request(DEV_RESET, device, detach ? + DEVF_RESET_DETACH : 0)); +} Index: sys/amd64/vmm/io/ppt.c =================================================================== --- sys/amd64/vmm/io/ppt.c +++ sys/amd64/vmm/io/ppt.c @@ -356,25 +356,12 @@ static void ppt_pci_reset(device_t dev) { - int ps; if (pcie_flr(dev, - max(pcie_get_max_completion_timeout(dev) / 1000, 10), - true)) + max(pcie_get_max_completion_timeout(dev) / 1000, 10), true)) return; - /* - * If FLR fails, attempt a power-management reset by cycling - * the device in/out of D3 state. - * PCI spec says we can only go into D3 state from D0 state. - * Transition from D[12] into D0 before going to D3 state. - */ - ps = pci_get_powerstate(dev); - if (ps != PCI_POWERSTATE_D0 && ps != PCI_POWERSTATE_D3) - pci_set_powerstate(dev, PCI_POWERSTATE_D0); - if (pci_get_powerstate(dev) != PCI_POWERSTATE_D3) - pci_set_powerstate(dev, PCI_POWERSTATE_D3); - pci_set_powerstate(dev, ps); + pci_power_reset(dev); } int Index: sys/dev/pci/pci.c =================================================================== --- sys/dev/pci/pci.c +++ sys/dev/pci/pci.c @@ -125,6 +125,9 @@ u_int irq); static void pci_hint_device_unit(device_t acdev, device_t child, 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, enum pci_id_type type, uintptr_t *rid); @@ -149,6 +152,9 @@ DEVMETHOD(bus_driver_added, pci_driver_added), DEVMETHOD(bus_setup_intr, pci_setup_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_resource_list,pci_get_resource_list), @@ -6365,6 +6371,89 @@ 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 + * 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); + return (0); +} + +static int +pci_reset_prepare(device_t dev, device_t child) +{ + + if (dev == device_get_parent(child)) + pci_save_state(child); + return (0); +} + +static int +pci_reset(device_t dev, device_t child, int flags) +{ + int error; + + 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 * pci_match_device(device_t child, const struct pci_device_table *id, size_t nelt) { Index: sys/dev/pci/pci_pci.c =================================================================== --- sys/dev/pci/pci_pci.c +++ sys/dev/pci/pci_pci.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -80,6 +81,7 @@ #endif static int pcib_request_feature_default(device_t pcib, device_t dev, enum pci_feature feature); +static int pcib_reset(device_t dev, device_t child, int flags); static device_method_t pcib_methods[] = { /* Device interface */ @@ -106,6 +108,7 @@ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + DEVMETHOD(bus_reset, pcib_reset), /* pcib interface */ DEVMETHOD(pcib_maxslots, pcib_ari_maxslots), @@ -2909,3 +2912,31 @@ bus = device_get_parent(pcib); return (PCIB_REQUEST_FEATURE(device_get_parent(bus), dev, feature)); } + +static int +pcib_reset(device_t dev, device_t child, int flags) +{ + struct pci_devinfo *pdinfo; + int error; + + error = 0; + if (dev == NULL || device_get_parent(child) != dev) + goto out; + error = ENXIO; + if (device_get_devclass(child) != devclass_find("pci")) + goto out; + pdinfo = device_get_ivars(dev); + if (pdinfo->cfg.pcie.pcie_location != 0 && + (pdinfo->cfg.pcie.pcie_type != PCIEM_TYPE_DOWNSTREAM_PORT || + pdinfo->cfg.pcie.pcie_type == PCIEM_TYPE_ROOT_PORT)) { + error = bus_helper_reset_prepare(child, flags); + if (error == 0) { + error = pcie_link_reset(dev, + pdinfo->cfg.pcie.pcie_location); + /* XXXKIB call _post even if error != 0 ? */ + bus_helper_reset_post(child, flags); + } + } +out: + return (error); +} Index: sys/dev/pci/pcivar.h =================================================================== --- sys/dev/pci/pcivar.h +++ sys/dev/pci/pcivar.h @@ -681,6 +681,7 @@ void pci_restore_state(device_t dev); void pci_save_state(device_t dev); int pci_set_max_read_req(device_t dev, int size); +int pci_power_reset(device_t dev); uint32_t pcie_read_config(device_t dev, int reg, int width); void pcie_write_config(device_t dev, int reg, uint32_t value, int width); uint32_t pcie_adjust_config(device_t dev, int reg, uint32_t mask, @@ -688,6 +689,7 @@ bool pcie_flr(device_t dev, u_int max_delay, bool force); int pcie_get_max_completion_timeout(device_t dev); bool pcie_wait_for_pending_transactions(device_t dev, u_int max_delay); +int pcie_link_reset(device_t port, int pcie_location); void pci_print_faulted_dev(void); Index: sys/dev/smartpqi/smartpqi_cam.c =================================================================== --- sys/dev/smartpqi/smartpqi_cam.c +++ sys/dev/smartpqi/smartpqi_cam.c @@ -739,7 +739,7 @@ return error; } /* Check device reset */ - if (DEV_RESET(dvp)) { + if (dvp->reset_in_progress) { ccb->ccb_h.status = CAM_SCSI_BUSY | CAM_REQ_INPROG | CAM_BUSY; DBG_WARN("Device %d reset returned busy\n", ccb->ccb_h.target_id); return error; Index: sys/dev/smartpqi/smartpqi_defines.h =================================================================== --- sys/dev/smartpqi/smartpqi_defines.h +++ sys/dev/smartpqi/smartpqi_defines.h @@ -386,8 +386,6 @@ #define IS_AIO_PATH(dev) (dev->aio_enabled) #define IS_RAID_PATH(dev) (!dev->aio_enabled) -#define DEV_RESET(dvp) (dvp->reset_in_progress) - /* SOP data direction flags */ #define SOP_DATA_DIR_NONE 0x00 #define SOP_DATA_DIR_FROM_DEVICE 0x01 Index: sys/kern/bus_if.m =================================================================== --- sys/kern/bus_if.m +++ sys/kern/bus_if.m @@ -66,6 +66,21 @@ panic("bus_add_child is not implemented"); } + + static int null_reset_post(device_t bus, device_t dev) + { + return (0); + } + + static int null_reset_prepare(device_t bus, device_t dev) + { + return (0); + } + + static int null_reset(device_t bus, device_t dev, int flags) + { + return (ENXIO); + } }; /** @@ -848,3 +863,19 @@ size_t _setsize; cpuset_t *_cpuset; } DEFAULT bus_generic_get_cpus; + +METHOD int reset_prepare { + device_t _dev; + device_t _child; +} DEFAULT null_reset_prepare; + +METHOD int reset_post { + device_t _dev; + device_t _child; +} DEFAULT null_reset_post; + +METHOD int reset { + device_t _dev; + device_t _child; + int _flags; +} DEFAULT null_reset; Index: sys/kern/subr_bus.c =================================================================== --- sys/kern/subr_bus.c +++ sys/kern/subr_bus.c @@ -3864,6 +3864,79 @@ return (0); } +int +bus_helper_reset_post(device_t dev, int flags) +{ + device_t child; + int error, error1; + + error = 0; + TAILQ_FOREACH(child, &dev->children,link) { + BUS_RESET_POST(dev, child); + if ((flags & DEVF_RESET_DETACH) != 0) { + if (device_get_state(child) != DS_ATTACHED) + error1 = device_probe_and_attach(child); + } else { + error1 = BUS_RESUME_CHILD(dev, child); + } + if (error == 0 && error1 != 0) + error = error1; + } + return (error); +} + +static void +bus_helper_reset_prepare_rollback(device_t dev, device_t child, int flags) +{ + + child = TAILQ_NEXT(child, link); + if (child == NULL) + return; + TAILQ_FOREACH_FROM(child, &dev->children,link) { + BUS_RESET_POST(dev, child); + if ((flags & DEVF_RESET_DETACH) != 0) { + if (device_get_state(child) != DS_ATTACHED) + device_probe_and_attach(child); + } else { + BUS_RESUME_CHILD(dev, child); + } + } +} + +int +bus_helper_reset_prepare(device_t dev, int flags) +{ + device_t child; + int error; + + if (dev->state != DS_ATTACHED) + return (EBUSY); + + TAILQ_FOREACH_REVERSE(child, &dev->children, device_list, link) { + 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) { + error = BUS_RESET_PREPARE(dev, child); + if (error != 0) { + if ((flags & DEVF_RESET_DETACH) != 0) + device_probe_and_attach(child); + else + BUS_RESUME_CHILD(dev, child); + } + } + if (error != 0) { + bus_helper_reset_prepare_rollback(dev, child, flags); + return (error); + } + } + return (0); +} + + /** * @brief Helper function for implementing BUS_PRINT_CHILD(). * @@ -5556,6 +5629,7 @@ case DEV_CLEAR_DRIVER: case DEV_RESCAN: case DEV_DELETE: + case DEV_RESET: error = priv_check(td, PRIV_DRIVER); if (error == 0) error = find_device(req, &dev); @@ -5781,6 +5855,14 @@ device_frozen = false; } break; + case DEV_RESET: + if ((req->dr_flags & ~(DEVF_RESET_DETACH)) != 0) { + error = EINVAL; + break; + } + error = BUS_RESET(device_get_parent(dev), dev, + req->dr_flags); + break; } mtx_unlock(&Giant); return (error); Index: sys/sys/bus.h =================================================================== --- sys/sys/bus.h +++ sys/sys/bus.h @@ -130,6 +130,7 @@ #define DEV_DELETE _IOW('D', 10, struct devreq) #define DEV_FREEZE _IOW('D', 11, struct devreq) #define DEV_THAW _IOW('D', 12, struct devreq) +#define DEV_RESET _IOW('D', 13, struct devreq) /* Flags for DEV_DETACH and DEV_DISABLE. */ #define DEVF_FORCE_DETACH 0x0000001 @@ -143,6 +144,10 @@ /* Flags for DEV_DELETE. */ #define DEVF_FORCE_DELETE 0x0000001 +/* Flags for DEV_RESET */ +#define DEVF_RESET_DETACH 0x0000001 /* Detach drivers vs suspend + device */ + #ifdef _KERNEL #include @@ -494,6 +499,8 @@ struct resource_map *map); int bus_generic_write_ivar(device_t dev, device_t child, int which, uintptr_t value); +int bus_helper_reset_post(device_t dev, int flags); +int bus_helper_reset_prepare(device_t dev, int flags); int bus_null_rescan(device_t dev); /* Index: usr.sbin/devctl/devctl.c =================================================================== --- usr.sbin/devctl/devctl.c +++ usr.sbin/devctl/devctl.c @@ -82,7 +82,9 @@ " devctl rescan device\n" " devctl delete [-f] device\n" " devctl freeze\n" - " devctl thaw\n"); + " devctl thaw\n" + " devctl reset [-d] device\n" + ); exit(1); } @@ -384,6 +386,40 @@ } DEVCTL_COMMAND(top, thaw, thaw); +static void +reset_usage(void) +{ + + fprintf(stderr, "usage: devctl reset [-d] device\n"); + exit(1); +} + +static int +reset(int ac, char **av) +{ + bool detach; + int ch; + + detach = false; + while ((ch = getopt(ac, av, "d")) != -1) + switch (ch) { + case 'd': + detach = true; + break; + default: + reset_usage(); + } + ac -= optind; + av += optind; + + if (ac != 1) + reset_usage(); + if (devctl_reset(av[0], detach) < 0) + err(1, "Failed to reset %s", av[0]); + return (0); +} +DEVCTL_COMMAND(top, reset, reset); + int main(int ac, char *av[]) {