* [PATCH v6 1/4] PCI/DOE: Move common definitions to the header file
2026-06-23 9:07 [PATCH v6 0/4] PCI: Add DOE support for endpoint Aksh Garg
@ 2026-06-23 9:07 ` Aksh Garg
2026-06-23 9:13 ` sashiko-bot
2026-06-23 9:07 ` [PATCH v6 2/4] PCI: endpoint: Add DOE mailbox support for endpoint functions Aksh Garg
` (2 subsequent siblings)
3 siblings, 1 reply; 9+ messages in thread
From: Aksh Garg @ 2026-06-23 9:07 UTC (permalink / raw)
To: linux-pci, linux-doc, mani, kwilczynski, bhelgaas, corbet, kishon,
skhan, lukas, cassel, alistair
Cc: linux-arm-kernel, linux-kernel, rdunlap, Frank.Li, s-vadapalli,
danishanwar, srk, a-garg7
Move common macros and structures from drivers/pci/doe.c to
drivers/pci/pci.h to allow reuse across root complex and
endpoint DOE implementations.
PCI_DOE_MAX_LENGTH macro can be used outside the PCI core as well,
hence move the macro to include/linux/pci-doe.h.
These changes prepare the groundwork for the DOE endpoint implementation
that will reuse these common definitions.
Co-developed-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Aksh Garg <a-garg7@ti.com>
---
Changes from v5 to v6:
- None.
Changes from v4 to v5:
- None.
Changes from v3 to v4:
- None.
Changes from v2 to v3:
- Rebased on 7.1-rc1.
Changes since v1:
- Moved the common macros that need not be visible outside the PCI core
to drivers/pci/pci.h instead to include/linux/pci-doe.h as suggested
by Lukas Wunner
- Removed the redundant empty inlines guarded with CONFIG_PCI_DOE in
include/linux/pci-doe.h.
v5: https://lore.kernel.org/all/20260610100256.1889111-2-a-garg7@ti.com/
v4: https://lore.kernel.org/all/20260522052434.802034-2-a-garg7@ti.com/
v3: https://lore.kernel.org/all/20260427051725.223704-2-a-garg7@ti.com/
v2: https://lore.kernel.org/all/20260401073022.215805-2-a-garg7@ti.com/
v1: https://lore.kernel.org/all/20260213123603.420941-3-a-garg7@ti.com/
drivers/pci/doe.c | 11 -----------
drivers/pci/pci.h | 9 +++++++++
include/linux/pci-doe.h | 3 +++
3 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c
index 7b41da4ec11a..e8d9e95644b3 100644
--- a/drivers/pci/doe.c
+++ b/drivers/pci/doe.c
@@ -28,12 +28,6 @@
#define PCI_DOE_TIMEOUT HZ
#define PCI_DOE_POLL_INTERVAL (PCI_DOE_TIMEOUT / 128)
-#define PCI_DOE_FLAG_CANCEL 0
-#define PCI_DOE_FLAG_DEAD 1
-
-/* Max data object length is 2^18 dwords */
-#define PCI_DOE_MAX_LENGTH (1 << 18)
-
/**
* struct pci_doe_mb - State for a single DOE mailbox
*
@@ -63,11 +57,6 @@ struct pci_doe_mb {
#endif
};
-struct pci_doe_feature {
- u16 vid;
- u8 type;
-};
-
/**
* struct pci_doe_task - represents a single query/response
*
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 4a14f88e543a..5844deee2b5f 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -683,6 +683,15 @@ struct pci_sriov {
bool drivers_autoprobe; /* Auto probing of VFs by driver */
};
+/* DOE Mailbox state flags */
+#define PCI_DOE_FLAG_CANCEL 0
+#define PCI_DOE_FLAG_DEAD 1
+
+struct pci_doe_feature {
+ u16 vid;
+ u8 type;
+};
+
#ifdef CONFIG_PCI_DOE
void pci_doe_init(struct pci_dev *pdev);
void pci_doe_destroy(struct pci_dev *pdev);
diff --git a/include/linux/pci-doe.h b/include/linux/pci-doe.h
index bd4346a7c4e7..abb9b7ae8029 100644
--- a/include/linux/pci-doe.h
+++ b/include/linux/pci-doe.h
@@ -19,6 +19,9 @@ struct pci_doe_mb;
#define PCI_DOE_FEATURE_CMA 1
#define PCI_DOE_FEATURE_SSESSION 2
+/* Max data object length is 2^18 dwords */
+#define PCI_DOE_MAX_LENGTH (1 << 18)
+
struct pci_doe_mb *pci_find_doe_mailbox(struct pci_dev *pdev, u16 vendor,
u8 type);
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v6 2/4] PCI: endpoint: Add DOE mailbox support for endpoint functions
2026-06-23 9:07 [PATCH v6 0/4] PCI: Add DOE support for endpoint Aksh Garg
2026-06-23 9:07 ` [PATCH v6 1/4] PCI/DOE: Move common definitions to the header file Aksh Garg
@ 2026-06-23 9:07 ` Aksh Garg
2026-06-23 9:21 ` sashiko-bot
2026-06-23 9:07 ` [PATCH v6 3/4] PCI: endpoint: Add support for DOE initialization and setup in EPC core Aksh Garg
2026-06-23 9:07 ` [PATCH v6 4/4] Documentation: PCI: Add documentation for DOE endpoint support Aksh Garg
3 siblings, 1 reply; 9+ messages in thread
From: Aksh Garg @ 2026-06-23 9:07 UTC (permalink / raw)
To: linux-pci, linux-doc, mani, kwilczynski, bhelgaas, corbet, kishon,
skhan, lukas, cassel, alistair
Cc: linux-arm-kernel, linux-kernel, rdunlap, Frank.Li, s-vadapalli,
danishanwar, srk, a-garg7
DOE (Data Object Exchange) is a standard PCIe extended capability
feature defined in PCI Express Base Specification Revision 7.0,
Section 6.30. It provides a communication mechanism primarily used for
implementing PCIe security features such as device authentication, and
secure link establishment. Think of DOE as a sophisticated mailbox
system built into PCIe. The root complex can send structured requests
to the endpoint device through DOE mailboxes, and the endpoint device
responds with appropriate data.
Add the DOE support for PCIe endpoint devices, enabling endpoint
functions to process the DOE requests from the host. The implementation
provides framework APIs for EPC core driver and controller drivers to
register mailboxes, and request processing with workqueues ensuring
sequential handling per mailbox, and parallel handling across mailboxes.
The Discovery protocol is handled internally by the DOE core.
This implementation complements the existing DOE implementation for
root complex in drivers/pci/doe.c.
Co-developed-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Aksh Garg <a-garg7@ti.com>
---
Changes from v5 yo v6:
- Addressed the review comments provided by Bjorn Helgaas at v5
- Used xa_lock_irqsave() instead of xa_lock() in pci_ep_doe_get_mailbox()
- Added a spinlock to protect the work_queue of the doe_mb struct from
being destroyed while being called
Changes from v4 to v5:
- Addressed the review comments by Sashiko
- Added refcount per DOE Mailbox to fix Use-After-Free bug
- Change in the Abort Sequence:
* Instead of waiting on flush_workqueue() to clear the CANCEL flag,
return immediately after setting the CANCEL flag. The CANCEL flag
gets cleared in signal_task_complete(), allowing the mailbox to
accept new requests
* Abort sequence handling in various scenarios is updated and explained
in the documentation at PATCH 4/4
Changes from v3 to v4:
- Used 'Returns' instead of 'RETURNS' in the function docstrings to
comply with kernel-doc format, as suggested by Manivannan Sadhasivam.
- In pci_ep_doe_process_request(), changed the type of request buffer
from "const void *" to "void *", as the ownership is transferred to
DOE-EP framework, which is responsible to free the buffer.
- Added "struct pci_epc *epc" to typedef "pci_ep_doe_complete_t", to be
used by the EPC driver.
Changes from v2 to v3:
- Rebased on 7.1-rc1.
Changes since v1:
- Moved the DOE-EP core file to drivers/pci/endpoint/pci-ep-doe.c, and
corresponding Kconfig and Makefile to match the existing naming scheme,
as suggested by Niklas Cassel.
- Renamed the config from PCI_DOE_EP to PCI_ENDPOINT_DOE
- Moved the function declarations that need not be visible outside the
PCI core to drivers/pci/pci.h instead to include/linux/pci-doe.h as
suggested by Lukas Wunner
- Converted from synchronous to asynchronous request processing:
* Removed wait_for_completion() from pci_ep_doe_process_request()
* Function returns immediately after queuing to workqueue, hence
removed private data for completion in the task structure
* Added completion callback as an additional argument to
pci_ep_doe_process_request(), which takes the response and status
parameters as arguments (along with other required arguments), hence
removed task_status in the task structure
* Created a typedef pci_ep_doe_complete_t for completion callback
* Removed the pci_ep_doe_task_complete() function, as it would not be
required anymore with these changes
* Moved from INIT_WORK_ONSTACK() to INIT_WORK(), to initialize the work
on heap instead of stack
* signal_task_complete() now invokes the completion callback, once the
protocol handler completes its task
- Changed from dynamic xarray-based protocol registration to static array:
* Removed the register/unregister protocol APIs
* Replaced the dynamic xarray with static array of struct pci_doe_protocol
* Added discovery protocol to static array, instead of treating it specially,
hence removed the special handling for Discovery protocol in
doe_ep_task_work()
* Updated pci_ep_doe_handle_discovery() and pci_ep_doe_find_protocol()
accordingly.
- Memory Management:
* DOE core frees request buffer in signal_task_complete()
or during error handling
* pci_ep_doe_process_request() defines response_pl and response_pl_sz
as NULL and 0 respectively, whose pointer is passed to the protocol
handler, hence removed the arguments void **response, size_t *response_sz
to this function.
- Task structure refactoring:
* Response buffer: void **response_pl to void *response_pl
* Response size: size_t *response_pl_sz to size_t response_pl_sz
* Changed the completion callback to type pci_ep_doe_complete_t
* Removed void *private and int task_status
- Updated documentation comments of the functions according to the changes
v5: https://lore.kernel.org/all/20260610100256.1889111-3-a-garg7@ti.com/
v4: https://lore.kernel.org/all/20260522052434.802034-3-a-garg7@ti.com/
v3: https://lore.kernel.org/all/20260427051725.223704-3-a-garg7@ti.com/
v2: https://lore.kernel.org/all/20260401073022.215805-3-a-garg7@ti.com/
v1: https://lore.kernel.org/all/20260213123603.420941-4-a-garg7@ti.com/
drivers/pci/endpoint/Kconfig | 14 +
drivers/pci/endpoint/Makefile | 1 +
drivers/pci/endpoint/pci-ep-doe.c | 591 ++++++++++++++++++++++++++++++
drivers/pci/pci.h | 42 +++
include/linux/pci-doe.h | 5 +
include/linux/pci-epc.h | 3 +
6 files changed, 656 insertions(+)
create mode 100644 drivers/pci/endpoint/pci-ep-doe.c
diff --git a/drivers/pci/endpoint/Kconfig b/drivers/pci/endpoint/Kconfig
index 8dad291be8b8..15ae16aaa58f 100644
--- a/drivers/pci/endpoint/Kconfig
+++ b/drivers/pci/endpoint/Kconfig
@@ -36,6 +36,20 @@ config PCI_ENDPOINT_MSI_DOORBELL
doorbell. The RC can trigger doorbell in EP by writing data to a
dedicated BAR, which the EP maps to the controller's message address.
+config PCI_ENDPOINT_DOE
+ bool "PCI Endpoint Data Object Exchange (DOE) support"
+ depends on PCI_ENDPOINT
+ help
+ This enables support for Data Object Exchange (DOE) protocol
+ on PCI Endpoint controllers. It provides a communication
+ mechanism through mailboxes, primarily used for PCIe security
+ features.
+
+ Say Y here if you want be able to communicate using PCIe DOE
+ mailboxes.
+
+ If unsure, say N.
+
source "drivers/pci/endpoint/functions/Kconfig"
endmenu
diff --git a/drivers/pci/endpoint/Makefile b/drivers/pci/endpoint/Makefile
index b4869d52053a..1fa176b6792b 100644
--- a/drivers/pci/endpoint/Makefile
+++ b/drivers/pci/endpoint/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_PCI_ENDPOINT_CONFIGFS) += pci-ep-cfs.o
obj-$(CONFIG_PCI_ENDPOINT) += pci-epc-core.o pci-epf-core.o\
pci-epc-mem.o functions/
obj-$(CONFIG_PCI_ENDPOINT_MSI_DOORBELL) += pci-ep-msi.o
+obj-$(CONFIG_PCI_ENDPOINT_DOE) += pci-ep-doe.o
diff --git a/drivers/pci/endpoint/pci-ep-doe.c b/drivers/pci/endpoint/pci-ep-doe.c
new file mode 100644
index 000000000000..b2832253eaca
--- /dev/null
+++ b/drivers/pci/endpoint/pci-ep-doe.c
@@ -0,0 +1,591 @@
+// SPDX-License-Identifier: GPL-2.0-only OR MIT
+/*
+ * Data Object Exchange for PCIe Endpoint
+ * PCIe r7.0, sec 6.30 DOE
+ *
+ * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com
+ * Aksh Garg <a-garg7@ti.com>
+ * Siddharth Vadapalli <s-vadapalli@ti.com>
+ */
+
+#define dev_fmt(fmt) "DOE EP: " fmt
+
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/pci.h>
+#include <linux/pci-epc.h>
+#include <linux/pci-doe.h>
+#include <linux/refcount.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/xarray.h>
+
+#include "../pci.h"
+
+/* Forward declaration of discovery protocol handler */
+static int pci_ep_doe_handle_discovery(const void *request, size_t request_sz,
+ void **response, size_t *response_sz);
+
+/**
+ * struct pci_doe_protocol - DOE protocol handler entry
+ * @vid: Vendor ID
+ * @type: Protocol Type
+ * @handler: Handler function pointer
+ */
+struct pci_doe_protocol {
+ u16 vid;
+ u8 type;
+ pci_doe_protocol_handler_t handler;
+};
+
+/**
+ * struct pci_ep_doe_mb - State for a single DOE mailbox on EP
+ *
+ * This state is used to manage a single DOE mailbox capability on the
+ * endpoint side.
+ *
+ * @epc: PCI endpoint controller this mailbox belongs to
+ * @func_no: Physical function number of the function this mailbox belongs to
+ * @cap_offset: Capability offset
+ * @work_queue: Queue of work items
+ * @flags: Bit array of PCI_DOE_FLAG_* flags
+ * @refs: Refcount to manage mailbox lifetime and ensure safe cleanup
+ * @lock: Spinlock protecting work_queue access and DEAD flag checks
+ */
+struct pci_ep_doe_mb {
+ struct pci_epc *epc;
+ u8 func_no;
+ u16 cap_offset;
+ struct workqueue_struct *work_queue;
+ unsigned long flags;
+ refcount_t refs;
+ spinlock_t lock; /* Serialize work queue access */
+};
+
+/**
+ * struct pci_ep_doe_task - Represents a single DOE request/response task
+ *
+ * @feat: DOE feature (Vendor ID and Type)
+ * @request_pl: Request payload
+ * @request_pl_sz: Size of request payload in bytes
+ * @response_pl: Response buffer
+ * @response_pl_sz: Size of response buffer in bytes
+ * @complete: Completion callback
+ * @work: Work structure for workqueue
+ * @doe_mb: DOE mailbox handling this task
+ */
+struct pci_ep_doe_task {
+ struct pci_doe_feature feat;
+ const void *request_pl;
+ size_t request_pl_sz;
+ void *response_pl;
+ size_t response_pl_sz;
+ pci_ep_doe_complete_t complete;
+
+ /* Initialized by pci_ep_doe_submit_task() */
+ struct work_struct work;
+ struct pci_ep_doe_mb *doe_mb;
+};
+
+/*
+ * Global registry of protocol handlers.
+ * When a new DOE protocol, library is added, add an entry to this array.
+ */
+static const struct pci_doe_protocol pci_doe_protocols[] = {
+ {
+ .vid = PCI_VENDOR_ID_PCI_SIG,
+ .type = PCI_DOE_FEATURE_DISCOVERY,
+ .handler = pci_ep_doe_handle_discovery,
+ },
+};
+
+/*
+ * Combine function number and capability offset into a unique lookup key
+ * for storing/retrieving DOE mailboxes in an xarray.
+ */
+#define PCI_DOE_MB_KEY(func, offset) \
+ (((unsigned long)(func) << 16) | (offset))
+#define PCI_DOE_PROTOCOL_COUNT ARRAY_SIZE(pci_doe_protocols)
+
+/**
+ * pci_ep_doe_init() - Initialize the DOE framework for a controller in EP mode
+ * @epc: PCI endpoint controller
+ *
+ * Initialize the xarray that will hold the mailboxes.
+ */
+void pci_ep_doe_init(struct pci_epc *epc)
+{
+ xa_init(&epc->doe_mbs);
+}
+EXPORT_SYMBOL_GPL(pci_ep_doe_init);
+
+/**
+ * pci_ep_doe_add_mailbox() - Add a DOE mailbox for a physical function
+ * @epc: PCI endpoint controller
+ * @func_no: Physical function number
+ * @cap_offset: Offset of the DOE capability
+ *
+ * Create and register a DOE mailbox for the specified physical function
+ * and capability offset.
+ *
+ * EPC core driver calls this for each DOE capability discovered in the config
+ * space of each endpoint function if DOE support is available for the EPC.
+ *
+ * Return: 0 on success, -errno on failure
+ */
+int pci_ep_doe_add_mailbox(struct pci_epc *epc, u8 func_no, u16 cap_offset)
+{
+ struct pci_ep_doe_mb *doe_mb;
+ unsigned long key;
+ int ret;
+
+ doe_mb = kzalloc_obj(*doe_mb, GFP_KERNEL);
+ if (!doe_mb)
+ return -ENOMEM;
+
+ doe_mb->epc = epc;
+ doe_mb->func_no = func_no;
+ doe_mb->cap_offset = cap_offset;
+
+ doe_mb->work_queue = alloc_ordered_workqueue("pci_ep_doe[%s:pf%d:offset%x]",
+ 0, dev_name(&epc->dev),
+ func_no, cap_offset);
+ if (!doe_mb->work_queue) {
+ dev_err(epc->dev.parent,
+ "[pf%d:offset%x] failed to allocate work queue\n",
+ func_no, cap_offset);
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ /* Add to xarray with composite key */
+ key = PCI_DOE_MB_KEY(func_no, cap_offset);
+ ret = xa_insert(&epc->doe_mbs, key, doe_mb, GFP_KERNEL);
+ if (ret) {
+ dev_err(epc->dev.parent,
+ "[pf%d:offset%x] failed to insert mailbox: %d\n",
+ func_no, cap_offset, ret);
+ goto err_destroy;
+ }
+
+ refcount_set(&doe_mb->refs, 1);
+ spin_lock_init(&doe_mb->lock);
+
+ dev_dbg(epc->dev.parent,
+ "DOE mailbox added: pf%d offset 0x%x\n",
+ func_no, cap_offset);
+
+ return 0;
+
+err_destroy:
+ destroy_workqueue(doe_mb->work_queue);
+err_free:
+ kfree(doe_mb);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pci_ep_doe_add_mailbox);
+
+/**
+ * pci_ep_doe_cancel_tasks() - Cancel all pending tasks
+ * @doe_mb: DOE mailbox
+ *
+ * Cancel all pending tasks in the mailbox. Mark the mailbox as dead
+ * so no new tasks can be submitted.
+ */
+static void pci_ep_doe_cancel_tasks(struct pci_ep_doe_mb *doe_mb)
+{
+ /* Mark the mailbox as dead */
+ set_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags);
+
+ /* Stop all pending work items from starting */
+ set_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
+}
+
+/**
+ * pci_ep_doe_get_mailbox() - Get DOE mailbox by function and offset
+ * @epc: PCI endpoint controller
+ * @func_no: Physical function number
+ * @cap_offset: Offset of the DOE capability
+ *
+ * Internal helper to look up a DOE mailbox by its function number and
+ * capability offset.
+ *
+ * Return: Pointer to the mailbox or NULL if not found
+ */
+static struct pci_ep_doe_mb *pci_ep_doe_get_mailbox(struct pci_epc *epc,
+ u8 func_no, u16 cap_offset)
+{
+ struct pci_ep_doe_mb *doe_mb;
+ unsigned long key, flags;
+
+ key = PCI_DOE_MB_KEY(func_no, cap_offset);
+
+ xa_lock_irqsave(&epc->doe_mbs, flags);
+
+ doe_mb = xa_load(&epc->doe_mbs, key);
+ if (doe_mb && !refcount_inc_not_zero(&doe_mb->refs))
+ doe_mb = NULL;
+
+ xa_unlock_irqrestore(&epc->doe_mbs, flags);
+
+ return doe_mb;
+}
+
+/**
+ * pci_ep_doe_put_mailbox() - Release a reference to a DOE mailbox
+ * @doe_mb: The mailbox structure to release
+ *
+ * Drop the reference count. Free the memory allocated to the mailbox structure
+ * if this was the last active reference.
+ */
+static void pci_ep_doe_put_mailbox(struct pci_ep_doe_mb *doe_mb)
+{
+ if (refcount_dec_and_test(&doe_mb->refs))
+ kfree(doe_mb);
+}
+
+/**
+ * pci_ep_doe_find_protocol() - Find protocol handler in static array
+ * @vendor: Vendor ID
+ * @type: Protocol Type
+ *
+ * Look up a protocol handler in the static protocol array by matching
+ * Vendor ID and Protocol Type.
+ *
+ * Return: Handler function pointer or NULL if not found
+ */
+static pci_doe_protocol_handler_t pci_ep_doe_find_protocol(u16 vendor, u8 type)
+{
+ int i;
+
+ /* Search static protocol array */
+ for (i = 0; i < PCI_DOE_PROTOCOL_COUNT; i++) {
+ if (pci_doe_protocols[i].vid == vendor &&
+ pci_doe_protocols[i].type == type)
+ return pci_doe_protocols[i].handler;
+ }
+
+ return NULL;
+}
+
+/**
+ * pci_ep_doe_handle_discovery() - Handle Discovery protocol request
+ * @request: Request payload
+ * @request_sz: Request size
+ * @response: Output pointer for response buffer
+ * @response_sz: Output pointer for response size
+ *
+ * Handle the DOE Discovery protocol. The request contains an index specifying
+ * which protocol to query. This function creates a response containing the
+ * Vendor ID and Protocol Type for the requested index, along with the next
+ * index value for further discovery:
+ *
+ * - next_index = 0: Signals this is the last protocol supported
+ * - next_index = n (non-zero): Signals more protocols available,
+ * query index n next
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int pci_ep_doe_handle_discovery(const void *request, size_t request_sz,
+ void **response, size_t *response_sz)
+{
+ struct pci_doe_protocol protocol;
+ u8 requested_index, next_index;
+ u32 *response_pl;
+ u32 request_pl;
+ u16 vendor;
+ u8 type;
+
+ if (request_sz != sizeof(u32))
+ return -EINVAL;
+
+ request_pl = *(u32 *)request;
+ requested_index = FIELD_GET(PCI_DOE_DATA_OBJECT_DISC_REQ_3_INDEX,
+ request_pl);
+
+ if (requested_index >= PCI_DOE_PROTOCOL_COUNT) {
+ /* No more protocols to report */
+ vendor = 0;
+ type = 0;
+ } else {
+ /* Get protocol from array at requested_index */
+ protocol = pci_doe_protocols[requested_index];
+ vendor = protocol.vid;
+ type = protocol.type;
+ }
+
+ /* Calculate next index */
+ next_index = (requested_index + 1 < PCI_DOE_PROTOCOL_COUNT) ?
+ requested_index + 1 : 0;
+
+ response_pl = kzalloc_obj(*response_pl, GFP_KERNEL);
+ if (!response_pl)
+ return -ENOMEM;
+
+ /* Build response */
+ *response_pl = FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_RSP_3_VID, vendor) |
+ FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_RSP_3_TYPE, type) |
+ FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_RSP_3_NEXT_INDEX,
+ next_index);
+
+ *response = response_pl;
+ *response_sz = sizeof(*response_pl);
+
+ return 0;
+}
+
+static void signal_task_complete(struct pci_ep_doe_task *task, int status)
+{
+ struct pci_ep_doe_mb *doe_mb = task->doe_mb;
+
+ task->complete(doe_mb->epc, doe_mb->func_no, doe_mb->cap_offset,
+ status, task->feat.vid, task->feat.type,
+ task->response_pl, task->response_pl_sz);
+
+ /* Clear the CANCEL flag for next DOE request */
+ clear_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
+
+ kfree(task->request_pl);
+ kfree(task);
+
+ /* Release the mailbox reference acquired during process_request */
+ pci_ep_doe_put_mailbox(doe_mb);
+}
+
+/**
+ * doe_ep_task_work() - Work function for processing DOE EP tasks
+ * @work: Work structure
+ *
+ * Process a DOE request by calling the appropriate protocol handler.
+ */
+static void doe_ep_task_work(struct work_struct *work)
+{
+ struct pci_ep_doe_task *task = container_of(work,
+ struct pci_ep_doe_task,
+ work);
+ struct pci_ep_doe_mb *doe_mb = task->doe_mb;
+ pci_doe_protocol_handler_t handler;
+ int rc;
+
+ if (test_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags)) {
+ signal_task_complete(task, -EIO);
+ return;
+ }
+
+ /* Check if request was aborted */
+ if (test_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags)) {
+ signal_task_complete(task, -ECANCELED);
+ return;
+ }
+
+ handler = pci_ep_doe_find_protocol(task->feat.vid, task->feat.type);
+ if (!handler) {
+ dev_warn_ratelimited(doe_mb->epc->dev.parent,
+ "[%d:%x] Unsupported protocol VID=%04x TYPE=%02x\n",
+ doe_mb->func_no, doe_mb->cap_offset,
+ task->feat.vid, task->feat.type);
+ signal_task_complete(task, -EOPNOTSUPP);
+ return;
+ }
+
+ rc = handler(task->request_pl, task->request_pl_sz,
+ &task->response_pl, &task->response_pl_sz);
+
+ signal_task_complete(task, rc);
+}
+
+/**
+ * pci_ep_doe_submit_task() - Submit a task to be processed
+ * @doe_mb: DOE mailbox
+ * @task: Task to submit
+ *
+ * Submit a DOE task to the workqueue for asynchronous processing.
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int pci_ep_doe_submit_task(struct pci_ep_doe_mb *doe_mb,
+ struct pci_ep_doe_task *task)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&doe_mb->lock, flags);
+ if (test_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ task->doe_mb = doe_mb;
+ INIT_WORK(&task->work, doe_ep_task_work);
+ queue_work(doe_mb->work_queue, &task->work);
+
+out:
+ spin_unlock_irqrestore(&doe_mb->lock, flags);
+ return ret;
+}
+
+/**
+ * pci_ep_doe_process_request() - Process DOE request on endpoint
+ * @epc: PCI endpoint controller
+ * @func_no: Physical function number
+ * @cap_offset: DOE capability offset
+ * @vendor: Vendor ID from request header
+ * @type: Protocol Type from request header
+ * @request: Request payload in CPU-native format
+ * @request_sz: Size of request payload (bytes)
+ * @complete: Callback to invoke upon completion
+ *
+ * Asynchronously process a DOE request received on the endpoint. The request
+ * payload should not include the DOE header (vendor/type/length). Ownership
+ * of the request buffer is transferred to DOE EP core, which frees the buffer
+ * either on error or by signal_task_complete() after the completion callback
+ * fires. The protocol handler will allocate the response buffer, which the
+ * caller (controller driver) must free after use.
+ *
+ * This function returns immediately after queuing the request. The completion
+ * callback will be invoked asynchronously from workqueue context once the
+ * request is processed. The callback receives the function number and
+ * capability offset to identify the mailbox, along with a status code
+ * (0 on success, -errno on failure), and other required arguments.
+ *
+ * As per DOE specification, a mailbox processes one request at a time.
+ * Therefore, this function will never be called concurrently for the same
+ * mailbox by different callers.
+ *
+ * The caller is responsible for the conversion of the received DOE request
+ * with le32_to_cpu() before calling this function. Similarly, it is also
+ * responsible for converting the response payload with cpu_to_le32() before
+ * sending it back over the DOE mailbox.
+ *
+ * The caller is also responsible for ensuring that the request size is within
+ * the limits defined by PCI_DOE_MAX_LENGTH.
+ *
+ * Return: 0 if the request was successfully queued, -errno on failure
+ */
+int pci_ep_doe_process_request(struct pci_epc *epc, u8 func_no, u16 cap_offset,
+ u16 vendor, u8 type, void *request,
+ size_t request_sz,
+ pci_ep_doe_complete_t complete)
+{
+ struct pci_ep_doe_mb *doe_mb;
+ struct pci_ep_doe_task *task;
+ int ret;
+
+ doe_mb = pci_ep_doe_get_mailbox(epc, func_no, cap_offset);
+ if (!doe_mb) {
+ kfree(request);
+ return -ENODEV;
+ }
+
+ task = kzalloc_obj(*task, GFP_ATOMIC);
+ if (!task) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ task->feat.vid = vendor;
+ task->feat.type = type;
+ task->request_pl = request;
+ task->request_pl_sz = request_sz;
+ task->response_pl = NULL;
+ task->response_pl_sz = 0;
+ task->complete = complete;
+
+ ret = pci_ep_doe_submit_task(doe_mb, task);
+ if (ret)
+ goto err_task;
+
+ return 0;
+
+err_task:
+ kfree(task);
+err_free:
+ kfree(request);
+ pci_ep_doe_put_mailbox(doe_mb);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pci_ep_doe_process_request);
+
+/**
+ * pci_ep_doe_abort() - Abort DOE operations on a mailbox
+ * @epc: PCI endpoint controller
+ * @func_no: Physical function number
+ * @cap_offset: DOE capability offset
+ *
+ * Abort the queued or in-flight DOE operation for the specified mailbox.
+ * This function is called by the EP controller driver when the RC sets the
+ * DOE Abort bit in the DOE Control Register, and the DOE Busy bit is set in
+ * the DOE Status Register.
+ *
+ * Set the CANCEL flag on the mailbox to prevent queued requests
+ * from starting, and return immediately. The CANCEL flag gets cleared in
+ * signal_task_complete(), allowing the mailbox to accept new requests.
+ *
+ * Return: 0 on success, -errno on failure
+ */
+int pci_ep_doe_abort(struct pci_epc *epc, u8 func_no, u16 cap_offset)
+{
+ struct pci_ep_doe_mb *doe_mb;
+
+ doe_mb = pci_ep_doe_get_mailbox(epc, func_no, cap_offset);
+ if (!doe_mb)
+ return -ENODEV;
+
+ /* Set CANCEL flag - worker will abort queued requests */
+ set_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
+
+ dev_dbg_ratelimited(epc->dev.parent,
+ "DOE mailbox abort initialized: PF%d offset 0x%x\n",
+ func_no, cap_offset);
+
+ pci_ep_doe_put_mailbox(doe_mb);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_ep_doe_abort);
+
+/**
+ * pci_ep_doe_destroy_mb() - Destroy a single DOE mailbox
+ * @doe_mb: DOE mailbox to destroy
+ *
+ * Internal function to destroy a mailbox and free its resources.
+ */
+static void pci_ep_doe_destroy_mb(struct pci_ep_doe_mb *doe_mb)
+{
+ struct workqueue_struct *wq;
+ unsigned long flags;
+
+ pci_ep_doe_cancel_tasks(doe_mb);
+
+ spin_lock_irqsave(&doe_mb->lock, flags);
+ wq = doe_mb->work_queue;
+ doe_mb->work_queue = NULL;
+ spin_unlock_irqrestore(&doe_mb->lock, flags);
+
+ if (wq)
+ destroy_workqueue(wq);
+
+ pci_ep_doe_put_mailbox(doe_mb);
+}
+
+/**
+ * pci_ep_doe_destroy() - Destroy all DOE mailboxes
+ * @epc: PCI endpoint controller
+ *
+ * Destroy all DOE mailboxes and free associated resources.
+ *
+ * The EPC core driver calls this to free all DOE resources,
+ * if DOE support is available for the EPC.
+ */
+void pci_ep_doe_destroy(struct pci_epc *epc)
+{
+ struct pci_ep_doe_mb *doe_mb;
+ unsigned long index;
+
+ xa_for_each(&epc->doe_mbs, index, doe_mb) {
+ xa_erase(&epc->doe_mbs, index);
+ pci_ep_doe_destroy_mb(doe_mb);
+ }
+
+ xa_destroy(&epc->doe_mbs);
+}
+EXPORT_SYMBOL_GPL(pci_ep_doe_destroy);
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 5844deee2b5f..6d3b4b779d15 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -692,6 +692,13 @@ struct pci_doe_feature {
u8 type;
};
+struct pci_epc;
+
+typedef void (*pci_ep_doe_complete_t)(struct pci_epc *epc, u8 func_no,
+ u16 cap_offset, int status,
+ u16 vendor, u8 type,
+ void *response_pl, size_t response_pl_sz);
+
#ifdef CONFIG_PCI_DOE
void pci_doe_init(struct pci_dev *pdev);
void pci_doe_destroy(struct pci_dev *pdev);
@@ -702,6 +709,41 @@ static inline void pci_doe_destroy(struct pci_dev *pdev) { }
static inline void pci_doe_disconnected(struct pci_dev *pdev) { }
#endif
+#ifdef CONFIG_PCI_ENDPOINT_DOE
+void pci_ep_doe_init(struct pci_epc *epc);
+int pci_ep_doe_add_mailbox(struct pci_epc *epc, u8 func_no, u16 cap_offset);
+int pci_ep_doe_process_request(struct pci_epc *epc, u8 func_no, u16 cap_offset,
+ u16 vendor, u8 type, void *request,
+ size_t request_sz,
+ pci_ep_doe_complete_t complete);
+int pci_ep_doe_abort(struct pci_epc *epc, u8 func_no, u16 cap_offset);
+void pci_ep_doe_destroy(struct pci_epc *epc);
+#else
+static inline void pci_ep_doe_init(struct pci_epc *epc) { }
+static inline int pci_ep_doe_add_mailbox(struct pci_epc *epc, u8 func_no,
+ u16 cap_offset)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int pci_ep_doe_process_request(struct pci_epc *epc,
+ u8 func_no, u16 cap_offset,
+ u16 vendor, u8 type,
+ void *request, size_t request_sz,
+ pci_ep_doe_complete_t complete)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int pci_ep_doe_abort(struct pci_epc *epc, u8 func_no,
+ u16 cap_offset)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void pci_ep_doe_destroy(struct pci_epc *epc) { }
+#endif
+
#ifdef CONFIG_PCI_NPEM
void pci_npem_create(struct pci_dev *dev);
void pci_npem_remove(struct pci_dev *dev);
diff --git a/include/linux/pci-doe.h b/include/linux/pci-doe.h
index abb9b7ae8029..c46e42f3ce78 100644
--- a/include/linux/pci-doe.h
+++ b/include/linux/pci-doe.h
@@ -22,6 +22,11 @@ struct pci_doe_mb;
/* Max data object length is 2^18 dwords */
#define PCI_DOE_MAX_LENGTH (1 << 18)
+typedef int (*pci_doe_protocol_handler_t)(const void *request,
+ size_t request_sz,
+ void **response,
+ size_t *response_sz);
+
struct pci_doe_mb *pci_find_doe_mailbox(struct pci_dev *pdev, u16 vendor,
u8 type);
diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
index 1eca1264815b..dd26294c8175 100644
--- a/include/linux/pci-epc.h
+++ b/include/linux/pci-epc.h
@@ -182,6 +182,9 @@ struct pci_epc {
unsigned long function_num_map;
int domain_nr;
bool init_complete;
+#ifdef CONFIG_PCI_ENDPOINT_DOE
+ struct xarray doe_mbs;
+#endif
};
/**
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v6 2/4] PCI: endpoint: Add DOE mailbox support for endpoint functions
2026-06-23 9:07 ` [PATCH v6 2/4] PCI: endpoint: Add DOE mailbox support for endpoint functions Aksh Garg
@ 2026-06-23 9:21 ` sashiko-bot
0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-06-23 9:21 UTC (permalink / raw)
To: Aksh Garg; +Cc: linux-pci
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The `pci_ep_doe_process_request()` fallback stub leaks the request buffer when `CONFIG_PCI_ENDPOINT_DOE=n`.
- [High] Flawed DOE Abort mechanism causes a state leak of the `CANCEL` flag, silently dropping valid requests.
--
commit b18fff73f747154acf71d1ff81a4731b89d384ad
Author: Aksh Garg <a-garg7@ti.com>
PCI: endpoint: Add DOE mailbox support for endpoint functions
This patch adds Data Object Exchange (DOE) support for PCIe endpoint
devices. It implements framework APIs for the EPC core driver and controller
drivers to register mailboxes and process DOE requests from the host.
> diff --git a/drivers/pci/endpoint/pci-ep-doe.c b/drivers/pci/endpoint/pci-ep-doe.c
> index 0000000000000..b2832253eaca6
> --- /dev/null
> +++ b/drivers/pci/endpoint/pci-ep-doe.c
[ ... ]
> +static void signal_task_complete(struct pci_ep_doe_task *task, int status)
> +{
> + struct pci_ep_doe_mb *doe_mb = task->doe_mb;
> +
> + task->complete(doe_mb->epc, doe_mb->func_no, doe_mb->cap_offset,
> + status, task->feat.vid, task->feat.type,
> + task->response_pl, task->response_pl_sz);
> +
> + /* Clear the CANCEL flag for next DOE request */
> + clear_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
[ ... ]
> +int pci_ep_doe_abort(struct pci_epc *epc, u8 func_no, u16 cap_offset)
> +{
> + struct pci_ep_doe_mb *doe_mb;
> +
> + doe_mb = pci_ep_doe_get_mailbox(epc, func_no, cap_offset);
> + if (!doe_mb)
> + return -ENODEV;
> +
> + /* Set CANCEL flag - worker will abort queued requests */
> + set_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
[Severity: High]
Can a race condition here cause the CANCEL flag to be permanently set,
silently dropping the next valid request?
If the host sends a DOE Abort just as a task completes, the controller
driver could evaluate the busy state and call pci_ep_doe_abort().
Concurrently, the worker thread might finish processing, invoke
signal_task_complete(), and clear the flag:
worker thread in signal_task_complete():
clear_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
If pci_ep_doe_abort() then executes on the controller thread:
controller thread in pci_ep_doe_abort():
set_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
The mailbox becomes idle but still has the CANCEL flag set. Would the
next valid request then be immediately cancelled when doe_ep_task_work()
checks the flag?
> +
> + dev_dbg_ratelimited(epc->dev.parent,
> + "DOE mailbox abort initialized: PF%d offset 0x%x\n",
> + func_no, cap_offset);
> +
> + pci_ep_doe_put_mailbox(doe_mb);
> + return 0;
> +}
[ ... ]
> diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
> index 5844deee2b5f1..6d3b4b779d157 100644
> --- a/drivers/pci/pci.h
> +++ b/drivers/pci/pci.h
[ ... ]
> +#else
> +static inline void pci_ep_doe_init(struct pci_epc *epc) { }
> +static inline int pci_ep_doe_add_mailbox(struct pci_epc *epc, u8 func_no,
> + u16 cap_offset)
> +{
> + return -EOPNOTSUPP;
> +}
> +
> +static inline int pci_ep_doe_process_request(struct pci_epc *epc,
> + u8 func_no, u16 cap_offset,
> + u16 vendor, u8 type,
> + void *request, size_t request_sz,
> + pci_ep_doe_complete_t complete)
> +{
> + return -EOPNOTSUPP;
[Severity: High]
Does this fallback stub leak the request buffer when endpoint DOE support
is disabled?
The API documentation in pci-ep-doe.c states that ownership of the request
buffer is transferred to the DOE EP core, which frees it. Since this stub
just returns -EOPNOTSUPP, it looks like the buffer is never freed when
CONFIG_PCI_ENDPOINT_DOE is not enabled.
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260623090737.711656-1-a-garg7@ti.com?part=2
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v6 3/4] PCI: endpoint: Add support for DOE initialization and setup in EPC core
2026-06-23 9:07 [PATCH v6 0/4] PCI: Add DOE support for endpoint Aksh Garg
2026-06-23 9:07 ` [PATCH v6 1/4] PCI/DOE: Move common definitions to the header file Aksh Garg
2026-06-23 9:07 ` [PATCH v6 2/4] PCI: endpoint: Add DOE mailbox support for endpoint functions Aksh Garg
@ 2026-06-23 9:07 ` Aksh Garg
2026-06-23 9:24 ` sashiko-bot
2026-06-23 9:07 ` [PATCH v6 4/4] Documentation: PCI: Add documentation for DOE endpoint support Aksh Garg
3 siblings, 1 reply; 9+ messages in thread
From: Aksh Garg @ 2026-06-23 9:07 UTC (permalink / raw)
To: linux-pci, linux-doc, mani, kwilczynski, bhelgaas, corbet, kishon,
skhan, lukas, cassel, alistair
Cc: linux-arm-kernel, linux-kernel, rdunlap, Frank.Li, s-vadapalli,
danishanwar, srk, a-garg7
Add pci_epc_init_capabilities() in EPC core driver to initialize and
setup the capabilities supported by the EPC driver. This calls
pci_epc_doe_setup() to setup the DOE framework for an endpoint controller,
which discovers the DOE capabilities (extended capability ID 0x2E), and
registers each discovered DOE mailbox for all the functions in the
endpoint controller.
Add pci_epc_deinit_capabilities() in EPC core driver for cleanup of the
resources used by the capabilities of the EPC driver. This calls
pci_ep_doe_destroy() to destroy all DOE mailboxes and free associated
resources.
Co-developed-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Aksh Garg <a-garg7@ti.com>
---
Changes from v5 to v6:
- Addressed the review comments provided by Bjorn Helgaas at v5
Changes from v4 to v5:
- Addressed the review comments by Sashiko
Changes from v3 to v4:
- Call DOE setup and destroy APIs directly within the EPC core, instead of
relying on the EPC drivers to call them individually. EPC drivers do not
need to explicitly handle DOE setup, rather the EPC core manages this
transparently. (Suggested by Manivannan Sadhasivam).
- Removed pci_epc_doe_destroy() API, which was just calling pci_ep_doe_destroy().
Instead, called pci_ep_doe_destroy() directly during cleanup.
- Called pci_ep_doe_init() before the "!epc->ops->find_ext_capability" check,
because if doe-capable=1 and find_ext_capability() op is undefined, this
would not initialize the epc->doe_mbs xarray. However during cleanup, the
check "!epc->ops->find_ext_capability" would be unnecessary, and it will
try to destroy the epc->doe_mbs xarray even when it was not initialized.
Changes from v2 to v3:
- Rebased on 7.1-rc1.
Changes since v1:
- New patch added to v2 (not present in v1)
v5: https://lore.kernel.org/all/20260610100256.1889111-4-a-garg7@ti.com/
v4: https://lore.kernel.org/all/20260522052434.802034-4-a-garg7@ti.com/
v3: https://lore.kernel.org/all/20260427051725.223704-4-a-garg7@ti.com/
v2: https://lore.kernel.org/all/20260401073022.215805-4-a-garg7@ti.com/
This patch is introduced based on the feedback provided by Manivannan
Sadhasivam at [1].
[1]: https://lore.kernel.org/all/p57x6jleaim5w7t2k3v7tioujnaxuovfpj5euop5ogefvw23se@y5fw3che5p5d/
drivers/pci/endpoint/pci-epc-core.c | 101 ++++++++++++++++++++++++++++
include/linux/pci-epc.h | 6 ++
2 files changed, 107 insertions(+)
diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
index 6c3c58185fc5..96bd624559f2 100644
--- a/drivers/pci/endpoint/pci-epc-core.c
+++ b/drivers/pci/endpoint/pci-epc-core.c
@@ -14,6 +14,8 @@
#include <linux/pci-epf.h>
#include <linux/pci-ep-cfs.h>
+#include "../pci.h"
+
static const struct class pci_epc_class = {
.name = "pci_epc",
};
@@ -842,6 +844,78 @@ void pci_epc_linkdown(struct pci_epc *epc)
}
EXPORT_SYMBOL_GPL(pci_epc_linkdown);
+/**
+ * pci_epc_doe_setup() - Discover and setup DOE mailboxes for all functions
+ * @epc: the EPC device on which DOE mailboxes has to be setup
+ *
+ * Discover DOE (Data Object Exchange) capabilities for all physical
+ * functions in the endpoint controller and register DOE mailboxes.
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int pci_epc_doe_setup(struct pci_epc *epc)
+{
+ u8 func_no, vfunc_no = 0;
+ u16 cap_offset;
+ int ret;
+
+ if (!epc->ops || !epc->ops->find_ext_capability)
+ return -EINVAL;
+
+ /* Discover DOE capabilities for all functions */
+ for (func_no = 0; func_no < epc->max_functions; func_no++) {
+ mutex_lock(&epc->lock);
+ cap_offset = epc->ops->find_ext_capability(epc, func_no,
+ vfunc_no, 0,
+ PCI_EXT_CAP_ID_DOE);
+ mutex_unlock(&epc->lock);
+
+ while (cap_offset) {
+ /* Register this DOE mailbox */
+ ret = pci_ep_doe_add_mailbox(epc, func_no, cap_offset);
+ if (ret) {
+ dev_warn(&epc->dev,
+ "[pf%d:offset %x] failed to add DOE mailbox\n",
+ func_no, cap_offset);
+ }
+
+ mutex_lock(&epc->lock);
+ cap_offset = epc->ops->find_ext_capability(epc, func_no,
+ vfunc_no,
+ cap_offset,
+ PCI_EXT_CAP_ID_DOE);
+ mutex_unlock(&epc->lock);
+ }
+ }
+
+ dev_dbg(&epc->dev, "DOE mailboxes setup complete\n");
+ return 0;
+}
+
+/**
+ * pci_epc_init_capabilities() - Initialize EPC capabilities
+ * @epc: the EPC device whose capabilities need to be initialized
+ *
+ * Initialize capabilities supported by the EPC device.
+ */
+static void pci_epc_init_capabilities(struct pci_epc *epc)
+{
+ const struct pci_epc_features *epc_features;
+ int ret;
+
+ epc_features = pci_epc_get_features(epc, 0, 0);
+ if (!epc_features)
+ return;
+
+ if (IS_ENABLED(CONFIG_PCI_ENDPOINT_DOE) && epc_features->doe_capable) {
+ pci_ep_doe_init(epc);
+
+ ret = pci_epc_doe_setup(epc);
+ if (ret)
+ dev_warn(&epc->dev, "DOE setup failed: %d\n", ret);
+ }
+}
+
/**
* pci_epc_init_notify() - Notify the EPF device that EPC device initialization
* is completed.
@@ -857,6 +931,9 @@ void pci_epc_init_notify(struct pci_epc *epc)
if (IS_ERR_OR_NULL(epc))
return;
+ if (!epc->init_complete)
+ pci_epc_init_capabilities(epc);
+
mutex_lock(&epc->list_lock);
list_for_each_entry(epf, &epc->pci_epf, list) {
mutex_lock(&epf->lock);
@@ -890,6 +967,27 @@ void pci_epc_notify_pending_init(struct pci_epc *epc, struct pci_epf *epf)
}
EXPORT_SYMBOL_GPL(pci_epc_notify_pending_init);
+/**
+ * pci_epc_deinit_capabilities() - Clean up EPC capabilities
+ * @epc: the EPC device whose capabilities need to be cleaned up
+ *
+ * Clean up capabilities supported by the EPC device,
+ * and free the associated resources.
+ */
+static void pci_epc_deinit_capabilities(struct pci_epc *epc)
+{
+ const struct pci_epc_features *epc_features;
+
+ epc_features = pci_epc_get_features(epc, 0, 0);
+ if (!epc_features)
+ return;
+
+ if (IS_ENABLED(CONFIG_PCI_ENDPOINT_DOE) && epc_features->doe_capable) {
+ pci_ep_doe_destroy(epc);
+ dev_dbg(&epc->dev, "DOE mailboxes destroyed\n");
+ }
+}
+
/**
* pci_epc_deinit_notify() - Notify the EPF device about EPC deinitialization
* @epc: the EPC device whose deinitialization is completed
@@ -903,6 +1001,9 @@ void pci_epc_deinit_notify(struct pci_epc *epc)
if (IS_ERR_OR_NULL(epc))
return;
+ if (epc->init_complete)
+ pci_epc_deinit_capabilities(epc);
+
mutex_lock(&epc->list_lock);
list_for_each_entry(epf, &epc->pci_epf, list) {
mutex_lock(&epf->lock);
diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
index dd26294c8175..11474e337db3 100644
--- a/include/linux/pci-epc.h
+++ b/include/linux/pci-epc.h
@@ -84,6 +84,8 @@ struct pci_epc_map {
* @start: ops to start the PCI link
* @stop: ops to stop the PCI link
* @get_features: ops to get the features supported by the EPC
+ * @find_ext_capability: ops to find extended capability offset for a function
+ * in endpoint controller
* @owner: the module owner containing the ops
*/
struct pci_epc_ops {
@@ -115,6 +117,8 @@ struct pci_epc_ops {
void (*stop)(struct pci_epc *epc);
const struct pci_epc_features* (*get_features)(struct pci_epc *epc,
u8 func_no, u8 vfunc_no);
+ u16 (*find_ext_capability)(struct pci_epc *epc, u8 func_no,
+ u8 vfunc_no, u16 start, u8 cap);
struct module *owner;
};
@@ -270,6 +274,7 @@ struct pci_epc_bar_desc {
* @msi_capable: indicate if the endpoint function has MSI capability
* @msix_capable: indicate if the endpoint function has MSI-X capability
* @intx_capable: indicate if the endpoint can raise INTx interrupts
+ * @doe_capable: indicate if the endpoint function has DOE capability
* @bar: array specifying the hardware description for each BAR
* @align: alignment size required for BAR buffer allocation
*/
@@ -280,6 +285,7 @@ struct pci_epc_features {
unsigned int msi_capable : 1;
unsigned int msix_capable : 1;
unsigned int intx_capable : 1;
+ unsigned int doe_capable : 1;
struct pci_epc_bar_desc bar[PCI_STD_NUM_BARS];
size_t align;
};
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v6 3/4] PCI: endpoint: Add support for DOE initialization and setup in EPC core
2026-06-23 9:07 ` [PATCH v6 3/4] PCI: endpoint: Add support for DOE initialization and setup in EPC core Aksh Garg
@ 2026-06-23 9:24 ` sashiko-bot
0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-06-23 9:24 UTC (permalink / raw)
To: Aksh Garg; +Cc: linux-pci
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [High] Unprotected check of epc->init_complete causes a TOCTOU race in pci_epc_init_notify() and pci_epc_deinit_notify(), leading to double initialization, memory leaks, and xarray corruption.
- [High] Logic Flaw: DOE mailboxes are never initialized because pci_epc_init_notify() is called before functions are bound.
- [High] Incorrect teardown ordering in pci_epc_deinit_notify() causes a Use-After-Free during shutdown.
Pre-existing issues:
- [High] TOCTOU data race in pci_epc_notify_pending_init() sends spurious epc_init events after deinitialization.
--
commit 1a369cd1d0a1ffe8ebc7c7242501b1fde9e422c9
Author: Aksh Garg <a-garg7@ti.com>
PCI: endpoint: Add support for DOE initialization and setup in EPC core
Add pci_epc_init_capabilities() in EPC core driver to initialize and setup
capabilities supported by the EPC driver. This calls pci_epc_doe_setup() to
setup the DOE framework for an endpoint controller, which discovers DOE
capabilities and registers mailboxes. It also adds cleanup.
> diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
> index 6c3c58185fc5d..96bd624559f2a 100644
> --- a/drivers/pci/endpoint/pci-epc-core.c
> +++ b/drivers/pci/endpoint/pci-epc-core.c
[ ... ]
> @@ -842,6 +844,78 @@ void pci_epc_linkdown(struct pci_epc *epc)
> }
> EXPORT_SYMBOL_GPL(pci_epc_linkdown);
>
> +/**
> + * pci_epc_doe_setup() - Discover and setup DOE mailboxes for all functions
> + * @epc: the EPC device on which DOE mailboxes has to be setup
> + *
> + * Discover DOE (Data Object Exchange) capabilities for all physical
> + * functions in the endpoint controller and register DOE mailboxes.
> + *
> + * Return: 0 on success, -errno on failure
> + */
> +static int pci_epc_doe_setup(struct pci_epc *epc)
> +{
> + u8 func_no, vfunc_no = 0;
> + u16 cap_offset;
> + int ret;
> +
> + if (!epc->ops || !epc->ops->find_ext_capability)
> + return -EINVAL;
> +
> + /* Discover DOE capabilities for all functions */
> + for (func_no = 0; func_no < epc->max_functions; func_no++) {
> + mutex_lock(&epc->lock);
> + cap_offset = epc->ops->find_ext_capability(epc, func_no,
> + vfunc_no, 0,
> + PCI_EXT_CAP_ID_DOE);
[Severity: High]
Does hardcoding vfunc_no to 0 completely omit DOE mailbox discovery and
initialization for SR-IOV virtual functions?
> + mutex_unlock(&epc->lock);
> +
> + while (cap_offset) {
[ ... ]
> + }
> + }
> +
> + dev_dbg(&epc->dev, "DOE mailboxes setup complete\n");
> + return 0;
> +}
> +
> +/**
> + * pci_epc_init_capabilities() - Initialize EPC capabilities
> + * @epc: the EPC device whose capabilities need to be initialized
> + *
> + * Initialize capabilities supported by the EPC device.
> + */
> +static void pci_epc_init_capabilities(struct pci_epc *epc)
> +{
> + const struct pci_epc_features *epc_features;
> + int ret;
> +
> + epc_features = pci_epc_get_features(epc, 0, 0);
> + if (!epc_features)
> + return;
[Severity: High]
Could pci_epc_get_features() return NULL here and silently bypass DOE
initialization?
If pci_epc_init_notify() is invoked during controller probe before any
Endpoint Function (EPF) drivers are bound, pci_epc_get_features() may fail.
Since the functions are not bound yet, the function_num_map could be 0,
causing the initialization to abort immediately without setting up the
mailboxes.
> +
> + if (IS_ENABLED(CONFIG_PCI_ENDPOINT_DOE) && epc_features->doe_capable) {
[ ... ]
> + }
> +}
> +
> /**
> * pci_epc_init_notify() - Notify the EPF device that EPC device initialization
> * is completed.
> @@ -857,6 +931,9 @@ void pci_epc_init_notify(struct pci_epc *epc)
> if (IS_ERR_OR_NULL(epc))
> return;
>
> + if (!epc->init_complete)
> + pci_epc_init_capabilities(epc);
> +
[Severity: High]
Can a concurrent caller hit this unprotected check, bypassing the intended
protection?
Because the epc->init_complete condition is evaluated locklessly outside
epc->list_lock, multiple threads could proceed to initialize the
capabilities.
This double initialization might lead to memory leaks, as xa_init() would
reset the xarray pointers without freeing the already allocated elements.
> mutex_lock(&epc->list_lock);
> list_for_each_entry(epf, &epc->pci_epf, list) {
[ ... ]
> @@ -890,6 +967,27 @@ void pci_epc_notify_pending_init(struct pci_epc *epc, struct pci_epf *epf)
> }
> EXPORT_SYMBOL_GPL(pci_epc_notify_pending_init);
[Severity: High]
This isn't a bug introduced by this patch, but does the lockless check of
init_complete in pci_epc_notify_pending_init() allow a race condition
with deinitialization?
If a hardware link-down event triggers pci_epc_deinit_notify()
concurrently, pci_epc_notify_pending_init() could read the flag before it
is cleared, and subsequently send spurious epc_init events after the
hardware has been stopped.
> +
> +/**
> + * pci_epc_deinit_capabilities() - Clean up EPC capabilities
[ ... ]
> @@ -903,6 +1001,9 @@ void pci_epc_deinit_notify(struct pci_epc *epc)
> if (IS_ERR_OR_NULL(epc))
> return;
>
> + if (epc->init_complete)
> + pci_epc_deinit_capabilities(epc);
> +
[Severity: High]
Does this unprotected check of init_complete allow multiple threads to
concurrently destroy the mailboxes, potentially leading to double-free
crashes?
> mutex_lock(&epc->list_lock);
> list_for_each_entry(epf, &epc->pci_epf, list) {
> mutex_lock(&epf->lock);
> if (epf->event_ops && epf->event_ops->epc_deinit)
> epf->event_ops->epc_deinit(epf);
> mutex_unlock(&epf->lock);
> }
[Severity: High]
Does freeing the capability before iterating over the EPF list to send
the epc_deinit notification cause a use-after-free?
If an EPF driver attempts to flush or interact with a DOE mailbox during
its shutdown callback, it would access already freed memory. Teardown
generally needs to be the exact reverse of initialization, notifying EPFs
to stop using the hardware before destroying the underlying capabilities.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260623090737.711656-1-a-garg7@ti.com?part=3
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v6 4/4] Documentation: PCI: Add documentation for DOE endpoint support
2026-06-23 9:07 [PATCH v6 0/4] PCI: Add DOE support for endpoint Aksh Garg
` (2 preceding siblings ...)
2026-06-23 9:07 ` [PATCH v6 3/4] PCI: endpoint: Add support for DOE initialization and setup in EPC core Aksh Garg
@ 2026-06-23 9:07 ` Aksh Garg
2026-06-23 9:11 ` sashiko-bot
3 siblings, 1 reply; 9+ messages in thread
From: Aksh Garg @ 2026-06-23 9:07 UTC (permalink / raw)
To: linux-pci, linux-doc, mani, kwilczynski, bhelgaas, corbet, kishon,
skhan, lukas, cassel, alistair
Cc: linux-arm-kernel, linux-kernel, rdunlap, Frank.Li, s-vadapalli,
danishanwar, srk, a-garg7
Document the architecture and implementation details for the Data Object
Exchange (DOE) framework for PCIe Endpoint devices.
Co-developed-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Tested-by: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Aksh Garg <a-garg7@ti.com>
---
Changes from v5 to v6:
- Addressed the review comments provided by Bjorn Helgaas at v5
Changes from v4 to v5:
- Updated the DOE Abort handling setion.
Changes from v3 to v4:
- Updated the maximum size of the DOE object from 256KB to 1MB,
as per PCIe spec.
- Updated the DOE setup and cleanup sections.
Changes from v2 to v3:
- Rebased on 7.1-rc1.
Changes since v1:
- Squashed the patches [1] and [2], and moved the documentation file
to Documentation/PCI/endpoint/pci-endpoint-doe.rst to match the existing
naming scheme, as suggested by Niklas Cassel
- Updated the documentation as per the design and implementaion changes
made to previous patches in this series:
* Updated for static protocol array instead of dynamic registration
* Documented asynchronous callback model
* Updated request/response flow with new callback signature
* Updated memory ownership: DOE core frees request, driver frees response
* Updated initialization and cleanup sections for new APIs
v5: https://lore.kernel.org/all/20260610100256.1889111-5-a-garg7@ti.com/
v4: https://lore.kernel.org/all/20260522052434.802034-5-a-garg7@ti.com/
v3: https://lore.kernel.org/all/20260427051725.223704-5-a-garg7@ti.com/
v2: https://lore.kernel.org/all/20260401073022.215805-5-a-garg7@ti.com/
v1: [1] https://lore.kernel.org/all/20260213123603.420941-2-a-garg7@ti.com/
[2] https://lore.kernel.org/all/20260213123603.420941-5-a-garg7@ti.com/
Documentation/PCI/endpoint/index.rst | 1 +
.../PCI/endpoint/pci-endpoint-doe.rst | 352 ++++++++++++++++++
2 files changed, 353 insertions(+)
create mode 100644 Documentation/PCI/endpoint/pci-endpoint-doe.rst
diff --git a/Documentation/PCI/endpoint/index.rst b/Documentation/PCI/endpoint/index.rst
index dd1f62e731c9..7c03d5abd2ef 100644
--- a/Documentation/PCI/endpoint/index.rst
+++ b/Documentation/PCI/endpoint/index.rst
@@ -9,6 +9,7 @@ PCI Endpoint Framework
pci-endpoint
pci-endpoint-cfs
+ pci-endpoint-doe
pci-test-function
pci-test-howto
pci-ntb-function
diff --git a/Documentation/PCI/endpoint/pci-endpoint-doe.rst b/Documentation/PCI/endpoint/pci-endpoint-doe.rst
new file mode 100644
index 000000000000..49bb1d8236f0
--- /dev/null
+++ b/Documentation/PCI/endpoint/pci-endpoint-doe.rst
@@ -0,0 +1,352 @@
+.. SPDX-License-Identifier: GPL-2.0-only OR MIT
+
+.. include:: <isonum.txt>
+
+=============================================
+Data Object Exchange (DOE) for PCIe Endpoint
+=============================================
+
+:Copyright: |copy| 2026 Texas Instruments Incorporated
+:Author: Aksh Garg <a-garg7@ti.com>
+:Co-Author: Siddharth Vadapalli <s-vadapalli@ti.com>
+
+Overview
+========
+
+DOE (Data Object Exchange) is a standard PCIe extended capability feature
+defined in PCI Express Base Specification Revision 7.0, Section 6.30.
+It is an optional mechanism for system firmware/software running on root
+complex (host) to perform :ref:`data object <data-object-term>` exchanges
+with an endpoint function. Each data object is uniquely identified by the
+Vendor ID of the vendor publishing the data object definition and a Data
+Object Type value assigned by that vendor.
+
+Think of DOE as a sophisticated mailbox system built into PCIe. The root
+complex can send structured requests to the endpoint device through DOE
+mailboxes, and the endpoint device responds with appropriate data. These
+mailboxes are implemented as PCIe Extended Capabilities in endpoint devices,
+allowing multiple mailboxes per function, each potentially supporting
+different data object protocols.
+
+The DOE support for root complex devices has already been implemented in
+``drivers/pci/doe.c``.
+
+How DOE Works
+=============
+
+The DOE mailbox operates through a simple request-response model:
+
+1. **Host sends request**: The root complex writes a data object (Vendor ID,
+ Type, and Payload) to the DOE Write Data Mailbox Register (one DWORD at
+ a time) of the endpoint function's DOE Capability and sets the DOE Go bit
+ in the DOE Control Register to indicate that a request is ready for
+ processing.
+2. **Endpoint processes**: The endpoint function reads the request from DOE
+ Write Data Mailbox Register, sets the DOE Busy bit in the DOE Status
+ Register, identifies the protocol of the data object, and executes the
+ appropriate handler.
+3. **Endpoint responds**: The endpoint function writes the response data
+ object to the DOE Read Data Mailbox Register (one DWORD at a time), and
+ sets the Data Object Ready bit in the DOE Status Register to indicate that
+ the response is ready. If an error occurs during request processing (such
+ as unsupported protocol or handler failure), the endpoint sets the DOE
+ Error bit in the DOE Status Register instead of the Data Object Ready bit.
+4. **Host reads response**: The root complex retrieves the response data from
+ the DOE Read Data Mailbox Register once the Data Object Ready bit is set
+ in the DOE Status Register, and then writes any value to this register to
+ indicate a successful read. If the DOE Error bit was set, the root complex
+ discards the response and performs error handling as needed.
+
+Each mailbox operates independently and can handle one transaction at a
+time. The DOE specification supports data objects of size up to 1MB
+(2\ :sup:`18` dwords).
+
+For complete DOE Capability details, refer to `PCI Express Base Specification
+Revision 7.0, Section 6.30 - Data Object Exchange (DOE)`.
+
+Key Terminologies
+=================
+
+.. _data-object-term:
+
+**Data Object**
+ A structured, vendor-defined, or standard-defined message exchanged
+ between root complex and endpoint function via DOE Capability registers
+ in configuration space of the function.
+
+**Mailbox**
+ A DOE Capability on the endpoint device, where each physical function
+ can have multiple mailboxes.
+
+**Protocol**
+ A specific type of DOE communication data object identified by a Vendor ID
+ and Type.
+
+**Handler**
+ A function that processes DOE requests of a specific protocol and generates
+ responses.
+
+Architecture of DOE Implementation for Endpoint
+===============================================
+
+.. code-block:: text
+
+ +------------------+
+ | |
+ | Root Complex |
+ | |
+ +--------^---------+
+ |
+ | Config space access
+ | over PCIe link
+ |
+ +----------v-----------+
+ | |
+ | PCIe Controller |
+ | as Endpoint |
+ | |
+ | +-----------------+ |
+ | | DOE Mailbox | |
+ | +------^----------+ |
+ +---------|------------+
+ +----------|-------------------------------------------------------------+
+ | | +------------------+ |
+ | +--------v---------+ Allocate | +--------------+ | |
+ | | |------------------------------->| Request | | |
+ | | EP Controller | +-->| Buffer | | |
+ | | Driver | Free | | +--------------+ | |
+ | | |--------------------------+ | | | |
+ | +--------^---------+ | | | | |
+ | | | | | | |
+ | | | | | | |
+ | | pci_ep_doe_process_request() | | | | |
+ | | | | | | |
+ | +--------v---------+ Free | | | | |
+ | | |----------------------------+ | DDR | |
+ | | DOE EP Core |<----+ | | | |
+ | | (pci-ep-doe.c) | | Discovery | | | |
+ | | |-----+ Protocol Handler | | | |
+ | +--------^---------+ | | | |
+ | | | | | |
+ | | protocol_handler() | | | |
+ | | | | | |
+ | +--------v---------+ | | | |
+ | | | | | +--------------+ | |
+ | | Protocol Handler | +---->| Response | | |
+ | | Module |------------------------------->| Buffer | | |
+ | | (CMA/SPDM/Other) | Allocate | +--------------+ | |
+ | | | | | |
+ | +------------------+ | | |
+ | +------------------+ |
+ +------------------------------------------------------------------------+
+
+Initialization and Cleanup
+--------------------------
+
+**Framework Initialization and DOE Setup**
+
+The EPC core automatically initializes and sets up DOE mailboxes through the
+``pci_epc_init_capabilities()`` internal function, which is invoked during
+``pci_epc_init_notify()`` when the controller driver calls this API.
+Controller drivers do not need to explicitly handle DOE initialization,
+rather the EPC core manages this transparently.
+
+DOE initialization only occurs when the EPC driver reports DOE Capability
+through the ``doe_capable`` flag in its ``pci_epc_features``.
+
+This internal function performs the following steps:
+
+1. Calls ``pci_ep_doe_init(epc)`` to initialize the xarray data structure
+ (a resizable array data structure defined in linux) named ``doe_mbs`` that
+ stores metadata of DOE mailboxes for the controller in ``struct pci_epc``.
+2. Calls ``pci_epc_doe_setup(epc)`` to discover all DOE capabilities in the
+ endpoint function's configuration space for each function. For each
+ discovered DOE Capability, calls ``pci_ep_doe_add_mailbox(epc, func_no,
+ cap_offset)`` to register the mailbox.
+
+Each DOE mailbox structure created by ``pci_ep_doe_add_mailbox()`` gets an
+ordered workqueue allocated for processing DOE requests sequentially for that
+mailbox, enabling concurrent request handling across different mailboxes.
+Each mailbox is uniquely identified by the combination of physical function
+number and capability offset for that controller.
+
+**Cleanup**
+
+The EPC core automatically cleans up DOE mailboxes through the
+``pci_epc_deinit_capabilities()`` internal function, which is invoked during
+``pci_epc_deinit_notify()`` when the controller driver calls this API.
+Controller drivers do not need to explicitly handle DOE cleanup, rather
+the EPC core manages this transparently.
+
+DOE cleanup only occurs when the EPC device reported DOE Capability
+through the ``doe_capable`` flag in its ``pci_epc_features``.
+
+This internal function calls ``pci_ep_doe_destroy(epc)``, which destroys all
+registered mailboxes, cancels any pending tasks, flushes and destroys the
+workqueues, and frees all memory allocated to the mailboxes.
+
+Protocol Handler Support
+------------------------
+
+Protocol implementations (such as CMA, SPDM, or vendor-specific protocols)
+are supported through a static array of protocol handlers.
+
+When a new DOE protocol library is introduced, its handler function
+is added to the static ``pci_doe_protocols`` array in
+``drivers/pci/endpoint/pci-ep-doe.c``. The discovery protocol
+(VID = 0x0001 (PCI-SIG Vendor ID), Type = 0x00 (discovery protocol)) is
+included in this static array and handled internally by the DOE EP core.
+
+Request Handling
+----------------
+
+The complete flow of a DOE request from the root complex to the response:
+
+**Step 1: Root Complex → EP Controller Driver**
+
+The root complex writes a DOE request (Vendor ID, Type, and Payload) to the
+DOE Write Data Mailbox Register in the endpoint function's DOE Capability
+and sets the DOE Go bit in the DOE Control Register, indicating that the
+request is ready for processing.
+
+**Step 2: EP Controller Driver → DOE EP Core**
+
+The controller driver reads the request header to determine the data object
+length. Based on this length field, it allocates a request buffer in memory
+(DDR) of the appropriate size. The driver then reads the complete request
+payload from the DOE Write Data Mailbox Register and converts the data from
+little-endian format (the format followed in the PCIe transactions over the
+link) to CPU-native format using ``le32_to_cpu()``. The driver defines a
+completion callback function with signature ``void (*complete)(struct pci_epc
+*epc, u8 func_no, u16 cap_offset, int status, u16 vendor, u8 type, void
+*response_pl, size_t response_pl_sz)`` to be invoked when the request
+processing completes. The driver then calls ``pci_ep_doe_process_request(epc,
+func_no, cap_offset, vendor, type, request, request_sz, complete)`` to
+hand off the request to the DOE EP core. This function returns immediately
+after queuing the work (without blocking), and the driver sets the DOE Busy
+bit in the DOE Status Register.
+
+**Step 3: DOE EP Core Processing**
+
+The DOE EP core creates a task structure and submits it to the mailbox's
+ordered workqueue. This ensures that requests for each mailbox are processed
+sequentially, one at a time, as required by the DOE specification. It looks
+for the protocol handler based on the Vendor ID and Type from the request
+header, and executes the handler function.
+
+**Step 4: Protocol Handler Execution**
+
+The workqueue executes the task by calling the registered protocol handler:
+``handler(request, request_sz, &response, &response_sz)``. The handler
+processes the request, allocates a response buffer in memory (DDR), builds
+the response data, and returns the response pointer and size. For the
+discovery protocol, the DOE EP core handles this directly without invoking
+an external handler.
+
+**Step 5: DOE EP Core → EP Controller Driver**
+
+After the protocol handler completes, the DOE EP core frees the request
+buffer, and invokes the completion callback provided by the controller
+driver asynchronously. The callback receives the struct pci_epc, function
+number, capability offset (to identify the mailbox), status code indicating
+the result of request processing, Vendor ID and Type of the data object,
+the response buffer, and its size.
+
+**Step 6: EP Controller Driver → Root Complex**
+
+The controller driver converts the response from CPU-native format to
+little-endian format using ``cpu_to_le32()``, writes the response to DOE
+Read Data Mailbox Register, and sets the Data Object Ready bit in the DOE
+Status Register. The root complex then reads the response from the DOE Read
+Data Mailbox Register. Finally, the controller driver frees the response
+buffer (which the handler allocated).
+
+Asynchronous Request Processing
+-------------------------------
+
+The DOE-EP framework implements asynchronous request processing because an
+endpoint function can have multiple instances of DOE mailboxes, and requests
+may be interleaved across these mailboxes. Request processing of one mailbox
+should not result in blocking request processing of other mailboxes. Hence,
+requests on each mailbox need to be handled in parallel for optimization.
+
+For the EP controller driver to handle requests on multiple mailboxes in
+parallel, ``pci_ep_doe_process_request()`` must be asynchronous. The function
+returns immediately after submitting the request to the mailbox's workqueue,
+without waiting for the request to complete. A completion callback provided
+by the controller driver is invoked asynchronously when request processing
+finishes. This asynchronous design enables concurrent processing of requests
+across different mailboxes.
+
+Abort Handling
+--------------
+
+The DOE specification allows the root complex to abort ongoing DOE operations
+by setting the DOE Abort bit in the DOE Control Register.
+
+**Trigger**
+
+When the root complex sets the DOE Abort bit, the EP controller driver
+detects this condition (typically in an interrupt handler or register
+polling routine). The action taken depends on the timing of the abort:
+
+- **ABORT before request transfer**: If the DOE Abort bit is set before the
+ root complex transfers the request to the mailbox registers, the controller
+ driver should not call ``pci_ep_doe_abort()`` API.
+
+- **ABORT during request transfer**: If the DOE Abort bit is set while the
+ root complex is still transferring the request to the mailbox registers,
+ the controller driver should discard the request, and should not call
+ ``pci_ep_doe_abort()`` and ``pci_ep_doe_process_request()`` APIs in the
+ respective IRQ handlers.
+
+- **ABORT after request submission**: If the DOE Abort bit is set after
+ the request has been fully received and submitted to the DOE EP core via
+ ``pci_ep_doe_process_request()``, the controller driver must call
+ ``pci_ep_doe_abort(epc, func_no, cap_offset)`` for the affected mailbox
+ to perform abort sequence in the DOE EP core.
+
+**Abort Sequence**
+
+The abort function sets the CANCEL flag on the mailbox to prevent queued
+requests from starting. Instead of waiting for the workqueue to flush,
+it returns immediately.
+
+The CANCEL flag gets cleared after invoking the completion callback,
+allowing the mailbox to accept new requests.
+
+Queued requests that have not started execution will be aborted with an
+error status. The currently executing request will complete normally,
+and the controller will reject the response if it arrives after the abort
+sequence has been triggered.
+
+.. note::
+ Independent of when the DOE Abort bit is triggered, the controller
+ driver must clear the DOE Error, Busy, and Ready bits in the DOE Status
+ Register after completing the abort operation to reset the mailbox to
+ an idle state.
+
+Error Handling
+--------------
+
+Errors can occur during DOE request processing for various reasons, such as
+unsupported protocols, handler failures, or memory allocation failures.
+
+**Error Detection**
+
+When an error occurs during DOE request processing, the DOE EP core
+propagates this error back to the controller driver either through the
+``pci_ep_doe_process_request()`` return value, or the status code passed
+to the completion callback.
+
+**Error Response**
+
+When the controller driver receives an error code, it sets the DOE Error bit
+in the DOE Status Register instead of writing a response to the DOE Read Data
+Mailbox Register, and frees the buffers.
+
+API Reference
+=============
+
+.. kernel-doc:: drivers/pci/endpoint/pci-ep-doe.c
+ :export:
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread