All of lore.kernel.org
 help / color / mirror / Atom feed
From: netdev-bot+sashiko@kernel.org
To: illusion.wang@nebula-matrix.com
Cc: dimon.zhao@nebula-matrix.com, alvin.wang@nebula-matrix.com,
	sam.chen@nebula-matrix.com, netdev@vger.kernel.org,
	andrew+netdev@lunn.ch, corbet@lwn.net, kuba@kernel.org,
	horms@kernel.org, linux-doc@vger.kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, lukas.bulwahn@redhat.com,
	edumazet@google.com, enelsonmoore@gmail.com,
	skhan@linuxfoundation.org, hkallweit1@gmail.com,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH v28 net-next 09/10] net/nebula-matrix: add common/ctrl dev init/remove operation
Date: Thu, 17 Sep 2026 22:35:11 +0000	[thread overview]
Message-ID: <178968451158.22033.8034737519557582768@kernel.org> (raw)
In-Reply-To: <20260914123429.56596-10-illusion.wang@nebula-matrix.com>

Thank you for your contribution! Sashiko AI review found 5 potential
issue(s) to consider:

Critical: 0 · High: 0 · Medium: 5 · Low: 0

- [Medium] Commit message contradicts the code: it states that common
  device setup does "On the control PF only: fetch VSI/ETH identity via…
- [Medium] nbl_dev_setup_common_dev() calls
  cancel_work_sync(&common_dev->clean_mbx_task) on a work_struct that…
- [Medium] The patch introduces an unguarded chip-global cross-PF
  dependency: nbl_dev_setup_ctrl_dev() -> nbl_dev_setup_chan_qinfo() ->…
- [Medium] nbl_dev_remove_common_dev() (nbl_dev.c:115-125) discards the
  int return value of nbl_dev_remove_chan_queue() ->…
- [Medium] Hardware sequencing bug: the mailbox TX/RX rings are armed in
  hardware before the chip-side mailbox routing entry that identifies…

--

net/nebula-matrix: add common/ctrl dev init/remove operation

This adds the nbl_dev layer with nbl_dev_setup_common_dev() and
nbl_dev_setup_ctrl_dev() plus their teardown counterparts, wired into
nbl_dev_init() / nbl_dev_remove() from nbl_core_init() / nbl_core_remove().
Common dev setup brings up the mailbox channel queue and registers the
mailbox cleanup work; ctrl dev setup calls init_module() and programs the
mailbox QINFO routing map.

A few questions below.

>     Common device setup (nbl_dev_setup_common_dev):
>     - Allocate per-device state (nbl_dev_common)
>     - Set up mailbox channel queue and register cleanup work
>     - Initialize MSI-X service counts (mailbox = 1 vector; actual
>       allocation deferred to nbl_dev_start() in the next patch; this
>       patch only populates service vector counts)
>     - On the control PF only: fetch VSI/ETH identity via local
>       dispatch ops. Non-control PFs defer this to nbl_dev_start(),
>       when the mailbox channel is fully operational and the control
>       PF's responder is running; this keeps every intermediate commit
>       bisect-bootable on multi-PF systems.

[Severity: Medium]
Does the code match this description?  nbl_dev_setup_common_dev() only
allocates nbl_dev_common, sets up the mailbox channel queue, does
INIT_WORK() plus nbl_dev_register_chan_task(), stores common_dev and calls
nbl_dev_init_msix_cnt().  There is no get_vsi_id()/get_eth_id() call in
nbl_dev.c at all, and the in-code comment says the opposite of the
changelog:

	/*
	 * VSI/ETH identity fetch moved to nbl_dev_start().
	 * This avoids cross-PF probe race when manager PF is not ready.
	 */

So common->vsi_id / common->eth_id / common->eth_num stay zero after
nbl_dev_init() on every PF, including the control PF.

Should the changelog say the identity fetch is deferred to nbl_dev_start()
for all PFs rather than "On the control PF only: fetch VSI/ETH identity via
local dispatch ops"?

> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.c
> new file mode 100644
> index 0000000000000..75c0d83663b9c
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.c
> @@ -0,0 +1,235 @@

[ ... ]

