diff mbox series

[v3,1/3] usb: host: add xhci hooks for USB offload

Message ID 20221213141005.3068792-2-albertccwang@google.com (mailing list archive)
State New, archived
Headers show
Series add xhci hooks for USB offload | expand

Commit Message

Albert Wang Dec. 13, 2022, 2:10 p.m. UTC
From: Howard Yen <howardyen@google.com>

This change is to provide USB offload function which allows to offload some
xHCI operations on co-processor. This is especially designed for USB audio
usecase. The co-processor is able to manipulate some USB structures in his
own memory, like SRAM.

There are several offload_ops introduced by this patch:

struct xhci_offload_ops - function callbacks for offlad specific operations
{
	@offload_init:
		- called for vendor init process during xhci-plat-hcd
		  probe.
	@offload_cleanup:
		- called for vendor cleanup process during xhci-plat-hcd
		  remove.
	@is_usb_offload_enabled:
		- called to check if usb offload enabled.
	@alloc_dcbaa:
		- called when allocating vendor specific dcbaa during
		  memory initializtion.
	@free_dcbaa:
		- called to free vendor specific dcbaa when cleanup the
		  memory.
	@alloc_transfer_ring:
		- called when vendor specific transfer ring allocation is required
	@free_transfer_ring:
		- called to free vendor specific transfer ring
	@usb_offload_skip_urb:
		- called to check if need to skip urb enqueue
}

The xhci hooks with prefix "xhci_vendor_" on the ops in xhci_offload_ops.
For example, offload_init ops will be invoked by xhci_vendor_offload_init()
hook,is_usb_offload_enabled ops will be invoked by
xhci_vendor_is_usb_offload_enabled(), and so on.

Signed-off-by: Howard Yen <howardyen@google.com>
---
 drivers/usb/host/xhci-mem.c  | 97 +++++++++++++++++++++++++++++++-----
 drivers/usb/host/xhci-plat.c | 23 +++++++++
 drivers/usb/host/xhci-plat.h |  1 +
 drivers/usb/host/xhci.c      | 21 ++++++++
 drivers/usb/host/xhci.h      | 31 ++++++++++++
 5 files changed, 160 insertions(+), 13 deletions(-)

Comments

Yujie Liu Dec. 16, 2022, 6:15 a.m. UTC | #1
Greeting,

FYI, we noticed BUG:KASAN:slab-out-of-bounds_in_xhci_offload_get_ops due to commit (built with gcc-11):

commit: 213d3f01e2e4c64d543594b4727bc8bef1772c90 ("[PATCH v3 1/3] usb: host: add xhci hooks for USB offload")
url: https://github.com/intel-lab-lkp/linux/commits/Albert-Wang/add-xhci-hooks-for-USB-offload/20221213-221410
base: https://git.kernel.org/cgit/linux/kernel/git/gregkh/usb.git usb-testing
patch link: https://lore.kernel.org/all/20221213141005.3068792-2-albertccwang@google.com/
patch subject: [PATCH v3 1/3] usb: host: add xhci hooks for USB offload

in testcase: kernel-selftests
version: kernel-selftests-x86_64-2ed09c3b-1_20221128
with following parameters:

	group: user_events

test-description: The kernel contains a set of "self tests" under the tools/testing/selftests/ directory. These are intended to be small unit tests to exercise individual code paths in the kernel.
test-url: https://www.kernel.org/doc/Documentation/kselftest.txt

on test machine: 4 threads Intel(R) Xeon(R) CPU E3-1225 v5 @ 3.30GHz (Skylake) with 16G memory

caused below changes (please refer to attached dmesg/kmsg for entire log/backtrace):


[ 10.836823][ T9] BUG: KASAN: slab-out-of-bounds in xhci_offload_get_ops (??:?) 
[   10.836823][    T9] Read of size 8 at addr ffff88816d77bcf0 by task kworker/u8:0/9
[   10.836823][    T9]
[   10.836823][    T9] CPU: 1 PID: 9 Comm: kworker/u8:0 Not tainted 6.1.0-rc7-00160-g213d3f01e2e4 #1
[   10.857572][    T1] serio: i8042 AUX port at 0x60,0x64 irq 12
[   10.836823][    T9] Hardware name: HP HP Z238 Microtower Workstation/8183, BIOS N51 Ver. 01.63 10/05/2017
[   10.836823][    T9] Workqueue: events_unbound async_run_entry_fn
[   10.836823][    T9] Call Trace:
[ 10.871868][ T1] initcall i8042_init+0x0/0x135 returned 0 after 45065 usecs 
[   10.836823][    T9]  <TASK>
[ 10.836823][ T9] dump_stack_lvl (??:?) 
[ 10.836823][ T9] print_address_description+0x87/0x2a1 
[ 10.886397][ T1] calling serport_init+0x0/0x2c @ 1 
[ 10.836823][ T9] print_report (report.c:?) 
[ 10.836823][ T9] ? kasan_addr_to_slab (??:?) 
[ 10.897467][ T1] initcall serport_init+0x0/0x2c returned 0 after 1 usecs 
[ 10.836823][ T9] ? xhci_offload_get_ops (??:?) 
[ 10.836823][ T9] kasan_report (??:?) 
[ 10.912792][ T1] calling input_leds_init+0x0/0x11 @ 1 
[ 10.836823][ T9] ? xhci_offload_get_ops (??:?) 
[ 10.836823][ T9] xhci_offload_get_ops (??:?) 
[ 10.921995][ T1] initcall input_leds_init+0x0/0x11 returned 0 after 46 usecs 
[ 10.836823][ T9] xhci_mem_init (??:?) 
[ 10.836823][ T9] ? lockdep_hardirqs_on_prepare (lockdep.c:?) 
[ 10.836823][ T9] ? xhci_mem_cleanup (??:?) 
[ 10.836823][ T9] ? lockdep_init_map_type (??:?) 
[ 10.836823][ T9] xhci_init (xhci.c:?) 
[ 10.932015][ T1] calling mousedev_init+0x0/0x5f @ 1 
[ 10.836823][ T9] xhci_gen_setup (??:?) 
[ 10.836823][ T9] ? xhci_pci_suspend (xhci-pci.c:?) 
[   10.943237][    T1] mousedev: PS/2 mouse device common for all mice
[ 10.836823][ T9] ? trace_hardirqs_on (??:?) 
[ 10.836823][ T9] xhci_pci_setup (xhci-pci.c:?) 
[ 10.836823][ T9] usb_add_hcd.cold (hcd.c:?) 
[ 10.952345][ T1] initcall mousedev_init+0x0/0x5f returned 0 after 9518 usecs 
[ 10.836823][ T9] usb_hcd_pci_probe (??:?) 
[ 10.836823][ T9] ? lockdep_hardirqs_on_prepare (lockdep.c:?) 
[ 10.964107][ T1] calling evdev_init+0x0/0x11 @ 1 
[ 10.836823][ T9] ? xhci_pci_resume (xhci-pci.c:?) 
[ 10.836823][ T9] xhci_pci_probe (xhci-pci.c:?) 
[ 10.974550][ T1] initcall evdev_init+0x0/0x11 returned 0 after 1119 usecs 
[ 10.836823][ T9] ? xhci_pci_resume (xhci-pci.c:?) 
[ 10.836823][ T9] local_pci_probe (pci-driver.c:?) 
[ 10.836823][ T9] pci_call_probe (pci-driver.c:?) 
[ 10.983920][ T1] calling atkbd_init+0x0/0x26 @ 1 
[ 10.836823][ T9] ? lock_is_held_type (??:?) 
[ 10.836823][ T9] ? pci_pm_suspend_noirq (pci-driver.c:?) 
[ 10.836823][ T9] ? pci_match_device (pci-driver.c:?) 
[ 10.996241][ T1] initcall atkbd_init+0x0/0x26 returned 0 after 117 usecs 
[ 10.836823][ T9] ? pci_match_device (pci-driver.c:?) 
[ 10.836823][ T9] ? kernfs_put (??:?) 
[ 11.007109][ T1] calling psmouse_init+0x0/0x86 @ 1 
[ 10.836823][ T9] pci_device_probe (pci-driver.c:?) 
[ 10.836823][ T9] really_probe (dd.c:?) 
[ 11.018064][ T1] initcall psmouse_init+0x0/0x86 returned 0 after 581 usecs 
[ 10.836823][ T9] ? ktime_get (??:?) 
[ 10.836823][ T9] ? driver_probe_device (dd.c:?) 
[ 10.836823][ T9] __driver_probe_device (dd.c:?) 
[ 11.026737][ T1] calling cmos_init+0x0/0x70 @ 1 
[ 10.836823][ T9] driver_probe_device (dd.c:?) 
[ 10.836823][ T9] __driver_attach_async_helper (dd.c:?) 
[   11.036281][    T1] rtc_cmos 00:03: RTC can wake from S4
[ 10.836823][ T9] async_run_entry_fn (async.c:?) 
[ 10.836823][ T9] process_one_work (workqueue.c:?) 
[   11.049786][    T1] rtc_cmos 00:03: registered as rtc0
[ 10.836823][ T9] ? rcu_read_unlock (main.c:?) 
[ 10.836823][ T9] ? pwq_dec_nr_in_flight (workqueue.c:?) 
[ 10.836823][ T9] ? rwlock_bug+0x90/0x90 
[   11.056724][    T1] rtc_cmos 00:03: setting system clock to 2022-12-15T23:47:28 UTC (1671148048)
[ 10.836823][ T9] worker_thread (workqueue.c:?) 
[ 10.836823][ T9] ? process_one_work (workqueue.c:?) 
[ 10.836823][ T9] kthread (kthread.c:?) 
[ 10.836823][ T9] ? kthread_complete_and_exit (kthread.c:?) 
[   11.068886][    T1] rtc_cmos 00:03: alarms up to one month, y3k, 242 bytes nvram
[ 10.836823][ T9] ret_from_fork (??:?) 
[   10.836823][    T9]  </TASK>
[ 11.080121][ T1] initcall cmos_init+0x0/0x70 returned 0 after 43971 usecs 
[   10.836823][    T9]
[   10.836823][    T9] The buggy address belongs to the physical page:
[   10.836823][    T9] page:0000000081d85dee refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x16d778
[   10.836823][    T9] head:0000000081d85dee order:2 compound_mapcount:0 compound_pincount:0
[ 11.089266][ T1] calling thermal_throttle_init_device+0x0/0x4f @ 1 
[   10.836823][    T9] flags: 0x17ffffc0010000(head|node=0|zone=2|lastcpupid=0x1fffff)
[   10.836823][    T9] raw: 0017ffffc0010000 0000000000000000 dead000000000122 0000000000000000
[   11.335927][    T9] raw: 0000000000000000 0000000000000000 00000001ffffffff 0000000000000000
[   11.345903][    T9] page dumped because: kasan: bad access detected
[   11.350905][    T9]
[   11.355905][    T9] Memory state around the buggy address:
[   11.360904][    T9]  ffff88816d77bb80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[   11.370903][    T9]  ffff88816d77bc00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[   11.375903][    T9] >ffff88816d77bc80: 00 00 00 00 00 00 00 00 00 00 00 00 fe fe fe fe
[   11.385902][    T9]                                                              ^
[   11.390907][    T9]  ffff88816d77bd00: fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe
[   11.400904][    T9]  ffff88816d77bd80: fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe
[   11.405905][    T9] ==================================================================
[   11.418806][    T9] Disabling lock debugging due to kernel taint
[ 11.425206][ T1] initcall thermal_throttle_init_device+0x0/0x4f returned 0 after 324091 usecs 
[   11.425310][    T9] xhci_hcd 0000:00:14.0: hcc params 0x200077c1 hci version 0x100 quirks 0x0000000001109810
[ 11.434035][ T1] calling esb_driver_init+0x0/0x1a @ 1 
[ 11.434142][ T1] initcall esb_driver_init+0x0/0x1a returned 0 after 102 usecs 
[   11.449912][    T9] xhci_hcd 0000:00:14.0: xHCI Host Controller


