mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
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


  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®