mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Wesley Cheng <wesley.cheng@oss.qualcomm.com>
To: Mathias Nyman <mathias.nyman@linux.intel.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 v4 3/5] usb: xhci: sideband: allocate sideband ring segments from a dedicated pool
Date: Mon, 12 Oct 2026 18:01:50 -0700	[thread overview]
Message-ID: <82e0b9c7-3b3f-4dce-a72e-64989b08ef98@oss.qualcomm.com> (raw)
In-Reply-To: <68b34f04-f7e2-4468-8a18-ac97decb987d@linux.intel.com>



On 10/5/2026 4:03 AM, Mathias Nyman wrote:
> On 9/10/26 04:50, Wesley Cheng wrote:
>> 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.
> 
> xhci is the one allocating the ring when adding an endpoint, and telling
> xHC hardware to write to that ring. It seems more approriate that xhci should
> be the one freeing the ring after it told xHC hardware to drop the endpoint,
> and there no longer is a risk on hardware accessing it.
> 

Hi Mathias,

Sorry for the late response.  Was out last week.

Specifically in the disconnect path (while audio offload is active), when 
we utilize the usb_set_interface() path, it'll return early due to the 
iface->unregistering check.  This would lead to the drop_endpoint not to be 
called, and rely on the xhci_free_dev() to handle the transfer ring 
cleanup.  This wouldn't work in the offload situation, because 
qc_audio_offload is the only entity managing the DMA pool, and that would 
be freed during the .disconnect callback, which happens BEFORE xhci_free_dev().

Would you be more inclined to prefer a solution where we can defer the DMA 
pool free'ing to the xHCI core during xhci_free_dev, or maybe adding 
another sideband notifier (sb->notify_client) enum to tell the client 
driver that it can release the pool?

> usb audio (class) driver controls both when and endpoint is added or dropped
> by setting configurations and interfaces, and also the one registering and
> unregistering sideband, and adding and removing endpoint to/from sideband use.
> 
> I think usb audio driver should ensure endpoints are dropped before 
> unregistering
> sideband.
> 
>>
>> Assisted-by: Claude:claude-sonnet-5
>> Signed-off-by: Wesley Cheng <wesley.cheng@oss.qualcomm.com>
>> ---
>>   drivers/usb/host/xhci-sideband.c  | 38 ++++++++++++++++++++++++++++++++ 
>> +++---
>>   drivers/usb/host/xhci.h           | 10 +++-------
>>   include/linux/usb/xhci-sideband.h | 20 +++++++++++++++++---
>>   sound/usb/qcom/qc_audio_offload.c | 32 ++++++++++++++++++++++++++++----
>>   4 files changed, 83 insertions(+), 17 deletions(-)
>>
>> diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci- 
>> sideband.c
>> index 274da38f333d..1bb6e5034b58 100644
>> --- a/drivers/usb/host/xhci-sideband.c
>> +++ b/drivers/usb/host/xhci-sideband.c
>> @@ -9,6 +9,7 @@
>>    */
>>   #include <linux/usb/xhci-sideband.h>
>> +#include <linux/dmapool.h>
>>   #include "xhci.h"
>> @@ -67,6 +68,7 @@ __xhci_sideband_remove_endpoint(struct xhci_sideband 
>> *sb, struct xhci_virt_ep *e
>>       xhci_stop_endpoint_sync(sb->xhci, ep, 0, GFP_KERNEL);
>>       ep->sideband = NULL;
>> +    ep->priv_seg_pool = NULL;
>>       sb->eps[ep->ep_index] = NULL;
>>   }
>> @@ -113,6 +115,8 @@ EXPORT_SYMBOL_GPL(xhci_sideband_notify_ep_ring_free);
>>    * xhci_sideband_add_endpoint - add endpoint to sideband access list
>>    * @sb: sideband instance for this usb device
>>    * @host_ep: usb host endpoint
>> + * @pool: dma pool to allocate this endpoint's ring segments from, or NULL
>> + *        to leave the endpoint's current pool selection untouched
> 
> Add some comment about if pool is set then xhci_sideband_add_endpoint() needs
> to be called before setting configuration or interface that enables as
> allocated the endpoint.
> 
> Also add note that this is not suitable for ep0 as ring is allocated 
> immedialely
> when device is created.
> 