If you fix the issue, kindly add following tag
| Reported-by: kernel test robot <yujie.liu@intel.com>
| Link: https://lore.kernel.org/oe-lkp/202212161133.8876fda2-yujie.liu@intel.com


To reproduce:

        git clone https://github.com/intel/lkp-tests.git
        cd lkp-tests
        sudo bin/lkp install job.yaml           # job file is attached in this email
        bin/lkp split-job --compatible job.yaml # generate the yaml file for lkp run
        sudo bin/lkp run generated-yaml-file

        # if come across any failure that blocks the test,
        # please remove ~/.lkp and /lkp dir to run from a clean state.
diff mbox series

Patch

diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 81ca2bc1f0be..ab0ef19d4fa3 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -361,6 +361,38 @@  static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci,
 	return 0;
 }
 
+static struct xhci_ring *xhci_vendor_alloc_transfer_ring(struct xhci_hcd *xhci,
+		u32 endpoint_type, enum xhci_ring_type ring_type,
+		unsigned int max_packet, gfp_t mem_flags)
+{
+	struct xhci_offload_ops *ops = xhci_offload_get_ops(xhci);
+
+	if (ops && ops->alloc_transfer_ring)
+		return ops->alloc_transfer_ring(xhci, endpoint_type, ring_type,
+						max_packet, mem_flags);
+	return 0;
+}
+
+static void xhci_vendor_free_transfer_ring(struct xhci_hcd *xhci,
+		struct xhci_virt_device *virt_dev, unsigned int ep_index)
+{
+	struct xhci_offload_ops *ops = xhci_offload_get_ops(xhci);
+
+	if (ops && ops->free_transfer_ring)
+		ops->free_transfer_ring(xhci, virt_dev, ep_index);
+}
+
+static bool xhci_vendor_is_offload_enabled(struct xhci_hcd *xhci,
+		struct xhci_virt_device *virt_dev, unsigned int ep_index)
+{
+	struct xhci_offload_ops *ops = xhci_offload_get_ops(xhci);
+
+	if (ops && ops->is_offload_enabled)
+		return ops->is_offload_enabled(xhci, virt_dev, ep_index);
+
+	return false;
+}
+
 /*
  * Create a new ring with zero or more segments.
  *
@@ -412,7 +444,11 @@  void xhci_free_endpoint_ring(struct xhci_hcd *xhci,
 		struct xhci_virt_device *virt_dev,
 		unsigned int ep_index)
 {
-	xhci_ring_free(xhci, virt_dev->eps[ep_index].ring);
+	if (xhci_vendor_is_offload_enabled(xhci, virt_dev, ep_index))
+		xhci_vendor_free_transfer_ring(xhci, virt_dev, ep_index);
+	else
+		xhci_ring_free(xhci, virt_dev->eps[ep_index].ring);
+
 	virt_dev->eps[ep_index].ring = NULL;
 }
 
@@ -885,7 +921,7 @@  void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
 
 	for (i = 0; i < 31; i++) {
 		if (dev->eps[i].ring)
-			xhci_ring_free(xhci, dev->eps[i].ring);
+			xhci_free_endpoint_ring(xhci, dev, i);
 		if (dev->eps[i].stream_info)
 			xhci_free_stream_info(xhci,
 					dev->eps[i].stream_info);
@@ -1487,8 +1523,16 @@  int xhci_endpoint_init(struct xhci_hcd *xhci,
 		mult = 0;
 
 	/* Set up the endpoint ring */
