From: Mathias Nyman <mathias.nyman@linux.intel.com>
To: Wesley Cheng <wesley.cheng@oss.qualcomm.com>,
Mathias Nyman <mathias.nyman@intel.com>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.com>,
Michal Pecio <michal.pecio@gmail.com>
Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-sound@vger.kernel.org
Subject: Re: [PATCH v3 2/4] usb: xhci: sideband: allocate sideband ring segments from a dedicated pool
Date: Wed, 9 Sep 2026 13:08:39 +0300 [thread overview]
Message-ID: <3e8baa09-2ecf-413b-9ced-73479add6f8b@linux.intel.com> (raw)
In-Reply-To: <20260903-16k_offload_v1_b4-v3-2-135928dc2408@oss.qualcomm.com>
On 9/4/26 09:57, Wesley Cheng wrote:
> Ring segments are normally allocated from a shared DMA pool sized and
> aligned to TRB_SEGMENT_SIZE (4096 bytes). On kernels built with a
> larger PAGE_SIZE (e.g. 16K or 64K page arches), a segment can end up
> at a non-page-aligned offset within its enclosing CPU page, and
> multiple segments can share the same physical page.
>
> A sideband client that maps a ring buffer directly via the IOMMU
> (which operates at page granularity) needs to know exactly which
> page(s) back the ring, and only pages that are actually intended to
> be exposed to that client should ever be mapped this way.
>
> Allow each xhci_sideband endpoint to pass its own segment_pool, allocated
> separately from the core xhci->segment_pool, so every segment backing
> a sideband-tagged endpoint always comes from a page that is meant to
> be visible by the entity handling the offloaded endpoints. Normal
> (non-offloaded) endpoints are unaffected, as they keep allocating from
> xhci->segment_pool.
>
> The offload client owns the pool's full lifetime, and since
> that lifetime is no longer tied to the sideband instance itself,
> xhci_sideband_unregister() must free any ring still backed by a
> client-supplied pool before returning, rather than leaving it for xhci
> to free later when the client and its pool may already be gone.
>
> Assisted-by: Claude:claude-sonnet-5
> Signed-off-by: Wesley Cheng <wesley.cheng@oss.qualcomm.com>
> ---
> drivers/usb/host/xhci-mem.c | 63 +++++++++++++++++++++++++--------------
> drivers/usb/host/xhci-sideband.c | 40 ++++++++++++++++++++++---
> drivers/usb/host/xhci.h | 16 +++++-----
> include/linux/usb/xhci-sideband.h | 20 +++++++++++--
> sound/usb/qcom/qc_audio_offload.c | 21 +++++++++++--
> 5 files changed, 121 insertions(+), 39 deletions(-)
>
> diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
> index 7a21ac81f9c8..a041a35fcd4f 100644
> --- a/drivers/usb/host/xhci-mem.c
> +++ b/drivers/usb/host/xhci-mem.c
> @@ -28,6 +28,7 @@
> * "All components of all Command and Transfer TRBs shall be initialized to '0'"
> */
> static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci,
> + struct dma_pool *pool,
> unsigned int max_packet,
> unsigned int num,
> gfp_t flags)
> @@ -40,7 +41,7 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci,
> if (!seg)
> return NULL;
>
> - seg->trbs = dma_pool_zalloc(xhci->segment_pool, flags, &dma);
> + seg->trbs = dma_pool_zalloc(pool, flags, &dma);
> if (!seg->trbs) {
> kfree(seg);
> return NULL;
> @@ -50,7 +51,7 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci,
> seg->bounce_buf = kzalloc_node(max_packet, flags,
> dev_to_node(dev));
> if (!seg->bounce_buf) {
> - dma_pool_free(xhci->segment_pool, seg->trbs, dma);
> + dma_pool_free(pool, seg->trbs, dma);
> kfree(seg);
> return NULL;
> }
> @@ -62,10 +63,11 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci,
> return seg;
> }
>
> -static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg)
> +static void xhci_segment_free(struct xhci_hcd *xhci, struct dma_pool *pool,
> + struct xhci_segment *seg)
> {
> if (seg->trbs) {
> - dma_pool_free(xhci->segment_pool, seg->trbs, seg->dma);
> + dma_pool_free(pool, seg->trbs, seg->dma);
> seg->trbs = NULL;
> }
> kfree(seg->bounce_buf);
> @@ -81,7 +83,7 @@ static void xhci_ring_segments_free(struct xhci_hcd *xhci, struct xhci_ring *rin
>
> while (seg) {
> next = seg->next;
> - xhci_segment_free(xhci, seg);
> + xhci_segment_free(xhci, ring->segment_pool, seg);
> seg = next;
> }
> }
> @@ -334,7 +336,7 @@ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, struct xhci_ring
> struct xhci_segment *prev;
> unsigned int num = 0;
>
> - prev = xhci_segment_alloc(xhci, ring->bounce_buf_len, num, flags);
> + prev = xhci_segment_alloc(xhci, ring->segment_pool, ring->bounce_buf_len, num, flags);
> if (!prev)
> return -ENOMEM;
> num++;
> @@ -343,7 +345,8 @@ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, struct xhci_ring
> while (num < ring->num_segs) {
> struct xhci_segment *next;
>
> - next = xhci_segment_alloc(xhci, ring->bounce_buf_len, num, flags);
> + next = xhci_segment_alloc(xhci, ring->segment_pool, ring->bounce_buf_len,
> + num, flags);
> if (!next)
> goto free_segments;
>
> @@ -362,15 +365,10 @@ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, struct xhci_ring
> return -ENOMEM;
> }
>
> -/*
> - * Create a new ring with zero or more segments.
> - *
> - * Link each segment together into a ring.
> - * Set the end flag and the cycle toggle bit on the last segment.
> - * See section 4.9.1 and figures 15 and 16.
> - */
> -struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, unsigned int num_segs,
> - enum xhci_ring_type type, unsigned int max_packet, gfp_t flags)
> +static struct xhci_ring *
> +xhci_ring_alloc_from_pool(struct xhci_hcd *xhci, unsigned int num_segs,
> + enum xhci_ring_type type, unsigned int max_packet,
> + struct dma_pool *pool, gfp_t flags)
> {
> struct xhci_ring *ring;
> int ret;
> @@ -382,6 +380,7 @@ struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, unsigned int num_segs,
>
> ring->num_segs = num_segs;
> ring->bounce_buf_len = max_packet;
> + ring->segment_pool = pool;
> INIT_LIST_HEAD(&ring->td_list);
> ring->type = type;
> if (num_segs == 0)
> @@ -398,6 +397,20 @@ struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, unsigned int num_segs,
> return NULL;
> }
>
> +/*
> + * Create a new ring with zero or more segments.
> + *
> + * Link each segment together into a ring.
> + * Set the end flag and the cycle toggle bit on the last segment.
> + * See section 4.9.1 and figures 15 and 16.
> + */
> +struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, unsigned int num_segs,
> + enum xhci_ring_type type, unsigned int max_packet, gfp_t flags)
> +{
> + return xhci_ring_alloc_from_pool(xhci, num_segs, type, max_packet,
> + xhci->segment_pool, flags);
> +}
> +
> void xhci_free_endpoint_ring(struct xhci_hcd *xhci,
> struct xhci_virt_device *virt_dev,
> unsigned int ep_index)
> @@ -422,6 +435,7 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
> new_ring.num_segs = num_new_segs;
> new_ring.bounce_buf_len = ring->bounce_buf_len;
> new_ring.type = ring->type;
> + new_ring.segment_pool = ring->segment_pool;
> ret = xhci_alloc_segments_for_ring(xhci, &new_ring, flags);
> if (ret)
> return -ENOMEM;
> @@ -1424,6 +1438,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
> unsigned int mult;
> unsigned int avg_trb_len;
> unsigned int err_count = 0;
> + struct dma_pool *pool;
>
> ep_index = xhci_get_endpoint_index(&ep->desc);
> ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index);
> @@ -1487,8 +1502,10 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
> avg_trb_len = 8;
>
> /* Set up the endpoint ring */
> + pool = virt_dev->eps[ep_index].priv_seg_pool ?
> + virt_dev->eps[ep_index].priv_seg_pool : xhci->segment_pool;
> virt_dev->eps[ep_index].new_ring =
> - xhci_ring_alloc(xhci, 2, ring_type, max_packet, mem_flags);
> + xhci_ring_alloc_from_pool(xhci, 2, ring_type, max_packet, pool, mem_flags);
> if (!virt_dev->eps[ep_index].new_ring)
> return -ENOMEM;
>
> @@ -2291,7 +2308,8 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
> }
>
> static struct xhci_interrupter *
> -xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int segs, gfp_t flags)
> +xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int segs,
> + struct dma_pool *pool, gfp_t flags)
> {
> struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
> struct xhci_interrupter *ir;
> @@ -2308,7 +2326,7 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int segs, gfp_t flags)
> if (!ir)
> return NULL;
>
> - ir->event_ring = xhci_ring_alloc(xhci, segs, TYPE_EVENT, 0, flags);
> + ir->event_ring = xhci_ring_alloc_from_pool(xhci, segs, TYPE_EVENT, 0, pool, flags);
> if (!ir->event_ring) {
> xhci_warn(xhci, "Failed to allocate interrupter event ring\n");
> kfree(ir);
> @@ -2356,7 +2374,8 @@ void xhci_add_interrupter(struct xhci_hcd *xhci, unsigned int intr_num)
>
> struct xhci_interrupter *
> xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
> - u32 imod_interval, unsigned int intr_num)
> + struct dma_pool *pool, u32 imod_interval,
> + unsigned int intr_num)
> {
> struct xhci_hcd *xhci = hcd_to_xhci(hcd);
> struct xhci_interrupter *ir;
> @@ -2367,7 +2386,7 @@ xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
> intr_num >= xhci->max_interrupters)
> return NULL;
>
> - ir = xhci_alloc_interrupter(xhci, segs, GFP_KERNEL);
> + ir = xhci_alloc_interrupter(xhci, segs, pool, GFP_KERNEL);
Passing a custom dma pool to xhci_create_secondary_interrupters() shuld be optional.
Callers shoudn't need to be aware of the xhci->segment_pool, and pass it in the default case.
Instead use the default xhci->segment_pool if caller passes NULL for pool.
> if (!ir)
> return NULL;
>
> @@ -2498,7 +2517,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
> if (!xhci->interrupters)
> goto fail;
>
> - xhci->interrupters[0] = xhci_alloc_interrupter(xhci, 0, flags);
> + xhci->interrupters[0] = xhci_alloc_interrupter(xhci, 0, xhci->segment_pool, flags);
> if (!xhci->interrupters[0])
> goto fail;
I would split the patch here.
Do all xhci "core" code above in one patch, and the sideband changes in a separate patch.
Only sideband change needed in first patch would be passing NULL for pool when
creating the secondary interrupter:
sb->ir = xhci_create_secondary_interrupter(xhci_to_hcd(sb->xhci),
- num_seg, imod_interval,
- intr_num);
+ num_seg, NULL,
+ imod_interval, intr_num);
Thanks
Mathias
next prev parent reply other threads:[~2026-09-09 10:08 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-04 6:57 [PATCH v3 0/4] Add larger page size support for USB audio offload path Wesley Cheng
2026-09-04 6:57 ` [PATCH v3 1/4] xhci: sideband: fix ring sg table for sub-page TRB segments Wesley Cheng
2026-09-04 6:57 ` [PATCH v3 2/4] usb: xhci: sideband: allocate sideband ring segments from a dedicated pool Wesley Cheng
2026-09-09 10:08 ` Mathias Nyman [this message]
2026-09-10 0:37 ` Wesley Cheng
2026-09-04 6:57 ` [PATCH v3 3/4] ALSA: usb-audio: qcom: tag sideband endpoints before ring allocation Wesley Cheng
2026-09-04 6:57 ` [PATCH v3 4/4] ALSA: usb-audio: qcom: fix xfer ring IOMMU unmap on 16K+ page kernels Wesley Cheng
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=3e8baa09-2ecf-413b-9ced-73479add6f8b@linux.intel.com \
--to=mathias.nyman@linux.intel.com \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-sound@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=mathias.nyman@intel.com \
--cc=michal.pecio@gmail.com \
--cc=perex@perex.cz \
--cc=tiwai@suse.com \
--cc=wesley.cheng@oss.qualcomm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®