> +/* ----------  Channel config  ---------- */
> +static void nbl_dev_setup_chan_qinfo(struct nbl_dev_mgt *dev_mgt, u8 chan_type)
> +{
> +	struct nbl_channel_ops *chan_ops = dev_mgt->chan_ops_tbl->ops;
> +	struct nbl_channel_mgt *priv = dev_mgt->chan_ops_tbl->priv;
> +	struct nbl_common_info *common = dev_mgt->common;
> +
> +	if (!chan_ops->check_queue_exist(priv, chan_type))
> +		return;
> +
> +	chan_ops->cfg_chan_qinfo_map_table(priv, common->hw_bus, common->devid);
> +}

[ ... ]

> +/* ----------  Dev init process  ---------- */
> +static int nbl_dev_setup_common_dev(struct nbl_adapter *adapter)
> +{
> +	struct nbl_dev_mgt *dev_mgt = adapter->core.dev_mgt;
> +	struct nbl_dev_common *common_dev;
> +	int ret;
> +
> +	common_dev = devm_kzalloc(&adapter->pdev->dev, sizeof(*common_dev),
> +				  GFP_KERNEL);
> +	if (!common_dev)
> +		return -ENOMEM;
> +	common_dev->dev_mgt = dev_mgt;
> +
> +	ret = nbl_dev_setup_chan_queue(dev_mgt, NBL_CHAN_TYPE_MAILBOX);
> +	if (ret)
> +		goto err_cleanup;

[Severity: Medium]
Is the hardware ordering here safe with respect to the mailbox routing map?
This call arms the mailbox rings:

nbl_dev_setup_chan_queue() -> nbl_chan_setup_queue()
    nbl_hw_config_mailbox_txq() / nbl_hw_config_mailbox_rxq()  (QUEUE_EN=1)
    nbl_chan_update_tail_ptr()                                 (rx doorbell)

but the per-function BUS/DEVID/FUNCTION routing entry is only written later,
and only when common->has_ctrl:

nbl_dev_init()
    nbl_dev_setup_common_dev()          <- queues enabled here
    if (common->has_ctrl)
        nbl_dev_setup_ctrl_dev()
            nbl_dev_setup_chan_qinfo()  <- QINFO_MAP written here
                nbl_chan_cfg_qinfo_map_table() -> nbl_hw_cfg_mailbox_qinfo()

	data |= FIELD_PREP(NBL_MAILBOX_QINFO_MAP_FUNCTION_MASK, function) |
	       FIELD_PREP(NBL_MAILBOX_QINFO_MAP_DEVID_MASK, devid) |
	       FIELD_PREP(NBL_MAILBOX_QINFO_MAP_BUS_MASK, bus);

A non-control PF never programs its own entry, and this patch states firmware
clears those chip-global entries when the control PF sets driver_status=false.

Can a PF then run with mailbox queues enabled while its QINFO_MAP entry is
zero or stale, for example when a non-control PF is bound while function 0 is
not bound, or in the window on control PF re-probe between arming its rings
and writing QINFO_MAP for function 0?  Would mailbox DMA in that window be
tagged with 00:00.0 or a stale bus?

Would it be safer to program a function's QINFO routing entry before enabling
that function's mailbox queue?

> +
> +	INIT_WORK(&common_dev->clean_mbx_task, nbl_dev_clean_mailbox_task);
> +	nbl_dev_register_chan_task(dev_mgt, NBL_CHAN_TYPE_MAILBOX,
> +				   &common_dev->clean_mbx_task);
> +	/*
> +	 * VSI/ETH identity fetch moved to nbl_dev_start().
> +	 * This avoids cross-PF probe race when manager PF is not ready.
> +	 */
> +	dev_mgt->common_dev = common_dev;
> +	nbl_dev_init_msix_cnt(dev_mgt);
> +
> +	return 0;
> +err_cleanup:
> +	cancel_work_sync(&common_dev->clean_mbx_task);
> +	nbl_dev_remove_chan_queue(dev_mgt, NBL_CHAN_TYPE_MAILBOX);
> +	nbl_dev_register_chan_task(dev_mgt, NBL_CHAN_TYPE_MAILBOX, NULL);
> +	return ret;
> +}

[Severity: Medium]
Can this warn on an uninitialized work item?  The only jump to err_cleanup is
the nbl_dev_setup_chan_queue() failure, which is before the INIT_WORK(), so
clean_mbx_task is still all zeros from devm_kzalloc() (work->func == NULL, no
lockdep map) when cancel_work_sync() runs:

cancel_work_sync() -> __cancel_work_sync() -> __flush_work(work, true)

kernel/workqueue.c:__flush_work() {
	...
	if (WARN_ON(!work->func))
		return false;
	...
}

