* [PATCH 0/3] usb: xhci: allocate arrays based on supported slot amount
@ 2026-05-07 8:39 Niklas Neronin
2026-05-07 8:39 ` [PATCH 1/3] usb: xhci: refactor DCBAA struct Niklas Neronin
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Niklas Neronin @ 2026-05-07 8:39 UTC (permalink / raw)
To: mathias.nyman; +Cc: linux-usb, Niklas Neronin
The xHCI specification allows for up to 255 device slots, but the actual
number of slots supported by the controller may be lower.
Prior to this patch, the xhci driver allocated the devices and device
context pointer arrays to 255 slots, regardless of the supported amount.
Each entry is 8 bytes, which results in 4080 bytes being allocated even
on systems that support far fewer slots.
Address this by allocating these arrays based on the number of slots
supported by the controller. This approach is consistent with how the
driver already handles allocation for ports and interrupters.
Niklas Neronin (3):
usb: xhci: refactor DCBAA struct
usb: xhci: allocate DCBAA based on host controller max slots
usb: xhci: allocate internal DCBAA mirror dynamically
drivers/usb/host/xhci-mem.c | 54 +++++++++++++++++++++---------------
drivers/usb/host/xhci-ring.c | 6 ++--
drivers/usb/host/xhci.c | 10 +++----
drivers/usb/host/xhci.h | 27 ++++++++++--------
4 files changed, 54 insertions(+), 43 deletions(-)
--
2.50.1
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 1/3] usb: xhci: refactor DCBAA struct
2026-05-07 8:39 [PATCH 0/3] usb: xhci: allocate arrays based on supported slot amount Niklas Neronin
@ 2026-05-07 8:39 ` Niklas Neronin
2026-05-07 8:39 ` [PATCH 2/3] usb: xhci: allocate DCBAA based on host controller max slots Niklas Neronin
2026-05-07 8:39 ` [PATCH 3/3] usb: xhci: allocate internal DCBAA mirror dynamically Niklas Neronin
2 siblings, 0 replies; 4+ messages in thread
From: Niklas Neronin @ 2026-05-07 8:39 UTC (permalink / raw)
To: mathias.nyman; +Cc: linux-usb, Niklas Neronin
Embed the 'xhci_device_context_array' structure directly within 'xhci_hcd'
instead of allocating it as a separate block. Only the array of device
context addresses is now allocated separately.
Since the device context addresses are no longer part of an array
structure, rename 'dev_context_ptrs' to 'ctx_array' for clearer access
semantics.
Also remove the redundant comment next to the 'ctx_array' allocation;
using dma_alloc_coherent() for 64-bit * N allocations guarantees both
physically contiguous and properly aligned for 64-byte boundaries.
The xHCI section (5.4.6) refers to DCBAAP instead of DCBAA (6.1).
This change does not modify the number of host controller slots but
simplifies memory management and prepares the driver for a variable number
of HC slots in the future.
Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
---
drivers/usb/host/xhci-mem.c | 40 ++++++++++++++++++------------------
drivers/usb/host/xhci-ring.c | 2 +-
drivers/usb/host/xhci.c | 6 +++---
drivers/usb/host/xhci.h | 13 +++++++-----
4 files changed, 32 insertions(+), 29 deletions(-)
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 997fe90f54e5..93c6053dc71b 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -883,8 +883,8 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, struct xhci_virt_device *dev,
/* If device ctx array still points to _this_ device, clear it */
if (dev->out_ctx &&
- xhci->dcbaa->dev_context_ptrs[slot_id] == cpu_to_le64(dev->out_ctx->dma))
- xhci->dcbaa->dev_context_ptrs[slot_id] = 0;
+ xhci->dcbaa.ctx_array[slot_id] == cpu_to_le64(dev->out_ctx->dma))
+ xhci->dcbaa.ctx_array[slot_id] = 0;
trace_xhci_free_virt_device(dev);
@@ -1022,11 +1022,11 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
dev->udev = udev;
/* Point to output device context in dcbaa. */
- xhci->dcbaa->dev_context_ptrs[slot_id] = cpu_to_le64(dev->out_ctx->dma);
+ xhci->dcbaa.ctx_array[slot_id] = cpu_to_le64(dev->out_ctx->dma);
xhci_dbg(xhci, "Set slot id %d dcbaa entry %p to 0x%llx\n",
slot_id,
- &xhci->dcbaa->dev_context_ptrs[slot_id],
- le64_to_cpu(xhci->dcbaa->dev_context_ptrs[slot_id]));
+ &xhci->dcbaa.ctx_array[slot_id],
+ le64_to_cpu(xhci->dcbaa.ctx_array[slot_id]));
trace_xhci_alloc_virt_device(dev);
@@ -1677,7 +1677,7 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags)
if (!xhci->scratchpad->sp_buffers)
goto fail_sp3;
- xhci->dcbaa->dev_context_ptrs[0] = cpu_to_le64(xhci->scratchpad->sp_dma);
+ xhci->dcbaa.ctx_array[0] = cpu_to_le64(xhci->scratchpad->sp_dma);
for (i = 0; i < num_sp; i++) {
dma_addr_t dma;
void *buf = dma_alloc_coherent(dev, xhci->page_size, &dma,
@@ -1933,6 +1933,7 @@ void xhci_rh_bw_cleanup(struct xhci_hcd *xhci)
void xhci_mem_cleanup(struct xhci_hcd *xhci)
{
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
+ struct xhci_device_context_array *dcbaa;
int i;
cancel_delayed_work_sync(&xhci->cmd_timer);
@@ -1978,10 +1979,12 @@ 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);
- xhci->dcbaa = NULL;
+ dcbaa = &xhci->dcbaa;
+ if (dcbaa->ctx_array) {
+ dma_free_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), MAX_HC_SLOTS),
+ dcbaa->ctx_array, dcbaa->dma);
+ dcbaa->ctx_array = NULL;
+ }
scratchpad_free(xhci);
@@ -2409,23 +2412,20 @@ EXPORT_SYMBOL_GPL(xhci_create_secondary_interrupter);
int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
{
- struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
- dma_addr_t dma;
+ struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
+ struct xhci_device_context_array *dcbaa = &xhci->dcbaa;
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Starting %s", __func__);
- /*
- * 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)
+ dcbaa->ctx_array =
+ dma_alloc_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), MAX_HC_SLOTS),
+ &dcbaa->dma, flags);
+ if (!dcbaa->ctx_array)
goto fail;
- xhci->dcbaa->dma = dma;
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Device context base array address = %pad (DMA), %p (virt)",
- &xhci->dcbaa->dma, xhci->dcbaa);
+ &dcbaa->dma, dcbaa->ctx_array);
/*
* Initialize the ring segment pool. The ring must be a contiguous
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index e47e644b296e..22f663daf625 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1613,7 +1613,7 @@ static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id,
/* Delete default control endpoint resources */
xhci_free_device_endpoint_resources(xhci, virt_dev, true);
if (cmd_comp_code == COMP_SUCCESS) {
- xhci->dcbaa->dev_context_ptrs[slot_id] = 0;
+ xhci->dcbaa.ctx_array[slot_id] = 0;
xhci->devs[slot_id] = NULL;
}
}
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index a54f5b57f205..9a471bd72265 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -558,7 +558,7 @@ static void xhci_init(struct usb_hcd *hcd)
xhci_set_cmd_ring_deq(xhci);
/* Set Device Context Base Address Array pointer */
- xhci_write_64(xhci, xhci->dcbaa->dma, &xhci->op_regs->dcbaa_ptr);
+ xhci_write_64(xhci, xhci->dcbaa.dma, &xhci->op_regs->dcbaa_ptr);
/* Set Doorbell array pointer */
xhci_set_doorbell_ptr(xhci);
@@ -4460,9 +4460,9 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
xhci_dbg_trace(xhci, trace_xhci_dbg_address,
"Slot ID %d dcbaa entry @%p = %#016llx",
udev->slot_id,
- &xhci->dcbaa->dev_context_ptrs[udev->slot_id],
+ &xhci->dcbaa.ctx_array[udev->slot_id],
(unsigned long long)
- le64_to_cpu(xhci->dcbaa->dev_context_ptrs[udev->slot_id]));
+ le64_to_cpu(xhci->dcbaa.ctx_array[udev->slot_id]));
xhci_dbg_trace(xhci, trace_xhci_dbg_address,
"Output Context DMA address = %#08llx",
(unsigned long long)virt_dev->out_ctx->dma);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index aeecd301f207..58d20b8459ec 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -792,12 +792,15 @@ struct xhci_tt_bw_info {
/**
* struct xhci_device_context_array
- * @dev_context_ptr array of 64-bit DMA addresses for device contexts
+ * @ctx_array: Pointer to an array of addresses
+ * @dma: DMA address to @ctx_array
+ *
+ * Device Context Base Address Array (DCBAA) - Section 6.1.
+ * ctx_array[0]: Scratchpad Buffer Array Base Address
+ * ctx_array[1-MaxSlots]: Device Context Base Address
*/
struct xhci_device_context_array {
- /* 64-bit device addresses; we only write 32-bit addresses */
- __le64 dev_context_ptrs[MAX_HC_SLOTS];
- /* private xHCD pointers */
+ __le64 *ctx_array;
dma_addr_t dma;
};
/*
@@ -1532,7 +1535,7 @@ struct xhci_hcd {
/* optional reset controller */
struct reset_control *reset;
/* data structures */
- struct xhci_device_context_array *dcbaa;
+ struct xhci_device_context_array dcbaa;
struct xhci_interrupter **interrupters;
struct xhci_ring *cmd_ring;
unsigned int cmd_ring_state;
--
2.50.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/3] usb: xhci: allocate DCBAA based on host controller max slots
2026-05-07 8:39 [PATCH 0/3] usb: xhci: allocate arrays based on supported slot amount Niklas Neronin
2026-05-07 8:39 ` [PATCH 1/3] usb: xhci: refactor DCBAA struct Niklas Neronin
@ 2026-05-07 8:39 ` Niklas Neronin
2026-05-07 8:39 ` [PATCH 3/3] usb: xhci: allocate internal DCBAA mirror dynamically Niklas Neronin
2 siblings, 0 replies; 4+ messages in thread
From: Niklas Neronin @ 2026-05-07 8:39 UTC (permalink / raw)
To: mathias.nyman; +Cc: linux-usb, Niklas Neronin
Allocate the Device Context Base Address Array (DCBAA) according to the
maximum number of device slots supported by the host controller, instead
of always allocating the absolute maximum of 255 entries.
The xHCI specification defines the DCBAA size as (MaxSlotsEnabled + 1)
entries. In the xhci driver there is currently no distinction between
MaxSlots and MaxSlotsEnabled, as all available slots are enabled during
initialization. As a result, 'max_slots' effectively represents both
values.
This change allows the xHCI driver to respect custom slot limits, reduces
unnecessary memory usage, and removes the obsolete "TODO" comment.
Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
---
drivers/usb/host/xhci-mem.c | 6 +++---
drivers/usb/host/xhci-ring.c | 4 ++--
drivers/usb/host/xhci.h | 7 ++-----
3 files changed, 7 insertions(+), 10 deletions(-)
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 93c6053dc71b..ac915dacd886 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1981,7 +1981,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
dcbaa = &xhci->dcbaa;
if (dcbaa->ctx_array) {
- dma_free_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), MAX_HC_SLOTS),
+ dma_free_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), xhci->max_slots + 1),
dcbaa->ctx_array, dcbaa->dma);
dcbaa->ctx_array = NULL;
}
@@ -2417,8 +2417,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Starting %s", __func__);
- dcbaa->ctx_array =
- dma_alloc_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), MAX_HC_SLOTS),
+ xhci->dcbaa.ctx_array =
+ dma_alloc_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), xhci->max_slots + 1),
&dcbaa->dma, flags);
if (!dcbaa->ctx_array)
goto fail;
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 22f663daf625..67231741f474 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -609,7 +609,7 @@ static struct xhci_virt_ep *xhci_get_virt_ep(struct xhci_hcd *xhci,
unsigned int slot_id,
unsigned int ep_index)
{
- if (slot_id == 0 || slot_id >= MAX_HC_SLOTS) {
+ if (slot_id == 0 || slot_id > xhci->max_slots) {
xhci_warn(xhci, "Invalid slot_id %u\n", slot_id);
return NULL;
}
@@ -1804,7 +1804,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
struct xhci_command *cmd;
u32 cmd_type;
- if (slot_id >= MAX_HC_SLOTS) {
+ if (slot_id > xhci->max_slots) {
xhci_warn(xhci, "Invalid slot_id %u\n", slot_id);
return;
}
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 58d20b8459ec..b467b875eeba 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -792,7 +792,8 @@ struct xhci_tt_bw_info {
/**
* struct xhci_device_context_array
- * @ctx_array: Pointer to an array of addresses
+ * @ctx_array: Pointer to an array of addresses. The array size depends on Max
+ * Slots read from HCSPARAMS1.
* @dma: DMA address to @ctx_array
*
* Device Context Base Address Array (DCBAA) - Section 6.1.
@@ -803,10 +804,6 @@ struct xhci_device_context_array {
__le64 *ctx_array;
dma_addr_t dma;
};
-/*
- * TODO: change this to be dynamically sized at HC mem init time since the HC
- * might not be able to handle the maximum number of devices possible.
- */
struct xhci_transfer_event {
--
2.50.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 3/3] usb: xhci: allocate internal DCBAA mirror dynamically
2026-05-07 8:39 [PATCH 0/3] usb: xhci: allocate arrays based on supported slot amount Niklas Neronin
2026-05-07 8:39 ` [PATCH 1/3] usb: xhci: refactor DCBAA struct Niklas Neronin
2026-05-07 8:39 ` [PATCH 2/3] usb: xhci: allocate DCBAA based on host controller max slots Niklas Neronin
@ 2026-05-07 8:39 ` Niklas Neronin
2 siblings, 0 replies; 4+ messages in thread
From: Niklas Neronin @ 2026-05-07 8:39 UTC (permalink / raw)
To: mathias.nyman; +Cc: linux-usb, Niklas Neronin
Allocate the internal virtual device array dynamically based on the
maximum number of slots reported by the host controller. Previously,
the array was always allocated to the absolute maximum of 255 entries.
Repurpose the 'MAX_HC_SLOTS' macro to limit the number of enabled slots.
This mirrors how the maximum number of ports and interrupters are handled.
The allocation now uses kcalloc_node(), which zeroes the memory
automatically, making the explicit memset() call unnecessary.
Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
---
drivers/usb/host/xhci-mem.c | 14 ++++++++++++--
drivers/usb/host/xhci.c | 4 +---
drivers/usb/host/xhci.h | 9 ++++++---
3 files changed, 19 insertions(+), 8 deletions(-)
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index ac915dacd886..1effc9f08678 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1953,8 +1953,11 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed command ring");
xhci_cleanup_command_queue(xhci);
- for (i = xhci->max_slots; i > 0; i--)
- xhci_free_virt_devices_depth_first(xhci, i);
+ if (xhci->devs) {
+ for (i = xhci->max_slots; i > 0; i--)
+ xhci_free_virt_devices_depth_first(xhci, i);
+ kfree(xhci->devs);
+ }
dma_pool_destroy(xhci->segment_pool);
xhci->segment_pool = NULL;
@@ -2011,6 +2014,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
xhci->rh_bw = NULL;
xhci->port_caps = NULL;
xhci->interrupters = NULL;
+ xhci->devs = NULL;
xhci->usb2_rhub.bus_state.bus_suspended = 0;
xhci->usb3_rhub.bus_state.bus_suspended = 0;
@@ -2417,6 +2421,12 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Starting %s", __func__);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocating internal virtual device array");
+ xhci->devs = kcalloc_node(xhci->max_slots + 1, sizeof(*xhci->devs), flags,
+ dev_to_node(dev));
+ if (!xhci->devs)
+ goto fail;
+
xhci->dcbaa.ctx_array =
dma_alloc_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), xhci->max_slots + 1),
&dcbaa->dma, flags);
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 9a471bd72265..151a759806f8 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -5460,7 +5460,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
if (xhci->hci_version > 0x100)
xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2);
- xhci->max_slots = HCS_MAX_SLOTS(hcs_params1);
+ xhci->max_slots = min(HCS_MAX_SLOTS(hcs_params1), MAX_HC_SLOTS);
xhci->max_ports = min(HCS_MAX_PORTS(hcs_params1), MAX_HC_PORTS);
/* xhci-plat or xhci-pci might have set max_interrupters already */
if (!xhci->max_interrupters)
@@ -5533,8 +5533,6 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
init_completion(&xhci->cmd_ring_stop_completion);
xhci_hcd_page_size(xhci);
- memset(xhci->devs, 0, MAX_HC_SLOTS * sizeof(*xhci->devs));
-
/* Allocate xHCI data structures */
retval = xhci_mem_init(xhci, GFP_KERNEL);
if (retval)
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index b467b875eeba..f977c8e6a90a 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -33,8 +33,11 @@
/* xHCI PCI Configuration Registers */
#define XHCI_SBRN_OFFSET (0x60)
-/* Max number of USB devices for any host controller - limit in section 6.1 */
-#define MAX_HC_SLOTS 256
+/*
+ * Max number of Devices Slots. xHCI specification section 5.3.3
+ * Valid values are in the range of 1 to 255.
+ */
+#define MAX_HC_SLOTS 255
/*
* Max Number of Ports. xHCI specification section 5.3.3
* Valid values are in the range of 1 to 255.
@@ -1552,7 +1555,7 @@ struct xhci_hcd {
/* these are not thread safe so use mutex */
struct mutex mutex;
/* Internal mirror of the HW's dcbaa */
- struct xhci_virt_device *devs[MAX_HC_SLOTS];
+ struct xhci_virt_device **devs;
/* For keeping track of bandwidth domains per roothub. */
struct xhci_root_port_bw_info *rh_bw;
--
2.50.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-05-07 8:41 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-07 8:39 [PATCH 0/3] usb: xhci: allocate arrays based on supported slot amount Niklas Neronin
2026-05-07 8:39 ` [PATCH 1/3] usb: xhci: refactor DCBAA struct Niklas Neronin
2026-05-07 8:39 ` [PATCH 2/3] usb: xhci: allocate DCBAA based on host controller max slots Niklas Neronin
2026-05-07 8:39 ` [PATCH 3/3] usb: xhci: allocate internal DCBAA mirror dynamically Niklas Neronin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox