Page MenuHomeFreeBSD

D19646.id55719.diff
No OneTemporary

D19646.id55719.diff

Index: lib/libdevctl/devctl.h
===================================================================
--- lib/libdevctl/devctl.h
+++ lib/libdevctl/devctl.h
@@ -30,6 +30,7 @@
#include <stdbool.h>
+__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.3
===================================================================
--- lib/libdevctl/devctl.3
+++ lib/libdevctl/devctl.3
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd August 22, 2018
+.Dd April 2, 2019
.Dt DEVCTL 3
.Os
.Sh NAME
@@ -37,6 +37,7 @@
.Nm devctl_enable ,
.Nm devctl_freeze ,
.Nm devctl_rescan ,
+.Nm devctl_reset ,
.Nm devctl_resume ,
.Nm devctl_set_driver ,
.Nm devctl_suspend ,
@@ -63,6 +64,8 @@
.Ft int
.Fn devctl_rescan "const char *device"
.Ft int
+.Fn devctl_reset "const char *device" "bool detach"
+.Ft int
.Fn devctl_resume "const char *device"
.Ft int
.Fn devctl_set_driver "const char *device" "const char *driver" "bool force"
@@ -204,6 +207,15 @@
.Fn devctl_thaw
function resumes (thaws the freeze) probe and attach processing
initiated in response to drivers being loaded.
+.Pp
+The
+.Fn devctl_reset
+function resets the specified device using bus-specific reset method.
+The
+.Fa detach
+argument, if true, specifies that the device driver is detached before
+the reset, and re-attached afterwards.
+If false, the device is suspended before the reset, and resumed after.
.Sh RETURN VALUES
.Rv -std devctl_attach devctl_clear_driver devctl_delete devctl_detach \
devctl_disable devctl_enable devctl_suspend devctl_rescan devctl_resume \
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
@@ -126,6 +126,10 @@
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_child(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);
@@ -150,6 +154,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_child, pci_reset_child),
DEVMETHOD(bus_get_dma_tag, pci_get_dma_tag),
DEVMETHOD(bus_get_resource_list,pci_get_resource_list),
@@ -6387,6 +6394,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_child(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 <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/module.h>
+#include <sys/pciio.h>
#include <sys/rman.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
@@ -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_child(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_child, pcib_reset_child),
/* 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_child(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,16 @@
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);
+ }
};
/**
@@ -848,3 +858,48 @@
size_t _setsize;
cpuset_t *_cpuset;
} DEFAULT bus_generic_get_cpus;
+
+/**
+ * @brief Prepares the given child of the bus for reset
+ *
+ * Typically bus detaches or suspends children' drivers, and then
+ * calls this method to save bus-specific information, for instance,
+ * PCI config space, which is damaged by reset.
+ *
+ * The bus_helper_reset_prepare() helper is provided to ease
+ * implementing bus reset methods.
+ *
+ * @param _dev the bus device
+ * @param _child the child device
+ */
+METHOD int reset_prepare {
+ device_t _dev;
+ device_t _child;
+} DEFAULT null_reset_prepare;
+
+/**
+ * @brief Restores the child operations after the reset
+ *
+ * The bus_helper_reset_post() helper is provided to ease
+ * implementing bus reset methods.
+ *
+ * @param _dev the bus device
+ * @param _child the child device
+ */
+METHOD int reset_post {
+ device_t _dev;
+ device_t _child;
+} DEFAULT null_reset_post;
+
+/**
+ * @brief Performs reset of the child
+ *
+ * @param _dev the bus device
+ * @param _child the child device
+ * @param _flags DEVF_RESET_ flags
+ */
+METHOD int reset_child {
+ device_t _dev;
+ device_t _child;
+ int _flags;
+};
Index: sys/kern/subr_bus.c
===================================================================
--- sys/kern/subr_bus.c
+++ sys/kern/subr_bus.c
@@ -3864,6 +3864,74 @@
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);
+ error1 = (flags & DEVF_RESET_DETACH) != 0 ?
+ device_probe_and_attach(child) :
+ 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)
+ 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 +5624,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 +5850,14 @@
device_frozen = false;
}
break;
+ case DEV_RESET:
+ if ((req->dr_flags & ~(DEVF_RESET_DETACH)) != 0) {
+ error = EINVAL;
+ break;
+ }
+ error = BUS_RESET_CHILD(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 <sys/eventhandler.h>
@@ -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.8
===================================================================
--- usr.sbin/devctl/devctl.8
+++ usr.sbin/devctl/devctl.8
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd August 29, 2016
+.Dd April 2, 2019
.Dt DEVCTL 8
.Os
.Sh NAME
@@ -66,6 +66,10 @@
.Cm delete
.Op Fl f
.Ar device
+.Nm
+.Cm reset
+.Op Fl d
+.Ar device
.Sh DESCRIPTION
The
.Nm
@@ -119,6 +123,9 @@
.It Cm suspend Ar device
Suspend a device.
This may include placing the device in a reduced power state.
+.Pp
+If you have suspended a device explicitly and then do global
+system suspend/resume, the device will end up resumed.
.It Cm resume Ar device
Resume a suspended device to a fully working state.
.It Xo Cm set driver
@@ -166,7 +173,35 @@
This command should be used with care as a device that is deleted but present
can no longer be used unless the parent bus device rediscovers the device via
a rescan request.
+.It Xo Cm reset
+.Op Fl d
+.Ar device
+.Xc
+Reset the device, using bus-specific reset method.
+Drivers for the devices being reset are suspended around the reset.
+If the
+.Fl d
+option is specified, drivers are detached instead.
+.Pp
+Currently, resets are implemented for PCIe buses and PCI devices.
+For PCIe bus, the link is disabled and then re-trained, causing all
+children of the bus to reset.
+Use
+.Fl p
+option of
+.Xr devinfo 8
+tool to report parent bus for the device.
+For PCI device, if Function-Level Reset is implemented by it, FLR is
+tried first; if failed or not implemented, power reset is tried.
+.Pp
+If you have detached or suspended a child device explicitly and then
+do a reset, the child device will end up attached.
.El
+.Sh BUGS
+Currently there is no administrative flag to prevent re-attach or un-suspend
+of the manually detached or suspended devices after reset.
+Similarly, there is no flag to prevent un-suspending of the the manually
+suspended devices after system resume.
.Sh SEE ALSO
.Xr devctl 3 ,
.Xr devinfo 8
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[])
{

File Metadata

Mime Type
text/plain
Expires
Tue, Dec 24, 6:26 AM (19 h, 54 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15586062
Default Alt Text
D19646.id55719.diff (17 KB)

Event Timeline