That gives a WARNING backtrace (panic with panic_on_warn) instead of a clean
probe failure.

The same label also tears down a queue that was never brought up
(nbl_chan_teardown_queue() then logs "channel not active, skip duplicate
teardown") and unregisters a task that was never registered.

Would moving INIT_WORK() above nbl_dev_setup_chan_queue(), or just returning
ret directly on that path, be preferable?

> +
> +static void nbl_dev_remove_common_dev(struct nbl_adapter *adapter)
> +{
> +	struct nbl_dev_mgt *dev_mgt = adapter->core.dev_mgt;
> +	struct nbl_dev_common *common_dev = dev_mgt->common_dev;
> +
> +	if (!common_dev)
> +		return;
> +	cancel_work_sync(&common_dev->clean_mbx_task);
> +	nbl_dev_remove_chan_queue(dev_mgt, NBL_CHAN_TYPE_MAILBOX);
> +	nbl_dev_register_chan_task(dev_mgt, NBL_CHAN_TYPE_MAILBOX, NULL);
> +}

[Severity: Medium]
Should the return value of nbl_dev_remove_chan_queue() be checked here?  It
propagates -ETIMEDOUT from the drain in nbl_chan_teardown_queue():

nbl_channel.c:nbl_chan_teardown_queue() {
	...
	ret = wait_event_timeout(chan_info->inflight_wait,
				 atomic_read(&chan_info->inflight_tx_cnt) == 0,
				 msecs_to_jiffies(5000));

	if (!ret) {
		dev_warn(chan_mgt->common->dev,
			 "teardown: inflight tx drain timeout\n");
		ret = -ETIMEDOUT;
	...
}

whose comment says callers must not access queue resources while an inflight
sender may still be active.  nbl_dev_remove() then proceeds to
nbl_dev_remove_ctrl_dev() -> deinit_module() -> nbl_hw_deinit_module(), which
documents a premise nobody verifies:

nbl_hw_leonis.c:nbl_hw_deinit_module() {
	 * Caller must ensure no new DMA is initiated after this point.
	 * The mailbox channel is stopped by nbl_chan_teardown_queue()
	 * before this function is called, so no in-flight mailbox DMA
	 * remains.
}

On the timeout path that premise does not hold: driver_status=false starts the
asynchronous firmware cleanup while a sender may still be writing descriptors
into the dmam_alloc_coherent() rings, which devres frees right after remove()
returns.

Can this at least log or bail out on the -ETIMEDOUT case rather than
discarding it?

> +
> +static int nbl_dev_setup_ctrl_dev(struct nbl_adapter *adapter)
> +{
> +	struct nbl_dev_mgt *dev_mgt = adapter->core.dev_mgt;
> +	struct nbl_dispatch_ops *disp_ops = dev_mgt->disp_ops_tbl->ops;
> +	int ret;
> +
> +	ret = disp_ops->init_module(dev_mgt->disp_ops_tbl->priv);
> +	if (ret)
> +		return ret;
> +
> +	nbl_dev_setup_chan_qinfo(dev_mgt, NBL_CHAN_TYPE_MAILBOX);
> +
> +	return 0;
> +}
> +
> +/*
> + * Tear down control device: deinit_module sets driver_status=false
> + * to notify firmware to clean all per-PF hardware state (including
> + * qinfo registers).  The qinfo map programmed in setup_ctrl_dev is
> + * not explicitly cleared; firmware handles it on driver_status change.
> + *
> + * NOTE: Firmware clears chip-global qinfo routing entries when control PF
> + * deinit runs.  MUST unbind all sibling non-control PFs on the same chip
> + * BEFORE unbinding the control PF. Unbinding control PF while siblings are
> + * still bound will leave those sibling PFs with broken mailbox RPC,
> + * causing RPC ACK timeouts. This operation sequence is NOT supported.
> + */
> +static void nbl_dev_remove_ctrl_dev(struct nbl_adapter *adapter)
> +{
> +	struct nbl_dev_mgt *dev_mgt = adapter->core.dev_mgt;
> +	struct nbl_dispatch_ops *disp_ops = dev_mgt->disp_ops_tbl->ops;
> +
> +	disp_ops->deinit_module(dev_mgt->disp_ops_tbl->priv);
> +}

[Severity: Medium]
Can anything stop the unbind order that this comment says is unsupported?

nbl_dev_setup_ctrl_dev() -> nbl_dev_setup_chan_qinfo() ->
nbl_chan_cfg_qinfo_map_table() programs NBL_MAILBOX_QINFO_MAP_REG_ARR() for
every hardware-active PF, and nbl_dev_remove_ctrl_dev() calls deinit_module()
-> nbl_hw_deinit_module() (driver_status=false) unconditionally.

nbl_remove() runs per PCI function with no cross-function coordination:

nbl_remove()
    pci_set_drvdata(pdev, NULL);
    nbl_core_remove(adapter);
        nbl_dev_remove()
            if (common->has_ctrl)
                nbl_dev_remove_ctrl_dev()   <- chip-global deinit

So a plain

  echo <pf0-bdf> > /sys/bus/pci/drivers/nbl/unbind

silently breaks every still-bound sibling PF: their mailbox RPCs are no longer
routed, so each dispatch RPC ends in an ACK timeout (3s ACK wait, 5s drain),
and their own later teardown RPCs fail too.

Would a refusal or a reference count on the control PF be better than
documenting the hazard, or can the chip-global dependency be avoided?

[ ... ]

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260914123429.56596-1-illusion.wang%40nebula-matrix.com

  reply	other threads:[~2026-09-17 22:35 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-14 12:34 [PATCH v28 net-next 00/10] nbl driver for Nebulamatrix NICs illusion.wang
2026-09-14 12:34 ` [PATCH v28 net-next 01/10] net/nebula-matrix: add minimum nbl build framework illusion.wang
2026-09-17 22:34   ` netdev-bot+sashiko
2026-09-14 12:34 ` [PATCH v28 net-next 02/10] net/nebula-matrix: add core driver architecture and HW layer initialization illusion.wang
2026-09-17 22:35   ` netdev-bot+sashiko
2026-09-14 12:34 ` [PATCH v28 net-next 03/10] net/nebula-matrix: add channel layer illusion.wang
2026-09-17 22:35   ` netdev-bot+sashiko
2026-09-19 23:30   ` Jakub Kicinski
2026-09-14 12:34 ` [PATCH v28 net-next 04/10] net/nebula-matrix: add common resource implementation illusion.wang
2026-09-17 22:35   ` netdev-bot+sashiko
2026-09-14 12:34 ` [PATCH v28 net-next 05/10] net/nebula-matrix: add intr " illusion.wang
2026-09-17 22:35   ` netdev-bot+sashiko
2026-09-14 12:34 ` [PATCH v28 net-next 06/10] net/nebula-matrix: add chip-wide hardware init/deinit implementation illusion.wang
2026-09-17 22:35   ` netdev-bot+sashiko
2026-09-14 12:34 ` [PATCH v28 net-next 07/10] net/nebula-matrix: dispatch: add control-level routing core infrastructure illusion.wang
2026-09-17 22:35   ` netdev-bot+sashiko
2026-09-14 12:34 ` [PATCH v28 net-next 08/10] net/nebula-matrix: dispatch: implement channel RPC framework and serialize hardware ops illusion.wang
2026-09-17 22:35   ` netdev-bot+sashiko
2026-09-14 12:34 ` [PATCH v28 net-next 09/10] net/nebula-matrix: add common/ctrl dev init/remove operation illusion.wang
2026-09-17 22:35   ` netdev-bot+sashiko [this message]
2026-09-14 12:34 ` [PATCH v28 net-next 10/10] net/nebula-matrix: add common dev start/stop operation illusion.wang
2026-09-17 22:35   ` netdev-bot+sashiko
2026-09-19 23:50 ` [PATCH v28 net-next 00/10] nbl driver for Nebulamatrix NICs patchwork-bot+netdevbpf

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=178968451158.22033.8034737519557582768@kernel.org \
    --to=netdev-bot+sashiko@kernel.org \
    --cc=alvin.wang@nebula-matrix.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=corbet@lwn.net \
    --cc=dimon.zhao@nebula-matrix.com \
    --cc=edumazet@google.com \
    --cc=enelsonmoore@gmail.com \
    --cc=hkallweit1@gmail.com \
    --cc=horms@kernel.org \
    --cc=illusion.wang@nebula-matrix.com \
    --cc=kuba@kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lukas.bulwahn@redhat.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=sam.chen@nebula-matrix.com \
    --cc=skhan@linuxfoundation.org \
    --cc=vadim.fedorenko@linux.dev \
    /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.