-	virt_dev->eps[ep_index].new_ring =
-		xhci_ring_alloc(xhci, 2, 1, ring_type, max_packet, mem_flags);
+	if (xhci_vendor_is_offload_enabled(xhci, virt_dev, ep_index) &&
+	    usb_endpoint_xfer_isoc(&ep->desc)) {
+		virt_dev->eps[ep_index].new_ring =
+			xhci_vendor_alloc_transfer_ring(xhci, endpoint_type, ring_type,
+							max_packet, mem_flags);
+	} else {
+		virt_dev->eps[ep_index].new_ring =
+			xhci_ring_alloc(xhci, 2, 1, ring_type, max_packet, mem_flags);
+	}
+
 	if (!virt_dev->eps[ep_index].new_ring)
 		return -ENOMEM;
 
@@ -1832,6 +1876,23 @@  void xhci_free_erst(struct xhci_hcd *xhci, struct xhci_erst *erst)
 	erst->entries = NULL;
 }
 
+static void xhci_vendor_alloc_dcbaa(
+		struct xhci_hcd *xhci, gfp_t flags)
+{
+	struct xhci_offload_ops *ops = xhci_offload_get_ops(xhci);
+
+	if (ops && ops->alloc_dcbaa)
+		return ops->alloc_dcbaa(xhci, flags);
+}
+
+static void xhci_vendor_free_dcbaa(struct xhci_hcd *xhci)
+{
+	struct xhci_offload_ops *ops = xhci_offload_get_ops(xhci);
+
+	if (ops && ops->free_dcbaa)
+		ops->free_dcbaa(xhci);
+}
+
 void xhci_mem_cleanup(struct xhci_hcd *xhci)
 {
 	struct device	*dev = xhci_to_hcd(xhci)->self.sysdev;
@@ -1883,9 +1944,13 @@  void xhci_mem_cleanup(struct xhci_hcd *xhci)
 	xhci_dbg_trace(xhci, trace_xhci_dbg_init,
 			"Freed medium stream array pool");
 
-	if (xhci->dcbaa)
-		dma_free_coherent(dev, sizeof(*xhci->dcbaa),
-				xhci->dcbaa, xhci->dcbaa->dma);
+	if (xhci_vendor_is_offload_enabled(xhci, NULL, 0)) {
+		xhci_vendor_free_dcbaa(xhci);
+	} else {
+		if (xhci->dcbaa)
+			dma_free_coherent(dev, sizeof(*xhci->dcbaa),
+					xhci->dcbaa, xhci->dcbaa->dma);
+	}
 	xhci->dcbaa = NULL;
 
 	scratchpad_free(xhci);
@@ -2422,15 +2487,21 @@  int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
 	 * xHCI section 5.4.6 - Device Context array must be
 	 * "physically contiguous and 64-byte (cache line) aligned".
 	 */
-	xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma,
-			flags);
-	if (!xhci->dcbaa)
-		goto fail;
-	xhci->dcbaa->dma = dma;
+	if (xhci_vendor_is_offload_enabled(xhci, NULL, 0)) {
+		xhci_vendor_alloc_dcbaa(xhci, flags);
+		if (!xhci->dcbaa)
+			goto fail;
+	} else {
+		xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma,
+				flags);
+		if (!xhci->dcbaa)
+			goto fail;
+		xhci->dcbaa->dma = dma;
+	}
 	xhci_dbg_trace(xhci, trace_xhci_dbg_init,
 			"// Device context base array address = 0x%llx (DMA), %p (virt)",
 			(unsigned long long)xhci->dcbaa->dma, xhci->dcbaa);