Will do.

> 
>>    *
>>    * Adds an endpoint to the list of sideband accessed endpoints for this 
>> usb
>>    * device.
>> @@ -123,7 +127,8 @@ EXPORT_SYMBOL_GPL(xhci_sideband_notify_ep_ring_free);
>>    */
>>   int
>>   xhci_sideband_add_endpoint(struct xhci_sideband *sb,
>> -               struct usb_host_endpoint *host_ep)
>> +               struct usb_host_endpoint *host_ep,
>> +               struct dma_pool *pool)
>>   {
>>       struct xhci_virt_ep *ep;
>>       unsigned int ep_index;
>> @@ -153,6 +158,9 @@ xhci_sideband_add_endpoint(struct xhci_sideband *sb,
>>       ep->sideband = sb;
>>       sb->eps[ep_index] = ep;
>> +    if (pool)
>> +        ep->priv_seg_pool = pool;
>> +
> 
> Fail here ring is already allocated from another pool:
> 
> if (pool) {
>      if (ep->ring && ep->ring->segment_pool != pool)
>          return -SOME_SUITABLE_ERROR;
>      ep->priv_seg_pool = pool
> }
> 

Got it.

> 
> 
> 
>>       return 0;
>>   }
>>   EXPORT_SYMBOL_GPL(xhci_sideband_add_endpoint);
>> @@ -288,6 +296,7 @@ EXPORT_SYMBOL_GPL(xhci_sideband_check);
>>    * xhci_sideband_create_interrupter - creates a new interrupter for 
>> this sideband
>>    * @sb: sideband instance for this usb device
>>    * @num_seg: number of event ring segments to allocate
>> + * @pool: dma pool to allocate the interrupter's event ring segments from
>>    * @ip_autoclear: IP autoclearing support such as MSI implemented
>>    *
>>    * Sets up a xhci interrupter that can be used for this sideband 
>> accessed usb
>> @@ -301,7 +310,8 @@ EXPORT_SYMBOL_GPL(xhci_sideband_check);
>>    */
>>   int
>>   xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
>> -                 bool ip_autoclear, u32 imod_interval, int intr_num)
>> +                 struct dma_pool *pool, bool ip_autoclear,
>> +                 u32 imod_interval, int intr_num)
>>   {
>>       if (!sb || !sb->xhci)
>>           return -ENODEV;
>> @@ -315,7 +325,7 @@ xhci_sideband_create_interrupter(struct xhci_sideband 
>> *sb, int num_seg,
>>           return -EBUSY;
>>       sb->ir = xhci_create_secondary_interrupter(xhci_to_hcd(sb->xhci),
>> -                           num_seg, NULL,
>> +                           num_seg, pool,
>>                              imod_interval, intr_num);
>>       if (!sb->ir)
>>           return -ENOMEM;
>> @@ -370,6 +380,8 @@ EXPORT_SYMBOL_GPL(xhci_sideband_interrupter_id);
>>   /**
>>    * xhci_sideband_register - register a sideband for a usb device
>>    * @intf: usb interface associated with the sideband device
>> + * @type: xHCI sideband type
>> + * @notify_client: callback for xHCI sideband sequences
>>    *
>>    * Allows for clients to utilize XHCI interrupters and fetch transfer 
>> and event
>>    * ring parameters for executing data transfers.
>> @@ -436,6 +448,15 @@ EXPORT_SYMBOL_GPL(xhci_sideband_register);
>>    * After this the endpoint and interrupter event buffers should no longer
>>    * be accessed via sideband. The xhci driver can now take over handling
>>    * the buffers.
>> + * Any transfer ring allocated from a client supplied dma pool is freed 
>> here
>> + * as well, as the client is not expected to keep that pool alive any 
>> longer
>> + * than this call. This includes rings of endpoints already removed with
>> + * xhci_sideband_remove_endpoint(), which xhci would otherwise only free 
>> once
>> + * the device is reconfigured or torn down, i.e. after the client is gone.
>> + *
>> + * The caller must ensure the usb device is no longer streaming through the
>> + * normal, non-sideband path when calling this, as the freed rings are 
>> still
>> + * referenced by the endpoint contexts until xhci reconfigures the device.
>>    */
>>   void
>>   xhci_sideband_unregister(struct xhci_sideband *sb)
>> @@ -458,6 +479,17 @@ xhci_sideband_unregister(struct xhci_sideband *sb)
>>               if (sb->eps[i])
>>                   __xhci_sideband_remove_endpoint(sb, sb->eps[i]);
>> +        spin_lock_irq(&xhci->lock);
>> +        for (i = 0; i < EP_CTX_PER_DEV; i++) {
>> +            struct xhci_ring *ring = vdev->eps[i].ring;
>> +
>> +            if (ring && ring->segment_pool != xhci->segment_pool) {
>> +                xhci_ring_free(xhci, ring);
>> +                vdev->eps[i].ring = NULL;
> 
> 
> Sideband should not make decisions for all endpoints of a device based on the
> pool memory is _not_ allocated from.
> 
> Endpoint may not even belong to audio interface, for example can be part if 
> HID.
> 
> Can't assume that just because ring isn't allocatd from xhci->segment_pool 
> that
> it means it belongs to sideband. It's true today, but dedicated pools may 
> be used
> for VTIO/virtualization in future.
>

Make sense.  If we end up keeping this change (based on the first comment), 
I'll rework it to only apply this to rings owned by the sideband entity 
being unregistered.

> Maybe add some warning to xhci_unregister_sideband() if an endpoint assined
> to sideband  still has a ring allocated before calling
> __xhci_sideband_remove_endpoint(sb, sb->eps[i]);
> 

Sure.

>> +            }
>> +        }
>> +        spin_unlock_irq(&xhci->lock);
>> +
>>           __xhci_sideband_remove_interrupter(sb);
>>           sb->vdev = NULL;
>> diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
>> index 15ce0bb7aa3f..1353d6fa2776 100644
>> --- a/drivers/usb/host/xhci.h
>> +++ b/drivers/usb/host/xhci.h
>> @@ -19,6 +19,7 @@
>>   #include <linux/usb/hcd.h>
>>   #include <linux/io-64-nonatomic-lo-hi.h>
>>   #include <linux/io-64-nonatomic-hi-lo.h>
>> +#include <linux/usb/xhci-sideband.h>
>>   /* Code sharing between pci-quirks and xhci hcd */
>>   #include    "xhci-ext-caps.h"
>> @@ -740,8 +741,6 @@ struct xhci_interval_bw_table {
>>       unsigned int        ss_bw_out;
>>   };
>> -#define EP_CTX_PER_DEV        31
>> -
>>   struct xhci_virt_device {
>>       int                slot_id;
>>       struct usb_device        *udev;
>> @@ -1255,14 +1254,11 @@ static inline const char *xhci_trb_type_string(u8 
>> type)
>>   #define NEC_FW_MAJOR(p)        (((p) >> 8) & 0xff)
>>   /*
>> - * TRBS_PER_SEGMENT must be a multiple of 4,
>> - * since the command ring is 64-byte aligned.
>> - * It must also be greater than 16.
>> + * TRBS_PER_SEGMENT and TRB_SEGMENT_SIZE are defined in
>> + * <linux/usb/xhci-sideband.h>, shared with sideband client drivers.
> 
> This seems backwards.
> 
> Maybe we should just add ring_seg_size and xHC device pointer to struct 
> xhci__sideband
> 
> Or optionally helper(s) that provides those

Yeah...I think you mentioned you wanted to explicitly wanted to keep 
sideband out of the core.  Will look at a better way to address it.

> 
>>    */
>> -#define TRBS_PER_SEGMENT    256
>>   /* Allow two commands + a link TRB, along with any reserved command 
>> TRBs */
>>   #define MAX_RSVD_CMD_TRBS    (TRBS_PER_SEGMENT - 3)
>> -#define TRB_SEGMENT_SIZE    (TRBS_PER_SEGMENT*16)
>>   #define TRB_SEGMENT_SHIFT    (ilog2(TRB_SEGMENT_SIZE))
>>   /* TRB buffer pointers can't cross 64KB boundaries */
>>   #define TRB_MAX_BUFF_SHIFT        16
>> diff --git a/include/linux/usb/xhci-sideband.h b/include/linux/usb/xhci- 
>> sideband.h
>> index 005257085dcb..6d318e6a3bf6 100644
>> --- a/include/linux/usb/xhci-sideband.h
>> +++ b/include/linux/usb/xhci-sideband.h
>> @@ -13,7 +13,19 @@
>>   #include <linux/usb.h>
>>   #include <linux/usb/hcd.h>
>> -#define    EP_CTX_PER_DEV        31    /* FIXME defined twice, from 
>> xhci.h */
>> +/*
>> + * Constants shared with the xHCI host driver (drivers/usb/host/xhci.h),
>> + * which includes this header for its canonical definitions.
>> + */
>> +#define EP_CTX_PER_DEV      31
>> +
>> +/*
>> + * TRBS_PER_SEGMENT must be a multiple of 4,
>> + * since the command ring is 64-byte aligned.
>> + * It must also be greater than 16.
>> + */
>> +#define TRBS_PER_SEGMENT    256
>> +#define TRB_SEGMENT_SIZE    (TRBS_PER_SEGMENT * 16)
>>   struct xhci_sideband;
>> @@ -72,7 +84,8 @@ void
>>   xhci_sideband_unregister(struct xhci_sideband *sb);
>>   int
>>   xhci_sideband_add_endpoint(struct xhci_sideband *sb,
>> -               struct usb_host_endpoint *host_ep);
>> +               struct usb_host_endpoint *host_ep,
>> +               struct dma_pool *pool);
>>   int
>>   xhci_sideband_remove_endpoint(struct xhci_sideband *sb,
>>                     struct usb_host_endpoint *host_ep);
>> @@ -94,7 +107,8 @@ static inline bool xhci_sideband_check(struct usb_hcd 
>> *hcd)
>>   int
>>   xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
>> -                 bool ip_autoclear, u32 imod_interval, int intr_num);
>> +                 struct dma_pool *pool, bool ip_autoclear,
>> +                 u32 imod_interval, int intr_num);
>>   void
>>   xhci_sideband_remove_interrupter(struct xhci_sideband *sb);
>>   int
>> diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/ 
>> qc_audio_offload.c
>> index e4bfd43a2488..c94b15423a9a 100644
>> --- a/sound/usb/qcom/qc_audio_offload.c
>> +++ b/sound/usb/qcom/qc_audio_offload.c
>> @@ -7,6 +7,7 @@
>>   #include <linux/ctype.h>
>>   #include <linux/dma-mapping.h>
>>   #include <linux/dma-map-ops.h>
>> +#include <linux/dmapool.h>
>>   #include <linux/init.h>
>>   #include <linux/iommu.h>
>>   #include <linux/module.h>
>> @@ -131,6 +132,7 @@ struct uaudio_dev {
>>       /* xhci sideband */
>>       struct xhci_sideband *sb;
>> +    struct dma_pool *segment_pool;
>>       /* SoC USB device */
>>       struct snd_soc_usb_device *sdev;
>> @@ -1140,7 +1142,8 @@ uaudio_endpoint_setup(struct snd_usb_substream *subs,
>>       memcpy(ep_desc, &ep->desc, sizeof(ep->desc));
>> -    ret = xhci_sideband_add_endpoint(uadev[card_num].sb, ep);
>> +    ret = xhci_sideband_add_endpoint(uadev[card_num].sb, ep,
>> +                     uadev[card_num].segment_pool);
>>       if (ret < 0) {
>>           dev_err(&subs->dev->dev,
>>               "failed to add data ep to sec intr: %d\n", ret);
>> @@ -1211,8 +1214,9 @@ static int uaudio_event_ring_setup(struct 
>> snd_usb_substream *subs,
>>           goto exit;
>>       /* event ring */
>> -    ret = xhci_sideband_create_interrupter(uadev[card_num].sb, 1, false,
>> -                           0, uaudio_qdev->data->intr_num);
>> +    ret = xhci_sideband_create_interrupter(uadev[card_num].sb, 1,
>> +                           uadev[card_num].segment_pool,
>> +                           false, 0, uaudio_qdev->data->intr_num);
>>       if (ret < 0) {
>>           dev_err(&subs->dev->dev, "failed to fetch interrupter\n");
>>           goto put_offload;
>> @@ -1786,6 +1790,7 @@ static void qc_usb_audio_offload_probe(struct 
>> snd_usb_audio *chip)
>>       struct usb_interface_descriptor *altsd;
>>       struct usb_host_interface *alts;
>>       struct snd_soc_usb_device *sdev;
>> +    struct dma_pool *segment_pool;
>>       struct xhci_sideband *sb;
>>       /*
>> @@ -1804,10 +1809,21 @@ static void qc_usb_audio_offload_probe(struct 
>> snd_usb_audio *chip)
>>           if (!sdev)
>>               return;
>> +        segment_pool = dma_pool_create("xHCI sideband ring segments",
>> +                           interface_to_usbdev(intf)->bus->sysdev,
>> +                           TRB_SEGMENT_SIZE, TRB_SEGMENT_SIZE,
>> +                           TRB_SEGMENT_SIZE);
>> +        if (!segment_pool)
>> +            goto free_sdev;
>> +
>>           sb = xhci_sideband_register(intf, XHCI_SIDEBAND_VENDOR,
>>                           uaudio_sideband_notifier);
>> -        if (!sb)
>> +        if (!sb) {
>> +            dma_pool_destroy(segment_pool);
>>               goto free_sdev;
>> +        }
>> +
>> +        uadev[chip->card->number].segment_pool = segment_pool;
> 
> If struct xhci_sideband had dev pointer and segment size then we would just 
> swap the order:
> 
> 
> sb = xhci_sideband_register(intf, XHCI_SIDEBAND_VENDOR,
>                  uaudio_sideband_notifier);
> 
> 
> segment_pool = dma_pool_create("xHCI sideband ring segments",
>                  sb->hcdev,
>                  sb->ring_seg_size, sb->ring_seg_size,
>                  sb->ring_seg_size);
> 
> segment_pool isn't needed yet when resistering the sideband.
>
This could be a good way.  Let me take a look and get back to you.

Thanks
Wesley Cheng

  reply	other threads:[~2026-10-13  1:01 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-10  1:50 [PATCH v4 0/5] Add larger page size support for USB audio offload path Wesley Cheng
2026-09-10  1:50 ` [PATCH v4 1/5] usb: xhci: sideband: fix ring sg table for sub-page TRB segments Wesley Cheng
2026-09-10  1:50 ` [PATCH v4 2/5] usb: xhci: Allow to specify a different segment pool for allocations Wesley Cheng
2026-09-10  1:50 ` [PATCH v4 3/5] usb: xhci: sideband: allocate sideband ring segments from a dedicated pool Wesley Cheng
2026-10-05 11:03   ` Mathias Nyman
2026-10-13  1:01     ` Wesley Cheng [this message]
2026-10-13  9:11       ` Michal Pecio
2026-10-13 22:53         ` Wesley Cheng
2026-10-14  9:17           ` Michal Pecio
2026-10-14 14:26         ` Mathias Nyman
2026-09-10  1:51 ` [PATCH v4 4/5] ALSA: usb-audio: qcom: tag sideband endpoints before ring allocation Wesley Cheng
2026-09-10  1:51 ` [PATCH v4 5/5] ALSA: usb-audio: qcom: fix xfer ring IOMMU unmap on 16K+ page kernels Wesley Cheng
2026-09-30 22:23 ` [PATCH v4 0/5] Add larger page size support for USB audio offload path Wesley Cheng
2026-10-01 22:33   ` Mathias Nyman
2026-10-02  1:42     ` 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=82e0b9c7-3b3f-4dce-a72e-64989b08ef98@oss.qualcomm.com \
    --to=wesley.cheng@oss.qualcomm.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=mathias.nyman@linux.intel.com \
    --cc=michal.pecio@gmail.com \
    --cc=perex@perex.cz \
    --cc=tiwai@suse.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®