Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

usb: xhci: optimize resuming from S4 (suspend-to-disk)

On resume from S4 (power loss after suspend/hibernation), the xHCI
driver previously freed, reallocated, and fully reinitialized all
data structures. Most of this is unnecessary because the data is
restored from a saved image; only the xHCI registers lose their values.

This patch optimizes S4 resume by performing only a host controller
reset, which includes:
* Freeing or clearing runtime-created data.
* Rewriting xHCI registers.

Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Link: https://patch.msgid.link/20260402131342.2628648-13-mathias.nyman@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Niklas Neronin and committed by
Greg Kroah-Hartman
2a70e5dc 951564d2

+35 -24
+2 -2
drivers/usb/host/xhci-mem.c
··· 936 936 * that tt_info, then free the child first. Recursive. 937 937 * We can't rely on udev at this point to find child-parent relationships. 938 938 */ 939 - static void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id) 939 + void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id) 940 940 { 941 941 struct xhci_virt_device *vdev; 942 942 struct list_head *tt_list_head; ··· 1905 1905 EXPORT_SYMBOL_GPL(xhci_remove_secondary_interrupter); 1906 1906 1907 1907 /* Cleanup roothub bandwidth data */ 1908 - static void xhci_rh_bw_cleanup(struct xhci_hcd *xhci) 1908 + void xhci_rh_bw_cleanup(struct xhci_hcd *xhci) 1909 1909 { 1910 1910 struct xhci_root_port_bw_info *rh_bw; 1911 1911 struct xhci_tt_bw_info *tt_info, *tt_next;
+31 -22
drivers/usb/host/xhci.c
··· 1082 1082 { 1083 1083 u32 command, temp = 0; 1084 1084 struct usb_hcd *hcd = xhci_to_hcd(xhci); 1085 + struct xhci_segment *seg; 1085 1086 int retval = 0; 1086 1087 bool pending_portevent = false; 1087 1088 bool suspended_usb3_devs = false; 1089 + bool reset_registers = false; 1088 1090 1089 1091 if (!hcd->state) 1090 1092 return 0; ··· 1105 1103 1106 1104 spin_lock_irq(&xhci->lock); 1107 1105 1108 - if (xhci->quirks & XHCI_RESET_ON_RESUME || xhci->broken_suspend) 1109 - power_lost = true; 1110 - 1111 - if (!power_lost) { 1106 + if (power_lost || xhci->broken_suspend || xhci->quirks & XHCI_RESET_ON_RESUME) { 1107 + xhci_dbg(xhci, "HC state lost, performing host controller reset\n"); 1108 + reset_registers = true; 1109 + } else { 1110 + xhci_dbg(xhci, "HC state intact, continuing without reset\n"); 1112 1111 /* 1113 1112 * Some controllers might lose power during suspend, so wait 1114 1113 * for controller not ready bit to clear, just as in xHC init. ··· 1147 1144 temp = readl(&xhci->op_regs->status); 1148 1145 if ((temp & (STS_SRE | STS_HCE)) && !(xhci->xhc_state & XHCI_STATE_REMOVING)) { 1149 1146 xhci_warn(xhci, "xHC error in resume, USBSTS 0x%x, Reinit\n", temp); 1150 - power_lost = true; 1147 + reset_registers = true; 1151 1148 } 1152 1149 } 1153 1150 1154 - if (power_lost) { 1151 + if (reset_registers) { 1155 1152 if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && 1156 1153 !(xhci_all_ports_seen_u0(xhci))) { 1157 1154 timer_delete_sync(&xhci->comp_mode_recovery_timer); ··· 1175 1172 if (retval) 1176 1173 return retval; 1177 1174 1178 - xhci_dbg(xhci, "// Disabling event ring interrupts\n"); 1179 - temp = readl(&xhci->op_regs->status); 1180 - writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); 1181 - xhci_disable_interrupter(xhci, xhci->interrupters[0]); 1175 + cancel_delayed_work_sync(&xhci->cmd_timer); 1182 1176 1183 - xhci_dbg(xhci, "cleaning up memory\n"); 1184 - xhci_mem_cleanup(xhci); 1177 + /* Delete all remaining commands */ 1178 + xhci_cleanup_command_queue(xhci); 1179 + 1180 + /* Clear data which is re-initilized during runtime */ 1181 + xhci_for_each_ring_seg(xhci->interrupters[0]->event_ring->first_seg, seg) 1182 + memset(seg->trbs, 0, sizeof(union xhci_trb) * TRBS_PER_SEGMENT); 1183 + 1184 + for (int i = xhci->max_slots; i > 0; i--) 1185 + xhci_free_virt_devices_depth_first(xhci, i); 1186 + 1187 + xhci_rh_bw_cleanup(xhci); 1188 + 1189 + xhci->cmd_ring_reserved_trbs = 0; 1190 + xhci_for_each_ring_seg(xhci->cmd_ring->first_seg, seg) 1191 + memset(seg->trbs, 0, sizeof(union xhci_trb) * TRBS_PER_SEGMENT); 1192 + 1185 1193 xhci_debugfs_exit(xhci); 1186 - xhci_dbg(xhci, "xhci_stop completed - status = %x\n", 1187 - readl(&xhci->op_regs->status)); 1188 - 1189 - /* USB core calls the PCI reinit and start functions twice: 1190 - * first with the primary HCD, and then with the secondary HCD. 1191 - * If we don't do the same, the host will never be started. 1192 - */ 1193 - retval = xhci_mem_init(xhci, GFP_KERNEL); 1194 - if (retval) 1195 - return retval; 1196 1194 1197 1195 xhci_init(hcd); 1198 1196 1197 + /* 1198 + * USB core calls the PCI reinit and start functions twice: 1199 + * first with the primary HCD, and then with the secondary HCD. 1200 + * If we don't do the same, the host will never be started. 1201 + */ 1199 1202 xhci_dbg(xhci, "Start the primary HCD\n"); 1200 1203 retval = xhci_run(hcd); 1201 1204 if (!retval && xhci->shared_hcd) {
+2
drivers/usb/host/xhci.h
··· 1793 1793 void xhci_mem_cleanup(struct xhci_hcd *xhci); 1794 1794 int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags); 1795 1795 void xhci_free_virt_device(struct xhci_hcd *xhci, struct xhci_virt_device *dev, int slot_id); 1796 + void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id); 1796 1797 int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, struct usb_device *udev, gfp_t flags); 1797 1798 int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *udev); 1798 1799 void xhci_copy_ep0_dequeue_into_input_ctx(struct xhci_hcd *xhci, ··· 1805 1804 struct xhci_virt_device *virt_dev, 1806 1805 int old_active_eps); 1807 1806 void xhci_clear_endpoint_bw_info(struct xhci_bw_info *bw_info); 1807 + void xhci_rh_bw_cleanup(struct xhci_hcd *xhci); 1808 1808 void xhci_update_bw_info(struct xhci_hcd *xhci, 1809 1809 struct xhci_container_ctx *in_ctx, 1810 1810 struct xhci_input_control_ctx *ctrl_ctx,