| From: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Date: Fri, 2 Jun 2017 16:36:26 +0300 |
| Subject: [PATCH] usb: optimize acpi companion search for usb port devices |
| References: bnc#1060662 |
| Patch-mainline: v4.12.9 |
| Git-commit: ed18c5fa945768a9bec994e786edbbbc7695acf6 |
| |
| commit ed18c5fa945768a9bec994e786edbbbc7695acf6 upstream. |
| |
| This optimization significantly reduces xhci driver load time. |
| |
| In ACPI tables the acpi companion port devices are children of |
| the hub device. The port devices are identified by their port number |
| returned by the ACPI _ADR method. |
| _ADR 0 is reserved for the root hub device. |
| |
| The current implementation to find a acpi companion port device |
| loops through all acpi port devices under that parent hub, evaluating |
| their _ADR method each time a new port device is added. |
| |
| for a xHC controller with 25 ports under its roothub it |
| will end up invoking ACPI bytecode 625 times before all ports |
| are ready, making it really slow. |
| |
| The _ADR values are already read and cached earler. So instead of |
| running the bytecode again we can check the cached _ADR value first, |
| and then fall back to the old way. |
| |
| As one of the more significant changes, the xhci load time on |
| Intel kabylake reduced by 70%, (28ms) from |
| initcall xhci_pci_init+0x0/0x49 returned 0 after 39537 usecs |
| to |
| initcall xhci_pci_init+0x0/0x49 returned 0 after 11270 usecs |
| |
| Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Jiri Slaby <jslaby@suse.cz> |
| |
| drivers/usb/core/usb-acpi.c | 26 +++++++++++++++++++++++--- |
| 1 file changed, 23 insertions(+), 3 deletions(-) |
| |
| diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c |
| index 2776cfe64c09..ef9cf4a21afe 100644 |
| |
| |
| @@ -127,6 +127,22 @@ out: |
| */ |
| #define USB_ACPI_LOCATION_VALID (1 << 31) |
| |
| +static struct acpi_device *usb_acpi_find_port(struct acpi_device *parent, |
| + int raw) |
| +{ |
| + struct acpi_device *adev; |
| + |
| + if (!parent) |
| + return NULL; |
| + |
| + list_for_each_entry(adev, &parent->children, node) { |
| + if (acpi_device_adr(adev) == raw) |
| + return adev; |
| + } |
| + |
| + return acpi_find_child_device(parent, raw, false); |
| +} |
| + |
| static struct acpi_device *usb_acpi_find_companion(struct device *dev) |
| { |
| struct usb_device *udev; |
| @@ -174,8 +190,10 @@ static struct acpi_device *usb_acpi_find_companion(struct device *dev) |
| int raw; |
| |
| raw = usb_hcd_find_raw_port_number(hcd, port1); |
| - adev = acpi_find_child_device(ACPI_COMPANION(&udev->dev), |
| - raw, false); |
| + |
| + adev = usb_acpi_find_port(ACPI_COMPANION(&udev->dev), |
| + raw); |
| + |
| if (!adev) |
| return NULL; |
| } else { |
| @@ -186,7 +204,9 @@ static struct acpi_device *usb_acpi_find_companion(struct device *dev) |
| return NULL; |
| |
| acpi_bus_get_device(parent_handle, &adev); |
| - adev = acpi_find_child_device(adev, port1, false); |
| + |
| + adev = usb_acpi_find_port(adev, port1); |
| + |
| if (!adev) |
| return NULL; |
| } |
| -- |
| 2.14.2 |
| |