From: Wesley Cheng <wesley.cheng@oss.qualcomm.com>
To: Mathias Nyman <mathias.nyman@intel.com>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.com>
Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-sound@vger.kernel.org,
Wesley Cheng <wesley.cheng@oss.qualcomm.com>
Subject: [PATCH 1/2] xhci: sideband: support page-aligned ring segment allocation
Date: Mon, 24 Aug 2026 19:06:55 -0700 [thread overview]
Message-ID: <20260824-16k_offload_v1_b4-v1-1-49a6be60ca30@oss.qualcomm.com> (raw)
In-Reply-To: <20260824-16k_offload_v1_b4-v1-0-49a6be60ca30@oss.qualcomm.com>
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.
A sideband client that maps a ring buffer directly via the IOMMU
(which operates at page granularity) needs the ring to start at a
page boundary and occupy whole pages, otherwise the IOVA it is given
does not correspond to the actual start of the ring.
Add an alignment_req parameter to the ring/segment allocation path
(xhci_ring_alloc(), xhci_segment_alloc(), xhci_endpoint_init(),
xhci_alloc_interrupter(), xhci_create_secondary_interrupter()) and to
the sideband API (xhci_sideband_add_endpoint(),
xhci_sideband_create_interrupter()) so a sideband client can request
a specific alignment for its endpoint ring and interrupter event
ring. When the requested alignment exceeds TRB_SEGMENT_SIZE, allocate
the segment directly with dma_alloc_coherent() at that size instead of
from the shared pool. Store the alignment requirement on struct
xhci_ring so later segments added via ring expansion keep the same
alignment for the life of the ring.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Wesley Cheng <wesley.cheng@oss.qualcomm.com>
---
drivers/usb/host/xhci-mem.c | 70 ++++++++++++++++++++++++++++-----------
drivers/usb/host/xhci-sideband.c | 9 +++--
drivers/usb/host/xhci.h | 11 ++++--
include/linux/usb/xhci-sideband.h | 7 ++--
4 files changed, 70 insertions(+), 27 deletions(-)
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 7a21ac81f9c8..5f09b36c127b 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -15,6 +15,7 @@
#include <linux/dmapool.h>
#include <linux/dma-mapping.h>
#include <linux/bitfield.h>
+#include <linux/usb/xhci-sideband.h>
#include "xhci.h"
#include "xhci-trace.h"
@@ -30,6 +31,7 @@
static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci,
unsigned int max_packet,
unsigned int num,
+ unsigned int alignment_req,
gfp_t flags)
{
struct xhci_segment *seg;
@@ -40,7 +42,14 @@ 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);
+ if (alignment_req > TRB_SEGMENT_SIZE) {
+ seg->trbs = dma_alloc_coherent(dev, alignment_req, &dma, flags);
+ if (seg->trbs)
+ seg->alloc_size = alignment_req;
+ } else {
+ seg->trbs = dma_pool_zalloc(xhci->segment_pool, flags, &dma);
+ }
+
if (!seg->trbs) {
kfree(seg);
return NULL;
@@ -50,7 +59,10 @@ 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);
+ if (seg->alloc_size)
+ dma_free_coherent(dev, seg->alloc_size, seg->trbs, dma);
+ else
+ dma_pool_free(xhci->segment_pool, seg->trbs, dma);
kfree(seg);
return NULL;
}
@@ -65,7 +77,11 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci,
static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg)
{
if (seg->trbs) {
- dma_pool_free(xhci->segment_pool, seg->trbs, seg->dma);
+ if (seg->alloc_size)
+ dma_free_coherent(xhci_to_hcd(xhci)->self.sysdev,
+ seg->alloc_size, seg->trbs, seg->dma);
+ else
+ dma_pool_free(xhci->segment_pool, seg->trbs, seg->dma);
seg->trbs = NULL;
}
kfree(seg->bounce_buf);
@@ -334,7 +350,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->bounce_buf_len, num, ring->alignment_req, flags);
if (!prev)
return -ENOMEM;
num++;
@@ -343,7 +359,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->bounce_buf_len, num,
+ ring->alignment_req, flags);
if (!next)
goto free_segments;
@@ -370,7 +387,8 @@ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, struct xhci_ring
* 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)
+ enum xhci_ring_type type, unsigned int max_packet,
+ unsigned int alignment_req, gfp_t flags)
{
struct xhci_ring *ring;
int ret;
@@ -382,6 +400,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->alignment_req = alignment_req;
INIT_LIST_HEAD(&ring->td_list);
ring->type = type;
if (num_segs == 0)
@@ -421,6 +440,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.alignment_req = ring->alignment_req;
new_ring.type = ring->type;
ret = xhci_alloc_segments_for_ring(xhci, &new_ring, flags);
if (ret)
@@ -663,7 +683,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
for (cur_stream = 1; cur_stream < num_streams; cur_stream++) {
stream_info->stream_rings[cur_stream] =
- xhci_ring_alloc(xhci, 2, TYPE_STREAM, max_packet, mem_flags);
+ xhci_ring_alloc(xhci, 2, TYPE_STREAM, max_packet, 0, mem_flags);
cur_ring = stream_info->stream_rings[cur_stream];
if (!cur_ring)
goto cleanup_rings;
@@ -1007,7 +1027,7 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
}
/* Allocate endpoint 0 ring */
- dev->eps[0].ring = xhci_ring_alloc(xhci, 2, TYPE_CTRL, 0, flags);
+ dev->eps[0].ring = xhci_ring_alloc(xhci, 2, TYPE_CTRL, 0, 0, flags);
if (!dev->eps[0].ring)
goto fail;
@@ -1486,11 +1506,20 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
if (usb_endpoint_xfer_control(&ep->desc) && xhci->hci_version >= 0x100)
avg_trb_len = 8;
- /* Set up the endpoint ring */
- virt_dev->eps[ep_index].new_ring =
- xhci_ring_alloc(xhci, 2, ring_type, max_packet, mem_flags);
- if (!virt_dev->eps[ep_index].new_ring)
- return -ENOMEM;
+ if (virt_dev->eps[ep_index].sideband && virt_dev->eps[ep_index].sideband->alignment_req) {
+ virt_dev->eps[ep_index].new_ring =
+ xhci_ring_alloc(xhci, 2, ring_type, max_packet,
+ virt_dev->eps[ep_index].sideband->alignment_req,
+ mem_flags);
+ if (!virt_dev->eps[ep_index].new_ring)
+ return -ENOMEM;
+ } else {
+ /* Set up the endpoint ring */
+ virt_dev->eps[ep_index].new_ring =
+ xhci_ring_alloc(xhci, 2, ring_type, max_packet, 0, mem_flags);
+ if (!virt_dev->eps[ep_index].new_ring)
+ return -ENOMEM;
+ }
virt_dev->eps[ep_index].skip = false;
virt_dev->eps[ep_index].next_uframe = -1;
@@ -2291,7 +2320,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,
+ unsigned int alignment_req, gfp_t flags)
{
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
struct xhci_interrupter *ir;
@@ -2307,8 +2337,7 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int segs, gfp_t flags)
ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev));
if (!ir)
return NULL;
-
- ir->event_ring = xhci_ring_alloc(xhci, segs, TYPE_EVENT, 0, flags);
+ ir->event_ring = xhci_ring_alloc(xhci, segs, TYPE_EVENT, 0, alignment_req, flags);
if (!ir->event_ring) {
xhci_warn(xhci, "Failed to allocate interrupter event ring\n");
kfree(ir);
@@ -2356,7 +2385,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)
+ u32 imod_interval, unsigned int intr_num,
+ unsigned int alignment_req)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_interrupter *ir;
@@ -2367,7 +2397,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, alignment_req, GFP_KERNEL);
if (!ir)
return NULL;
@@ -2485,7 +2515,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
goto fail;
/* Set up the command ring to have one segments for now. */
- xhci->cmd_ring = xhci_ring_alloc(xhci, 1, TYPE_COMMAND, 0, flags);
+ xhci->cmd_ring = xhci_ring_alloc(xhci, 1, TYPE_COMMAND, 0, 0, flags);
if (!xhci->cmd_ring)
goto fail;
@@ -2498,7 +2528,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, 0, flags);
if (!xhci->interrupters[0])
goto fail;
diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c
index a5deeee4d5dc..471219d7d86d 100644
--- a/drivers/usb/host/xhci-sideband.c
+++ b/drivers/usb/host/xhci-sideband.c
@@ -144,7 +144,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,
+ unsigned int alignment_req)
{
struct xhci_virt_ep *ep;
unsigned int ep_index;
@@ -171,6 +172,7 @@ xhci_sideband_add_endpoint(struct xhci_sideband *sb,
if (sb->eps[ep_index] || ep->sideband)
return -EBUSY;
+ sb->alignment_req = alignment_req;
ep->sideband = sb;
sb->eps[ep_index] = ep;
@@ -322,7 +324,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)
+ bool ip_autoclear, u32 imod_interval, int intr_num,
+ unsigned int alignment_req)
{
if (!sb || !sb->xhci)
return -ENODEV;
@@ -337,7 +340,7 @@ xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
sb->ir = xhci_create_secondary_interrupter(xhci_to_hcd(sb->xhci),
num_seg, imod_interval,
- intr_num);
+ intr_num, alignment_req);
if (!sb->ir)
return -ENOMEM;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index c7bfa7f028d3..e356405c6351 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1290,6 +1290,10 @@ struct xhci_segment {
void *bounce_buf;
unsigned int bounce_offs;
unsigned int bounce_len;
+ /* nonzero if trbs was allocated via dma_alloc_coherent() at this size,
+ * instead of from xhci->segment_pool
+ */
+ unsigned int alloc_size;
};
enum xhci_cancelled_td_status {
@@ -1377,6 +1381,7 @@ struct xhci_ring {
unsigned int stream_id;
unsigned int num_segs;
unsigned int bounce_buf_len;
+ unsigned int alignment_req;
enum xhci_ring_type type;
u32 old_trb_comp_code;
struct radix_tree_root *trb_address_map;
@@ -1823,7 +1828,8 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev,
struct usb_device *udev, struct usb_host_endpoint *ep,
gfp_t mem_flags);
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);
+ enum xhci_ring_type type, unsigned int max_packet,
+ unsigned int alignment_req, gfp_t flags);
void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring);
int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
unsigned int num_trbs, gfp_t flags);
@@ -1865,7 +1871,8 @@ void xhci_free_port_bw_ctx(struct xhci_hcd *xhci,
struct xhci_container_ctx *ctx);
struct xhci_interrupter *
xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
- u32 imod_interval, unsigned int intr_num);
+ u32 imod_interval, unsigned int intr_num,
+ unsigned int alignment_req);
void xhci_remove_secondary_interrupter(struct usb_hcd
*hcd, struct xhci_interrupter *ir);
void xhci_skip_sec_intr_events(struct xhci_hcd *xhci,
diff --git a/include/linux/usb/xhci-sideband.h b/include/linux/usb/xhci-sideband.h
index 005257085dcb..8ab4e1cb0fc6 100644
--- a/include/linux/usb/xhci-sideband.h
+++ b/include/linux/usb/xhci-sideband.h
@@ -62,6 +62,7 @@ struct xhci_sideband {
struct usb_interface *intf;
int (*notify_client)(struct usb_interface *intf,
struct xhci_sideband_event *evt);
+ unsigned int alignment_req;
};
struct xhci_sideband *
@@ -72,7 +73,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,
+ unsigned int alignment_req);
int
xhci_sideband_remove_endpoint(struct xhci_sideband *sb,
struct usb_host_endpoint *host_ep);
@@ -94,7 +96,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);
+ bool ip_autoclear, u32 imod_interval, int intr_num,
+ unsigned int alignment_req);
void
xhci_sideband_remove_interrupter(struct xhci_sideband *sb);
int
--
2.34.1
next prev parent reply other threads:[~2026-08-25 2:07 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-25 2:06 [PATCH 0/2] Add larger page size support for USB audio offload path Wesley Cheng
2026-08-25 2:06 ` Wesley Cheng [this message]
2026-08-25 2:06 ` [PATCH 2/2] ALSA: usb-audio: qcom: request page-aligned xHCI ring buffers Wesley Cheng
2026-08-25 7:43 ` [PATCH 0/2] Add larger page size support for USB audio offload path Michal Pecio
2026-08-25 19:08 ` Wesley Cheng
2026-08-26 7:50 ` Wesley Cheng
2026-08-26 10:25 ` Michal Pecio
2026-08-26 11:44 ` Mathias Nyman
2026-08-26 19:58 ` Wesley Cheng
2026-08-25 11:09 ` Takashi Iwai
2026-08-25 19:09 ` 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=20260824-16k_offload_v1_b4-v1-1-49a6be60ca30@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=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