* Re: [PATCH ipsec] xfrm: policy: use hlist_del_init_rcu in xfrm_hash_rebuild to avoid bydst poison
From: Xiang Mei @ 2026-07-02 22:11 UTC (permalink / raw)
To: Florian Westphal
Cc: steffen.klassert, herbert, davem, netdev, horms, edumazet, kuba,
pabeni, AutonomousCodeSecurity, tgopinath, kys
In-Reply-To: <aka5pwAGGI56QrrS@strlen.de>
On Thu, Jul 2, 2026 at 12:19 PM Florian Westphal <fw@strlen.de> wrote:
>
> Xiang Mei (Microsoft) <xmei5@asu.edu> wrote:
> > xfrm_hash_rebuild() unlinks each policy from its bydst chain with
> > hlist_del_rcu() and re-inserts it. For an inexact policy the re-insert goes
> > through xfrm_policy_inexact_insert(), which can fail on a GFP_ATOMIC
> > allocation; on failure the error path only WARN_ONCE()s and continues, so the
> > policy is left with a poisoned bydst node (LIST_POISON2). The next rebuild
> > calls hlist_del_rcu() on that node again, dereferences the poison, and takes a
> > general protection fault.
> >
> > Use hlist_del_init_rcu() instead, so a failed-reinsert node is left unhashed
> > (pprev == NULL) rather than poisoned. The next rebuild's hlist_del_init_rcu()
> > is then a no-op for it, and the non-failing case is unchanged.
> >
> > The reinsert allocation is GFP_ATOMIC (it runs under xfrm_policy_lock), so in
> > practice this is only reached under memory pressure; the crash below was
> > reproduced deterministically by forcing that allocation to fail with fault
> > injection (failslab).
> >
> > Crash:
> > Oops: general protection fault, probably for non-canonical address
> > 0xfbd59c0000000024: 0000 [#1] SMP KASAN NOPTI
> > KASAN: maybe wild-memory-access in range [0xdead000000000120-0xdead000000000127]
> > ...
> > Workqueue: events xfrm_hash_rebuild
> > RIP: 0010:xfrm_hash_rebuild+0x5b3/0x1190
> > RAX: dead000000000122 (LIST_POISON2 + offset)
> > ...
> > Call Trace:
> > hlist_del_rcu (include/linux/rculist.h:599)
> > xfrm_hash_rebuild (net/xfrm/xfrm_policy.c:1365)
> > process_one_work (kernel/workqueue.c:3322)
> > worker_thread (kernel/workqueue.c:3486)
> > kthread (kernel/kthread.c:436)
> > ret_from_fork (arch/x86/kernel/process.c:158)
> > ret_from_fork_asm (arch/x86/entry/entry_64.S:245)
> > ...
> > Kernel panic - not syncing: Fatal exception in interrupt
> >
> > Fixes: 563d5ca93e88 ("xfrm: switch migrate to xfrm_policy_lookup_bytype")
> > Reported-by: AutonomousCodeSecurity@microsoft.com
> > Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
> > ---
> > net/xfrm/xfrm_policy.c | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
> > index 7ef861a0e823..2612a405542b 100644
> > --- a/net/xfrm/xfrm_policy.c
> > +++ b/net/xfrm/xfrm_policy.c
> > @@ -1362,7 +1362,7 @@ static void xfrm_hash_rebuild(struct work_struct *work)
> > if (xfrm_policy_is_dead_or_sk(policy))
> > continue;
> >
> > - hlist_del_rcu(&policy->bydst);
> > + hlist_del_init_rcu(&policy->bydst);
>
> This patch is dubious. I looks to me as if it papers over the
> actual bug.
>
> Why is there a memory allocation error?
>
> The first loop -- before unlink -- is supposed to preallocate the new
> bins and chain heads.
>
Agreed, and my patch hides it instead of avoiding it. The problem is
the prep loop's guard is inverted:
if (policy->selector.prefixlen_d < dbits ||
policy->selector.prefixlen_s < sbits)
continue;
That skips exactly the policies reinserted via the tree (prefixlen <
threshold => policy_hash_bysel() NULL => xfrm_policy_inexact_insert()), and
preallocates for the exact ones instead, which never allocate and get
pruned again at out_unlock. So the inexact bin/node is allocated GFP_ATOMIC
after the hlist_del_rcu(), and that's the failure the WARN catches.
The reproducer lowers then raises the threshold so those bins are pruned
and reallocated during reinsert; failslab just makes the failure
deterministic, OOM would do the same.
v2 inverts the guard so prep prepares the set that's actually reinserted:
- if (policy->selector.prefixlen_d < dbits ||
- policy->selector.prefixlen_s < sbits)
+ if (policy->selector.prefixlen_d >= dbits &&
+ policy->selector.prefixlen_s >= sbits)
continue;
Then inexact_insert() finds bin+node present and allocates nothing, so the
reinsert can't fail; a prep failure still hits goto out_unlock before any
unlink. hlist_del_rcu vs _init_ then no longer matters.
---
net/xfrm/xfrm_policy.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 7ef861a0e823..932a313b9460 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -1329,8 +1329,8 @@ static void xfrm_hash_rebuild(struct work_struct *work)
}
}
- if (policy->selector.prefixlen_d < dbits ||
- policy->selector.prefixlen_s < sbits)
+ if (policy->selector.prefixlen_d >= dbits &&
+ policy->selector.prefixlen_s >= sbits)
continue;
bin = xfrm_policy_inexact_alloc_bin(policy, dir);
---
I checked the new patch on the reproducer, and the crash can't be triggered.
If you agree with this new patch, I'll send this as v2. I can also share
the reproducer if you need that to do further checks.
Xiang
Xiang
> This is also why there is a WARN. No memory allocations are supposed to
> occur after the hlist_del_rcu(), there is supposed to be a guarantee
> that the insertion succeeds.
>
^ permalink raw reply related
* [PATCH net-next v5] net: mana: Add Interrupt Moderation support
From: Haiyang Zhang @ 2026-07-02 22:01 UTC (permalink / raw)
To: linux-hyperv, netdev, K. Y. Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, Long Li, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Konstantin Taranov, Simon Horman,
Erni Sri Satya Vennela, Dipayaan Roy, Shradha Gupta, Aditya Garg,
Stanislav Fomichev, Breno Leitao, linux-kernel, linux-rdma
Cc: paulros
From: Haiyang Zhang <haiyangz@microsoft.com>
Add Static and Dynamic Interrupt Moderation (DIM) support for
Rx and Tx.
Update queue creation procedure with new data struct with the related
settings.
Add functions to collect stat for DIM, and workers to update DIM data
and settings.
Update ethtool handler to get/set the moderation settings from a user.
To avoid detach/re-attach ops, ring DIM doorbell to change settings
at run time.
By default, adaptive-rx/tx (DIM) are enabled if supported by HW.
Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: Simon Horman <horms@kernel.org>
---
v5:
Updated function return type and comments suggested by Paolo Abeni.
v4:
Fixed tx stat, concurrency, and mb issues from Simon's review.
v3:
Updated to avoid detach/re-attach ops as suggested by Paolo.
v2:
Updated with comments from Jedrzej.
---
drivers/net/ethernet/microsoft/Kconfig | 1 +
.../net/ethernet/microsoft/mana/gdma_main.c | 29 +++
drivers/net/ethernet/microsoft/mana/mana_en.c | 175 ++++++++++++++++++
.../ethernet/microsoft/mana/mana_ethtool.c | 167 ++++++++++++++++-
include/net/mana/gdma.h | 24 ++-
include/net/mana/mana.h | 54 ++++++
6 files changed, 441 insertions(+), 9 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/Kconfig b/drivers/net/ethernet/microsoft/Kconfig
index 3f36ee6a8ece..e9be18c92ca5 100644
--- a/drivers/net/ethernet/microsoft/Kconfig
+++ b/drivers/net/ethernet/microsoft/Kconfig
@@ -21,6 +21,7 @@ config MICROSOFT_MANA
depends on X86_64 || (ARM64 && !CPU_BIG_ENDIAN)
depends on PCI_HYPERV
select AUXILIARY_BUS
+ select DIMLIB
select PAGE_POOL
select NET_SHAPER
help
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
index e8b7ffb47eb9..aef3b77229c1 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright (c) 2021, Microsoft Corporation. */
+#include <linux/bitfield.h>
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/pci.h>
@@ -466,6 +467,7 @@ static int mana_gd_disable_queue(struct gdma_queue *queue)
#define DOORBELL_OFFSET_RQ 0x400
#define DOORBELL_OFFSET_CQ 0x800
#define DOORBELL_OFFSET_EQ 0xFF8
+#define DOORBELL_OFFSET_DIM 0x820
static void mana_gd_ring_doorbell(struct gdma_context *gc, u32 db_index,
enum gdma_queue_type q_type, u32 qid,
@@ -506,6 +508,16 @@ static void mana_gd_ring_doorbell(struct gdma_context *gc, u32 db_index,
addr += DOORBELL_OFFSET_SQ;
break;
+ case GDMA_DIM:
+ e.dim.id = qid;
+ e.dim.mod_usec = FIELD_GET(MANA_INTR_MODR_USEC_MAX, tail_ptr);
+ e.dim.mod_usec_vld = !!(tail_ptr & MANA_INTR_MODR_USEC_VLD);
+ e.dim.mod_comps = FIELD_GET(MANA_INTR_MODR_COMP_MASK, tail_ptr);
+ e.dim.mod_comps_vld = num_req;
+
+ addr += DOORBELL_OFFSET_DIM;
+ break;
+
default:
WARN_ON(1);
return;
@@ -540,6 +552,23 @@ void mana_gd_ring_cq(struct gdma_queue *cq, u8 arm_bit)
}
EXPORT_SYMBOL_NS(mana_gd_ring_cq, "NET_MANA");
+void mana_gd_ring_dim(struct gdma_queue *cq, u32 mod_usec, bool mod_usec_vld,
+ u32 mod_comps, bool mod_comps_vld)
+{
+ struct gdma_context *gc = cq->gdma_dev->gdma_context;
+ u32 dim_val;
+
+ /* Convert the DIM values to doorbell parameters */
+ dim_val = FIELD_PREP(MANA_INTR_MODR_USEC_MAX, mod_usec) |
+ FIELD_PREP(MANA_INTR_MODR_COMP_MASK, mod_comps);
+ if (mod_usec_vld)
+ dim_val |= MANA_INTR_MODR_USEC_VLD;
+
+ mana_gd_ring_doorbell(gc, cq->gdma_dev->doorbell, GDMA_DIM, cq->id,
+ dim_val, mod_comps_vld);
+}
+EXPORT_SYMBOL_NS(mana_gd_ring_dim, "NET_MANA");
+
#define MANA_SERVICE_PERIOD 10
static void mana_serv_rescan(struct pci_dev *pdev)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 7438ea6b3f26..5ce0b96c50f6 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -1591,6 +1591,15 @@ int mana_create_wq_obj(struct mana_port_context *apc,
mana_gd_init_req_hdr(&req.hdr, MANA_CREATE_WQ_OBJ,
sizeof(req), sizeof(resp));
+
+ /* Our driver uses different message versions for request and
+ * response in this case.
+ * Our firmware is forward compatible with newer message versions, so
+ * the old firmware still properly handles this message, just the new
+ * feature fields are ignored, and queue creation will be successful.
+ */
+ req.hdr.req.msg_version = GDMA_MESSAGE_V3;
+ req.hdr.resp.msg_version = GDMA_MESSAGE_V2;
req.vport = vport;
req.wq_type = wq_type;
req.wq_gdma_region = wq_spec->gdma_region;
@@ -1599,6 +1608,9 @@ int mana_create_wq_obj(struct mana_port_context *apc,
req.cq_size = cq_spec->queue_size;
req.cq_moderation_ctx_id = cq_spec->modr_ctx_id;
req.cq_parent_qid = cq_spec->attached_eq;
+ req.req_cq_moderation = cq_spec->req_cq_moderation;
+ req.cq_moderation_comp = cq_spec->cq_moderation_comp;
+ req.cq_moderation_usec = cq_spec->cq_moderation_usec;
err = mana_send_request(apc->ac, &req, sizeof(req), &resp,
sizeof(resp));
@@ -1856,6 +1868,7 @@ static void mana_poll_tx_cq(struct mana_cq *cq)
struct gdma_posted_wqe_info *wqe_info;
unsigned int pkt_transmitted = 0;
unsigned int wqe_unit_cnt = 0;
+ unsigned int tx_bytes = 0;
struct mana_txq *txq = cq->txq;
struct mana_port_context *apc;
struct netdev_queue *net_txq;
@@ -1937,6 +1950,8 @@ static void mana_poll_tx_cq(struct mana_cq *cq)
mana_unmap_skb(skb, apc);
+ tx_bytes += skb->len;
+
napi_consume_skb(skb, cq->budget);
pkt_transmitted++;
@@ -1967,6 +1982,10 @@ static void mana_poll_tx_cq(struct mana_cq *cq)
if (atomic_sub_return(pkt_transmitted, &txq->pending_sends) < 0)
WARN_ON_ONCE(1);
+ /* Feed DIM with the completion rate observed here, in NAPI context. */
+ cq->tx_dim_pkts += pkt_transmitted;
+ cq->tx_dim_bytes += tx_bytes;
+
cq->work_done = pkt_transmitted;
}
@@ -2318,6 +2337,117 @@ static void mana_poll_rx_cq(struct mana_cq *cq)
xdp_do_flush();
}
+static void mana_rx_dim_work(struct work_struct *work)
+{
+ struct dim *dim = container_of(work, struct dim, work);
+ struct dim_cq_moder cur_moder;
+ struct mana_cq *cq;
+
+ cur_moder = net_dim_get_rx_moderation(dim->mode, dim->profile_ix);
+ cq = container_of(dim, struct mana_cq, dim);
+
+ cur_moder.usec = min_t(u16, cur_moder.usec, MANA_INTR_MODR_USEC_MAX);
+ cur_moder.pkts = min_t(u16, cur_moder.pkts, MANA_INTR_MODR_COMP_MAX);
+
+ mana_gd_ring_dim(cq->gdma_cq, cur_moder.usec, true,
+ cur_moder.pkts, true);
+
+ dim->state = DIM_START_MEASURE;
+}
+
+static void mana_tx_dim_work(struct work_struct *work)
+{
+ struct dim *dim = container_of(work, struct dim, work);
+ struct dim_cq_moder cur_moder;
+ struct mana_cq *cq;
+
+ cur_moder = net_dim_get_tx_moderation(dim->mode, dim->profile_ix);
+ cq = container_of(dim, struct mana_cq, dim);
+
+ cur_moder.usec = min_t(u16, cur_moder.usec, MANA_INTR_MODR_USEC_MAX);
+ cur_moder.pkts = min_t(u16, cur_moder.pkts, MANA_INTR_MODR_COMP_MAX);
+
+ mana_gd_ring_dim(cq->gdma_cq, cur_moder.usec, true,
+ cur_moder.pkts, true);
+
+ dim->state = DIM_START_MEASURE;
+}
+
+/* The caller must update apc->rx/tx_dim_enabled before disabling and
+ * after enabling. And synchronize_net() before draining the DIM work,
+ * so that NAPI cannot observe a stale flag.
+ */
+void mana_dim_change(struct mana_cq *cq, bool enable)
+{
+ bool is_rx = cq->type == MANA_CQ_TYPE_RX;
+ struct mana_port_context *apc;
+ work_func_t work_func;
+ u32 usec, comp;
+
+ if (is_rx) {
+ apc = netdev_priv(cq->rxq->ndev);
+ usec = apc->intr_modr_rx_usec;
+ comp = apc->intr_modr_rx_comp;
+ work_func = mana_rx_dim_work;
+ } else {
+ apc = netdev_priv(cq->txq->ndev);
+ usec = apc->intr_modr_tx_usec;
+ comp = apc->intr_modr_tx_comp;
+ work_func = mana_tx_dim_work;
+ }
+
+ /* On enable, zero the DIM state so net_dim() starts measuring from
+ * scratch.
+ * On disable, drain any pending DIM work and restore the static
+ * moderation values.
+ */
+ if (enable) {
+ memset(&cq->dim, 0, sizeof(cq->dim));
+ cq->dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE;
+ INIT_WORK(&cq->dim.work, work_func);
+ } else {
+ cancel_work_sync(&cq->dim.work);
+ mana_gd_ring_dim(cq->gdma_cq, usec, true, comp, true);
+ }
+}
+
+static void mana_update_rx_dim(struct mana_cq *cq)
+{
+ struct mana_port_context *apc = netdev_priv(cq->rxq->ndev);
+ struct dim_sample dim_sample = {};
+ struct mana_rxq *rxq = cq->rxq;
+
+ /* Pairs with smp_store_release() in mana_set_coalesce(): observing the
+ * enable flag set guarantees the DIM (re)initialization is visible.
+ */
+ if (!smp_load_acquire(&apc->rx_dim_enabled))
+ return;
+
+ dim_update_sample(READ_ONCE(cq->dim_event_ctr), rxq->stats.packets,
+ rxq->stats.bytes, &dim_sample);
+ net_dim(&cq->dim, &dim_sample);
+}
+
+static void mana_update_tx_dim(struct mana_cq *cq)
+{
+ struct mana_port_context *apc = netdev_priv(cq->txq->ndev);
+ struct dim_sample dim_sample = {};
+
+ /* Pairs with smp_store_release() in mana_set_coalesce(): observing the
+ * enable flag set guarantees the DIM (re)initialization is visible.
+ */
+ if (!smp_load_acquire(&apc->tx_dim_enabled))
+ return;
+
+ /* cq->tx_dim_pkts/bytes are accumulated in mana_poll_tx_cq(), in the
+ * same NAPI context as this read, so they track the hardware
+ * completion rate and need no u64_stats_sync protection.
+ */
+ dim_update_sample(READ_ONCE(cq->dim_event_ctr), cq->tx_dim_pkts,
+ cq->tx_dim_bytes, &dim_sample);
+ net_dim(&cq->dim, &dim_sample);
+}
+
static int mana_cq_handler(void *context, struct gdma_queue *gdma_queue)
{
struct mana_cq *cq = context;
@@ -2336,6 +2466,15 @@ static int mana_cq_handler(void *context, struct gdma_queue *gdma_queue)
if (w < cq->budget) {
mana_gd_ring_cq(gdma_queue, SET_ARM_BIT);
cq->work_done_since_doorbell = 0;
+
+ /* Update DIM before napi_complete_done() to prevent running
+ * net_dim() concurrently.
+ */
+ if (cq->type == MANA_CQ_TYPE_RX)
+ mana_update_rx_dim(cq);
+ else
+ mana_update_tx_dim(cq);
+
napi_complete_done(&cq->napi, w);
} else if (cq->work_done_since_doorbell >=
(cq->gdma_cq->queue_size / COMP_ENTRY_SIZE) * 4) {
@@ -2368,6 +2507,7 @@ static void mana_schedule_napi(void *context, struct gdma_queue *gdma_queue)
{
struct mana_cq *cq = context;
+ WRITE_ONCE(cq->dim_event_ctr, cq->dim_event_ctr + 1);
napi_schedule_irqoff(&cq->napi);
}
@@ -2410,6 +2550,7 @@ static void mana_destroy_txq(struct mana_port_context *apc)
if (apc->tx_qp[i]->txq.napi_initialized) {
napi_synchronize(napi);
napi_disable_locked(napi);
+ cancel_work_sync(&apc->tx_qp[i]->tx_cq.dim.work);
netif_napi_del_locked(napi);
apc->tx_qp[i]->txq.napi_initialized = false;
}
@@ -2543,6 +2684,11 @@ static int mana_create_txq(struct mana_port_context *apc,
cq_spec.modr_ctx_id = 0;
cq_spec.attached_eq = cq->gdma_cq->cq.parent->id;
+ /* DIM setting can be changed at runtime */
+ cq_spec.req_cq_moderation = true;
+ cq_spec.cq_moderation_usec = apc->intr_modr_tx_usec;
+ cq_spec.cq_moderation_comp = apc->intr_modr_tx_comp;
+
err = mana_create_wq_obj(apc, apc->port_handle, GDMA_SQ,
&wq_spec, &cq_spec,
&apc->tx_qp[i]->tx_object);
@@ -2573,6 +2719,13 @@ static int mana_create_txq(struct mana_port_context *apc,
set_bit(NAPI_STATE_NO_BUSY_POLL, &cq->napi.state);
netif_napi_add_locked(net, &cq->napi, mana_poll);
+
+ /* Initialize the DIM work before enabling NAPI, so that a poll
+ * cannot reach net_dim() with an uninitialized cq->dim.work.
+ */
+ INIT_WORK(&cq->dim.work, mana_tx_dim_work);
+ cq->dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE;
+
napi_enable_locked(&cq->napi);
txq->napi_initialized = true;
@@ -2610,6 +2763,7 @@ static void mana_destroy_rxq(struct mana_port_context *apc,
napi_synchronize(napi);
napi_disable_locked(napi);
+ cancel_work_sync(&rxq->rx_cq.dim.work);
netif_napi_del_locked(napi);
}
@@ -2848,6 +3002,11 @@ static struct mana_rxq *mana_create_rxq(struct mana_port_context *apc,
cq_spec.modr_ctx_id = 0;
cq_spec.attached_eq = cq->gdma_cq->cq.parent->id;
+ /* DIM setting can be changed at runtime */
+ cq_spec.req_cq_moderation = true;
+ cq_spec.cq_moderation_usec = apc->intr_modr_rx_usec;
+ cq_spec.cq_moderation_comp = apc->intr_modr_rx_comp;
+
err = mana_create_wq_obj(apc, apc->port_handle, GDMA_RQ,
&wq_spec, &cq_spec, &rxq->rxobj);
if (err)
@@ -2880,6 +3039,12 @@ static struct mana_rxq *mana_create_rxq(struct mana_port_context *apc,
WARN_ON(xdp_rxq_info_reg_mem_model(&rxq->xdp_rxq, MEM_TYPE_PAGE_POOL,
rxq->page_pool));
+ /* Initialize the DIM work before enabling NAPI, so that a poll
+ * cannot reach net_dim() with an uninitialized cq->dim.work.
+ */
+ INIT_WORK(&cq->dim.work, mana_rx_dim_work);
+ cq->dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE;
+
napi_enable_locked(&cq->napi);
mana_gd_ring_cq(cq->gdma_cq, SET_ARM_BIT);
@@ -3546,6 +3711,16 @@ static int mana_probe_port(struct mana_context *ac, int port_idx,
apc->link_cfg_error = 1;
apc->cqe_coalescing_enable = 0;
+ /* Initialize interrupt moderation settings if supported by HW */
+ if (gc->pf_cap_flags1 & GDMA_PF_CAP_FLAG_1_DYN_INTERRUPT_MODERATION) {
+ apc->intr_modr_rx_usec = MANA_INTR_MODR_USEC_DEF;
+ apc->intr_modr_rx_comp = MANA_INTR_MODR_COMP_DEF;
+ apc->intr_modr_tx_usec = MANA_INTR_MODR_USEC_DEF;
+ apc->intr_modr_tx_comp = MANA_INTR_MODR_COMP_DEF;
+ apc->rx_dim_enabled = MANA_ADAPTIVE_RX_DEF;
+ apc->tx_dim_enabled = MANA_ADAPTIVE_TX_DEF;
+ }
+
mutex_init(&apc->vport_mutex);
apc->vport_use_count = 0;
diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
index 881df597d7f9..9e31e2595ae3 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
@@ -419,6 +419,15 @@ static int mana_get_coalesce(struct net_device *ndev,
!kernel_coal->rx_cqe_nsecs)
kernel_coal->rx_cqe_nsecs = MANA_RX_CQE_NSEC_DEF;
+ ec->rx_coalesce_usecs = apc->intr_modr_rx_usec;
+ ec->rx_max_coalesced_frames = apc->intr_modr_rx_comp;
+
+ ec->tx_coalesce_usecs = apc->intr_modr_tx_usec;
+ ec->tx_max_coalesced_frames = apc->intr_modr_tx_comp;
+
+ ec->use_adaptive_rx_coalesce = apc->rx_dim_enabled;
+ ec->use_adaptive_tx_coalesce = apc->tx_dim_enabled;
+
return 0;
}
@@ -428,9 +437,34 @@ static int mana_set_coalesce(struct net_device *ndev,
struct netlink_ext_ack *extack)
{
struct mana_port_context *apc = netdev_priv(ndev);
- u8 saved_cqe_coalescing_enable;
+ struct {
+ u16 intr_modr_rx_usec;
+ u16 intr_modr_rx_comp;
+ u16 intr_modr_tx_usec;
+ u16 intr_modr_tx_comp;
+ u8 cqe_coalescing_enable;
+ bool rx_dim_enabled;
+ bool tx_dim_enabled;
+ } saved;
+ bool modr_changed = false;
+ bool dim_changed = false;
+ struct gdma_context *gc;
int err;
+ gc = apc->ac->gdma_dev->gdma_context;
+
+ /* Both static and dynamic interrupt moderation (DIM) rely on the
+ * same HW capability advertised by the PF.
+ */
+ if ((ec->use_adaptive_rx_coalesce || ec->use_adaptive_tx_coalesce ||
+ ec->rx_coalesce_usecs || ec->tx_coalesce_usecs ||
+ ec->rx_max_coalesced_frames || ec->tx_max_coalesced_frames) &&
+ !(gc->pf_cap_flags1 & GDMA_PF_CAP_FLAG_1_DYN_INTERRUPT_MODERATION)) {
+ NL_SET_ERR_MSG(extack,
+ "Interrupt Moderation is not supported by HW");
+ return -EOPNOTSUPP;
+ }
+
if (kernel_coal->rx_cqe_frames != 1 &&
kernel_coal->rx_cqe_frames != MANA_RXCOMP_OOB_NUM_PPI) {
NL_SET_ERR_MSG_FMT(extack,
@@ -440,18 +474,129 @@ static int mana_set_coalesce(struct net_device *ndev,
return -EINVAL;
}
- saved_cqe_coalescing_enable = apc->cqe_coalescing_enable;
+ if (ec->rx_coalesce_usecs > MANA_INTR_MODR_USEC_MAX ||
+ ec->tx_coalesce_usecs > MANA_INTR_MODR_USEC_MAX) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "coalesce usecs must be <= %lu",
+ MANA_INTR_MODR_USEC_MAX);
+ return -EINVAL;
+ }
+
+ if (ec->rx_max_coalesced_frames > MANA_INTR_MODR_COMP_MAX ||
+ ec->tx_max_coalesced_frames > MANA_INTR_MODR_COMP_MAX) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "coalesce frames must be <= %lu",
+ MANA_INTR_MODR_COMP_MAX);
+ return -EINVAL;
+ }
+
+ if (ec->rx_coalesce_usecs != apc->intr_modr_rx_usec ||
+ ec->rx_max_coalesced_frames != apc->intr_modr_rx_comp ||
+ ec->tx_coalesce_usecs != apc->intr_modr_tx_usec ||
+ ec->tx_max_coalesced_frames != apc->intr_modr_tx_comp)
+ modr_changed = true;
+
+ saved.intr_modr_rx_usec = apc->intr_modr_rx_usec;
+ saved.intr_modr_rx_comp = apc->intr_modr_rx_comp;
+ saved.intr_modr_tx_usec = apc->intr_modr_tx_usec;
+ saved.intr_modr_tx_comp = apc->intr_modr_tx_comp;
+
+ apc->intr_modr_rx_usec = ec->rx_coalesce_usecs;
+ apc->intr_modr_rx_comp = ec->rx_max_coalesced_frames;
+ apc->intr_modr_tx_usec = ec->tx_coalesce_usecs;
+ apc->intr_modr_tx_comp = ec->tx_max_coalesced_frames;
+
+ if (!!ec->use_adaptive_rx_coalesce != apc->rx_dim_enabled ||
+ !!ec->use_adaptive_tx_coalesce != apc->tx_dim_enabled)
+ dim_changed = true;
+
+ saved.rx_dim_enabled = apc->rx_dim_enabled;
+ saved.tx_dim_enabled = apc->tx_dim_enabled;
+
+ saved.cqe_coalescing_enable = apc->cqe_coalescing_enable;
apc->cqe_coalescing_enable =
kernel_coal->rx_cqe_frames == MANA_RXCOMP_OOB_NUM_PPI;
- if (!apc->port_is_up)
+ if (!apc->port_is_up) {
+ WRITE_ONCE(apc->rx_dim_enabled, !!ec->use_adaptive_rx_coalesce);
+ WRITE_ONCE(apc->tx_dim_enabled, !!ec->use_adaptive_tx_coalesce);
return 0;
+ }
- err = mana_config_rss(apc, TRI_STATE_TRUE, false, false);
- if (err)
- apc->cqe_coalescing_enable = saved_cqe_coalescing_enable;
+ if (apc->cqe_coalescing_enable != saved.cqe_coalescing_enable) {
+ /* CQE coalescing setting is applied via RSS configuration. */
+ err = mana_config_rss(apc, TRI_STATE_TRUE, false, false);
+ if (err) {
+ netdev_err(ndev, "Change CQE coalescing failed: %d\n",
+ err);
+ apc->cqe_coalescing_enable =
+ saved.cqe_coalescing_enable;
+ apc->intr_modr_rx_usec = saved.intr_modr_rx_usec;
+ apc->intr_modr_rx_comp = saved.intr_modr_rx_comp;
+ apc->intr_modr_tx_usec = saved.intr_modr_tx_usec;
+ apc->intr_modr_tx_comp = saved.intr_modr_tx_comp;
+ return err;
+ }
+ }
- return err;
+ if (modr_changed || dim_changed) {
+ bool new_rx_dim = !!ec->use_adaptive_rx_coalesce;
+ bool new_tx_dim = !!ec->use_adaptive_tx_coalesce;
+ bool disable_rx_dim = saved.rx_dim_enabled && !new_rx_dim;
+ bool disable_tx_dim = saved.tx_dim_enabled && !new_tx_dim;
+ bool enable_rx_dim = !saved.rx_dim_enabled && new_rx_dim;
+ bool enable_tx_dim = !saved.tx_dim_enabled && new_tx_dim;
+ int q;
+
+ /* On disable: clear the per-port flag first and
+ * synchronize_net() so any in-flight NAPI poll observes
+ * the new value and will not schedule further DIM work;
+ * then drain pending work and restore the static
+ * moderation values.
+ */
+ if (disable_rx_dim)
+ WRITE_ONCE(apc->rx_dim_enabled, false);
+ if (disable_tx_dim)
+ WRITE_ONCE(apc->tx_dim_enabled, false);
+ if (disable_rx_dim || disable_tx_dim)
+ synchronize_net();
+
+ for (q = 0; q < apc->num_queues; q++) {
+ struct mana_cq *rx_cq = &apc->rxqs[q]->rx_cq;
+ struct mana_cq *tx_cq = &apc->tx_qp[q]->tx_cq;
+
+ if (disable_rx_dim)
+ mana_dim_change(rx_cq, false);
+ else if (enable_rx_dim)
+ mana_dim_change(rx_cq, true);
+ else if (!new_rx_dim && modr_changed)
+ mana_gd_ring_dim(rx_cq->gdma_cq,
+ apc->intr_modr_rx_usec, true,
+ apc->intr_modr_rx_comp, true);
+
+ if (disable_tx_dim)
+ mana_dim_change(tx_cq, false);
+ else if (enable_tx_dim)
+ mana_dim_change(tx_cq, true);
+ else if (!new_tx_dim && modr_changed)
+ mana_gd_ring_dim(tx_cq->gdma_cq,
+ apc->intr_modr_tx_usec, true,
+ apc->intr_modr_tx_comp, true);
+ }
+
+ /* Publish the enable flag with release semantics so a
+ * concurrent NAPI poll that observes it set also sees the DIM
+ * (re)init done by mana_dim_change() above.
+ */
+ if (enable_rx_dim)
+ /* pairs with smp_load_acquire() in mana_update_rx_dim() */
+ smp_store_release(&apc->rx_dim_enabled, true);
+ if (enable_tx_dim)
+ /* pairs with smp_load_acquire() in mana_update_tx_dim() */
+ smp_store_release(&apc->tx_dim_enabled, true);
+ }
+
+ return 0;
}
/* mana_set_channels - change the number of queues on a port
@@ -595,7 +740,13 @@ static int mana_get_link_ksettings(struct net_device *ndev,
}
const struct ethtool_ops mana_ethtool_ops = {
- .supported_coalesce_params = ETHTOOL_COALESCE_RX_CQE_FRAMES,
+ .supported_coalesce_params = ETHTOOL_COALESCE_RX_CQE_FRAMES |
+ ETHTOOL_COALESCE_RX_USECS |
+ ETHTOOL_COALESCE_RX_MAX_FRAMES |
+ ETHTOOL_COALESCE_TX_USECS |
+ ETHTOOL_COALESCE_TX_MAX_FRAMES |
+ ETHTOOL_COALESCE_USE_ADAPTIVE_RX |
+ ETHTOOL_COALESCE_USE_ADAPTIVE_TX,
.op_needs_rtnl = ETHTOOL_OP_NEEDS_RTNL_SCHANNELS |
ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM |
ETHTOOL_OP_NEEDS_RTNL_GLINK,
diff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h
index 0c395917b214..8529cef0d7c4 100644
--- a/include/net/mana/gdma.h
+++ b/include/net/mana/gdma.h
@@ -47,6 +47,7 @@ enum gdma_queue_type {
GDMA_RQ,
GDMA_CQ,
GDMA_EQ,
+ GDMA_DIM,
};
enum gdma_work_request_flags {
@@ -126,6 +127,17 @@ union gdma_doorbell_entry {
u64 tail_ptr : 31;
u64 arm : 1;
} eq;
+
+ struct {
+ u64 id : 24;
+ u64 reserved : 8;
+ u64 mod_usec : 10;
+ u64 reserve1 : 5;
+ u64 mod_usec_vld : 1;
+ u64 mod_comps : 8;
+ u64 reserve2 : 7;
+ u64 mod_comps_vld: 1;
+ } dim;
}; /* HW DATA */
struct gdma_msg_hdr {
@@ -502,6 +514,9 @@ void mana_gd_ring_cq(struct gdma_queue *cq, u8 arm_bit);
int mana_schedule_serv_work(struct gdma_context *gc, enum gdma_eqe_type type);
+void mana_gd_ring_dim(struct gdma_queue *cq, u32 mod_usec, bool mod_usec_vld,
+ u32 mod_comps, bool mod_comps_vld);
+
struct gdma_wqe {
u32 reserved :24;
u32 last_vbytes :8;
@@ -650,6 +665,9 @@ enum {
/* Driver supports self recovery on Hardware Channel timeouts */
#define GDMA_DRV_CAP_FLAG_1_HWC_TIMEOUT_RECOVERY BIT(25)
+/* Driver supports dynamic interrupt moderation - DIM */
+#define GDMA_DRV_CAP_FLAG_1_DYN_INTERRUPT_MODERATION BIT(28)
+
#define GDMA_DRV_CAP_FLAGS1 \
(GDMA_DRV_CAP_FLAG_1_EQ_SHARING_MULTI_VPORT | \
GDMA_DRV_CAP_FLAG_1_NAPI_WKDONE_FIX | \
@@ -665,7 +683,8 @@ enum {
GDMA_DRV_CAP_FLAG_1_PROBE_RECOVERY | \
GDMA_DRV_CAP_FLAG_1_HANDLE_STALL_SQ_RECOVERY | \
GDMA_DRV_CAP_FLAG_1_HWC_TIMEOUT_RECOVERY | \
- GDMA_DRV_CAP_FLAG_1_EQ_MSI_UNSHARE_MULTI_VPORT)
+ GDMA_DRV_CAP_FLAG_1_EQ_MSI_UNSHARE_MULTI_VPORT | \
+ GDMA_DRV_CAP_FLAG_1_DYN_INTERRUPT_MODERATION)
#define GDMA_DRV_CAP_FLAGS2 0
@@ -701,6 +720,9 @@ struct gdma_verify_ver_req {
u8 os_ver_str4[128];
}; /* HW DATA */
+/* HW supports dynamic interrupt moderation - DIM */
+#define GDMA_PF_CAP_FLAG_1_DYN_INTERRUPT_MODERATION BIT(15)
+
struct gdma_verify_ver_resp {
struct gdma_resp_hdr hdr;
u64 gdma_protocol_ver;
diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h
index 13c87baf018e..48f4445aa87a 100644
--- a/include/net/mana/mana.h
+++ b/include/net/mana/mana.h
@@ -4,6 +4,7 @@
#ifndef _MANA_H
#define _MANA_H
+#include <linux/dim.h>
#include <net/xdp.h>
#include <net/net_shaper.h>
@@ -64,6 +65,19 @@ enum TRI_STATE {
/* Maximum number of packets per coalesced CQE */
#define MANA_RXCOMP_OOB_NUM_PPI 4
+/* Default/max interrupt moderation settings */
+#define MANA_INTR_MODR_USEC_DEF 0
+#define MANA_INTR_MODR_COMP_DEF 0
+
+#define MANA_ADAPTIVE_RX_DEF true
+#define MANA_ADAPTIVE_TX_DEF true
+
+/* DIM doorbell value field layout */
+#define MANA_INTR_MODR_USEC_MAX GENMASK(9, 0)
+#define MANA_INTR_MODR_USEC_VLD BIT(15)
+#define MANA_INTR_MODR_COMP_MAX GENMASK(7, 0)
+#define MANA_INTR_MODR_COMP_MASK GENMASK(23, 16)
+
/* Update this count whenever the respective structures are changed */
#define MANA_STATS_RX_COUNT (6 + MANA_RXCOMP_OOB_NUM_PPI - 1)
#define MANA_STATS_TX_COUNT 11
@@ -297,6 +311,17 @@ struct mana_cq {
int work_done;
int work_done_since_doorbell;
int budget;
+
+ /* DIM - Dynamic Interrupt Moderation */
+ struct dim dim;
+ u16 dim_event_ctr;
+
+ /* Cumulative TX completions fed to DIM. Updated and read only in
+ * NAPI context (mana_poll_tx_cq() / mana_update_tx_dim()), so they
+ * measure the hardware completion rate and need no u64_stats_sync.
+ */
+ u64 tx_dim_pkts;
+ u64 tx_dim_bytes;
};
struct mana_recv_buf_oob {
@@ -573,6 +598,15 @@ struct mana_port_context {
u8 cqe_coalescing_enable;
u32 cqe_coalescing_timeout_ns;
+ /* Interrupt moderation settings */
+ u16 intr_modr_rx_usec;
+ u16 intr_modr_rx_comp;
+ u16 intr_modr_tx_usec;
+ u16 intr_modr_tx_comp;
+
+ bool rx_dim_enabled;
+ bool tx_dim_enabled;
+
struct mana_ethtool_stats eth_stats;
struct mana_ethtool_phy_stats phy_stats;
@@ -598,6 +632,8 @@ int mana_alloc_queues(struct net_device *ndev);
int mana_attach(struct net_device *ndev);
int mana_detach(struct net_device *ndev, bool from_close);
+void mana_dim_change(struct mana_cq *cq, bool enable);
+
int mana_probe(struct gdma_dev *gd, bool resuming);
void mana_remove(struct gdma_dev *gd, bool suspending);
@@ -633,6 +669,9 @@ struct mana_obj_spec {
u32 queue_size;
u32 attached_eq;
u32 modr_ctx_id;
+ u8 req_cq_moderation;
+ u16 cq_moderation_comp;
+ u16 cq_moderation_usec;
};
enum mana_command_code {
@@ -764,6 +803,15 @@ struct mana_create_wqobj_req {
u32 cq_size;
u32 cq_moderation_ctx_id;
u32 cq_parent_qid;
+
+ /* V2 */
+ u8 allow_rqwqe_chain;
+
+ /* V3 */
+ u8 req_cq_moderation;
+ u16 cq_moderation_comp;
+ u16 cq_moderation_usec;
+ u8 reserved2[2];
}; /* HW DATA */
struct mana_create_wqobj_resp {
@@ -771,6 +819,12 @@ struct mana_create_wqobj_resp {
u32 wq_id;
u32 cq_id;
mana_handle_t wq_obj;
+
+ /* V2 */
+ u16 cq_moderation_comp;
+ u16 cq_moderation_usec;
+ u8 cq_moderation_enabled;
+ u8 reserved1[3];
}; /* HW DATA */
/* Destroy WQ Object */
--
2.34.1
^ permalink raw reply related
* Re: [syzbot ci] Re: net: Support per-netns device unregistration
From: Kuniyuki Iwashima @ 2026-07-02 21:59 UTC (permalink / raw)
To: syzbot+ci052b96c9bf56ca1d
Cc: andrew, davem, edumazet, horms, kuba, kuni1840, kuniyu, netdev,
pabeni, syzbot, syzkaller-bugs
In-Reply-To: <6a461706.52c20a74.1f8b39.0008.GAE@google.com>
From: syzbot ci <syzbot+ci052b96c9bf56ca1d@syzkaller.appspotmail.com>
Date: Thu, 02 Jul 2026 00:45:10 -0700
> syzbot ci has tested the following series
>
> [v1] net: Support per-netns device unregistration
> https://lore.kernel.org/all/20260701214334.266991-1-kuniyu@google.com
> * [PATCH v1 net-next 01/14] rtnetlink: Lock sock_net(skb->sk) in rtnl_newlink().
> * [PATCH v1 net-next 02/14] rtnetlink: Call unregister_netdevice_many() only once in rtnl_link_unregister().
> * [PATCH v1 net-next 03/14] rtnetlink: Add per-netns rtnl_work.
> * [PATCH v1 net-next 04/14] net: Wrap default_device_exit_net() with __rtnl_net_lock().
> * [PATCH v1 net-next 05/14] net: Hold __rtnl_net_lock() in netdev_wait_allrefs_any().
> * [PATCH v1 net-next 06/14] net: Add per-netns netdev unregistration infra.
> * [PATCH v1 net-next 07/14] net: Call unregister_netdevice_many() per netns.
> * [PATCH v1 net-next 08/14] veth: Support per-netns device unregistration.
> * [PATCH v1 net-next 09/14] bareudp: Protect bareudp_list with mutex.
> * [PATCH v1 net-next 10/14] bareudp: Support per-netns netdev unregistration.
> * [PATCH v1 net-next 11/14] ipvlan: Convert ipvl_port.count to refcount_t.
> * [PATCH v1 net-next 12/14] ipvlan: Synchronise ipvlan_init() and ipvlan_uninit() for the same lower dev.
> * [PATCH v1 net-next 13/14] ipvlan: Protect ipvl_port.ipvlans with mutex.
> * [PATCH v1 net-next 14/14] ipvlan: Support per-netns netdev unregistration.
>
> and found the following issue:
> possible deadlock in __dev_change_net_namespace
>
> Full report is available here:
> https://ci.syzbot.org/series/a744b257-d741-4780-8a53-f156b2a7afc9
>
> ***
>
> possible deadlock in __dev_change_net_namespace
>
> tree: net-next
> URL: https://kernel.googlesource.com/pub/scm/linux/kernel/git/netdev/net-next.git
> base: d6e81529749190123aa0040626c7e5dbc20fdc9a
> arch: amd64
> compiler: Debian clang version 22.1.6 (++20260514074242+fc4aad7b5db3-1~exp1~20260514074407.73), Debian LLD 22.1.6
> config: https://ci.syzbot.org/builds/243cd0ec-28f9-4d21-8f16-3d2fbad8388d/config
> syz repro: https://ci.syzbot.org/findings/a8a0740d-fdec-4a20-9aa5-7cb955707913/syz_repro
>
> veth0_macvtap: left promiscuous mode
> ============================================
> WARNING: possible recursive locking detected
> syzkaller #0 Not tainted
> --------------------------------------------
> syz.1.18/5814 is trying to acquire lock:
> ffffffff9a9b0418 (&net->dev_unreg_lock){+.+.}-{3:3}, at: spin_lock include/linux/spinlock.h:342 [inline]
> ffffffff9a9b0418 (&net->dev_unreg_lock){+.+.}-{3:3}, at: unregister_netdevice_move_net net/core/dev.c:-1 [inline]
> ffffffff9a9b0418 (&net->dev_unreg_lock){+.+.}-{3:3}, at: __dev_change_net_namespace+0x1479/0x2200 net/core/dev.c:12768
>
> but task is already holding lock:
> ffff88810b6cf8d8 (&net->dev_unreg_lock){+.+.}-{3:3}, at: spin_lock include/linux/spinlock.h:342 [inline]
> ffff88810b6cf8d8 (&net->dev_unreg_lock){+.+.}-{3:3}, at: unregister_netdevice_move_net net/core/dev.c:-1 [inline]
> ffff88810b6cf8d8 (&net->dev_unreg_lock){+.+.}-{3:3}, at: __dev_change_net_namespace+0x146a/0x2200 net/core/dev.c:12768
>
> other info that might help us debug this:
> Possible unsafe locking scenario:
>
> CPU0
> ----
> lock(&net->dev_unreg_lock);
> lock(&net->dev_unreg_lock);
>
> *** DEADLOCK ***
>
> May be due to missing lock nesting notation
Oh right, I'll squash this to patch 6.
---8<---
diff --git a/net/core/dev.c b/net/core/dev.c
index 57fb4741d0ac..fcd58c2aa030 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -12574,10 +12574,10 @@ static void unregister_netdevice_move_net(struct net *net_old,
{
if (net_old > net) {
spin_lock(&net->dev_unreg_lock);
- spin_lock(&net_old->dev_unreg_lock);
+ spin_lock_nested(&net_old->dev_unreg_lock, SINGLE_DEPTH_NESTING);
} else {
spin_lock(&net_old->dev_unreg_lock);
- spin_lock(&net->dev_unreg_lock);
+ spin_lock_nested(&net->dev_unreg_lock, SINGLE_DEPTH_NESTING);
}
if (!list_empty(&dev->unreg_list_net)) {
---8<---
^ permalink raw reply related
* Re: [PATCH net] net/tls: Consume empty data records in tls_sw_read_sock()
From: Sabrina Dubroca @ 2026-07-02 21:50 UTC (permalink / raw)
To: Chuck Lever
Cc: john.fastabend, Jakub Kicinski, davem, edumazet, Paolo Abeni,
Simon Horman, netdev
In-Reply-To: <0a03d16e-d4ce-422d-9492-3e31d910d8e5@app.fastmail.com>
2026-07-02, 15:52:49 -0400, Chuck Lever wrote:
>
>
> On Thu, Jul 2, 2026, at 2:05 PM, Sabrina Dubroca wrote:
> > 2026-06-30, 15:15:51 -0400, Chuck Lever wrote:
> >> A peer may send a zero-length TLS application_data record; TLS 1.3
> >> explicitly permits these as a traffic-analysis countermeasure (RFC
> >> 8446, Section 5.1). After decryption such a record has full_len ==
> >> 0. tls_sw_read_sock() hands it to the read_actor, which has no
> >> payload to consume and returns zero. The loop treats a zero return
> >> as backpressure (used <= 0), requeues the skb at the head of
> >> rx_list, and stops. rx_list is serviced head-first on the next
> >> call, so the empty record is dequeued, fails the same way, and is
> >> requeued again; every later record on the connection is blocked
> >> behind it.
> >>
> >> tls_sw_recvmsg() does not stall on this: a zero-length data record
> >> copies nothing and falls through to consume_skb(). Mirror that in
> >> the read_sock() path by recognizing an empty data record before
> >> the actor runs, consuming it, and continuing.
> >>
> >> Fixes: 662fbcec32f4 ("net/tls: implement ->read_sock()")
> >> Signed-off-by: Chuck Lever <cel@kernel.org>
> >> ---
> >> net/tls/tls_sw.c | 11 +++++++++++
> >> 1 file changed, 11 insertions(+)
> >
> > Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
> >
> > I think tls_sw_splice_read() suffers from a similar issue (returning 0
> > even though more data may be available). Sashiko agrees, and also
> > found a few more pre-existing issues.
>
> Do you want a v2 series with those issues addressed?
I'd be ok with this patch going in on its own, and the other issues
being addressed separately. If you have time to look into those,
that'd be great.
--
Sabrina
^ permalink raw reply
* RE: [PATCH net-next v6 12/15] onsemi: s2500: Add driver support for TS2500 MAC-PHY
From: Selvamani Rajagopal @ 2026-07-02 21:23 UTC (permalink / raw)
To: Julian Braha, Andrew Lunn, Piergiorgio Beruto, Heiner Kallweit,
Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Andrew Lunn, Parthiban Veerasooran, Richard Cochran,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Simon Horman,
Jonathan Corbet, Shuah Khan
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
devicetree@vger.kernel.org, linux-doc@vger.kernel.org, Jerry Ray
In-Reply-To: <d6a56d05-0c6f-49a5-9281-1194b62ab86e@gmail.com>
> -----Original Message-----
> From: Julian Braha <julian.braha@gmail.com> On Behalf Of Julian Braha
> Subject: Re: [PATCH net-next v6 12/15] onsemi: s2500: Add driver support for TS2500 MAC-PHY
>
> > +endif # NET_VENDOR_ONSEMI
>
> S2500_MACPHY still has that duplicate dependency from being inside two
> of these:
> 'if NET_VENDOR_ONSEMI..endif'
>
> And I already pointed it out on v5:
Somehow, I missed your feedback. Sorry about that. Will remove the duplication.
(Also will go over all the responses to see if I missed anything else)
> :(
>
> - Julian Braha
^ permalink raw reply
* Re: [PATCH v2] selftests: Open /dev/udmabuf O_RDONLY
From: Bobby Eshleman @ 2026-07-02 20:53 UTC (permalink / raw)
To: T.J. Mercier
Cc: kraxel, vivek.kasireddy, kuba, Shuah Khan, Andrew Lunn,
David S. Miller, Eric Dumazet, Paolo Abeni, linux-kselftest,
linux-kernel, netdev, bpf
In-Reply-To: <20260701192210.2997769-1-tjmercier@google.com>
On Wed, Jul 01, 2026 at 12:22:08PM -0700, T.J. Mercier wrote:
> Write permissions on the /dev/udmabuf device file are not required to
> issue ioctls and allocate udmabufs. Applications should be opening this
> file as O_RDONLY. The BPF dmabuf_iter selftest already does this. [1]
>
> Users are pointing to these selftests as examples of how use udmabuf,
> and encountering permission errors on systems where write permissions
> are not available on /dev/udmabuf. Apply the principle of least
> privilege to selftests which use udmabuf by removing the write access
> mode from drivers/dma-buf/udmabuf.c and drivers/net/hw/ncdevmem.c.
>
> [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/testing/selftests/bpf/prog_tests/dmabuf_iter.c?h=v7.1#n49
>
> Signed-off-by: T.J. Mercier <tjmercier@google.com>
> ---
> tools/testing/selftests/drivers/dma-buf/udmabuf.c | 2 +-
> tools/testing/selftests/drivers/net/hw/ncdevmem.c | 2 +-
> 2 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/tools/testing/selftests/drivers/dma-buf/udmabuf.c b/tools/testing/selftests/drivers/dma-buf/udmabuf.c
> index d78aec662586..ced0b95c876c 100644
> --- a/tools/testing/selftests/drivers/dma-buf/udmabuf.c
> +++ b/tools/testing/selftests/drivers/dma-buf/udmabuf.c
> @@ -140,7 +140,7 @@ int main(int argc, char *argv[])
> ksft_print_header();
> ksft_set_plan(7);
>
> - devfd = open("/dev/udmabuf", O_RDWR);
> + devfd = open("/dev/udmabuf", O_RDONLY);
> if (devfd < 0) {
> ksft_print_msg(
> "%s: [skip,no-udmabuf: Unable to access DMA buffer device file]\n",
> diff --git a/tools/testing/selftests/drivers/net/hw/ncdevmem.c b/tools/testing/selftests/drivers/net/hw/ncdevmem.c
> index e098d6534c3c..8114a29692fd 100644
> --- a/tools/testing/selftests/drivers/net/hw/ncdevmem.c
> +++ b/tools/testing/selftests/drivers/net/hw/ncdevmem.c
> @@ -149,7 +149,7 @@ static struct memory_buffer *udmabuf_alloc(size_t size)
>
> ctx->size = size;
>
> - ctx->devfd = open("/dev/udmabuf", O_RDWR);
> + ctx->devfd = open("/dev/udmabuf", O_RDONLY);
> if (ctx->devfd < 0) {
> pr_err("[skip,no-udmabuf: Unable to access DMA buffer device file]");
> goto err_free_ctx;
>
> base-commit: fbb7ad31ab376c5101b2ac7205fad0344fd2de60
> --
> 2.55.0.rc0.799.gd6f94ed593-goog
>
Reviewed-by: Bobby Eshleman <bobbyeshleman@meta.com>
^ permalink raw reply
* [PATCH net-next v4 2/2] net: dsa: realtek: rtl8365mb: add HSGMII support for RTL8367S
From: Johan Alvarado @ 2026-07-02 20:47 UTC (permalink / raw)
To: linusw, alsi, andrew, olteanv, kuba, davem, edumazet, pabeni,
linux
Cc: luizluca, maxime.chevallier, namiltd, netdev, linux-kernel,
contact
In-Reply-To: <20260702204648.276112-1-contact@c127.dev>
In addition to SGMII, the RTL8367S SerDes also supports HSGMII, which
carries 2.5 Gbps with the same signaling as SGMII at 2.5x clock rate.
The chip info table already declares HSGMII as a supported interface
mode for external interface 1.
Extend the SerDes PCS to handle HSGMII, which phylink represents as
2500base-x:
- Select the HSGMII SerDes tuning parameters and external interface
mode, and mux the SerDes to MAC8 in HSGMII mode, from pcs_config()
according to the interface. The parameters are again lifted from the
GPL-licensed Realtek rtl8367c vendor driver, and again only cover
the tuning variant for a non-zero chip option, so the mode is gated
on the option probed at setup.
- Advertise 2500base-x and MAC_2500FD on ports whose external
interface supports HSGMII.
- Accept SPEED_2500 in the forced link configuration. The MAC speed
field has no 2.5 Gbps value: the rate is determined by the HSGMII
SerDes configuration, and the vendor driver programs the 1 Gbps
value here, so do the same.
Tested on a Mercusys MR80X v2.20, where the RTL8367S is connected to
the SoC over HSGMII.
Suggested-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
Signed-off-by: Johan Alvarado <contact@c127.dev>
---
drivers/net/dsa/realtek/rtl8365mb_main.c | 75 +++++++++++++++++++-----
1 file changed, 59 insertions(+), 16 deletions(-)
diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/realtek/rtl8365mb_main.c
index 2d202120cfd9..38e5804f9ff6 100644
--- a/drivers/net/dsa/realtek/rtl8365mb_main.c
+++ b/drivers/net/dsa/realtek/rtl8365mb_main.c
@@ -40,8 +40,8 @@
* driver has only been tested with a fixed-link, but in principle it should not
* matter.
*
- * NOTE: Currently, only the RGMII and SGMII interfaces are implemented in this
- * driver.
+ * NOTE: Currently, only the RGMII, SGMII and HSGMII interfaces are implemented
+ * in this driver.
*
* The interrupt line is asserted on link UP/DOWN events. The driver creates a
* custom irqchip to handle this interrupt and demultiplex the events by reading
@@ -637,6 +637,18 @@ static const struct rtl8365mb_jam_tbl_entry rtl8365mb_sds_jam_sgmii[] = {
{ 0x0424, 0xD810 }, { 0x002E, 0x83F2 },
};
+/* HSGMII SerDes tuning parameters, lifted from the vendor driver sources. As
+ * with the SGMII table, the vendor driver keeps several variants and selects
+ * one based on the chip option register; these are the values for a non-zero
+ * option, which is what RTL8367S parts seen so far report. See
+ * rtl8365mb_sds_probe_option().
+ */
+static const struct rtl8365mb_jam_tbl_entry rtl8365mb_sds_jam_hsgmii[] = {
+ { 0x0500, 0x82F0 }, { 0x0501, 0xF195 }, { 0x0502, 0x31A2 },
+ { 0x0503, 0x7960 }, { 0x0504, 0x9728 }, { 0x0423, 0x9D85 },
+ { 0x0424, 0xD810 }, { 0x0001, 0x0F80 }, { 0x002E, 0x83F2 },
+};
+
enum rtl8365mb_phy_interface_mode {
RTL8365MB_PHY_INTERFACE_MODE_INVAL = 0,
RTL8365MB_PHY_INTERFACE_MODE_INTERNAL = BIT(0),
@@ -1247,9 +1259,12 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
const unsigned long *advertising,
bool permit_pause_to_mac)
{
+ const struct rtl8365mb_jam_tbl_entry *sds_jam;
const int id = RTL8365MB_SDS_EXT_INTERFACE_ID;
struct rtl8365mb *mb = pcs_to_rtl8365mb(pcs);
struct realtek_priv *priv;
+ size_t sds_jam_size;
+ u32 mode;
u16 val;
int ret;
int i;
@@ -1264,6 +1279,16 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
return -EOPNOTSUPP;
+ if (interface == PHY_INTERFACE_MODE_2500BASEX) {
+ sds_jam = rtl8365mb_sds_jam_hsgmii;
+ sds_jam_size = ARRAY_SIZE(rtl8365mb_sds_jam_hsgmii);
+ mode = RTL8365MB_EXT_PORT_MODE_HSGMII;
+ } else {
+ sds_jam = rtl8365mb_sds_jam_sgmii;
+ sds_jam_size = ARRAY_SIZE(rtl8365mb_sds_jam_sgmii);
+ mode = RTL8365MB_EXT_PORT_MODE_SGMII;
+ }
+
/* Hold the embedded DW8051 microcontroller in reset and keep it
* disabled. The vendor driver loads firmware into it to manage the
* SerDes link, but the firmware only duplicates work that phylink
@@ -1291,24 +1316,24 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
return ret;
/* Tune the SerDes with vendor-prescribed parameters */
- for (i = 0; i < ARRAY_SIZE(rtl8365mb_sds_jam_sgmii); i++) {
- ret = rtl8365mb_sds_write(priv,
- rtl8365mb_sds_jam_sgmii[i].reg,
- rtl8365mb_sds_jam_sgmii[i].val);
+ for (i = 0; i < sds_jam_size; i++) {
+ ret = rtl8365mb_sds_write(priv, sds_jam[i].reg,
+ sds_jam[i].val);
if (ret)
return ret;
}
- /* Mux the SerDes to MAC8 in SGMII mode */
+ /* Mux the SerDes to MAC8 in the requested mode */
ret = regmap_update_bits(priv->map, RTL8365MB_SDS_MISC_REG,
RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK |
RTL8365MB_SDS_MISC_MAC8_SEL_HSGMII_MASK,
- RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK);
+ mode == RTL8365MB_EXT_PORT_MODE_SGMII ?
+ RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK :
+ RTL8365MB_SDS_MISC_MAC8_SEL_HSGMII_MASK);
if (ret)
return ret;
- val = RTL8365MB_EXT_PORT_MODE_SGMII
- << RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(id);
+ val = mode << RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(id);
ret = regmap_update_bits(priv->map,
RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(id),
RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(id),
@@ -1354,7 +1379,8 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
static bool rtl8365mb_interface_is_serdes(phy_interface_t interface)
{
- return interface == PHY_INTERFACE_MODE_SGMII;
+ return interface == PHY_INTERFACE_MODE_SGMII ||
+ interface == PHY_INTERFACE_MODE_2500BASEX;
}
static unsigned int rtl8365mb_pcs_inband_caps(struct phylink_pcs *pcs,
@@ -1409,7 +1435,9 @@ static void rtl8365mb_pcs_get_state(struct phylink_pcs *pcs,
switch (FIELD_GET(RTL8365MB_SDS_MISC_SGMII_SPD_MASK, val)) {
case RTL8365MB_PORT_SPEED_1000M:
- state->speed = SPEED_1000;
+ state->speed =
+ state->interface == PHY_INTERFACE_MODE_2500BASEX ?
+ SPEED_2500 : SPEED_1000;
break;
case RTL8365MB_PORT_SPEED_100M:
state->speed = SPEED_100;
@@ -1434,7 +1462,11 @@ static void rtl8365mb_pcs_link_up(struct phylink_pcs *pcs,
u32 r_speed;
int ret;
- if (speed == SPEED_1000) {
+ /* The speed field has no value for 2.5 Gbps: the rate is determined by
+ * the HSGMII SerDes configuration, and the vendor driver programs the
+ * 1 Gbps value here.
+ */
+ if (speed == SPEED_2500 || speed == SPEED_1000) {
r_speed = RTL8365MB_PORT_SPEED_1000M;
} else if (speed == SPEED_100) {
r_speed = RTL8365MB_PORT_SPEED_100M;
@@ -1494,7 +1526,11 @@ static int rtl8365mb_ext_config_forcemode(struct realtek_priv *priv, int port,
r_rx_pause = rx_pause ? 1 : 0;
r_tx_pause = tx_pause ? 1 : 0;
- if (speed == SPEED_1000) {
+ /* The speed field has no value for 2.5 Gbps: the rate is
+ * determined by the HSGMII SerDes configuration, and the
+ * vendor driver programs the 1 Gbps value here.
+ */
+ if (speed == SPEED_2500 || speed == SPEED_1000) {
r_speed = RTL8365MB_PORT_SPEED_1000M;
} else if (speed == SPEED_100) {
r_speed = RTL8365MB_PORT_SPEED_100M;
@@ -1577,6 +1613,13 @@ static void rtl8365mb_phylink_get_caps(struct dsa_switch *ds, int port,
mb->sds_supported)
__set_bit(PHY_INTERFACE_MODE_SGMII,
config->supported_interfaces);
+
+ if (extint->supported_interfaces & RTL8365MB_PHY_INTERFACE_MODE_HSGMII &&
+ mb->sds_supported) {
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX,
+ config->supported_interfaces);
+ config->mac_capabilities |= MAC_2500FD;
+ }
}
static struct phylink_pcs *
@@ -1618,8 +1661,8 @@ static void rtl8365mb_phylink_mac_config(struct phylink_config *config,
return;
}
- /* SGMII is handled by the SerDes PCS, configured through the
- * phylink_pcs ops, so there is nothing to do here for it.
+ /* SGMII and 2500base-x are handled by the SerDes PCS, configured
+ * through the phylink_pcs ops, so nothing to do here for them.
*/
if (rtl8365mb_interface_is_serdes(state->interface))
return;
--
2.55.0
^ permalink raw reply related
* [PATCH net-next v4 1/2] net: dsa: realtek: rtl8365mb: add SGMII support for RTL8367S
From: Johan Alvarado @ 2026-07-02 20:47 UTC (permalink / raw)
To: linusw, alsi, andrew, olteanv, kuba, davem, edumazet, pabeni,
linux
Cc: luizluca, maxime.chevallier, namiltd, netdev, linux-kernel,
contact
In-Reply-To: <20260702204648.276112-1-contact@c127.dev>
The RTL8367S can mux its embedded SerDes to external interface 1,
which is typically used to connect the switch to a CPU port. The chip
info table already declares SGMII as a supported interface mode for
this chip, but the driver only implements RGMII so far.
Implement SGMII support as a phylink PCS, with the configuration
sequence derived from the GPL-licensed Realtek rtl8367c vendor driver
as distributed in the Mercusys MR80X GPL code drop:
- Add accessors for the SerDes indirect access registers (SDS_INDACS),
through which the SerDes internal registers are reached.
- Register a phylink_pcs for the SerDes, selected from mac_select_pcs
for the SGMII interface, so the SerDes handling lives in the PCS
operations rather than in the MAC operations.
- Probe the SerDes tuning variant from the chip option register once
at setup. The vendor driver keeps two sets of SerDes tuning
parameters and selects between them based on this option; only the
variant for a non-zero option (which all RTL8367S parts seen so far
report) has been validated on hardware, so the SerDes interface
modes are only advertised in that case. An unsupported variant thus
fails at phylink validation time instead of at link configuration
time.
- Keep the embedded DW8051 microcontroller in reset and disabled. The
vendor driver loads firmware into it to manage the SerDes link, but
analysis of that firmware shows it only duplicates the link
management phylink already performs: it polls the port status and
writes the external interface force registers behind the driver's
back.
- Clear the line rate bypass bit for the external interface, tune the
SerDes with the vendor-prescribed parameters, mux the SerDes to MAC8
in SGMII mode and only then take the SerDes out of reset, as the
vendor driver does.
- After deasserting the SerDes reset, reset the SerDes data path via
the SerDes BMCR register to flush the FIFOs and resync the PLL.
This mirrors what the vendor firmware does right after deasserting
the SerDes reset, and ensures a clean link state from cold boot.
- Force the SGMII link parameters (link, speed, duplex) in the SDS_MISC
register from pcs_link_up(). SGMII in-band autonegotiation is not
implemented, so only fixed-link and conventional PHY setups are
supported, just like RGMII. This is reported to phylink through
pcs_inband_caps() returning LINK_INBAND_DISABLE, so phylink never
selects an in-band-enabled negotiation mode for this PCS.
- Program the SerDes pause controls in SDS_MISC from the resolved
pause modes when forcing the MAC external interface in mac_link_up,
as the vendor driver does, rather than leaving whatever state the
boot firmware left there. This is done in the MAC layer because
pcs_link_up() carries no pause information.
- Implement pcs_get_state() by reading the link status from the
SerDes, with the forced speed and duplex read back from SDS_MISC.
Although the supported fixed-link and conventional PHY setups do not
use it, the PCS owns the SerDes link state, and phylink consults
pcs_get_state() to track the physical link when operating in in-band
mode with autonegotiation disabled. The SerDes has no link interrupt
wired up, so the PCS sets its poll flag.
Tested on a Mercusys MR80X v2.20, where the RTL8367S is connected to
the SoC over SGMII.
Suggested-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
Suggested-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Suggested-by: Mieczyslaw Nalewaj <namiltd@yahoo.com>
Signed-off-by: Johan Alvarado <contact@c127.dev>
---
drivers/net/dsa/realtek/rtl8365mb_main.c | 518 ++++++++++++++++++++++-
1 file changed, 514 insertions(+), 4 deletions(-)
diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/realtek/rtl8365mb_main.c
index 5ac091bf93c9..2d202120cfd9 100644
--- a/drivers/net/dsa/realtek/rtl8365mb_main.c
+++ b/drivers/net/dsa/realtek/rtl8365mb_main.c
@@ -40,7 +40,8 @@
* driver has only been tested with a fixed-link, but in principle it should not
* matter.
*
- * NOTE: Currently, only the RGMII interface is implemented in this driver.
+ * NOTE: Currently, only the RGMII and SGMII interfaces are implemented in this
+ * driver.
*
* The interrupt line is asserted on link UP/DOWN events. The driver creates a
* custom irqchip to handle this interrupt and demultiplex the events by reading
@@ -94,11 +95,13 @@
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
+#include <linux/mii.h>
#include <linux/mutex.h>
#include <linux/of_irq.h>
#include <linux/regmap.h>
#include <linux/if_bridge.h>
#include <linux/if_vlan.h>
+#include <linux/phylink.h>
#include "realtek.h"
#include "realtek-smi.h"
@@ -129,6 +132,7 @@
/* Chip reset register */
#define RTL8365MB_CHIP_RESET_REG 0x1322
+#define RTL8365MB_CHIP_RESET_DW8051_MASK 0x0010
#define RTL8365MB_CHIP_RESET_SW_MASK 0x0002
#define RTL8365MB_CHIP_RESET_HW_MASK 0x0001
@@ -238,6 +242,76 @@
#define RTL8365MB_EXT_RGMXF_RXDELAY_MASK 0x0007
#define RTL8365MB_EXT_RGMXF_TXDELAY_MASK 0x0008
+/* External interface line rate bypass register - one bit per external
+ * interface, indexed by the external port number with port 5 (the first
+ * external port) as the base. Other RTL8367 families index this register
+ * differently (e.g. the RTL8367R uses (id + 1) % 2), so this mapping only
+ * holds for the RTL8367C-style parts this driver supports.
+ */
+#define RTL8365MB_BYPASS_LINE_RATE_REG 0x03F7
+#define RTL8365MB_BYPASS_LINE_RATE_MASK(_port) BIT((_port) - 5)
+
+/* SerDes indirect access registers */
+#define RTL8365MB_SDS_INDACS_CMD_REG 0x6600
+#define RTL8365MB_SDS_INDACS_CMD_BUSY_MASK 0x0100
+#define RTL8365MB_SDS_INDACS_CMD_RUN_MASK 0x0080
+#define RTL8365MB_SDS_INDACS_CMD_WR_MASK 0x0040
+#define RTL8365MB_SDS_INDACS_ADR_REG 0x6601
+#define RTL8365MB_SDS_INDACS_DATA_REG 0x6602
+
+/* SerDes miscellaneous configuration register */
+#define RTL8365MB_SDS_MISC_REG 0x1D11
+#define RTL8365MB_SDS_MISC_SGMII_RXFC_MASK 0x4000
+#define RTL8365MB_SDS_MISC_SGMII_TXFC_MASK 0x2000
+#define RTL8365MB_SDS_MISC_MAC8_SEL_HSGMII_MASK 0x0800
+#define RTL8365MB_SDS_MISC_SGMII_FDUP_MASK 0x0400
+#define RTL8365MB_SDS_MISC_SGMII_LINK_MASK 0x0200
+#define RTL8365MB_SDS_MISC_SGMII_SPD_MASK 0x0180
+#define RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK 0x0040
+
+/* SerDes internal registers, accessed via the SDS_INDACS registers. The BMCR
+ * data path reset holds BMCR_ANENABLE | BMCR_ISOLATE while toggling the
+ * vendor-specific low bits from phase 1 to phase 2, which triggers a data path
+ * reset and PLL resync.
+ */
+#define RTL8365MB_SDS_REG_BMCR 0x0000
+#define RTL8365MB_SDS_BMCR_DPRST_PHASE1 (BMCR_ANENABLE | BMCR_ISOLATE | 0x1)
+#define RTL8365MB_SDS_BMCR_DPRST_PHASE2 (BMCR_ANENABLE | BMCR_ISOLATE | 0x3)
+#define RTL8365MB_SDS_REG_NWAY 0x0002
+#define RTL8365MB_SDS_NWAY_EN_MASK 0x0200
+#define RTL8365MB_SDS_NWAY_RESTART_MASK 0x0100
+#define RTL8365MB_SDS_REG_RESET 0x0003
+#define RTL8365MB_SDS_RESET_DEASSERT 0x7106
+#define RTL8365MB_SDS_REG_LINK_STATUS 0x003d
+#define RTL8365MB_SDS_LINK_STATUS_LINK_MASK 0x0010
+
+/* The embedded SerDes can only be muxed to external interface 1 (MAC8),
+ * which is port 6.
+ */
+#define RTL8365MB_SDS_EXT_INTERFACE_ID 1
+#define RTL8365MB_SDS_EXT_INTERFACE_PORT 6
+
+/* Line rate bypass bit for the SerDes external interface */
+#define RTL8365MB_SDS_BYPASS_LINE_RATE_MASK \
+ RTL8365MB_BYPASS_LINE_RATE_MASK(RTL8365MB_SDS_EXT_INTERFACE_PORT)
+
+/* SerDes tuning parameter variant selector. The vendor driver picks between
+ * two sets of SerDes tuning parameters based on this chip option. Reading it
+ * requires first arming the read by writing a magic key to the arm register,
+ * then disarming it afterwards.
+ */
+#define RTL8365MB_SDS_OPTION_ARM_REG 0x13C0
+#define RTL8365MB_SDS_OPTION_ARM_KEY 0x0249
+#define RTL8365MB_SDS_OPTION_REG 0x13C1
+
+/* Embedded DW8051 microcontroller control registers. The microcontroller
+ * can run firmware to manage the SerDes link, but this driver keeps it in
+ * reset and disabled: phylink already performs the link management that
+ * the firmware would otherwise do.
+ */
+#define RTL8365MB_MISC_CFG0_REG 0x130C
+#define RTL8365MB_MISC_CFG0_DW8051_EN_MASK 0x0020
+
/* External interface port speed values - used in DIGITAL_INTERFACE_FORCE */
#define RTL8365MB_PORT_SPEED_10M 0
#define RTL8365MB_PORT_SPEED_100M 1
@@ -551,6 +625,18 @@ static const struct rtl8365mb_jam_tbl_entry rtl8365mb_init_jam_common[] = {
{ 0x1D32, 0x0002 },
};
+/* SGMII SerDes tuning parameters, lifted from the vendor driver sources. The
+ * vendor driver keeps two variants of this table and selects between them
+ * based on the chip option register; these are the values for a non-zero
+ * option, which is what RTL8367S parts seen so far report. See
+ * rtl8365mb_sds_probe_option().
+ */
+static const struct rtl8365mb_jam_tbl_entry rtl8365mb_sds_jam_sgmii[] = {
+ { 0x0480, 0x04D7 }, { 0x0481, 0xF994 }, { 0x0482, 0x2420 },
+ { 0x0483, 0x6960 }, { 0x0484, 0x9728 }, { 0x0423, 0x9D85 },
+ { 0x0424, 0xD810 }, { 0x002E, 0x83F2 },
+};
+
enum rtl8365mb_phy_interface_mode {
RTL8365MB_PHY_INTERFACE_MODE_INVAL = 0,
RTL8365MB_PHY_INTERFACE_MODE_INTERNAL = BIT(0),
@@ -730,6 +816,9 @@ struct rtl8365mb_port {
* @cpu: CPU tagging and CPU port configuration for this chip
* @mib_lock: prevent concurrent reads of MIB counters
* @ports: per-port data
+ * @pcs: PCS for the SerDes external interface
+ * @sds_supported: SerDes tuning parameters match the chip option, so the
+ * SerDes interface modes can be advertised
*
* Private data for this driver.
*/
@@ -740,8 +829,12 @@ struct rtl8365mb {
struct rtl8365mb_cpu cpu;
struct mutex mib_lock;
struct rtl8365mb_port ports[RTL8365MB_MAX_NUM_PORTS];
+ struct phylink_pcs pcs;
+ bool sds_supported;
};
+#define pcs_to_rtl8365mb(_pcs) container_of((_pcs), struct rtl8365mb, pcs)
+
static int rtl8365mb_phy_poll_busy(struct realtek_priv *priv)
{
u32 val;
@@ -1042,6 +1135,342 @@ static int rtl8365mb_ext_config_rgmii(struct realtek_priv *priv, int port,
return 0;
}
+static int rtl8365mb_sds_write(struct realtek_priv *priv, u16 addr, u16 data)
+{
+ int ret;
+
+ ret = regmap_write(priv->map, RTL8365MB_SDS_INDACS_DATA_REG, data);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(priv->map, RTL8365MB_SDS_INDACS_ADR_REG, addr);
+ if (ret)
+ return ret;
+
+ /* The SerDes indirect access engine completes the command within the
+ * register write transaction, so there is no need to wait or poll for
+ * completion before the next access, matching the vendor driver.
+ */
+ return regmap_write(priv->map, RTL8365MB_SDS_INDACS_CMD_REG,
+ RTL8365MB_SDS_INDACS_CMD_RUN_MASK |
+ RTL8365MB_SDS_INDACS_CMD_WR_MASK);
+}
+
+static int rtl8365mb_sds_read(struct realtek_priv *priv, u16 addr, u16 *data)
+{
+ u32 val;
+ int ret;
+
+ ret = regmap_write(priv->map, RTL8365MB_SDS_INDACS_ADR_REG, addr);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(priv->map, RTL8365MB_SDS_INDACS_CMD_REG,
+ RTL8365MB_SDS_INDACS_CMD_RUN_MASK);
+ if (ret)
+ return ret;
+
+ /* Wait for the indirect read to complete: the engine clears the BUSY
+ * bit once the data register holds the result.
+ */
+ ret = regmap_read_poll_timeout(priv->map, RTL8365MB_SDS_INDACS_CMD_REG,
+ val,
+ !(val & RTL8365MB_SDS_INDACS_CMD_BUSY_MASK),
+ 10, 1000);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(priv->map, RTL8365MB_SDS_INDACS_DATA_REG, &val);
+ if (ret)
+ return ret;
+
+ *data = val;
+
+ return 0;
+}
+
+/* The vendor driver selects between two sets of SerDes tuning parameters based
+ * on the chip option register. Only the variant for a non-zero option has been
+ * tested on real hardware - the RTL8367S parts seen so far all report 1. The
+ * variant for option 0 uses different tuning values that cannot be verified,
+ * so probe the option once at setup and only advertise the SerDes interface
+ * modes when the tuning parameters are known to match, so that an unsupported
+ * variant fails at phylink validation time rather than when configuring the
+ * link.
+ */
+static int rtl8365mb_sds_probe_option(struct realtek_priv *priv)
+{
+ struct rtl8365mb *mb = priv->chip_data;
+ const struct rtl8365mb_extint *extint;
+ u32 option;
+ int ret;
+ int i;
+
+ /* Nothing to probe if no external interface is wired to the SerDes */
+ for (i = 0; i < RTL8365MB_MAX_NUM_EXTINTS; i++) {
+ extint = &mb->chip_info->extints[i];
+
+ if (extint->supported_interfaces &
+ (RTL8365MB_PHY_INTERFACE_MODE_SGMII |
+ RTL8365MB_PHY_INTERFACE_MODE_HSGMII))
+ break;
+ }
+ if (i == RTL8365MB_MAX_NUM_EXTINTS)
+ return 0;
+
+ ret = regmap_write(priv->map, RTL8365MB_SDS_OPTION_ARM_REG,
+ RTL8365MB_SDS_OPTION_ARM_KEY);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(priv->map, RTL8365MB_SDS_OPTION_REG, &option);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(priv->map, RTL8365MB_SDS_OPTION_ARM_REG, 0);
+ if (ret)
+ return ret;
+
+ if (option == 0) {
+ dev_warn(priv->dev,
+ "unsupported SerDes tuning variant (chip option 0), disabling SerDes interface modes\n");
+ return 0;
+ }
+
+ mb->sds_supported = true;
+
+ return 0;
+}
+
+static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
+ phy_interface_t interface,
+ const unsigned long *advertising,
+ bool permit_pause_to_mac)
+{
+ const int id = RTL8365MB_SDS_EXT_INTERFACE_ID;
+ struct rtl8365mb *mb = pcs_to_rtl8365mb(pcs);
+ struct realtek_priv *priv;
+ u16 val;
+ int ret;
+ int i;
+
+ priv = mb->priv;
+
+ /* This driver does not implement SGMII in-band autonegotiation yet, so
+ * the link parameters are forced from rtl8365mb_pcs_link_up() instead.
+ * rtl8365mb_pcs_inband_caps() reports this to phylink, which should
+ * therefore never select an in-band-enabled negotiation mode.
+ */
+ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
+ return -EOPNOTSUPP;
+
+ /* Hold the embedded DW8051 microcontroller in reset and keep it
+ * disabled. The vendor driver loads firmware into it to manage the
+ * SerDes link, but the firmware only duplicates work that phylink
+ * already does: it polls the port status and forces the external
+ * interface configuration in the very registers this driver manages.
+ * Letting it run would race with phylink.
+ */
+ ret = regmap_update_bits(priv->map, RTL8365MB_CHIP_RESET_REG,
+ RTL8365MB_CHIP_RESET_DW8051_MASK,
+ RTL8365MB_CHIP_RESET_DW8051_MASK);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(priv->map, RTL8365MB_MISC_CFG0_REG,
+ RTL8365MB_MISC_CFG0_DW8051_EN_MASK, 0);
+ if (ret)
+ return ret;
+
+ /* The vendor driver clears the line rate bypass for all interface
+ * modes except TMII.
+ */
+ ret = regmap_update_bits(priv->map, RTL8365MB_BYPASS_LINE_RATE_REG,
+ RTL8365MB_SDS_BYPASS_LINE_RATE_MASK, 0);
+ if (ret)
+ return ret;
+
+ /* Tune the SerDes with vendor-prescribed parameters */
+ for (i = 0; i < ARRAY_SIZE(rtl8365mb_sds_jam_sgmii); i++) {
+ ret = rtl8365mb_sds_write(priv,
+ rtl8365mb_sds_jam_sgmii[i].reg,
+ rtl8365mb_sds_jam_sgmii[i].val);
+ if (ret)
+ return ret;
+ }
+
+ /* Mux the SerDes to MAC8 in SGMII mode */
+ ret = regmap_update_bits(priv->map, RTL8365MB_SDS_MISC_REG,
+ RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK |
+ RTL8365MB_SDS_MISC_MAC8_SEL_HSGMII_MASK,
+ RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK);
+ if (ret)
+ return ret;
+
+ val = RTL8365MB_EXT_PORT_MODE_SGMII
+ << RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(id);
+ ret = regmap_update_bits(priv->map,
+ RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(id),
+ RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(id),
+ val);
+ if (ret)
+ return ret;
+
+ /* Take the SerDes out of reset. The vendor driver does this only
+ * after the SerDes mux and the interface mode are configured.
+ */
+ ret = rtl8365mb_sds_write(priv, RTL8365MB_SDS_REG_RESET,
+ RTL8365MB_SDS_RESET_DEASSERT);
+ if (ret)
+ return ret;
+
+ /* Reset the SerDes data path and resync its PLL, mirroring what the
+ * vendor firmware does right after deasserting the SerDes reset.
+ * This flushes the FIFOs and ensures a clean state for the link,
+ * preventing silent drops and CRC errors.
+ */
+ ret = rtl8365mb_sds_write(priv, RTL8365MB_SDS_REG_BMCR,
+ RTL8365MB_SDS_BMCR_DPRST_PHASE1);
+ if (ret)
+ return ret;
+
+ ret = rtl8365mb_sds_write(priv, RTL8365MB_SDS_REG_BMCR,
+ RTL8365MB_SDS_BMCR_DPRST_PHASE2);
+ if (ret)
+ return ret;
+
+ /* Keep SGMII in-band autonegotiation disabled: the link parameters are
+ * forced from rtl8365mb_pcs_link_up() instead.
+ */
+ ret = rtl8365mb_sds_read(priv, RTL8365MB_SDS_REG_NWAY, &val);
+ if (ret)
+ return ret;
+
+ val &= ~RTL8365MB_SDS_NWAY_EN_MASK;
+ val |= RTL8365MB_SDS_NWAY_RESTART_MASK;
+
+ return rtl8365mb_sds_write(priv, RTL8365MB_SDS_REG_NWAY, val);
+}
+
+static bool rtl8365mb_interface_is_serdes(phy_interface_t interface)
+{
+ return interface == PHY_INTERFACE_MODE_SGMII;
+}
+
+static unsigned int rtl8365mb_pcs_inband_caps(struct phylink_pcs *pcs,
+ phy_interface_t interface)
+{
+ /* In-band autonegotiation is not implemented; the link is always
+ * forced. Report that to phylink so that it never selects an
+ * in-band-enabled negotiation mode for this PCS.
+ */
+ return LINK_INBAND_DISABLE;
+}
+
+static void rtl8365mb_pcs_get_state(struct phylink_pcs *pcs,
+ unsigned int neg_mode,
+ struct phylink_link_state *state)
+{
+ struct rtl8365mb *mb = pcs_to_rtl8365mb(pcs);
+ struct realtek_priv *priv = mb->priv;
+ u16 status;
+ u32 val;
+ int ret;
+
+ /* In-band autonegotiation is not implemented, so the link parameters are
+ * forced from rtl8365mb_pcs_link_up(). The real link state must still be
+ * read from the SerDes itself: the embedded DW8051 microcontroller that
+ * the vendor firmware uses to poll the SerDes is kept disabled (see
+ * rtl8365mb_pcs_config()), so the link status register can be read
+ * directly through the SDS_INDACS window without racing the auto-poll.
+ */
+ ret = rtl8365mb_sds_read(priv, RTL8365MB_SDS_REG_LINK_STATUS, &status);
+ if (ret) {
+ state->link = false;
+ return;
+ }
+
+ state->link = !!(status & RTL8365MB_SDS_LINK_STATUS_LINK_MASK);
+ state->an_complete = state->link;
+ if (!state->link)
+ return;
+
+ /* The speed and duplex are forced; read them back from the values
+ * programmed into the SerDes MISC register.
+ */
+ ret = regmap_read(priv->map, RTL8365MB_SDS_MISC_REG, &val);
+ if (ret) {
+ state->link = false;
+ return;
+ }
+
+ state->duplex = (val & RTL8365MB_SDS_MISC_SGMII_FDUP_MASK) ?
+ DUPLEX_FULL : DUPLEX_HALF;
+
+ switch (FIELD_GET(RTL8365MB_SDS_MISC_SGMII_SPD_MASK, val)) {
+ case RTL8365MB_PORT_SPEED_1000M:
+ state->speed = SPEED_1000;
+ break;
+ case RTL8365MB_PORT_SPEED_100M:
+ state->speed = SPEED_100;
+ break;
+ case RTL8365MB_PORT_SPEED_10M:
+ state->speed = SPEED_10;
+ break;
+ }
+}
+
+static void rtl8365mb_pcs_link_up(struct phylink_pcs *pcs,
+ unsigned int neg_mode,
+ phy_interface_t interface, int speed,
+ int duplex)
+{
+ struct rtl8365mb *mb = pcs_to_rtl8365mb(pcs);
+ struct realtek_priv *priv = mb->priv;
+ u32 mask = RTL8365MB_SDS_MISC_SGMII_FDUP_MASK |
+ RTL8365MB_SDS_MISC_SGMII_LINK_MASK |
+ RTL8365MB_SDS_MISC_SGMII_SPD_MASK;
+ u32 val = RTL8365MB_SDS_MISC_SGMII_LINK_MASK;
+ u32 r_speed;
+ int ret;
+
+ if (speed == SPEED_1000) {
+ r_speed = RTL8365MB_PORT_SPEED_1000M;
+ } else if (speed == SPEED_100) {
+ r_speed = RTL8365MB_PORT_SPEED_100M;
+ } else if (speed == SPEED_10) {
+ r_speed = RTL8365MB_PORT_SPEED_10M;
+ } else {
+ dev_err(priv->dev, "unsupported SerDes speed %s\n",
+ phy_speed_to_str(speed));
+ return;
+ }
+
+ val |= FIELD_PREP(RTL8365MB_SDS_MISC_SGMII_SPD_MASK, r_speed);
+
+ if (duplex == DUPLEX_FULL)
+ val |= RTL8365MB_SDS_MISC_SGMII_FDUP_MASK;
+
+ /* pcs_link_up() carries no pause information, so the SerDes flow
+ * control bits are programmed together with the MAC external interface
+ * force from rtl8365mb_phylink_mac_link_up(), where the resolved pause
+ * modes are known.
+ */
+ ret = regmap_update_bits(priv->map, RTL8365MB_SDS_MISC_REG, mask, val);
+ if (ret) {
+ dev_err(priv->dev, "failed to force SerDes link: %pe\n",
+ ERR_PTR(ret));
+ return;
+ }
+}
+
+static const struct phylink_pcs_ops rtl8365mb_pcs_ops = {
+ .pcs_inband_caps = rtl8365mb_pcs_inband_caps,
+ .pcs_config = rtl8365mb_pcs_config,
+ .pcs_get_state = rtl8365mb_pcs_get_state,
+ .pcs_link_up = rtl8365mb_pcs_link_up,
+};
+
static int rtl8365mb_ext_config_forcemode(struct realtek_priv *priv, int port,
bool link, int speed, int duplex,
bool tx_pause, bool rx_pause)
@@ -1118,6 +1547,8 @@ static void rtl8365mb_phylink_get_caps(struct dsa_switch *ds, int port,
{
const struct rtl8365mb_extint *extint =
rtl8365mb_get_port_extint(ds->priv, port);
+ struct realtek_priv *priv = ds->priv;
+ struct rtl8365mb *mb = priv->chip_data;
config->mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE |
MAC_10 | MAC_100 | MAC_1000FD;
@@ -1141,6 +1572,25 @@ static void rtl8365mb_phylink_get_caps(struct dsa_switch *ds, int port,
if (extint->supported_interfaces & RTL8365MB_PHY_INTERFACE_MODE_RGMII)
phy_interface_set_rgmii(config->supported_interfaces);
+
+ if (extint->supported_interfaces & RTL8365MB_PHY_INTERFACE_MODE_SGMII &&
+ mb->sds_supported)
+ __set_bit(PHY_INTERFACE_MODE_SGMII,
+ config->supported_interfaces);
+}
+
+static struct phylink_pcs *
+rtl8365mb_phylink_mac_select_pcs(struct phylink_config *config,
+ phy_interface_t interface)
+{
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct realtek_priv *priv = dp->ds->priv;
+ struct rtl8365mb *mb = priv->chip_data;
+
+ if (rtl8365mb_interface_is_serdes(interface))
+ return &mb->pcs;
+
+ return NULL;
}
static void rtl8365mb_phylink_mac_config(struct phylink_config *config,
@@ -1168,6 +1618,12 @@ static void rtl8365mb_phylink_mac_config(struct phylink_config *config,
return;
}
+ /* SGMII is handled by the SerDes PCS, configured through the
+ * phylink_pcs ops, so there is nothing to do here for it.
+ */
+ if (rtl8365mb_interface_is_serdes(state->interface))
+ return;
+
/* TODO: Implement MII and RMII modes, which the RTL8365MB-VC also
* supports
*/
@@ -1188,7 +1644,13 @@ static void rtl8365mb_phylink_mac_link_down(struct phylink_config *config,
p = &mb->ports[port];
cancel_delayed_work_sync(&p->mib_work);
- if (phy_interface_mode_is_rgmii(interface)) {
+ /* phylink has no pcs_link_down callback, so on the SerDes path only the
+ * MAC external interface force is reset here. Clearing the MAC force is
+ * enough to bring the link down; the SerDes keeps presenting its last
+ * forced state until the next pcs_link_up() reprograms it.
+ */
+ if (phy_interface_mode_is_rgmii(interface) ||
+ rtl8365mb_interface_is_serdes(interface)) {
ret = rtl8365mb_ext_config_forcemode(priv, port, false, 0, 0,
false, false);
if (ret)
@@ -1218,14 +1680,46 @@ static void rtl8365mb_phylink_mac_link_up(struct phylink_config *config,
p = &mb->ports[port];
schedule_delayed_work(&p->mib_work, 0);
- if (phy_interface_mode_is_rgmii(interface)) {
+ /* The SerDes forced link state is programmed by the PCS in
+ * rtl8365mb_pcs_link_up(); here only the MAC external interface force
+ * is configured, for both RGMII and SerDes.
+ */
+ if (phy_interface_mode_is_rgmii(interface) ||
+ rtl8365mb_interface_is_serdes(interface)) {
ret = rtl8365mb_ext_config_forcemode(priv, port, true, speed,
duplex, tx_pause,
rx_pause);
- if (ret)
+ if (ret) {
dev_err(priv->dev,
"failed to force mode on port %d: %pe\n", port,
ERR_PTR(ret));
+ return;
+ }
+
+ /* The SerDes has its own pause controls; program them from
+ * the resolved pause modes, as the vendor driver does when
+ * forcing the link on a SerDes external interface. This is
+ * done here rather than in rtl8365mb_pcs_link_up() because
+ * pcs_link_up() carries no pause information.
+ */
+ if (rtl8365mb_interface_is_serdes(interface)) {
+ u32 val = 0;
+
+ if (tx_pause)
+ val |= RTL8365MB_SDS_MISC_SGMII_TXFC_MASK;
+ if (rx_pause)
+ val |= RTL8365MB_SDS_MISC_SGMII_RXFC_MASK;
+
+ ret = regmap_update_bits(priv->map,
+ RTL8365MB_SDS_MISC_REG,
+ RTL8365MB_SDS_MISC_SGMII_TXFC_MASK |
+ RTL8365MB_SDS_MISC_SGMII_RXFC_MASK,
+ val);
+ if (ret)
+ dev_err(priv->dev,
+ "failed to force SerDes pause modes on port %d: %pe\n",
+ port, ERR_PTR(ret));
+ }
return;
}
@@ -2419,6 +2913,14 @@ static int rtl8365mb_setup(struct dsa_switch *ds)
mb = priv->chip_data;
cpu = &mb->cpu;
+ mb->pcs.ops = &rtl8365mb_pcs_ops;
+
+ /* The SerDes has no link interrupt wired up, so phylink must poll the
+ * PCS for link changes when it tracks the link through pcs_get_state()
+ * (in-band mode with autonegotiation disabled).
+ */
+ mb->pcs.poll = true;
+
ret = rtl8365mb_reset_chip(priv);
if (ret) {
dev_err(priv->dev, "failed to reset chip: %pe\n",
@@ -2426,6 +2928,13 @@ static int rtl8365mb_setup(struct dsa_switch *ds)
goto out_error;
}
+ ret = rtl8365mb_sds_probe_option(priv);
+ if (ret) {
+ dev_err(priv->dev, "failed to probe SerDes chip option: %pe\n",
+ ERR_PTR(ret));
+ goto out_error;
+ }
+
/* Configure switch to vendor-defined initial state */
ret = rtl8365mb_switch_init(priv);
if (ret) {
@@ -2658,6 +3167,7 @@ static int rtl8365mb_detect(struct realtek_priv *priv)
}
static const struct phylink_mac_ops rtl8365mb_phylink_mac_ops = {
+ .mac_select_pcs = rtl8365mb_phylink_mac_select_pcs,
.mac_config = rtl8365mb_phylink_mac_config,
.mac_link_down = rtl8365mb_phylink_mac_link_down,
.mac_link_up = rtl8365mb_phylink_mac_link_up,
base-commit: d6e81529749190123aa0040626c7e5dbc20fdc9a
--
2.55.0
^ permalink raw reply related
* [PATCH net-next v4 0/2] net: dsa: realtek: rtl8365mb: add SGMII/HSGMII support for RTL8367S
From: Johan Alvarado @ 2026-07-02 20:47 UTC (permalink / raw)
To: linusw, alsi, andrew, olteanv, kuba, davem, edumazet, pabeni,
linux
Cc: luizluca, maxime.chevallier, namiltd, netdev, linux-kernel,
contact
The RTL8367S is a 5+2 port switch from the same family as the
RTL8365MB-VC already supported by this driver. Its chip info table
entry declares SGMII and HSGMII on external interface 1, but the
driver so far only implements RGMII, leaving boards that wire the
switch to the CPU over the SerDes without a working CPU port.
This series implements both modes. The configuration sequence and the
SerDes tuning parameters are derived from the GPL-licensed Realtek
rtl8367c vendor driver, as distributed in the Mercusys MR80X GPL code
drop, and cross-checked against the real register sequence captured at
runtime by chainloading a custom U-Boot ahead of the stock firmware
and logging the live SerDes accesses on hardware.
The vendor driver brings up the SerDes by loading firmware into the
switch's embedded DW8051 microcontroller. Analysis of that firmware
(by Luiz Angelo Daros de Luca) showed it only performs a SerDes
data-path reset right after the SerDes reset is deasserted, and then
runs a link-polling loop that writes the external interface force
registers -- duplicating, and racing with, the link management phylink
already performs. This series therefore keeps the DW8051 disabled and
performs the one necessary action (the data-path reset via the SerDes
BMCR register) directly in the driver, avoiding both the race and a
dependency on a redistributable firmware blob.
The SerDes is modelled as a phylink PCS: mac_select_pcs() hands the
SerDes interfaces to a phylink_pcs whose pcs_config()/pcs_link_up()
ops own the SerDes register sequence, keeping it out of the MAC
operations. In-band autonegotiation is not implemented; the link is
forced (fixed-link or conventional PHY), as for RGMII, and the PCS
reports this to phylink through pcs_inband_caps().
Patch 1 adds the SerDes indirect access helpers, the PCS and SGMII
(1 Gbps) support. Patch 2 extends the PCS to HSGMII (2.5 Gbps), which
phylink represents as 2500base-x.
Tested on a Mercusys MR80X v2.20 (RTL8367S wired to the SoC over the
SerDes), in both SGMII and HSGMII modes with a fixed-link device tree
description: link bring-up verified across cold boots, warm reboots,
module reloads and link down/up cycles, with sustained traffic and no
CRC/symbol errors. The HSGMII link is confirmed running at 2.5G at the
register level (SoC uniphy mode and gmac clocks); per-direction
throughput could not be pushed past ~1 Gbps on this board because the
SoC side is driven by the IPQ5018 SSDK and the user-facing PHY is 1G,
so full 2.5G line-rate throughput remains unverified on my hardware.
The RTL8367SB also declares SGMII and HSGMII in its chip info entry
and therefore gains both modes as well. The vendor driver drives the
two chips through the same code path, keyed only on the chip option
register (both report chip id 0x6367), so this is expected to work
there too, but I have no RTL8367SB hardware to confirm it.
Signed-off-by: Johan Alvarado <contact@c127.dev>
---
v4:
- Drop the chip model name from the driver's NOTE comment; which
interfaces a given chip exposes is described by its chip_info entry,
not the file header. Pointed out by Luiz Angelo Daros de Luca.
- Build the SerDes BMCR data-path-reset values from the standard
BMCR_ANENABLE | BMCR_ISOLATE bits instead of a bare magic number, so
the meaning is in the code rather than only in a comment. Pointed
out by Luiz Angelo Daros de Luca.
- Use a temporary for the DIGITAL_INTERFACE_SELECT value instead of
wrapping the expression across the regmap_update_bits() arguments.
Pointed out by Luiz Angelo Daros de Luca.
- Reject the untested SerDes tuning variant. The vendor driver keeps
two sets of SerDes tuning parameters and selects between them based
on the chip option register (0x13C1); the tables in this series are
the variant for a non-zero option, which is what the RTL8367S parts
seen so far report. The option is probed once at setup and the
SerDes interface modes are only advertised to phylink when the
tuning parameters match, so an unsupported variant fails at phylink
validation time instead of when configuring the link. Thanks to
Luiz Angelo Daros de Luca for pointing out the conditional.
- Express the external interface line rate bypass bit through a
parametric macro keyed on the port number (with port 5 as the base),
instead of an open-coded BIT(interface id) that only matched by
coincidence; other RTL8367 families index this register differently.
Suggested by Luiz Angelo Daros de Luca.
- Drop the arbitrary usleep_range() after each SerDes indirect access.
SerDes writes are now fire-and-forget and reads poll the self-clearing
BUSY bit with regmap_read_poll_timeout(), matching the vendor driver,
which never sleeps. On the MR80X the BUSY bit is never even observed
set: the access completes within the register transaction. Pointed out
by Luiz Angelo Daros de Luca; poll approach suggested by Mieczyslaw
Nalewaj.
- Drop the always-zero SerDes index argument from the SerDes indirect
access helpers, along with the INDACS command index field whose
width was questioned during review; this chip has a single SerDes
block reachable through this window, so the index served no purpose.
Raised by Luiz Angelo Daros de Luca.
- Stop hardcoding external interface 1 with an early -EOPNOTSUPP in
the SerDes configuration path. The SerDes interface modes are now
advertised in phylink_get_caps() from the chip_info
supported_interfaces, and mac_select_pcs() returns the PCS only for
those modes. Pointed out by Luiz Angelo Daros de Luca.
- Keep the new register definitions as raw hex masks, matching the
prevailing style of the file. A file-wide GENMASK/BIT conversion,
raised by Luiz Angelo Daros de Luca during review, is left for a
separate cleanup patch so this series stays focused on the feature.
- Convert the SerDes path to a phylink_pcs, as suggested by Maxime
Chevallier. The SGMII/HSGMII SerDes handling now lives in
pcs_config()/pcs_get_state()/pcs_link_up() selected via
mac_select_pcs(), instead of being driven from the MAC
mac_config()/mac_link_up()/mac_link_down() operations. This
separates the MAC and SerDes layers and makes future in-band
autonegotiation an additive change. No functional change intended
for the forced-link path; retested on the MR80X v2.20. In-band
autonegotiation remains unimplemented and is left for a follow-up,
once hardware is available to validate it.
- Implement pcs_inband_caps(), returning LINK_INBAND_DISABLE so that
phylink knows this PCS cannot do in-band autonegotiation and never
selects an in-band-enabled negotiation mode for it. pcs_config()
rejects PHYLINK_PCS_NEG_INBAND_ENABLED with -EOPNOTSUPP instead of
the previous warn-and-force.
- Program the SerDes pause controls in SDS_MISC from the resolved
pause modes when forcing the MAC external interface, as the vendor
driver does, instead of leaving whatever state the boot firmware
left there. Done in mac_link_up() because pcs_link_up() carries no
pause information.
- Set the PCS poll flag: the SerDes has no link interrupt wired up,
so phylink must poll pcs_get_state() when it tracks the link
through the PCS (in-band mode with autonegotiation disabled).
- Report link down from pcs_get_state() if reading back the forced
speed/duplex fails, rather than reporting link up with a stale
state.
- Reword the misleading "disable in-band aneg" comment.
v3: https://lore.kernel.org/netdev/20260613232136.24246-1-contact@c127.dev/
- Drop the DW8051 firmware loading entirely. Analysis of the vendor
firmware showed it only duplicates the link management phylink
already does; the one needed action (SerDes data-path reset via
the BMCR register) is now performed directly in the driver, with
the DW8051 kept disabled. This removes the dependency on the
rtl8367s-sgmii.bin firmware blob, which could not be redistributed
via linux-firmware (the GPL vendor source ships it as a byte array
without the corresponding microcode source). Thanks to Luiz Angelo
Daros de Luca for the firmware analysis.
v2: https://lore.kernel.org/netdev/0100019eb0b1822e-ffc5626c-1b9f-4c8a-8a1a-759a9e665f4f-000000@email.amazonses.com/
- No code changes; resend because the SMTP provider used for v1
corrupted the mails and patch 1/2 never reached the list.
v1: https://lore.kernel.org/netdev/aebccaad-eca3-4ea4-99dd-ae7edbc8981b@smtp-relay.sendinblue.com/
Johan Alvarado (2):
net: dsa: realtek: rtl8365mb: add SGMII support for RTL8367S
net: dsa: realtek: rtl8365mb: add HSGMII support for RTL8367S
drivers/net/dsa/realtek/rtl8365mb_main.c | 563 ++++++++++++++++++++++-
1 file changed, 558 insertions(+), 5 deletions(-)
base-commit: d6e81529749190123aa0040626c7e5dbc20fdc9a
--
2.55.0
^ permalink raw reply
* Re: [PATCHv2 net-next] net: dsa: qca8k: fall back to ethernet-ports node name for LEDs
From: Andrew Lunn @ 2026-07-02 20:43 UTC (permalink / raw)
To: Rosen Penev
Cc: netdev, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, open list
In-Reply-To: <20260630015137.1591152-1-rosenp@gmail.com>
On Mon, Jun 29, 2026 at 06:51:37PM -0700, Rosen Penev wrote:
> The device tree binding allows both "ports" and "ethernet-ports" as
> the container node name. Try "ethernet-ports" when "ports" is absent
> so that newer DTBs with the preferred name work.
>
> This matches the handling already present in qca8k-8xxx.c
>
> Assisted-by: opencode:big-pickle
> Signed-off-by: Rosen Penev <rosenp@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Andrew
^ permalink raw reply
* Re: [PATCH v2 1/2] net: fman: move IRQ registration after init to prevent NULL deref and UAF
From: Andrew Lunn @ 2026-07-02 20:42 UTC (permalink / raw)
To: ZhaoJinming
Cc: pabeni, andrew+netdev, davem, edumazet, horms, kuba, linux-kernel,
madalin.bucur, netdev, sean.anderson
In-Reply-To: <20260702092815.1206704-2-zhaojinming@uniontech.com>
On Thu, Jul 02, 2026 at 05:28:15PM +0800, ZhaoJinming wrote:
> read_dts_node() registers shared interrupt handlers via
> devm_request_irq() with fman as dev_id. Two bugs exist in the
> current code:
Please start a new thread with a new version of the patch. The CI
system just thinks this is part of the discussion, not something it
must test.
Please also read:
https://www.kernel.org/doc/html/latest/process/maintainer-netdev.html
and set the Subject: line correctly, etc.
Andrew
^ permalink raw reply
* [PATCH net] af_unix: fix listen() succeeding on sockets in the wrong state
From: John Ericson @ 2026-07-02 20:20 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Kuniyuki Iwashima
Cc: John Ericson, Simon Horman, Christian Brauner, David Rheinsberg,
Cong Wang, John Ericson, Sergei Zimmerman, netdev, linux-kernel
From: John Ericson <mail@johnericson.me>
Commit fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for
reaped sk->sk_peer_pid") inserted a `prepare_peercred()` call between
`err = -EINVAL` and the socket-state check in `unix_listen()`. Since
`prepare_peercred()` leaves `err` at 0 on success, `listen()` on an
AF_UNIX socket that is not in `TCP_CLOSE` or `TCP_LISTEN` state (e.g.
one that is already connected) now silently returns success without
doing anything, instead of failing with `EINVAL` as it did before.
To fix this bug, and avoid such bugs in the future, switch to a style
where `err = -E...;` instead happens right before the `goto`. (`err =
other_function(...);` is not changed.) Then there is no spooky-action-
at-a-distance between the `err` initialization and the `goto`, something
which is easier to slip by code review.
Fixes: fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for reaped sk->sk_peer_pid")
Assisted-by: Claude:claude-fable-5
Signed-off-by: John Ericson <mail@johnericson.me>
---
net/unix/af_unix.c | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index f7a9d55eee8a..7878b27bbaf8 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -813,18 +813,22 @@ static int unix_listen(struct socket *sock, int backlog)
struct unix_sock *u = unix_sk(sk);
struct unix_peercred peercred = {};
- err = -EOPNOTSUPP;
- if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET)
+ if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET) {
+ err = -EOPNOTSUPP;
goto out; /* Only stream/seqpacket sockets accept */
- err = -EINVAL;
- if (!READ_ONCE(u->addr))
+ }
+ if (!READ_ONCE(u->addr)) {
+ err = -EINVAL;
goto out; /* No listens on an unbound socket */
+ }
err = prepare_peercred(&peercred);
if (err)
goto out;
unix_state_lock(sk);
- if (sk->sk_state != TCP_CLOSE && sk->sk_state != TCP_LISTEN)
+ if (sk->sk_state != TCP_CLOSE && sk->sk_state != TCP_LISTEN) {
+ err = -EINVAL;
goto out_unlock;
+ }
if (backlog > sk->sk_max_ack_backlog)
wake_up_interruptible_all(&u->peer_wait);
sk->sk_max_ack_backlog = backlog;
--
2.54.0
^ permalink raw reply related
* Re: [PATCH net 1/2] vsock/virtio: collapse receive queue under memory pressure
From: Bobby Eshleman @ 2026-07-02 20:09 UTC (permalink / raw)
To: Stefano Garzarella
Cc: netdev, Jason Wang, Jakub Kicinski, Paolo Abeni,
Michael S. Tsirkin, kvm, virtualization, Xuan Zhuo, Eric Dumazet,
Simon Horman, linux-kernel, Stefan Hajnoczi, David S. Miller,
Eugenio Pérez, stable, Brien Oberstein
In-Reply-To: <akYl38_9Y4ydXuqE@sgarzare-redhat>
On Thu, Jul 02, 2026 at 10:56:04AM +0200, Stefano Garzarella wrote:
> On Wed, Jul 01, 2026 at 09:34:35AM -0700, Bobby Eshleman wrote:
> > On Fri, Jun 26, 2026 at 03:48:22PM +0200, Stefano Garzarella wrote:
>
> [...]
>
> > > +out:
> > > + if (new_skb)
> > > + __skb_queue_tail(&new_queue, new_skb);
> > > +
> > > + skb_queue_splice(&new_queue, &vvs->rx_queue);
> >
> > I think the new skbs will also need skb_set_owner_sk_safe(skb, sk)
> > when adding to rx_queue?
>
> IIRC we added it in the rx path, mainily for loopback to pass the ownership
> from the tx socket to the rx socket, but here we are already in the rx path,
> so the skb will never leave this socket.
>
Ah that's right, I stand corrected. There is no sender to leak in this
case.
> Maybe it's necessary for the eBPF path?
Looking through sockmap, I don't think it depends on skb->sk being
non-null either (it reassigns owner to the redirect socket anyway using
skb_set_owner_r()).
Sorry for the false alarm. LGTM.
Reviewed-by: Bobby Eshleman <bobbyeshleman@meta.com>
^ permalink raw reply
* Re: [PATCH net] net/tls: Consume empty data records in tls_sw_read_sock()
From: Chuck Lever @ 2026-07-02 19:52 UTC (permalink / raw)
To: Sabrina Dubroca
Cc: john.fastabend, Jakub Kicinski, davem, edumazet, Paolo Abeni,
Simon Horman, netdev
In-Reply-To: <akaoXcfamBp8_mYe@krikkit>
On Thu, Jul 2, 2026, at 2:05 PM, Sabrina Dubroca wrote:
> 2026-06-30, 15:15:51 -0400, Chuck Lever wrote:
>> A peer may send a zero-length TLS application_data record; TLS 1.3
>> explicitly permits these as a traffic-analysis countermeasure (RFC
>> 8446, Section 5.1). After decryption such a record has full_len ==
>> 0. tls_sw_read_sock() hands it to the read_actor, which has no
>> payload to consume and returns zero. The loop treats a zero return
>> as backpressure (used <= 0), requeues the skb at the head of
>> rx_list, and stops. rx_list is serviced head-first on the next
>> call, so the empty record is dequeued, fails the same way, and is
>> requeued again; every later record on the connection is blocked
>> behind it.
>>
>> tls_sw_recvmsg() does not stall on this: a zero-length data record
>> copies nothing and falls through to consume_skb(). Mirror that in
>> the read_sock() path by recognizing an empty data record before
>> the actor runs, consuming it, and continuing.
>>
>> Fixes: 662fbcec32f4 ("net/tls: implement ->read_sock()")
>> Signed-off-by: Chuck Lever <cel@kernel.org>
>> ---
>> net/tls/tls_sw.c | 11 +++++++++++
>> 1 file changed, 11 insertions(+)
>
> Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
>
> I think tls_sw_splice_read() suffers from a similar issue (returning 0
> even though more data may be available). Sashiko agrees, and also
> found a few more pre-existing issues.
Do you want a v2 series with those issues addressed?
--
Chuck Lever
^ permalink raw reply
* Re: [PATCH net-next v7 1/2] dinghai: add ZTE network driver support
From: Julian Braha @ 2026-07-02 19:35 UTC (permalink / raw)
To: han.junyang, andrew+netdev, davem, edumazet, kuba, pabeni, horms
Cc: linux-kernel, netdev, ran.ming, han.chengfei, zhang.yanze
In-Reply-To: <20260630111300830lukBczfNSgWE5wt6qR95k@zte.com.cn>
Hi Junyang,
On 6/30/26 04:13, han.junyang@zte.com.cn wrote:
> +if NET_VENDOR_ZTE
> +
> +source "drivers/net/ethernet/zte/dinghai/Kconfig"
> +
> +endif # NET_VENDOR_ZTE
> +++ b/drivers/net/ethernet/zte/dinghai/Kconfig
> @@ -0,0 +1,34 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +#
> +# ZTE DingHai Ethernet driver configuration
> +#
> +
> +config DINGHAI
> + bool "ZTE DingHai Ethernet driver"
> + depends on NET_VENDOR_ZTE && PCI
DINGHAI has a duplicate dependency on NET_VENDOR_ZTE since you put the
import for the file inside 'if NET_VENDOR_ZTE..endif' and then also gave
it the 'depends on NET_VENDOR_ZTE'.
- Julian Braha
^ permalink raw reply
* Re: [PATCH net-next v6 12/15] onsemi: s2500: Add driver support for TS2500 MAC-PHY
From: Julian Braha @ 2026-07-02 19:29 UTC (permalink / raw)
To: Selvamani.Rajagopal, Andrew Lunn, Piergiorgio Beruto,
Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Andrew Lunn, Parthiban Veerasooran,
Richard Cochran, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Simon Horman, Jonathan Corbet, Shuah Khan
Cc: netdev, linux-kernel, devicetree, linux-doc, Jerry Ray
In-Reply-To: <20260629-s2500-mac-phy-support-v6-12-18ce79500371@onsemi.com>
Hi Selvamani,
On 6/29/26 18:23, Selvamani Rajagopal via B4 Relay wrote:
> +if NET_VENDOR_ONSEMI
> +
> +source "drivers/net/ethernet/onsemi/s2500/Kconfig"
> +
> +endif # NET_VENDOR_ONSEMI
> +
> diff --git a/drivers/net/ethernet/onsemi/Makefile b/drivers/net/ethernet/onsemi/Makefile
> new file mode 100644
> index 000000000000..f3d4eb154313
> --- /dev/null
> +++ b/drivers/net/ethernet/onsemi/Makefile
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +#
> +# Makefile for the onsemi network device drivers.
> +#
> +
> +obj-$(CONFIG_S2500_MACPHY) += s2500/
> +
> diff --git a/drivers/net/ethernet/onsemi/s2500/Kconfig b/drivers/net/ethernet/onsemi/s2500/Kconfig
> new file mode 100644
> index 000000000000..f2e8d5d1429d
> --- /dev/null
> +++ b/drivers/net/ethernet/onsemi/s2500/Kconfig
> @@ -0,0 +1,21 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +#
> +# onsemi S2500 Driver Support
> +#
> +
> +if NET_VENDOR_ONSEMI
> +
> +config S2500_MACPHY
> + tristate "S2500 support"
> + depends on SPI
> + select NCN26000_PHY
> + select OA_TC6
> + help
> + Support for the onsemi TS2500 MACPHY Ethernet chip.
> + It works under the framework that conform to OPEN Alliance
> + 10BASE-T1x Serial Interface specification.
> +
> + To compile this driver as a module, choose M here. The module will be
> + called s2500.
> +
> +endif # NET_VENDOR_ONSEMI
S2500_MACPHY still has that duplicate dependency from being inside two
of these:
'if NET_VENDOR_ONSEMI..endif'
And I already pointed it out on v5:
https://lore.kernel.org/all/90f84945-e83f-40a8-8d9e-a477c45579e9@gmail.com/
:(
- Julian Braha
^ permalink raw reply
* Re: [PATCH ipsec] xfrm: policy: use hlist_del_init_rcu in xfrm_hash_rebuild to avoid bydst poison
From: Florian Westphal @ 2026-07-02 19:19 UTC (permalink / raw)
To: Xiang Mei (Microsoft)
Cc: steffen.klassert, herbert, davem, netdev, horms, edumazet, kuba,
pabeni, AutonomousCodeSecurity, tgopinath, kys
In-Reply-To: <20260702185805.615241-1-xmei5@asu.edu>
Xiang Mei (Microsoft) <xmei5@asu.edu> wrote:
> xfrm_hash_rebuild() unlinks each policy from its bydst chain with
> hlist_del_rcu() and re-inserts it. For an inexact policy the re-insert goes
> through xfrm_policy_inexact_insert(), which can fail on a GFP_ATOMIC
> allocation; on failure the error path only WARN_ONCE()s and continues, so the
> policy is left with a poisoned bydst node (LIST_POISON2). The next rebuild
> calls hlist_del_rcu() on that node again, dereferences the poison, and takes a
> general protection fault.
>
> Use hlist_del_init_rcu() instead, so a failed-reinsert node is left unhashed
> (pprev == NULL) rather than poisoned. The next rebuild's hlist_del_init_rcu()
> is then a no-op for it, and the non-failing case is unchanged.
>
> The reinsert allocation is GFP_ATOMIC (it runs under xfrm_policy_lock), so in
> practice this is only reached under memory pressure; the crash below was
> reproduced deterministically by forcing that allocation to fail with fault
> injection (failslab).
>
> Crash:
> Oops: general protection fault, probably for non-canonical address
> 0xfbd59c0000000024: 0000 [#1] SMP KASAN NOPTI
> KASAN: maybe wild-memory-access in range [0xdead000000000120-0xdead000000000127]
> ...
> Workqueue: events xfrm_hash_rebuild
> RIP: 0010:xfrm_hash_rebuild+0x5b3/0x1190
> RAX: dead000000000122 (LIST_POISON2 + offset)
> ...
> Call Trace:
> hlist_del_rcu (include/linux/rculist.h:599)
> xfrm_hash_rebuild (net/xfrm/xfrm_policy.c:1365)
> process_one_work (kernel/workqueue.c:3322)
> worker_thread (kernel/workqueue.c:3486)
> kthread (kernel/kthread.c:436)
> ret_from_fork (arch/x86/kernel/process.c:158)
> ret_from_fork_asm (arch/x86/entry/entry_64.S:245)
> ...
> Kernel panic - not syncing: Fatal exception in interrupt
>
> Fixes: 563d5ca93e88 ("xfrm: switch migrate to xfrm_policy_lookup_bytype")
> Reported-by: AutonomousCodeSecurity@microsoft.com
> Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
> ---
> net/xfrm/xfrm_policy.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
> index 7ef861a0e823..2612a405542b 100644
> --- a/net/xfrm/xfrm_policy.c
> +++ b/net/xfrm/xfrm_policy.c
> @@ -1362,7 +1362,7 @@ static void xfrm_hash_rebuild(struct work_struct *work)
> if (xfrm_policy_is_dead_or_sk(policy))
> continue;
>
> - hlist_del_rcu(&policy->bydst);
> + hlist_del_init_rcu(&policy->bydst);
This patch is dubious. I looks to me as if it papers over the
actual bug.
Why is there a memory allocation error?
The first loop -- before unlink -- is supposed to preallocate the new
bins and chain heads.
This is also why there is a WARN. No memory allocations are supposed to
occur after the hlist_del_rcu(), there is supposed to be a guarantee
that the insertion succeeds.
^ permalink raw reply
* Re: [PATCH v9 00/14] firmware: qcom: Add OP-TEE PAS service support
From: Mathieu Poirier @ 2026-07-02 19:14 UTC (permalink / raw)
To: Sumit Garg
Cc: andersson, konradybcio, linux-arm-msm, devicetree, dri-devel,
freedreno, linux-media, netdev, linux-wireless, ath12k,
linux-remoteproc, robh, krzk+dt, conor+dt, robin.clark, sean,
akhilpo, lumag, abhinav.kumar, jesszhan0024, marijn.suijten,
airlied, simona, vikash.garodia, bod, mchehab, elder,
andrew+netdev, davem, edumazet, kuba, pabeni, jjohnson,
trilokkumar.soni, mukesh.ojha, pavan.kondeti, jorge.ramirez,
tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
jenswi, op-tee, apurupa, skare, linux-kernel, Sumit Garg
In-Reply-To: <20260702115835.167602-1-sumit.garg@kernel.org>
Hey Sumit - nice hearing from you...
Is there some kind of overarching design harminisation between what
you are proposing here and what Arnaud posted back in April [1] ?
[1]. https://lists.trustedfirmware.org/archives/list/op-tee@lists.trustedfirmware.org/thread/VMKTRATYUFWL2TP7NHN5KJ37MSVZZMPK/
On Thu, 2 Jul 2026 at 05:59, Sumit Garg <sumit.garg@kernel.org> wrote:
>
> From: Sumit Garg <sumit.garg@oss.qualcomm.com>
>
> Qcom platforms has the legacy of using non-standard SCM calls
> splintered over the various kernel drivers. These SCM calls aren't
> compliant with the standard SMC calling conventions which is a
> prerequisite to enable migration to the FF-A specifications from Arm.
>
> OP-TEE as an alternative trusted OS to Qualcomm TEE (QTEE) can't
> support these non-standard SCM calls. And even for newer architectures
> using S-EL2 with Hafnium support, QTEE won't be able to support SCM
> calls either with FF-A requirements coming in. And with both OP-TEE
> and QTEE drivers well integrated in the TEE subsystem, it makes further
> sense to reuse the TEE bus client drivers infrastructure.
>
> The added benefit of TEE bus infrastructure is that there is support
> for discoverable/enumerable services. With that client drivers don't
> have to manually invoke a special SCM call to know the service status.
>
> So enable the generic Peripheral Authentication Service (PAS) provided
> by the firmware. It acts as the common layer with different TZ
> backends plugged in whether it's an SCM implementation or a proper
> TEE bus based PAS service implementation.
>
> The TEE PAS service ABI is designed to be extensible with additional API
> as PTA_QCOM_PAS_CAPABILITIES. This allows to accommodate any future
> extensions of the PAS service needed while still maintaining backwards
> compatibility.
>
> Currently OP-TEE support is being added to provide the backend PAS
> service implementation which can be found as part of this PR [1].
> This implementation has been tested on Kodiak/RB3Gen2 and lemans
> EVK boards. In addition to that WIN/IPQ targets tested OP-TEE with
> this service too. Surely the backwards compatibility is maintained and
> tested for SCM backend.
>
> Note that kernel PAS service support while running in EL2 is at parity
> among OP-TEE vs QTEE. Especially the media (venus/iris) support depends
> on proper IOMMU support being worked out on the PAS client end.
>
> Patch summary:
> - Patch #1: adds generic PAS service.
> - Patch #2: migrates SCM backend to generic PAS service.
> - Patch #3: adds TEE/OP-TEE backend for generic PAS service.
> - Patch #4-#12: migrates all client drivers to generic PAS service.
> - Patch #13: drops legacy PAS SCM exported APIs.
>
> The patch-set is based on v7.2-rc1 and can be found in git tree
> here [2].
>
> Merge strategy:
>
> It is expected due to APIs dependency, the entire patch-set to go via
> the Qcom tree. All other subsystem maintainers, it will be great if I
> can get acks for the corresponding subsystem patches.
>
> [1] https://github.com/OP-TEE/optee_os/pull/7721 (already merged)
> [2] https://git.kernel.org/pub/scm/linux/kernel/git/sumit.garg/linux.git/log/?h=qcom-pas-v9
>
> ---
> Changes in v9:
> - Rebased to 7.2-rc1.
> - Enable SCM backend similar to TEE if ARCH_QCOM is set.
> - Address misc. comments from Konrad.
> - Add checks for corner cases (although not reachable as per OP-TEE ABI)
> reported by Shashiko on patch #3.
> - Picked up review tags from Konrad.
>
> Changes in v8:
> - Rebased on mainline tip (no functional changes).
> - Now Lemans EVK is also tested to support OP-TEE PAS here:
> https://github.com/OP-TEE/optee_os/pull/7845
> - Drop Kodiak DT patch as it is carried independently by Mukesh here:
> https://lore.kernel.org/lkml/20260624063952.2242702-1-mukesh.ojha@oss.qualcomm.com/
> - Regarding Sashiko comments, I have already replied in v6 the ones that
> don't apply but in v7 I got the same comments again. Specific context
> reasoning which Shashiko ignores:
> - ABI contract between Linux and TZ
> - No support for multiple concurrent backends
> - The TZ backend doesn’t detach during the entire boot cycle
>
> Changes in v7:
> - Rebased to qcom tree (for-next branch) tip.
> - Merged patch #5 and #7 due to build dependency.
> - Disabled modem for kodiak EL2 as it isn't tested yet.
> - Fix an issue found out by sashiko-bot for patch #4.
>
> Changes in v6:
> - Rebased to v7.1-rc4 tag.
> - Patch #14: fixed ret error print.
> - Add Kconfig descriptions for PAS symbols such that they are visible
> in menuconfig to update.
>
> Changes in v5:
> - Incorporated misc. comments from Mukesh.
> - Split up patch #11 into 2 to add an independent commit for passing
> proper PAS ID to set_remote_state API.
> - Picked up tags.
>
> Changes in v4:
> - Incorporate misc. comments on patch #4.
> - Picked up an ack for patch #10.
> - Clarify in cover letter about state of media support.
>
> Changes in v3:
> - Incorporated some style and misc. comments for patch #2, #3 and #4.
> - Add QCOM_PAS Kconfig dependency for various subsystems.
> - Switch from pseudo TA to proper TA invoke commands.
>
> Changes in v2:
> - Fixed kernel doc warnings.
> - Polish commit message and comments for patch #2.
> - Pass proper PAS ID in set_remote_state API for media firmware drivers.
> - Added Maintainer entry and dropped MODULE_AUTHOR.
>
> Sumit Garg (14):
> firmware: qcom: Add a generic PAS service
> firmware: qcom_scm: Migrate to generic PAS service
> firmware: qcom: Add a PAS TEE service
> remoteproc: qcom_q6v5_pas: Switch over to generic PAS TZ APIs
> remoteproc: qcom_q6v5_mss: Switch to generic PAS TZ APIs
> remoteproc: qcom_wcnss: Switch to generic PAS TZ APIs
> remoteproc: qcom: Select QCOM_PAS generic service
> drm/msm: Switch to generic PAS TZ APIs
> media: qcom: Switch to generic PAS TZ APIs
> media: qcom: Pass proper PAS ID to set_remote_state API
> net: ipa: Switch to generic PAS TZ APIs
> wifi: ath12k: Switch to generic PAS TZ APIs
> firmware: qcom_scm: Remove SCM PAS wrappers
> MAINTAINERS: Add maintainer entry for Qualcomm PAS TZ service
>
> MAINTAINERS | 9 +
> drivers/firmware/qcom/Kconfig | 22 +-
> drivers/firmware/qcom/Makefile | 2 +
> drivers/firmware/qcom/qcom_pas.c | 299 +++++++++++
> drivers/firmware/qcom/qcom_pas.h | 50 ++
> drivers/firmware/qcom/qcom_pas_tee.c | 479 ++++++++++++++++++
> drivers/firmware/qcom/qcom_scm.c | 302 ++++-------
> drivers/gpu/drm/msm/Kconfig | 1 +
> drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 4 +-
> drivers/gpu/drm/msm/adreno/adreno_gpu.c | 11 +-
> drivers/media/platform/qcom/iris/Kconfig | 27 +-
> .../media/platform/qcom/iris/iris_firmware.c | 9 +-
> drivers/media/platform/qcom/venus/Kconfig | 1 +
> drivers/media/platform/qcom/venus/firmware.c | 11 +-
> drivers/net/ipa/Kconfig | 2 +-
> drivers/net/ipa/ipa_main.c | 13 +-
> drivers/net/wireless/ath/ath12k/Kconfig | 2 +-
> drivers/net/wireless/ath/ath12k/ahb.c | 10 +-
> drivers/remoteproc/Kconfig | 4 +-
> drivers/remoteproc/qcom_q6v5_mss.c | 5 +-
> drivers/remoteproc/qcom_q6v5_pas.c | 51 +-
> drivers/remoteproc/qcom_wcnss.c | 12 +-
> drivers/soc/qcom/mdt_loader.c | 12 +-
> include/linux/firmware/qcom/qcom_pas.h | 43 ++
> include/linux/firmware/qcom/qcom_scm.h | 29 --
> include/linux/soc/qcom/mdt_loader.h | 6 +-
> 26 files changed, 1095 insertions(+), 321 deletions(-)
> create mode 100644 drivers/firmware/qcom/qcom_pas.c
> create mode 100644 drivers/firmware/qcom/qcom_pas.h
> create mode 100644 drivers/firmware/qcom/qcom_pas_tee.c
> create mode 100644 include/linux/firmware/qcom/qcom_pas.h
>
> --
> 2.53.0
>
^ permalink raw reply
* RE: [EXTERNAL] Re: [PATCH net-next v4] net: mana: Add Interrupt Moderation support
From: Haiyang Zhang @ 2026-07-02 19:02 UTC (permalink / raw)
To: Paolo Abeni, Haiyang Zhang, linux-hyperv@vger.kernel.org,
netdev@vger.kernel.org, KY Srinivasan, Wei Liu, Dexuan Cui,
Long Li, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Konstantin Taranov, Simon Horman,
Erni Sri Satya Vennela, Dipayaan Roy, Aditya Garg, Breno Leitao,
linux-kernel@vger.kernel.org, linux-rdma@vger.kernel.org
Cc: Paul Rosswurm
In-Reply-To: <8906f758-27fe-4ea8-8558-6d15089372d1@redhat.com>
> -----Original Message-----
> From: Paolo Abeni <pabeni@redhat.com>
> Sent: Thursday, July 2, 2026 4:57 AM
> To: Haiyang Zhang <haiyangz@linux.microsoft.com>; linux-
> hyperv@vger.kernel.org; netdev@vger.kernel.org; KY Srinivasan
> <kys@microsoft.com>; Haiyang Zhang <haiyangz@microsoft.com>; Wei Liu
> <wei.liu@kernel.org>; Dexuan Cui <DECUI@microsoft.com>; Long Li
> <longli@microsoft.com>; Andrew Lunn <andrew+netdev@lunn.ch>; David S.
> Miller <davem@davemloft.net>; Eric Dumazet <edumazet@google.com>; Jakub
> Kicinski <kuba@kernel.org>; Konstantin Taranov <kotaranov@microsoft.com>;
> Simon Horman <horms@kernel.org>; Erni Sri Satya Vennela
> <ernis@linux.microsoft.com>; Dipayaan Roy
> <dipayanroy@linux.microsoft.com>; Aditya Garg
> <gargaditya@linux.microsoft.com>; Breno Leitao <leitao@debian.org>; linux-
> kernel@vger.kernel.org; linux-rdma@vger.kernel.org
> Cc: Paul Rosswurm <paulros@microsoft.com>
> Subject: [EXTERNAL] Re: [PATCH net-next v4] net: mana: Add Interrupt
> Moderation support
>
> On 6/29/26 11:36 PM, Haiyang Zhang wrote:
> > diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c
> b/drivers/net/ethernet/microsoft/mana/mana_en.c
> > index 7438ea6b3f26..9391e9564605 100644
> > --- a/drivers/net/ethernet/microsoft/mana/mana_en.c
> > +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
> > @@ -1591,6 +1591,9 @@ int mana_create_wq_obj(struct mana_port_context
> *apc,
> >
> > mana_gd_init_req_hdr(&req.hdr, MANA_CREATE_WQ_OBJ,
> > sizeof(req), sizeof(resp));
> > +
> > + req.hdr.req.msg_version = GDMA_MESSAGE_V3;
> > + req.hdr.resp.msg_version = GDMA_MESSAGE_V2;
>
> Double checking the above is intentional; it feels strange to me that
> request and reply use different versions. Possibly a comment for future
> memory would make sense.
Yes, it's intentional. The request and reply versions can be different.
I will add comments.
> > +
> > +/* The caller must update apc->rx/tx_dim_enabled before disabling and
> > + * after enabling. And synchronize_net() before draining the DIM work,
> > + * so that NAPI cannot observe a stale flag.
> > + */
> > +int mana_dim_change(struct mana_cq *cq, bool enable)
>
> This always return 0, and the return value is not checked by the
> callers; return type should likelly changed to void
Will update.
Thanks,
- Haiyang
^ permalink raw reply
* [PATCH ipsec] xfrm: policy: use hlist_del_init_rcu in xfrm_hash_rebuild to avoid bydst poison
From: Xiang Mei (Microsoft) @ 2026-07-02 18:58 UTC (permalink / raw)
To: steffen.klassert, herbert, davem, netdev
Cc: horms, fw, edumazet, kuba, pabeni, AutonomousCodeSecurity,
tgopinath, kys, Xiang Mei (Microsoft)
xfrm_hash_rebuild() unlinks each policy from its bydst chain with
hlist_del_rcu() and re-inserts it. For an inexact policy the re-insert goes
through xfrm_policy_inexact_insert(), which can fail on a GFP_ATOMIC
allocation; on failure the error path only WARN_ONCE()s and continues, so the
policy is left with a poisoned bydst node (LIST_POISON2). The next rebuild
calls hlist_del_rcu() on that node again, dereferences the poison, and takes a
general protection fault.
Use hlist_del_init_rcu() instead, so a failed-reinsert node is left unhashed
(pprev == NULL) rather than poisoned. The next rebuild's hlist_del_init_rcu()
is then a no-op for it, and the non-failing case is unchanged.
The reinsert allocation is GFP_ATOMIC (it runs under xfrm_policy_lock), so in
practice this is only reached under memory pressure; the crash below was
reproduced deterministically by forcing that allocation to fail with fault
injection (failslab).
Crash:
Oops: general protection fault, probably for non-canonical address
0xfbd59c0000000024: 0000 [#1] SMP KASAN NOPTI
KASAN: maybe wild-memory-access in range [0xdead000000000120-0xdead000000000127]
...
Workqueue: events xfrm_hash_rebuild
RIP: 0010:xfrm_hash_rebuild+0x5b3/0x1190
RAX: dead000000000122 (LIST_POISON2 + offset)
...
Call Trace:
hlist_del_rcu (include/linux/rculist.h:599)
xfrm_hash_rebuild (net/xfrm/xfrm_policy.c:1365)
process_one_work (kernel/workqueue.c:3322)
worker_thread (kernel/workqueue.c:3486)
kthread (kernel/kthread.c:436)
ret_from_fork (arch/x86/kernel/process.c:158)
ret_from_fork_asm (arch/x86/entry/entry_64.S:245)
...
Kernel panic - not syncing: Fatal exception in interrupt
Fixes: 563d5ca93e88 ("xfrm: switch migrate to xfrm_policy_lookup_bytype")
Reported-by: AutonomousCodeSecurity@microsoft.com
Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
---
net/xfrm/xfrm_policy.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 7ef861a0e823..2612a405542b 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -1362,7 +1362,7 @@ static void xfrm_hash_rebuild(struct work_struct *work)
if (xfrm_policy_is_dead_or_sk(policy))
continue;
- hlist_del_rcu(&policy->bydst);
+ hlist_del_init_rcu(&policy->bydst);
newpos = NULL;
dir = xfrm_policy_id2dir(policy->index);
--
2.43.0
^ permalink raw reply related
* Re: [PATCH net-next 3/9] octeontx2-pf: switch: Add pf files hierarchy
From: Julian Braha @ 2026-07-02 18:55 UTC (permalink / raw)
To: Ratheesh Kannoth, linux-kernel, netdev
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, sgoutham
In-Reply-To: <20260630024715.4124281-4-rkannoth@marvell.com>
Hi Ratheesh,
On 6/30/26 03:47, Ratheesh Kannoth wrote:
> +config OCTEONTX_SWITCH
> + tristate "Marvell OcteonTX2 switch driver"
> + select OCTEONTX2_MBOX
> + select NET_DEVLINK
> + default n
> + select PAGE_POOL
> + depends on (64BIT && COMPILE_TEST) || ARM64
> + depends on OCTEONTX2_PF
Could you keep the 'select's together (move the 'default')?
- Julian Braha
^ permalink raw reply
* Re: [PATCH net-next 2/4] net: usb: centralize usbnet_cdc_zte_rx_fixup in usbnet
From: Manuel Ebner @ 2026-07-02 18:19 UTC (permalink / raw)
To: Oliver Neukum, andrew+netdev, davem, edumazet, kuba, pabeni,
shaoxul, netdev, linux-usb, linux-kernel
In-Reply-To: <20260702143142.890654-3-oneukum@suse.com>
On Thu, 2026-07-02 at 16:25 +0200, Oliver Neukum wrote:
> This helper is used by multiple drivers using usbnet.
> It is better to be provided by usbnet than one of them.
>
> Signed-off-by: Oliver Neukum <oneukum@suse.com>
> ---
> drivers/net/usb/cdc_ether.c | 19 -------------------
> drivers/net/usb/usbnet.c | 19 +++++++++++++++++++
> 2 files changed, 19 insertions(+), 19 deletions(-)
>
> ...
>
> diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
> index 5544af1f4aa5..7beea6d0e731 100644
> --- a/drivers/net/usb/usbnet.c
> +++ b/drivers/net/usb/usbnet.c
> @@ -2347,6 +2347,25 @@ void usbnet_cdc_status(struct usbnet *dev, struct urb *urb)
> }
> }
> EXPORT_SYMBOL_GPL(usbnet_cdc_status);
> +
> +/* Make sure packets have correct destination MAC address
/*
* Make sure packets have the correct destination MAC address
> + *
> + * A firmware bug observed on some devices (ZTE MF823/831/910) is that the
> + * device sends packets with a static, bogus, random MAC address (event if
(even if the device ...
> + * device MAC address has been updated). Always set MAC address to that of the
> + * device.
Always set the MAC address to the one of your device.
Thanks
Manuel
> + * device.
> + */
> +int usbnet_cdc_zte_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
> +{
> + if (skb->len < ETH_HLEN || !(skb->data[0] & 0x02))
> + return 1;
> +
> + skb_reset_mac_header(skb);
> + ether_addr_copy(eth_hdr(skb)->h_dest, dev->net->dev_addr);
> +
> + return 1;
> +}
> +EXPORT_SYMBOL_GPL(usbnet_cdc_zte_rx_fixup);
> /*-------------------------------------------------------------------------*/
>
> static int __init usbnet_init(void)
^ permalink raw reply
* Re: [PATCH net-next 0/4] net: usb: move exported code to usbnet
From: Manuel Ebner @ 2026-07-02 18:26 UTC (permalink / raw)
To: Oliver Neukum, andrew+netdev, davem, edumazet, kuba, pabeni,
shaoxul, netdev, linux-usb, linux-kernel
In-Reply-To: <20260702143142.890654-1-oneukum@suse.com>
Hallo,
On Thu, 2026-07-02 at 16:25 +0200, Oliver Neukum wrote:
I know this mail doesn't matter much, but i can't leave it as is.
> Some drivers are reusing common code originating in other drivers.
Some drivers are using code from other drivers.
> This means that two drivers need to be loaded for one device.
> Also maintainability is reduced if changes in one driver affect
> another driver.
> Shift common code to usbnet.
You can add my
Reviewed-by: Manuel Ebner <manuelebner@mailbox.org>
to the series.
Thanks
Manuel
^ permalink raw reply
* Re: [PATCH net-next 1/4] net: usb: move updating filter and status from cdc to usbnet
From: Andrew Lunn @ 2026-07-02 18:20 UTC (permalink / raw)
To: Oliver Neukum
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, shaoxul, netdev,
linux-usb, linux-kernel
In-Reply-To: <20260702143142.890654-2-oneukum@suse.com>
On Thu, Jul 02, 2026 at 04:25:30PM +0200, Oliver Neukum wrote:
> These helpers are used by multiple drivers and do not depend
> on the rest of cdc. Leavin them in a cdc driver means that
leaving.
> more drivers are loaded just for infrastructure, not hardware
> support.
Can there also be changes to Kconfig removing the dependency on
cdc_ether for some drivers? They now only depend on usbnet?
Andrew
^ permalink raw reply
* Re: [RFC net-next] bonding: Retry updating slave MAC after a failure
From: Jay Vosburgh @ 2026-07-02 18:17 UTC (permalink / raw)
To: Paritosh Potukuchi; +Cc: netdev, linux-kernel, paritosh.potukuchi
In-Reply-To: <CAMfiSebUf6rbHu4bVL-5vCFRs5cafiEeiNDpX3eQRdxKuRgGeQ@mail.gmail.com>
Paritosh Potukuchi <paritoshpotukuchi@gmail.com> wrote:
>> I think the proper thing to do is remove this comment block and
>make no other changes.
>
> > This comment dates to sometime before git, when it was common
>for network device drivers to lack the ability to change the MAC while
>the interface is up. To the best of my knowledge, that isn't a issue
>today.
>
>Sure Jay. That makes sense. Should I go ahead and post a patch
>removing this comment?
Yes, please do so.
-J
> -Paritosh
>
>On Wed, 1 Jul 2026 at 04:29, Jay Vosburgh <jv@jvosburgh.net> wrote:
>
> Paritosh Potukuchi <paritoshpotukuchi@gmail.com> wrote:
>
> >I came across this TODO in bond_set_mac_address() :
> >
> > /* TODO: consider downing the slave
> > * and retry ?
> > * User should expect communications
> > * breakage anyway until ARP finish
> > * updating, so...
> > */
> >
> >Currently, if the dev_set_mac_address() fails on a slave, we go
> >ahead and unwind the bond and its slaves.
> >
> >As the TODO suggests, one possible solution is to try setting
> >the MAC again, after putting down the interface. This is because some
> >drivers may reject changing the MAC when the device is UP.
> >
> >The solution I am proposing is as follows:
> >
> >dev_set_mac_address on the slave
> > - If this fails, temporarily stop the slave - ndo_stop
> > - If stop fails, unwind
> > - call dev_set_mac_address() on the slave
> > - If this fails, unwind
> > - Bring up the slave by calling ndo_open
> > - If this fails, unwind
> >If dev_set_mac_address on slave passes, we go to the next slave
> >
> >
> >Before working on a patch, I wanted to get feedback on whether
> >this interpretation of the TODO makes sense and whether there
> >are concerns with temporarily stopping and restarting a slave
> >during bond_set_mac_address().
>
> I think the proper thing to do is remove this comment block
> and
> make no other changes.
>
> This comment dates to sometime before git, when it was common
> for network device drivers to lack the ability to change the MAC while
> the interface is up. To the best of my knowledge, that isn't a issue
> today.
>
> -J
>
---
-Jay Vosburgh, jv@jvosburgh.net
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox