Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F105968999
D19646.id55719.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
17 KB
Referenced Files
None
Subscribers
None
D19646.id55719.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D19646: Add BUS_RESET() and devctl reset.
Attached
Detach File
Event Timeline
Log In to Comment