From: sashiko-bot@kernel.org
To: "Xin Tian" <tianx@yunsilicon.com>
Cc: linux-rdma@vger.kernel.org
Subject: Re: [PATCH v1 06/15] net/xsc: add the CQ and QP resource pools
Date: Thu, 05 Nov 2026 16:09:33 +0000 [thread overview]
Message-ID: <sashiko-outbox-199428@kernel.org> (raw)
In-Reply-To: <20261104155703.37053-7-tianx@yunsilicon.com>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] net/xsc: premature DESTROY_CQ and skipped refcount wait in CQ teardown
- [Medium] net/xsc: missing abort on invalid radix tree delete in xsc_core_destroy_cq()
- [Medium] net/xsc: inverted CQ/QP destroy ordering and inaccurate commit description
- [Low] net/xsc: duplicated CQ teardown in xsc_core_destroy_cq() and changelog mismatch
--
commit bc5206144b3c6edd9114d2a7f0311c3a018b5c6a
Author: Xin Tian <tianx@yunsilicon.com>
net/xsc: add the CQ and QP resource pools
This commit adds the device-wide radix-tree registries and lifecycle helpers
for completion queues and queue pairs, and wires completion and asynchronous
events from EQs into their respective callbacks.
> net/xsc: add the CQ and QP resource pools
>
> Add the device-wide registries for completion queues and queue pairs,
> and wire completion events from the EQs into them.
>
> CQs and QPs live in radix trees keyed by CQN and QPN and are looked
> up from the EQ dispatch under RCU with a reference count taken on
> hit; destroy removes the entry, synchronizes RCU and waits for
> in-flight users before issuing the firmware destroy command, so a
> completion arriving concurrently with teardown cannot touch freed
> memory. QP destroy issues two RST transitions first to drain
> pending work.
>
> Provide two CQ creation paths: a legacy one used by the ethernet
> function, and an extended one used by RDMA that falls back to the
> legacy command when firmware rejects it and splits large contexts
> into a context command followed by chunked buffer-address commands
> when the context exceeds the maximum command payload. New CQs are
> armed after creation.
>
> Add the QP creation, destruction and state transition helpers,
> including raw-TSO queues and multi-QP RSS receive queues needed by
> the ethernet function, and route asynchronous QP events to the
> event callback stored in the core QP.
>
> Signed-off-by: Xin Tian <tianx@yunsilicon.com>
[Severity: Medium]
Does the commit message match what the patch actually implements in a few
places?
First, the changelog states that both CQs and QPs are looked up under RCU
and synchronized with RCU on destroy, whereas xsc_qp_event() and
xsc_eq_cq_event() perform radix_tree_lookup() under spin_lock(&table->lock),
and xsc_core_destroy_resource_common() does not call synchronize_rcu().
Second, the changelog states that destroy waits for in-flight users before
issuing the firmware destroy command, but both xsc_core_eth_destroy_cq() and
xsc_core_destroy_cq() issue XSC_CMD_OP_DESTROY_CQ before decrementing
refcount and calling wait_for_completion().
Third, the changelog states that QP destroy issues two RST transitions
first, which appears to misread the opcode name XSC_CMD_OP_2RST_QP
(transition to RST): xsc_core_eth_destroy_qp() issues a single
XSC_CMD_OP_2RST_QP command, and xsc_core_destroy_qp() does not issue a RST
transition at all.
[ ... ]
> diff --git a/drivers/net/ethernet/yunsilicon/xsc/pci/cq.c b/drivers/net/ethernet/yunsilicon/xsc/pci/cq.c
> new file mode 100644
> index 0000000000000..e3e13ef531988
> --- /dev/null
> +++ b/drivers/net/ethernet/yunsilicon/xsc/pci/cq.c
> @@ -0,0 +1,319 @@
[ ... ]
> +static int xsc_destroy_cq(struct xsc_core_device *xdev, u32 cqn)
> +{
> + struct xsc_destroy_cq_mbox_out out;
> + struct xsc_destroy_cq_mbox_in in;
> +
> + memset(&in, 0, sizeof(in));
> + memset(&out, 0, sizeof(out));
> + in.hdr.opcode = cpu_to_be16(XSC_CMD_OP_DESTROY_CQ);
> + in.cqn = cpu_to_be32(cqn);
> +
> + return xsc_cmd_exec(xdev, &in, sizeof(in), &out, sizeof(out));
> +}
[ ... ]
> +int xsc_core_eth_destroy_cq(struct xsc_core_device *xdev,
> + struct xsc_core_cq *xcq)
> +{
> + struct xsc_cq_table *table = &xdev->dev_res->cq_table;
> + struct xsc_core_cq *tmp;
> + int err;
> +
> + spin_lock_irq(&table->lock);
> + tmp = radix_tree_delete(&table->tree, xcq->cqn);
> + spin_unlock_irq(&table->lock);
> + if (!tmp) {
> + err = -ENOENT;
> + goto err_delete_cq;
> + }
> +
> + if (tmp != xcq) {
> + err = -EINVAL;
> + goto err_delete_cq;
> + }
> +
> + /* the completion reader looks the cq up under rcu_read_lock and
> + * takes its reference afterwards; wait for it before dropping
> + * the tree reference so it never touches a freed cq
> + */
> + synchronize_rcu();
> +
> + err = xsc_destroy_cq(xdev, xcq->cqn);
> + if (err)
> + goto err_destroy_cq;
> +
> + if (atomic_dec_and_test(&xcq->refcount))
> + complete(&xcq->free);
> + wait_for_completion(&xcq->free);
> + return 0;
[Severity: High]
Should atomic_dec_and_test(&xcq->refcount) and
wait_for_completion(&xcq->free) happen before xsc_destroy_cq() here (and
likewise before xsc_cmd_exec() in xsc_core_destroy_cq())?
In xsc_cq_completion(), rcu_read_lock() only covers radix_tree_lookup() and
atomic_inc(&cq->refcount):
drivers/net/ethernet/yunsilicon/xsc/pci/eq.c:xsc_cq_completion() {
rcu_read_lock();
cq = radix_tree_lookup(&table->tree, cqn);
if (likely(cq))
atomic_inc(&cq->refcount);
rcu_read_unlock();
...
if (!cq->comp)
pci_err(xdev->pdev, "cq->comp is NULL\n");
else
cq->comp(cq);
if (atomic_dec_and_test(&cq->refcount))
complete(&cq->free);
}
Similarly, xsc_eq_cq_event() drops table->lock before calling cq->event().
Because cq->comp() and cq->event() run outside rcu_read_lock() and
table->lock, synchronize_rcu() only guarantees that no new reader can take a
reference; it does not wait for an in-flight callback that already took a
reference to finish. Issuing XSC_CMD_OP_DESTROY_CQ before waiting on
xcq->free destroys the hardware CQ (allowing the firmware to reassign the
CQN) while cq->comp() or cq->event() is still executing on another CPU.
Furthermore, if xsc_destroy_cq() fails and jumps to err_destroy_cq, xcq has
already been deleted from table->tree, but the function returns without
waiting for in-flight callbacks on xcq->free. If the caller frees xcq after
destroy returns, an in-flight xsc_cq_completion() or xsc_eq_cq_event() will
access freed memory; if the caller retries xsc_core_eth_destroy_cq(),
radix_tree_delete() will return NULL and fail with -ENOENT without
destroying the firmware CQ.
[ ... ]
> +int xsc_core_destroy_cq(struct xsc_core_device *xdev, struct xsc_core_cq *cq)
> +{
> + struct xsc_cq_table *table = &xdev->dev_res->cq_table;
> + struct xsc_destroy_cq_mbox_in in;
> + struct xsc_destroy_cq_mbox_out out;
> + struct xsc_core_cq *tmp;
> + int err;
> +
> + spin_lock_irq(&table->lock);
> + tmp = radix_tree_delete(&table->tree, cq->cqn);
> + spin_unlock_irq(&table->lock);
> + if (!tmp || tmp != cq)
> + pci_warn(xdev->pdev, "cq 0x%x not found in tree\n", cq->cqn);
[Severity: Medium]
If !tmp or tmp != cq here, shouldn't xsc_core_destroy_cq() abort and return
an error instead of continuing after pci_warn()?
If the entry was not found (for instance, if a prior destroy call deleted it
from table->tree before failing in xsc_cmd_exec(), or on a duplicate
destroy), continuing will execute XSC_CMD_OP_DESTROY_CQ and then call
atomic_dec_and_test(&cq->refcount) followed by
wait_for_completion(&cq->free). If cq->refcount was already decremented, it
will underflow to -1 and hang indefinitely in wait_for_completion().
Also, in both xsc_core_eth_destroy_cq() and xsc_core_destroy_cq(), calling
radix_tree_delete(&table->tree, cq->cqn) before verifying that the stored
entry matches cq will remove an unrelated active CQ (tmp) from table->tree
when tmp != cq. Would it be safer to check radix_tree_lookup() under
table->lock before deleting?
> +
> + /* wait for lockless completion readers as in
> + * xsc_core_eth_destroy_cq()
> + */
> + synchronize_rcu();
> +
> + memset(&in, 0, sizeof(in));
> + memset(&out, 0, sizeof(out));
> + in.hdr.opcode = cpu_to_be16(XSC_CMD_OP_DESTROY_CQ);
> + in.cqn = cpu_to_be32(cq->cqn);
> +
> + err = xsc_cmd_exec(xdev, &in, sizeof(in), &out, sizeof(out));
> + if (err)
> + return err;
> +
> + if (atomic_dec_and_test(&cq->refcount))
> + complete(&cq->free);
> + wait_for_completion(&cq->free);
> +
> + return 0;
> +}
[Severity: Low]
This isn't a bug, but xsc_core_destroy_cq() open-codes xsc_destroy_cq() and
duplicates the radix-tree removal, synchronize_rcu(), and completion wait
sequence from xsc_core_eth_destroy_cq(). Could xsc_core_destroy_cq() and
xsc_core_eth_destroy_cq() share this teardown logic?
[ ... ]
> diff --git a/drivers/net/ethernet/yunsilicon/xsc/pci/qp.c b/drivers/net/ethernet/yunsilicon/xsc/pci/qp.c
> new file mode 100644
> index 0000000000000..bbcdb18e1b84f
> --- /dev/null
> +++ b/drivers/net/ethernet/yunsilicon/xsc/pci/qp.c
> @@ -0,0 +1,355 @@
[ ... ]
> +int xsc_core_destroy_qp(struct xsc_core_device *xdev,
> + struct xsc_core_qp *qp)
> +{
> + struct xsc_destroy_qp_mbox_in in;
> + struct xsc_destroy_qp_mbox_out out;
> + int err;
> +
> + xsc_core_destroy_resource_common(xdev, qp);
> +
> + memset(&in, 0, sizeof(in));
> + memset(&out, 0, sizeof(out));
> + in.hdr.opcode = cpu_to_be16(XSC_CMD_OP_DESTROY_QP);
> + in.qpn = cpu_to_be32(qp->qpn);
> +
> + err = xsc_cmd_exec(xdev, &in, sizeof(in), &out, sizeof(out));
> + if (err)
> + return err;
> +
> + atomic_dec(&xdev->num_qps);
> + return 0;
> +}
[Severity: Medium]
If xsc_cmd_exec() fails in xsc_core_destroy_qp(), xsc_core_destroy_resource_common()
has already removed qp from qp_table.tree and consumed its initial refcount,
while xdev->num_qps remains incremented. How should a caller recover if
XSC_CMD_OP_DESTROY_QP fails after the QP has been removed from the resource
table?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261104155650.37053-1-tianx@yunsilicon.com?part=6
next prev parent reply other threads:[~2026-11-05 16:09 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-04 15:57 [PATCH v1 00/15] Yunsilicon XSC ethernet and RDMA drivers Xin Tian
2026-11-04 15:56 ` [PATCH v1 01/15] MAINTAINERS: add the Yunsilicon XSC drivers Xin Tian
2026-11-04 15:56 ` [PATCH v1 02/15] net/xsc: add PCI core driver skeleton Xin Tian
2026-11-05 16:09 ` sashiko-bot
2026-11-04 15:56 ` [PATCH v1 03/15] net/xsc: add the register HAL Xin Tian
2026-11-05 16:09 ` sashiko-bot
2026-11-04 15:57 ` [PATCH v1 04/15] net/xsc: add the command queue Xin Tian
2026-11-05 16:09 ` sashiko-bot
2026-11-04 15:57 ` [PATCH v1 05/15] net/xsc: add event queues, interrupts and the Xin Tian
2026-11-05 16:09 ` sashiko-bot
2026-11-04 15:57 ` [PATCH v1 06/15] net/xsc: add the CQ and QP resource pools Xin Tian
2026-11-05 16:09 ` sashiko-bot [this message]
2026-11-04 15:57 ` [PATCH v1 07/15] net/xsc: add ethernet netdev registration Xin Tian
2026-11-05 16:09 ` sashiko-bot
2026-11-07 21:03 ` Andrew Lunn
2026-11-04 15:57 ` [PATCH v1 08/15] net/xsc: add ethernet TX path Xin Tian
2026-11-05 16:09 ` sashiko-bot
2026-11-04 15:57 ` [PATCH v1 09/15] net/xsc: add ethernet RX path and statistics Xin Tian
2026-11-05 16:09 ` sashiko-bot
2026-11-04 15:57 ` [PATCH v1 10/15] RDMA/xsc: add RDMA device support Xin Tian
2026-11-05 16:09 ` sashiko-bot
2026-11-04 15:57 ` [PATCH v1 11/15] RDMA/xsc: add RDMA PD/MR Xin Tian
2026-11-05 16:09 ` sashiko-bot
2026-11-04 15:57 ` [PATCH v1 12/15] RDMA/xsc: add RDMA CQ/SRQ/AH Xin Tian
2026-11-05 16:09 ` sashiko-bot
2026-11-04 15:57 ` [PATCH v1 13/15] RDMA/xsc: add QP verbs Xin Tian
2026-11-05 16:09 ` sashiko-bot
2026-11-04 15:57 ` [PATCH v1 14/15] RDMA/xsc: add the RDMA send path Xin Tian
2026-11-05 16:09 ` sashiko-bot
2026-11-04 15:57 ` [PATCH v1 15/15] RDMA/xsc: add RDMA counters and sysfs Xin Tian
2026-11-05 16:09 ` sashiko-bot
2026-11-10 18:48 ` [PATCH v1 00/15] Yunsilicon XSC ethernet and RDMA drivers Jakub Kicinski
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=sashiko-outbox-199428@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=linux-rdma@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=tianx@yunsilicon.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.