-	xhci_write_64(xhci, dma, &xhci->op_regs->dcbaa_ptr);
+	xhci_write_64(xhci, xhci->dcbaa->dma, &xhci->op_regs->dcbaa_ptr);
 
 	/*
 	 * Initialize the ring segment pool.  The ring must be a contiguous
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 5fb55bf19493..2f04acb42fa6 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -173,6 +173,23 @@  static const struct of_device_id usb_xhci_of_match[] = {
 MODULE_DEVICE_TABLE(of, usb_xhci_of_match);
 #endif
 
+static int xhci_vendor_init(struct xhci_hcd *xhci)
+{
+	struct xhci_offload_ops *ops = xhci_offload_get_ops(xhci);
+
+	if (ops && ops->offload_init)
+		return ops->offload_init(xhci);
+	return 0;
+}
+
+static void xhci_vendor_cleanup(struct xhci_hcd *xhci)
+{
+	struct xhci_offload_ops *ops = xhci_offload_get_ops(xhci);
+
+	if (ops && ops->offload_cleanup)
+		ops->offload_cleanup(xhci);
+}
+
 static int xhci_plat_probe(struct platform_device *pdev)
 {
 	const struct xhci_plat_priv *priv_match;
@@ -317,6 +334,10 @@  static int xhci_plat_probe(struct platform_device *pdev)
 			goto disable_clk;
 	}
 
+	ret = xhci_vendor_init(xhci);
+	if (ret)
+		goto disable_usb_phy;
+
 	hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node);
 
 	if (priv && (priv->quirks & XHCI_SKIP_PHY_INIT))
@@ -410,6 +431,8 @@  static int xhci_plat_remove(struct platform_device *dev)
 	if (shared_hcd)
 		usb_put_hcd(shared_hcd);
 
+	xhci_vendor_cleanup(xhci);
+
 	clk_disable_unprepare(clk);
 	clk_disable_unprepare(reg_clk);
 	usb_put_hcd(hcd);
diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h
index 1fb149d1fbce..5aa0d38fa01a 100644
--- a/drivers/usb/host/xhci-plat.h
+++ b/drivers/usb/host/xhci-plat.h
@@ -13,6 +13,7 @@ 
 struct xhci_plat_priv {
 	const char *firmware_name;
 	unsigned long long quirks;
+	struct xhci_offload_ops *offload_ops;
 	void (*plat_start)(struct usb_hcd *);
 	int (*init_quirk)(struct usb_hcd *);
 	int (*suspend_quirk)(struct usb_hcd *);
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 79d7931c048a..75d39fe0d44d 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -22,6 +22,7 @@ 
 #include "xhci-trace.h"
 #include "xhci-debugfs.h"
 #include "xhci-dbgcap.h"
+#include "xhci-plat.h"
 
 #define DRIVER_AUTHOR "Sarah Sharp"
 #define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver"
@@ -1669,6 +1670,11 @@  static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
 		return -ENODEV;
 	}
 
+	if (xhci_vendor_usb_offload_skip_urb(xhci, urb)) {
+		xhci_dbg(xhci, "skip urb for usb offload\n");
+		return -EOPNOTSUPP;
+	}
+
 	if (usb_endpoint_xfer_isoc(&urb->ep->desc))
 		num_tds = urb->number_of_packets;
 	else if (usb_endpoint_is_bulk_out(&urb->ep->desc) &&
@@ -4441,6 +4447,21 @@  static int __maybe_unused xhci_change_max_exit_latency(struct xhci_hcd *xhci,
 	return ret;
 }
 
+struct xhci_offload_ops *xhci_offload_get_ops(struct xhci_hcd *xhci)
+{
+	return xhci_to_priv(xhci)->offload_ops;
+}
+EXPORT_SYMBOL_GPL(xhci_offload_get_ops);
+
+bool xhci_vendor_usb_offload_skip_urb(struct xhci_hcd *xhci, struct urb *urb)
+{
+	struct xhci_offload_ops *ops = xhci_offload_get_ops(xhci);
+
+	if (ops && ops->usb_offload_skip_urb)
+		return ops->usb_offload_skip_urb(xhci, urb);
+	return false;
+}
+
 #ifdef CONFIG_PM
 
 /* BESL to HIRD Encoding array for USB2 LPM */
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index c9f06c5e4e9d..4ffcc8f01bd7 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -2229,6 +2229,37 @@  static inline struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
 					urb->stream_id);
 }
 
+/**
+ * struct xhci_offload_ops - function callbacks for offload specific operations
+ * @offload_init: called for offload init process
+ * @offload_cleanup: called for offload cleanup process
+ * @is_usb_offload_enabled: called to check if xhci offload enabled
+ * @alloc_dcbaa: called when allocating specific dcbaa for offload
+ * @free_dcbaa: called to free specific dcbaa for offload
+ * @alloc_transfer_ring: called when remote transfer ring allocation is required
+ * @free_transfer_ring: called to free specific transfer ring for offload
+ * @usb_offload_skip_urb: called to check if need to skip urb
+ */
+struct xhci_offload_ops {
+	int (*offload_init)(struct xhci_hcd *xhci);
+	void (*offload_cleanup)(struct xhci_hcd *xhci);
+	bool (*is_offload_enabled)(struct xhci_hcd *xhci,
+				       struct xhci_virt_device *vdev,
+				       unsigned int ep_index);
+	void (*alloc_dcbaa)(struct xhci_hcd *xhci, gfp_t flags);
+	void (*free_dcbaa)(struct xhci_hcd *xhci);
+
+	struct xhci_ring *(*alloc_transfer_ring)(struct xhci_hcd *xhci,
+			u32 endpoint_type, enum xhci_ring_type ring_type,
+			unsigned int max_packet, gfp_t mem_flags);
+	void (*free_transfer_ring)(struct xhci_hcd *xhci,
+			struct xhci_virt_device *virt_dev, unsigned int ep_index);
+	bool (*usb_offload_skip_urb)(struct xhci_hcd *xhci, struct urb *urb);
+};
+
+struct xhci_offload_ops *xhci_offload_get_ops(struct xhci_hcd *xhci);
+bool xhci_vendor_usb_offload_skip_urb(struct xhci_hcd *xhci, struct urb *urb);
+
 /*
  * TODO: As per spec Isochronous IDT transmissions are supported. We bypass
  * them anyways as we where unable to find a device that matches the