* Re: [PATCH v13 net-next 08/11] net/nebula-matrix: add vsi resource implementation
From: Jakub Kicinski @ 2026-04-30 23:47 UTC (permalink / raw)
To: illusion.wang
Cc: Jakub Kicinski, dimon.zhao, alvin.wang, sam.chen, netdev,
andrew+netdev, corbet, linux-doc, lorenzo, pabeni, horms,
vadim.fedorenko, lukas.bulwahn, edumazet, enelsonmoore, skhan,
hkallweit1, linux-kernel
In-Reply-To: <20260428114910.2616-9-illusion.wang@nebula-matrix.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net/nebula-matrix: add vsi resource implementation
This patch adds the VSI resource implementation and chip initialization
for the DP, intf, and P4 register modules, covering sub-modules such as
dped, uped, dsch, ustore, dstore, dvn, uvn, and uqm. The new code
populates bitfield structs and writes them to MMIO registers via
nbl_hw_wr_regs().
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c
> index aa5e91c2b278..747273217203 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c
[ ... ]
> +static void nbl_configure_dped_checksum(struct nbl_hw_mgt *hw_mgt)
> +{
> + union dped_l4_ck_cmd_40_u l4_ck_cmd_40;
> +
> + /* DPED dped_l4_ck_cmd_40 for sctp */
> + nbl_hw_rd_regs(hw_mgt, NBL_DPED_L4_CK_CMD_40_ADDR, (u32 *)&l4_ck_cmd_40,
> + sizeof(l4_ck_cmd_40));
> + l4_ck_cmd_40.info.en = 1;
> + nbl_hw_wr_regs(hw_mgt, NBL_DPED_L4_CK_CMD_40_ADDR, (u32 *)&l4_ck_cmd_40,
> + sizeof(l4_ck_cmd_40));
> +}
Can this code produce a different bit pattern in the device register
depending on the host endianness?
The pattern used throughout the new init code is to populate C bitfield
structs and then cast them to u32 * and hand them to nbl_hw_wr_regs(),
which ultimately reaches writel(). The C standard leaves bitfield
placement within a storage unit implementation-defined, and GCC places
u32 f:N fields starting from the opposite end depending on the target
endianness. writel() performs a CPU-to-little-endian conversion on the
whole u32, so the register contents seen by the device differ between
little-endian and big-endian kernels compiled from the same source.
> +static void nbl_uped_init(struct nbl_hw_mgt *hw_mgt)
> +{
> + struct ped_hw_edit_profile hw_edit;
> +
> + nbl_hw_rd_regs(hw_mgt, NBL_UPED_HW_EDT_PROF_TABLE(NBL_DPED_V4_TCP_IDX),
> + (u32 *)&hw_edit, sizeof(hw_edit));
> + hw_edit.l3_len = 0;
> + nbl_hw_wr_regs(hw_mgt, NBL_UPED_HW_EDT_PROF_TABLE(NBL_DPED_V4_TCP_IDX),
> + (u32 *)&hw_edit, sizeof(hw_edit));
> +
> + nbl_hw_rd_regs(hw_mgt, NBL_UPED_HW_EDT_PROF_TABLE(NBL_DPED_V6_TCP_IDX),
> + (u32 *)&hw_edit, sizeof(hw_edit));
> + hw_edit.l3_len = 1;
> + nbl_hw_wr_regs(hw_mgt, NBL_UPED_HW_EDT_PROF_TABLE(NBL_DPED_V6_TCP_IDX),
> + (u32 *)&hw_edit, sizeof(hw_edit));
> +}
[ ... ]
> +static void nbl_shaping_eth_init(struct nbl_hw_mgt *hw_mgt, u8 eth_id, u8 speed)
> +{
> + struct nbl_shaping_dvn_dport dvn_dport = { 0 };
> + struct nbl_shaping_dport dport = { 0 };
> + u32 rate, half_rate;
> +
> + if (speed == NBL_FW_PORT_SPEED_100G) {
> + rate = NBL_SHAPING_DPORT_100G_RATE;
> + half_rate = NBL_SHAPING_DPORT_HALF_100G_RATE;
> + } else {
> + rate = NBL_SHAPING_DPORT_25G_RATE;
> + half_rate = NBL_SHAPING_DPORT_HALF_25G_RATE;
> + }
> +
> + dport.cir = rate;
> + dport.pir = rate;
> + dport.depth = max(dport.cir * 2, NBL_LR_LEONIS_NET_BUCKET_DEPTH);
> + dport.cbs = dport.depth;
> + dport.pbs = dport.depth;
> + dport.valid = 1;
> +
> + dvn_dport.cir = half_rate;
> + dvn_dport.pir = rate;
> + dvn_dport.depth = dport.depth;
> + dvn_dport.cbs = dvn_dport.depth;
> + dvn_dport.pbs = dvn_dport.depth;
> + dvn_dport.valid = 1;
> +
> + nbl_hw_wr_regs(hw_mgt, NBL_SHAPING_DPORT_REG(eth_id), (u32 *)&dport,
> + sizeof(dport));
> + nbl_hw_wr_regs(hw_mgt, NBL_SHAPING_DVN_DPORT_REG(eth_id),
> + (u32 *)&dvn_dport, sizeof(dvn_dport));
> +}
The same pattern appears in nbl_shaping_eth_init(), nbl_dsch_qid_max_init(),
nbl_ustore_init(), nbl_dstore_init(), nbl_dvn_descreq_num_cfg(),
nbl_dvn_init(), nbl_uvn_init(), nbl_uqm_init(), and
nbl_configure_dped_checksum().
[ ... ]
> +static void nbl_dvn_descreq_num_cfg(struct nbl_hw_mgt *hw_mgt, u32 descreq_num)
> +{
> + u32 split_ring_num = (descreq_num >> 16) & 0xffff;
> + struct nbl_dvn_descreq_num_cfg num_cfg = { 0 };
> + u32 packet_ring_num = descreq_num & 0xffff;
> +
> + packet_ring_num =
> + clamp(packet_ring_num, PACKET_RING_MIN, PACKET_RING_MAX);
> + num_cfg.packed_l1_num =
> + (packet_ring_num - PACKET_RING_BASE) / PACKET_RING_DIV;
> +
> + split_ring_num = clamp(split_ring_num, SPLIT_RING_MIN,
> + SPLIT_RING_MAX);
> + num_cfg.avring_cfg_num = split_ring_num > SPLIT_RING_MIN ?
> + SPLIT_RING_CFG_16 :
> + SPLIT_RING_CFG_8;
> +
> + nbl_hw_wr_regs(hw_mgt, NBL_DVN_DESCREQ_NUM_CFG, (u32 *)&num_cfg,
> + sizeof(num_cfg));
> +}
Taking struct nbl_dvn_descreq_num_cfg as one example:
struct nbl_dvn_descreq_num_cfg {
u32 avring_cfg_num:1;
u32 rsv0:3;
u32 packed_l1_num:3;
u32 rsv1:25;
};
On a little-endian build, avring_cfg_num ends up in bit 0 of the u32 sent
to the device. On a big-endian build the same field ends up in bit 31 of
the u32 that writel() then byte-swaps to little-endian for the bus,
producing a different pattern at the device. The same concern applies to
struct uvn_desc_prefetch_init, struct uvn_dif_req_ro_flag,
struct dsch_vn_quanta, struct nbl_shaping_dport,
struct nbl_shaping_dvn_dport, struct dstore_disc_bp_th,
struct dstore_port_drop_th, struct dstore_d_dport_fc_th,
struct ustore_pkt_len, struct nbl_ustore_port_drop_th,
struct ped_hw_edit_profile, struct nbl_uqm_que_type, and
union dped_l4_ck_cmd_40_u.
[ ... ]
> +static void nbl_uvn_init(struct nbl_hw_mgt *hw_mgt)
> +{
> + struct uvn_desc_prefetch_init prefetch_init = { 0 };
> + struct uvn_desc_wr_timeout desc_wr_timeout = { 0 };
> + struct uvn_dif_req_ro_flag flag = { 0 };
> + struct uvn_queue_err_mask mask = { 0 };
[ ... ]
> + nbl_hw_wr_regs(hw_mgt, NBL_UVN_DESC_PREFETCH_INIT,
> + (u32 *)&prefetch_init, sizeof(prefetch_init));
> +}
The driver Kconfig uses depends on PCI && (64BIT || COMPILE_TEST), which
permits ppc64be and s390x builds, and there is no BUILD_BUG_ON or
__LITTLE_ENDIAN_BITFIELD / __BIG_ENDIAN_BITFIELD guarding on any of these
structs.
Would it make sense to either add __LITTLE_ENDIAN_BITFIELD /
__BIG_ENDIAN_BITFIELD variants for each of these register-layout structs,
replace the bitfields with explicit u32 masks and shifts, or restrict the
Kconfig to little-endian hosts?
^ permalink raw reply
* Re: [PATCH v13 net-next 07/11] net/nebula-matrix: add intr resource implementation
From: Jakub Kicinski @ 2026-04-30 23:47 UTC (permalink / raw)
To: illusion.wang
Cc: Jakub Kicinski, dimon.zhao, alvin.wang, sam.chen, netdev,
andrew+netdev, corbet, linux-doc, lorenzo, pabeni, horms,
vadim.fedorenko, lukas.bulwahn, edumazet, enelsonmoore, skhan,
hkallweit1, linux-kernel
In-Reply-To: <20260428114910.2616-8-illusion.wang@nebula-matrix.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net/nebula-matrix: add intr resource implementation
This patch adds an interrupt resource module that tracks MSI-X vector
allocation per function via two bitmaps and a per-function array, and
introduces three resource ops (configure_msix_map, destroy_msix_map,
enable_mailbox_irq) plus the matching hw_ops (configure_msix_map,
configure_msix_info, enable_mailbox_irq) that program the device MSI-X
mapping and info registers.
A few questions below on the new code.
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c
> index 1ec1c28d99ff..1cceb0df5e1e 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c
> @@ -12,6 +12,7 @@ static struct nbl_resource_ops res_ops = {
> .get_eth_id = nbl_res_get_eth_id,
> };
>
> +static bool is_ops_inited;
Is this lazy initialization of a file-scope vtable safe against concurrent
probes?
nbl_res_setup_ops() does an unlocked check-then-set on is_ops_inited and
then mutates the file-scope res_ops by calling nbl_intr_setup_ops():
if (!is_ops_inited) {
ret = nbl_intr_setup_ops(&res_ops);
if (ret)
return ERR_PTR(-ENOMEM);
is_ops_inited = true;
}
Two probe threads for different functions on the same device could both
observe is_ops_inited == false and race on writing the same pointer fields
into res_ops. Would a static designated initializer in res_ops (keeping
res_ops const and dropping is_ops_inited / nbl_intr_setup_ops entirely)
remove the race and simplify the scaffolding?
Related: nbl_intr_setup_ops() only ever returns 0, so the caller's
"if (ret) return ERR_PTR(-ENOMEM);" is unreachable, and mapping any future
failure to -ENOMEM would also be incorrect. Could this function be void?
[ ... ]
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_interrupt.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_interrupt.c
> new file mode 100644
> index 000000000000..92a6fff5523e
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_interrupt.c
> @@ -0,0 +1,246 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include "nbl_interrupt.h"
> +
> +static int nbl_res_intr_destroy_msix_map(struct nbl_resource_mgt *res_mgt,
> + u16 func_id)
> +{
> + struct nbl_interrupt_mgt *intr_mgt = res_mgt->intr_mgt;
> + struct nbl_hw_ops *hw_ops = res_mgt->hw_ops_tbl->ops;
> + struct device *dev = res_mgt->common->dev;
> + struct nbl_msix_map_table *msix_map_table;
> + u16 *interrupts;
> + u16 intr_num, i;
> +
> + /* use ctrl dev bdf */
> + hw_ops->configure_msix_map(res_mgt->hw_ops_tbl->priv, func_id, false, 0,
> + 0, 0, 0);
> +
> + intr_num = intr_mgt->func_intr_res[func_id].num_interrupts;
> + interrupts = intr_mgt->func_intr_res[func_id].interrupts;
Is func_id bounds-checked anywhere on this path? intr_mgt->func_intr_res[]
is sized NBL_MAX_FUNC (520) but the u16 func_id (0..65535) is indexed
directly, and nbl_res_func_id_to_bdf() (which validates against max_pf) is
not called here. If func_id arrives from a mailbox message in a later
patch, can this index out of bounds?
The same question applies to nbl_res_intr_enable_mailbox_irq() and to the
first use of func_intr_res[func_id] in nbl_res_intr_configure_msix_map()
below, which happens before nbl_res_func_id_to_bdf() is called.
> +
> + if (!interrupts) {
> + dev_err(dev, "No interrupts to clr for func_id %u\n", func_id);
> + return -EINVAL;
> + }
> + for (i = 0; i < intr_num; i++) {
> + if (interrupts[i] >= NBL_MAX_OTHER_INTERRUPT)
> + clear_bit(interrupts[i] - NBL_MAX_OTHER_INTERRUPT,
> + intr_mgt->interrupt_net_bitmap);
> + else
> + clear_bit(interrupts[i],
> + intr_mgt->interrupt_others_bitmap);
What serializes access to intr_mgt->interrupt_net_bitmap,
intr_mgt->interrupt_others_bitmap and intr_mgt->func_intr_res[func_id]
across concurrent invocations of these three ops?
set_bit() / clear_bit() are individually atomic, but the sequence used in
nbl_res_intr_configure_msix_map():
intr_index = find_first_zero_bit(intr_mgt->interrupt_net_bitmap,
NBL_MAX_NET_INTERRUPT);
...
set_bit(intr_index, intr_mgt->interrupt_net_bitmap);
is a non-atomic read-modify-write. Two concurrent calls for different
func_ids could each observe the same zero bit and both claim it, resulting
in two functions being programmed with the same global MSI-X index.
The per-function state has a similar concern: configure, destroy and
enable_mailbox_irq all read/write func_intr_res[func_id].interrupts,
num_interrupts and msix_map_table without any lock held. Concurrent
callers on the same func_id can race the "if (interrupts) destroy" TOCTOU
and realloc, double-kfree() the interrupts array, or dma_free_coherent()
twice. enable_mailbox_irq dereferences interrupts[vector_id] which
destroy_msix_map has just freed and set to NULL -- is this a
use-after-free?
The WARN_ON(msix_map_table->base_addr) below suggests single-threaded use
is assumed; what enforces that?
[ ... ]
> + kfree(interrupts);
> + intr_mgt->func_intr_res[func_id].interrupts = NULL;
> + intr_mgt->func_intr_res[func_id].num_interrupts = 0;
> +
> + msix_map_table = &intr_mgt->func_intr_res[func_id].msix_map_table;
> + dma_free_coherent(dev, msix_map_table->size, msix_map_table->base_addr,
> + msix_map_table->dma);
Is there a teardown path that iterates over func_intr_res[] and calls
destroy_msix_map() for each active func_id on module unload / device
remove?
The interrupts array (kcalloc) and the msix_map_table (dma_alloc_coherent)
allocated in configure_msix_map are not devm-managed, and
nbl_res_remove_leonis() does not walk func_intr_res[]. Any func_id still
mapped at unload time would leak both allocations and leave a stale bus
address programmed in the hardware MSI-X map register.
> + msix_map_table->size = 0;
> + msix_map_table->base_addr = NULL;
> + msix_map_table->dma = 0;
> +
> + return 0;
> +}
> +
> +static int nbl_res_intr_configure_msix_map(struct nbl_resource_mgt *res_mgt,
> + u16 func_id, u16 num_net_msix,
> + u16 num_others_msix,
> + bool net_msix_mask_en)
> +{
> + struct nbl_interrupt_mgt *intr_mgt = res_mgt->intr_mgt;
> + struct nbl_hw_ops *hw_ops = res_mgt->hw_ops_tbl->ops;
> + struct nbl_common_info *common = res_mgt->common;
> + struct nbl_msix_map_table *msix_map_table;
> + struct nbl_msix_map *msix_map_entries;
> + struct device *dev = common->dev;
> + u16 requested, intr_index;
> + u8 bus, devid, function;
> + bool msix_mask_en;
> + u16 *interrupts;
> + int ret = 0;
> + u16 i;
> +
> + requested = num_net_msix + num_others_msix;
> + if (requested > NBL_MSIX_MAP_TABLE_MAX_ENTRIES)
> + return -EINVAL;
> + if (intr_mgt->func_intr_res[func_id].interrupts)
> + nbl_res_intr_destroy_msix_map(res_mgt, func_id);
Is this reconfigure ordering intentional?
The existing mapping is torn down (dma_free_coherent, kfree, and
hw_ops->configure_msix_map(valid=false)) before the new allocations are
attempted. If the subsequent dma_alloc_coherent or kcalloc fails, the
function returns -ENOMEM with the previously working interrupt setup
already gone and no rollback. Would allocate-then-swap be safer?
> +
> + ret = nbl_res_func_id_to_bdf(res_mgt, func_id, &bus, &devid, &function);
> + if (ret == U32_MAX)
> + return -EINVAL;
nbl_res_func_id_to_bdf() is declared to return int but returns U32_MAX on
error, and the caller stores it in int ret and compares against U32_MAX.
This currently works only because -1 sign-extends to 0xFFFFFFFF. If the
callee is later changed to return a normal negative errno (for example
-EINVAL), will this check silently stop detecting failures and let
uninitialised bus/devid/function be programmed into hardware?
> +
> + msix_map_table = &intr_mgt->func_intr_res[func_id].msix_map_table;
> + WARN_ON(msix_map_table->base_addr);
> + msix_map_table->size =
> + sizeof(struct nbl_msix_map) * NBL_MSIX_MAP_TABLE_MAX_ENTRIES;
> + msix_map_table->base_addr = dma_alloc_coherent(dev,
> + msix_map_table->size,
> + &msix_map_table->dma,
> + GFP_KERNEL);
[ ... ]
> + msix_map_entries = msix_map_table->base_addr;
> + for (i = 0; i < requested; i++) {
> + msix_map_entries[i].global_msix_index = interrupts[i];
> + msix_map_entries[i].valid = 1;
> +
> + if (i < num_net_msix && net_msix_mask_en)
> + msix_mask_en = 1;
> + else
> + msix_mask_en = 0;
> + hw_ops->configure_msix_info(res_mgt->hw_ops_tbl->priv, func_id,
> + true, interrupts[i], bus, devid,
> + function, msix_mask_en);
> + }
> +
> + /* use ctrl dev bdf */
> + hw_ops->configure_msix_map(res_mgt->hw_ops_tbl->priv, func_id, true,
> + msix_map_table->dma, common->hw_bus,
> + common->devid, common->function);
Is a dma_wmb() (or equivalent) needed between the stores into the
dma_alloc_coherent table (msix_map_entries[i] fields above) and the MMIO
writel() in hw_ops->configure_msix_map that arms the device to DMA-read
that table?
dma_alloc_coherent guarantees cache coherence but not ordering between
CPU stores into the coherent buffer and subsequent MMIO stores on
weakly-ordered architectures such as arm64 and ppc.
> +
> + return 0;
[ ... ]
> +static int nbl_res_intr_enable_mailbox_irq(struct nbl_resource_mgt *res_mgt,
> + u16 func_id, u16 vector_id,
> + bool enable_msix)
> +{
> + struct nbl_interrupt_mgt *intr_mgt = res_mgt->intr_mgt;
> + struct nbl_hw_ops *hw_ops = res_mgt->hw_ops_tbl->ops;
> + u16 global_vec_id;
> +
> + global_vec_id = intr_mgt->func_intr_res[func_id].interrupts[vector_id];
Is this dereference guarded anywhere? There is no check that
func_intr_res[func_id].interrupts is non-NULL or that vector_id is less
than num_interrupts.
After destroy_msix_map() runs, interrupts is NULL; and before
configure_msix_map() has ever run for that func_id it is also NULL, so
this becomes a NULL-pointer read at offset 2 * vector_id. When interrupts
is non-NULL but vector_id is out of range, this is an out-of-bounds slab
read whose value is then programmed into a hardware MSI-X register via
hw_ops->enable_mailbox_irq().
> + hw_ops->enable_mailbox_irq(res_mgt->hw_ops_tbl->priv, func_id,
> + enable_msix, global_vec_id);
> +
> + return 0;
> +}
> +
> +/* NBL_INTR_SET_OPS(ops_name, func)
> + *
> + * Use X Macros to reduce setup and remove codes.
> + */
> +#define NBL_INTR_OPS_TBL \
> +do { \
> + NBL_INTR_SET_OPS(configure_msix_map, \
> + nbl_res_intr_configure_msix_map); \
> + NBL_INTR_SET_OPS(destroy_msix_map, \
> + nbl_res_intr_destroy_msix_map); \
> + NBL_INTR_SET_OPS(enable_mailbox_irq, \
> + nbl_res_intr_enable_mailbox_irq); \
> +} while (0)
This isn't a bug, but could this X-macro scheme be replaced with a plain
designated initializer on a const struct nbl_resource_ops? The indirection
through NBL_INTR_OPS_TBL / NBL_INTR_SET_OPS and the NBL_NAME(x) helper
(whose stated purpose is "Used for macros to pass checkpatch") add no
type safety over:
static const struct nbl_resource_ops res_ops = {
.configure_msix_map = nbl_res_intr_configure_msix_map,
.destroy_msix_map = nbl_res_intr_destroy_msix_map,
.enable_mailbox_irq = nbl_res_intr_enable_mailbox_irq,
...
};
[ ... ]
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.h
> index 90c78aefa498..d4a1374e0dd2 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.h
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.h
> @@ -18,6 +18,37 @@
>
> struct nbl_resource_mgt;
>
> +/* --------- INTERRUPT ---------- */
> +#define NBL_MAX_OTHER_INTERRUPT 1024
> +#define NBL_MAX_NET_INTERRUPT 4096
> +
> +struct nbl_msix_map {
> + u16 valid:1;
> + u16 global_msix_index:13;
> + u16 rsv:2;
> +};
Is it safe to rely on C bitfield layout for a structure that is written
into dma_alloc_coherent memory and then consumed by the device? The bit
ordering of bitfields within a storage unit is implementation-defined, and
GCC reverses bit ordering on big-endian targets.
The same question applies to nbl_mailbox_qinfo_map_table,
nbl_host_msix_info, nbl_pcompleter_host_msix_fid_table and
nbl_function_msix_map, which are assembled with C bitfields and then cast
to u32 * to be written into MMIO registers in nbl_hw_leonis.c. Would
explicit FIELD_PREP / shifts-and-masks or __le* encoding be more portable?
> +
> +struct nbl_msix_map_table {
> + struct nbl_msix_map *base_addr;
> + dma_addr_t dma;
> + size_t size;
> +};
> +
> +struct nbl_func_interrupt_resource_mng {
> + u16 num_interrupts;
> + u16 num_net_interrupts;
> + u16 msix_base;
> + u16 msix_max;
msix_base and msix_max do not appear to be read or written anywhere in
this patch. Could these be added in the patch that first uses them?
> + u16 *interrupts;
> + struct nbl_msix_map_table msix_map_table;
> +};
[ ... ]
One last question on the commit message:
MSI-X Interrupt Configuration:
Dynamically allocate and manage MSI-X interrupt vectors, including
network interrupts and other types of interrupts.
...
Interrupt Information Query: Provide interfaces to obtain the
hardware register addresses and data of interrupts.
The patch programs the device-private MSI-X mapping and info registers and
tracks index bitmaps, but there is no pci_alloc_irq_vectors(), no
request_irq(), no IRQ handler, and no "interrupt information query" getter
exposed through nbl_resource_ops -- only configure_msix_map,
destroy_msix_map and enable_mailbox_irq are added. Could the commit
message be narrowed to what this patch actually implements?
^ permalink raw reply
* Re: [PATCH v13 net-next 04/11] net/nebula-matrix: channel msg value and msg struct
From: Jakub Kicinski @ 2026-04-30 23:47 UTC (permalink / raw)
To: illusion.wang
Cc: Jakub Kicinski, dimon.zhao, alvin.wang, sam.chen, netdev,
andrew+netdev, corbet, linux-doc, lorenzo, pabeni, horms,
vadim.fedorenko, lukas.bulwahn, edumazet, enelsonmoore, skhan,
hkallweit1, linux-kernel
In-Reply-To: <20260428114910.2616-5-illusion.wang@nebula-matrix.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net/nebula-matrix: channel msg value and msg struct
This patch introduces the channel message id enumeration and a few
message parameter structures for the nebula-matrix driver. The commit
message notes that for compatibility the msg id values are fixed and
fields can only be appended, not removed.
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h
> index ff03a53b9f5d..99e1d8455e0e 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h
> @@ -8,6 +8,255 @@
>
> struct nbl_channel_mgt;
> struct nbl_adapter;
> +enum nbl_chan_msg_type {
[ ... ]
> + NBL_CHAN_MSG_GET_GLOBAL_VECTOR,
> + NBL_CHAN_MSG_GET_VSI_ID,
> + NBL_CHAN_MSG_SET_PROSISC_MODE,
Is this a typo for NBL_CHAN_MSG_SET_PROMISC_MODE (promiscuous)?
The commit message states "the msg id value is fixed", so renaming this
identifier once later patches in the series start referencing it
becomes more churn than fixing it here.
> + NBL_CHAN_MSG_GET_FIRMWARE_VERSION,
[ ... ]
--
pw-bot: cr
^ permalink raw reply
* [PATCH net v2 2/2] selftests: openvswitch: add tests for tunnel vport refcounting
From: Ilya Maximets @ 2026-04-30 23:38 UTC (permalink / raw)
To: netdev
Cc: Aaron Conole, Eelco Chaudron, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Shuah Khan, Yuan Tan,
Yang Yang, dev, linux-kernel, linux-kselftest, Ilya Maximets
In-Reply-To: <20260430233848.440994-1-i.maximets@ovn.org>
There were a few issues found with the tunnel vport types around the
vport destruction code. Add some basic tests, so at least we know that
they can be properly added and removed without obvious issues.
The test creates OVS datapath, adds a non-LWT tunnel port, makes sure
they are created, and then removes the datapath and waits for all the
ports to be gone.
The dpctl script had a few bugs in the none-lwt tunnel creation code,
so fixing them as well to make the testing possible:
- The type of the --lwt option changed in order to properly disable it.
- Removed byte order conversion for the port numbers, as the value
supposed to be in the host order.
- Added missing 'gre' choice for the tunnel type.
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
---
.../selftests/net/openvswitch/openvswitch.sh | 37 +++++++++++++++++++
.../selftests/net/openvswitch/ovs-dpctl.py | 19 +++++++---
2 files changed, 50 insertions(+), 6 deletions(-)
diff --git a/tools/testing/selftests/net/openvswitch/openvswitch.sh b/tools/testing/selftests/net/openvswitch/openvswitch.sh
index b327d3061ed53..3cdd953f68132 100755
--- a/tools/testing/selftests/net/openvswitch/openvswitch.sh
+++ b/tools/testing/selftests/net/openvswitch/openvswitch.sh
@@ -26,6 +26,7 @@ tests="
netlink_checks ovsnl: validate netlink attrs and settings
upcall_interfaces ovs: test the upcall interfaces
tunnel_metadata ovs: test extraction of tunnel metadata
+ tunnel_refcount ovs: test tunnel vport reference cleanup
drop_reason drop: test drop reasons are emitted
psample psample: Sampling packets with psample"
@@ -830,6 +831,42 @@ test_tunnel_metadata() {
return 0
}
+test_tunnel_refcount() {
+ sbxname="test_tunnel_refcount"
+ sbx_add "${sbxname}" || return 1
+
+ ovs_sbx "${sbxname}" ip netns add trefns || return 1
+ on_exit "ovs_sbx ${sbxname} ip netns del trefns"
+
+ for tun_type in gre vxlan geneve; do
+ info "testing ${tun_type} tunnel vport refcount"
+
+ ovs_sbx "${sbxname}" ip netns exec trefns \
+ python3 $ovs_base/ovs-dpctl.py \
+ add-dp dp-${tun_type} || return 1
+
+ ovs_sbx "${sbxname}" ip netns exec trefns \
+ python3 $ovs_base/ovs-dpctl.py \
+ add-if --no-lwt -t ${tun_type} \
+ dp-${tun_type} ovs-${tun_type}0 || return 1
+
+ ovs_wait ip -netns trefns link show \
+ ovs-${tun_type}0 >/dev/null 2>&1 || return 1
+
+ info "deleting dp - may hang if reference counting is broken"
+ ovs_sbx "${sbxname}" ip netns exec trefns \
+ python3 $ovs_base/ovs-dpctl.py \
+ del-dp dp-${tun_type} &
+
+ dev_removed() {
+ ! ip -netns trefns link show "$1" >/dev/null 2>&1
+ }
+ ovs_wait dev_removed dp-${tun_type} || return 1
+ ovs_wait dev_removed ovs-${tun_type}0 || return 1
+ done
+ return 0
+}
+
run_test() {
(
tname="$1"
diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
index 848f61fdcee09..bbe35e2718d26 100644
--- a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
+++ b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
@@ -11,7 +11,6 @@ import logging
import math
import multiprocessing
import re
-import socket
import struct
import sys
import time
@@ -2069,7 +2068,7 @@ class OvsVport(GenericNetlinkSocket):
elif vport_type == "internal":
return OvsVport.OVS_VPORT_TYPE_INTERNAL
elif vport_type == "gre":
- return OvsVport.OVS_VPORT_TYPE_INTERNAL
+ return OvsVport.OVS_VPORT_TYPE_GRE
elif vport_type == "vxlan":
return OvsVport.OVS_VPORT_TYPE_VXLAN
elif vport_type == "geneve":
@@ -2121,6 +2120,7 @@ class OvsVport(GenericNetlinkSocket):
)
TUNNEL_DEFAULTS = [("geneve", 6081),
+ ("gre", 0),
("vxlan", 4789)]
for tnl in TUNNEL_DEFAULTS:
@@ -2129,9 +2129,13 @@ class OvsVport(GenericNetlinkSocket):
dport = tnl[1]
if not lwt:
+ if tnl[0] == "gre":
+ # GRE tunnels have no options.
+ break
+
vportopt = OvsVport.ovs_vport_msg.vportopts()
vportopt["attrs"].append(
- ["OVS_TUNNEL_ATTR_DST_PORT", socket.htons(dport)]
+ ["OVS_TUNNEL_ATTR_DST_PORT", dport]
)
msg["attrs"].append(
["OVS_VPORT_ATTR_OPTIONS", vportopt]
@@ -2145,6 +2149,9 @@ class OvsVport(GenericNetlinkSocket):
geneve_port=dport,
geneve_collect_metadata=True,
geneve_udp_zero_csum6_rx=1)
+ elif tnl[0] == "gre":
+ ipr.link("add", ifname=vport_ifname, kind="gretap",
+ gre_collect_metadata=True)
elif tnl[0] == "vxlan":
ipr.link("add", ifname=vport_ifname, kind=tnl[0],
vxlan_learning=0, vxlan_collect_metadata=1,
@@ -2563,7 +2570,7 @@ def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()):
if vpo:
dpo = vpo.get_attr("OVS_TUNNEL_ATTR_DST_PORT")
if dpo:
- opts += " tnl-dport:%s" % socket.ntohs(dpo)
+ opts += " tnl-dport:%s" % dpo
print(
" port %d: %s (%s%s)"
% (
@@ -2632,7 +2639,7 @@ def main(argv):
"--ptype",
type=str,
default="netdev",
- choices=["netdev", "internal", "geneve", "vxlan"],
+ choices=["netdev", "internal", "gre", "geneve", "vxlan"],
help="Interface type (default netdev)",
)
addifcmd.add_argument(
@@ -2645,7 +2652,7 @@ def main(argv):
addifcmd.add_argument(
"-l",
"--lwt",
- type=bool,
+ action=argparse.BooleanOptionalAction,
default=True,
help="Use LWT infrastructure instead of vport (default true)."
)
--
2.53.0
^ permalink raw reply related
* [PATCH net v2 1/2] openvswitch: vport: fix self-deadlock on release of tunnel ports
From: Ilya Maximets @ 2026-04-30 23:38 UTC (permalink / raw)
To: netdev
Cc: Aaron Conole, Eelco Chaudron, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Shuah Khan, Yuan Tan,
Yang Yang, dev, linux-kernel, linux-kselftest, Ilya Maximets,
stable
In-Reply-To: <20260430233848.440994-1-i.maximets@ovn.org>
vports are used concurrently and protected by RCU, so netdev_put()
must happen after the RCU grace period. So, either in an RCU call or
after the synchronize_net(). The rtnl_delete_link() must happen under
RTNL and so can't be executed in RCU context. Calling synchronize_net()
while holding RTNL is not a good idea for performance and system
stability under load in general, so calling netdev_put() in RCU call
is the right solution here.
However,
when the device is deleted, rtnl_unlock() will call netdev_run_todo()
and block until all the references are gone. In the current code this
means that we never reach the call_rcu() and the vport is never freed
and the reference is never released, causing a self-deadlock on device
removal.
Fix that by moving the rcu_call() before the rtnl_unlock(), so the
scheduled RCU callback will be executed when synchronize_net() is
called from the rtnl_unlock()->netdev_run_todo() while the RTNL itself
is already released.
Fixes: 6931d21f87bc ("openvswitch: defer tunnel netdev_put to RCU release")
Cc: stable@vger.kernel.org
Acked-by: Eelco Chaudron <echaudro@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
---
net/openvswitch/vport-netdev.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c
index 12055af832dc0..a1df551e915bc 100644
--- a/net/openvswitch/vport-netdev.c
+++ b/net/openvswitch/vport-netdev.c
@@ -196,9 +196,13 @@ void ovs_netdev_tunnel_destroy(struct vport *vport)
*/
if (vport->dev->reg_state == NETREG_REGISTERED)
rtnl_delete_link(vport->dev, 0, NULL);
- rtnl_unlock();
+ /* We can't put the device reference yet, since it can still be in
+ * use, but rtnl_unlock()->netdev_run_todo() will block until all
+ * the references are released, so the RCU call must be before it.
+ */
call_rcu(&vport->rcu, vport_netdev_free);
+ rtnl_unlock();
}
EXPORT_SYMBOL_GPL(ovs_netdev_tunnel_destroy);
--
2.53.0
^ permalink raw reply related
* [PATCH net v2 0/2] openvswitch: fix self-deadlock on release of tunnel vports
From: Ilya Maximets @ 2026-04-30 23:38 UTC (permalink / raw)
To: netdev
Cc: Aaron Conole, Eelco Chaudron, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Shuah Khan, Yuan Tan,
Yang Yang, dev, linux-kernel, linux-kselftest, Ilya Maximets
Two patches - the fix for the actual bug and the selftest that reproduces it.
I missed the self-deadlock in the original patch that introduced the issue,
because testing required code modification in the ovs-vswitchd to force it to
use legacy tunnel ports. I thought I made the change correctly, but apparently
something went wrong and the tests were run with the standard LWT infra instead.
The selftest added in this patch set will at least prevent this kind of mistakes
in the future.
I mentioned, however, that these tunnel vports are legacy and not actually used
by ovs-vswitchd. RTM_NEWLINK + COLLECT_METADATA is used in conjunction with the
standard OVS_VPORT_TYPE_NETDEV instead since 2017. The code to use the legacy
tunnels still exists in ovs-vswitchd however, but only as a fallback for older
kernels and we're planning to remove it in the next release. I'll be sending an
RFC to remove support for these legacy tunnel types from the kernel, as they
serve no real purpose today and only increase the uAPI surface for CVEs, but
we need to fix the known bugs for stable versions.
Version 2:
- Added Ack from Eelco to the first patch (not to the second as it
changed a little).
- Removed now unused import socket in the dpctl.py [pylint/ruff].
- Regarding comments from both Sashiko instances on the selftest patch:
* The background process is not waited for / not killed.
If it hangs it will not be killable anyway, so it's not a problem.
* The 'gre' choice for dpctl.py --ptype is not fully handled for --lwt.
While this is not needed for this patch, I agree that it's not
fully consistent. Added the proper handling in the TUNNEL_DEFAULTS
loop in this version.
* Python version concern for argparse.BooleanOptionalAction.
Python 3.9 is the oldest supported version and it has it, so it's
not an issue. Creating extra detection will only complicate the
script with no real benefits.
Version 1:
https://lore.kernel.org/netdev/20260429151756.4157670-1-i.maximets@ovn.org/
Ilya Maximets (2):
openvswitch: vport: fix self-deadlock on release of tunnel ports
selftests: openvswitch: add tests for tunnel vport refcounting
net/openvswitch/vport-netdev.c | 6 ++-
.../selftests/net/openvswitch/openvswitch.sh | 37 +++++++++++++++++++
.../selftests/net/openvswitch/ovs-dpctl.py | 19 +++++++---
3 files changed, 55 insertions(+), 7 deletions(-)
--
2.53.0
^ permalink raw reply
* Re: [PATCH net-next v7 2/4] net: stmmac: eic7700: enable clocks before syscon access and correct RX sampling timing
From: Jakub Kicinski @ 2026-04-30 23:35 UTC (permalink / raw)
To: 李志
Cc: andrew+netdev, devicetree, davem, edumazet, robh, krzk+dt,
conor+dt, netdev, pabeni, mcoquelin.stm32, alexandre.torgue,
rmk+kernel, pjw, palmer, aou, alex, linux-riscv, linux-stm32,
linux-arm-kernel, linux-kernel, maxime.chevallier, ningyu, linmin,
pinkesh.vaghela, pritesh.patel, weishangjuan, horms
In-Reply-To: <2117464.7991.19ddd2125d1.Coremail.lizhi2@eswincomputing.com>
On Thu, 30 Apr 2026 14:43:50 +0800 (GMT+08:00) 李志 wrote:
> > Why Fixes? If eth1 never worked this is not a fix but new functionality
> > If you want to make this a fix to prevent incompatibility - cut it down
> > just to the eth0 changes.
> >
> Thank you for the suggestion.
>
> You're right that eth1 never worked at Gigabit speed, so this should
> not be treated as a fix.
>
> In v8, I will split the changes into two patches within the same series:
> - Patch 1 will contain only the fixes affecting the existing eth0
> functionality, and will keep the Fixes tag.
> - Patch 2 will add the eth1 support (RX clock inversion workaround)
> as new functionality, without a Fixes tag.
>
> Please let me know if you would prefer a different split or ordering.
If you want to consider some part of this commit a fix it has to be
posted separately to the net tree (rather than net-next).
Once it's merged and makes it way over to the net-next tree (each
Thursday) you can post the net-next chnages for eth1
^ permalink raw reply
* Re: [PATCH net-next 1/2] net: bcmasp: Divide init to allow partial bring up
From: Justin Chen @ 2026-04-30 23:24 UTC (permalink / raw)
To: Jakub Kicinski
Cc: netdev, bcm-kernel-feedback-list, pabeni, edumazet, davem,
andrew+netdev, florian.fainelli
In-Reply-To: <20260429232021.2186988-2-kuba@kernel.org>
On 4/29/26 4:20 PM, Jakub Kicinski wrote:
> This is an AI-generated review of your patch. The human sending this
> email says "Blindly sending out since there will be a repost,
> anyway."
> ---
> net: bcmasp: Divide init to allow partial bring up
>
> To prepare for a partial bring up of the interface during resume,
> we break apart the bcmasp_netif_init() function into smaller chunks
> that can be called as necessary.
>
>> diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c b/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c
>> index ec63f50a849e..aff0a6d84126 100644
>> --- a/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c
>> +++ b/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c
> [ ... ]
>> -static void umac_reset(struct bcmasp_intf *intf)
>> +static void umac_reset_and_init(struct bcmasp_intf *intf,
>> + const unsigned char *addr)
>> {
>> + struct phy_device *phydev = intf->ndev->phydev;
>> + u32 mac0, mac1;
>>
>> umac_wl(intf, 0x0, UMC_CMD);
>> umac_wl(intf, UMC_CMD_SW_RESET, UMC_CMD);
>> usleep_range(10, 100);
>> /* We hold the umac in reset and bring it out of
>> * reset when phy link is up.
>> */
>> -}
>>
>> -static void umac_set_hw_addr(struct bcmasp_intf *intf,
>> - const unsigned char *addr)
>> -{
>> - u32 mac0 = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) |
>> - addr[3];
>> - u32 mac1 = (addr[4] << 8) | addr[5];
>> + umac_wl(intf, 0x800, UMC_FRM_LEN);
>> + umac_wl(intf, 0xffff, UMC_PAUSE_CNTRL);
>> + umac_wl(intf, 0x800, UMC_RX_MAX_PKT_SZ);
>> +
>> + mac0 = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) |
>> + addr[3];
>> + mac1 = (addr[4] << 8) | addr[5];
>>
>> umac_wl(intf, mac0, UMC_MAC0);
>> umac_wl(intf, mac1, UMC_MAC1);
>> +
>> + /* Reset shadow values since we reset the umac */
>> + intf->old_duplex = -1;
>> + intf->old_link = -1;
>> + intf->old_pause = -1;
>> + phydev->eee_cfg.tx_lpi_timer = umac_rl(intf, UMC_EEE_LPI_TIMER);
>> }
>
> Since UMC_CMD_SW_RESET was just asserted above, won't reading
> UMC_EEE_LPI_TIMER here return 0 or a hardware default value, effectively
> overwriting the bootloader-configured EEE TX LPI timer?
>
> Also, since umac_reset_and_init() is now called unconditionally during
> bcmasp_interface_resume(), does this dangerously overwrite
> phydev->eee_cfg.tx_lpi_timer without holding mutex_lock(&phydev->lock)?
> This seems like it could create a data race with concurrent accesses, for
> example from ethtool.
>
Hmm yea, let me remove this and put into bcmasp_open. We only want to
get the timer value on open, not every suspend cycle.
> [ ... ]
>> +static void bcmasp_netif_init(struct net_device *dev)
>> +{
>> + struct bcmasp_intf *intf = netdev_priv(dev);
>>
>> bcmasp_init_tx(intf);
>> netif_napi_add_tx(intf->ndev, &intf->tx_napi, bcmasp_tx_poll);
>
> This existed prior to this patch, but since bcmasp_netif_init() and
> bcmasp_netif_deinit() are called directly from bcmasp_interface_resume()
> and bcmasp_interface_suspend() (which execute asynchronously without
> rtnl_lock), can modifying the global dev->napi_list via netif_napi_add_tx()
> and netif_napi_del() cause a race condition with concurrent list readers?
>
> Suspend and resume paths typically rely on napi_enable() and napi_disable()
> rather than adding and deleting the NAPI structures entirely.
>
Not sure about this, but such a fix shouldn't be part of a restructure
commit like this one. So I'll investigate further and fix in another
patch if needed.
> [ ... ]
>> @@ -1454,17 +1436,19 @@ int bcmasp_interface_resume(struct bcmasp_intf *intf)
>> if (ret)
>> return ret;
>>
>> - ret = bcmasp_netif_init(dev, false);
>> - if (ret)
>> - goto out;
>> + bcmasp_core_clock_set_intf(intf, true);
>>
>> bcmasp_resume_from_wol(intf);
>>
>> + bcmasp_phy_hw_prepare(intf);
>> +
>> + umac_reset_and_init(intf, dev->dev_addr);
>> +
>
> If Wake-on-LAN is disabled, bcmasp_interface_suspend() un-prepares the PHY
> by asserting reset and disabling clocks. By calling bcmasp_resume_from_wol()
> before bcmasp_phy_hw_prepare(), if EEE is enabled and invokes eee_fixup(),
> won't the MDIO access timeout or cause a bus error because the PHY hardware
> isn't prepared yet?
>
eee_fixup() doesn't use a MDIO accesses.
> Additionally, won't any WOL state changes (like clearing
> UMC_MPD_CTRL_MPD_EN) be immediately wiped out by the MAC software reset
> inside the subsequent umac_reset_and_init() call?
>
Yes, but this was the original (incorrect) logic and as mentioned below,
it is required in the next commit where we do not always reset the UNIMAC.
Justin
> I noticed this is fixed later in the series by commit 6994d859a69dd
> (net: bcmasp: Keep PHY link during WoL sleep cycle), but does leaving it
> like this here break bisection?
>
>> + bcmasp_netif_init(dev);
>> +
>> + phy_start(dev->phydev);
>> +
>> netif_device_attach(dev);
>>
>> return 0;
>> -
>> -out:
>> - clk_disable_unprepare(intf->parent->clk);
>> - return ret;
>> }
^ permalink raw reply
* Re: [PATCH iwl-next v2 1/6] ixgbe: E610: add discovering EEE capability
From: Jacob Keller @ 2026-04-30 23:20 UTC (permalink / raw)
To: Jedrzej Jagielski, intel-wired-lan
Cc: anthony.l.nguyen, netdev, Aleksandr Loktionov
In-Reply-To: <20260120134434.1931602-2-jedrzej.jagielski@intel.com>
On 1/20/2026 5:44 AM, Jedrzej Jagielski wrote:
> Add detecting and parsing EEE device capability.
>
> Recently EEE functionality support has been introduced to E610 FW.
> Currently ixgbe driver has no possibility to detect whether NVM
> loaded on given adapter supports EEE.
>
> There's dedicated device capability element reflecting FW support
> for given EEE link speed.
>
> Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
> Signed-off-by: Jedrzej Jagielski <jedrzej.jagielski@intel.com>
> ---
> drivers/net/ethernet/intel/ixgbe/ixgbe_e610.c | 3 +++
> drivers/net/ethernet/intel/ixgbe/ixgbe_type_e610.h | 7 +++++++
> include/linux/intel/libie/adminq.h | 1 +
> 3 files changed, 11 insertions(+)
>
> diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_e610.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_e610.c
> index c2f8189a0738..9ae1e3620ee1 100644
> --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_e610.c
> +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_e610.c
> @@ -628,6 +628,9 @@ static bool ixgbe_parse_e610_caps(struct ixgbe_hw *hw,
> (phys_id & IXGBE_EXT_TOPO_DEV_IMG_PROG_EN) != 0;
> break;
> }
> + case LIBIE_AQC_CAPS_EEE:
> + caps->eee_support = (u8)number;
> + break;
> default:
> /* Not one of the recognized common capabilities */
> return false;
> diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type_e610.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type_e610.h
> index cde148eec38d..11dc7fc71b71 100644
> --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type_e610.h
> +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type_e610.h
> @@ -892,6 +892,7 @@ struct ixgbe_hw_caps {
> u8 apm_wol_support;
> u8 acpi_prog_mthd;
> u8 proxy_support;
> + u8 eee_support;
> bool nvm_update_pending_nvm;
> bool nvm_update_pending_orom;
> bool nvm_update_pending_netlist;
> @@ -927,6 +928,12 @@ struct ixgbe_hw_caps {
>
> #define IXGBE_OROM_CIV_SIGNATURE "$CIV"
>
> +#define IXGBE_EEE_SUPPORT_100BASE_TX BIT(0)
> +#define IXGBE_EEE_SUPPORT_1000BASE_T BIT(1)
> +#define IXGBE_EEE_SUPPORT_10GBASE_T BIT(2)
> +#define IXGBE_EEE_SUPPORT_5GBASE_T BIT(3)
> +#define IXGBE_EEE_SUPPORT_2_5GBASE_T BIT(4)
> +
These macros are unused by this series. Sashiko complains about them
existing, since the code appears to actually use IXGBE_ACI_PHY_EEE_*
macros which have different bit positions.
Since they are unused, I am going to drop them in the version I submit
to net-next.
If they ever become needed or useful in the future, then we can always
add them back later.
^ permalink raw reply
* [PATCH net v5] net: dsa: mt7530: fix .get_stats64 sleeping in atomic context
From: Daniel Golle @ 2026-04-30 23:13 UTC (permalink / raw)
To: Chester A. Unal, Daniel Golle, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Matthias Brugger, AngeloGioacchino Del Regno, Russell King,
Christian Marangi, netdev, linux-kernel, linux-arm-kernel,
linux-mediatek
The .get_stats64 callback runs in atomic context, but on
MDIO-connected switches every register read acquires the MDIO bus
mutex, which can sleep:
[ 12.645973] BUG: sleeping function called from invalid context at kernel/locking/mutex.c:609
[ 12.654442] in_atomic(): 0, irqs_disabled(): 0, non_block: 0, pid: 759, name: grep
[ 12.663377] preempt_count: 0, expected: 0
[ 12.667410] RCU nest depth: 1, expected: 0
[ 12.671511] INFO: lockdep is turned off.
[ 12.675441] CPU: 0 UID: 0 PID: 759 Comm: grep Tainted: G S W 7.0.0+ #0 PREEMPT
[ 12.675453] Tainted: [S]=CPU_OUT_OF_SPEC, [W]=WARN
[ 12.675456] Hardware name: Bananapi BPI-R64 (DT)
[ 12.675459] Call trace:
[ 12.675462] show_stack+0x14/0x1c (C)
[ 12.675477] dump_stack_lvl+0x68/0x8c
[ 12.675487] dump_stack+0x14/0x1c
[ 12.675495] __might_resched+0x14c/0x220
[ 12.675504] __might_sleep+0x44/0x80
[ 12.675511] __mutex_lock+0x50/0xb10
[ 12.675523] mutex_lock_nested+0x20/0x30
[ 12.675532] mt7530_get_stats64+0x40/0x2ac
[ 12.675542] dsa_user_get_stats64+0x2c/0x40
[ 12.675553] dev_get_stats+0x44/0x1e0
[ 12.675564] dev_seq_printf_stats+0x24/0xe0
[ 12.675575] dev_seq_show+0x14/0x3c
[ 12.675583] seq_read_iter+0x37c/0x480
[ 12.675595] seq_read+0xd0/0xec
[ 12.675605] proc_reg_read+0x94/0xe4
[ 12.675615] vfs_read+0x98/0x29c
[ 12.675625] ksys_read+0x54/0xdc
[ 12.675633] __arm64_sys_read+0x18/0x20
[ 12.675642] invoke_syscall.constprop.0+0x54/0xec
[ 12.675653] do_el0_svc+0x3c/0xb4
[ 12.675662] el0_svc+0x38/0x200
[ 12.675670] el0t_64_sync_handler+0x98/0xdc
[ 12.675679] el0t_64_sync+0x158/0x15c
For MDIO-connected switches, poll MIB counters asynchronously using a
delayed workqueue every second and let .get_stats64 return the cached
values under a spinlock. A mod_delayed_work() call on each read
triggers an immediate refresh so counters stay responsive when queried
more frequently.
MMIO-connected switches (MT7988, EN7581, AN7583) are not affected
because their regmap does not sleep, so they continue to read MIB
counters directly in .get_stats64.
Fixes: 88c810f35ed5 ("net: dsa: mt7530: implement .get_stats64")
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Acked-by: Chester A. Unal <chester.a.unal@arinc9.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
v5:
* spin_lock_init() and INIT_DELAYED_WORK() in probe()
* drop teardown as cancel_delayed_work_sync() in remove() is
sufficient and now symmetric with init happening in probe()
v4:
* extract mt7530_stats_refresh() helper from mt7530_stats_poll()
-> mt7530_stats_poll() now just refreshes and re-arms
* call helper synchronously in mt753x_setup() to seed the cache
* avoid zeroed counters during the first poll interval
* avoid INITIAL_JIFFIES vs stats_last==0 wraparound on 32-bit
* swap deprecated system_wq for system_percpu_wq in get_stats64
* keeps on-demand refresh on the same queue as schedule_*_work
v3:
* move `stats_last` access under the spinlock to avoid potential race
v2:
* use spin_lock_bh()/spin_unlock_bh() to prevent potential deadlock
* rate-limit mod_delayed_work() refresh to at most once per 100ms
* move cancel_delayed_work_sync() after dsa_unregister_switch()
* add mt753x_teardown() callback to cancel the stats work
* fix commit message
---
drivers/net/dsa/mt7530.c | 68 ++++++++++++++++++++++++++++++++++++++--
drivers/net/dsa/mt7530.h | 8 +++++
2 files changed, 73 insertions(+), 3 deletions(-)
diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
index b9423389c2ef..e769914d726c 100644
--- a/drivers/net/dsa/mt7530.c
+++ b/drivers/net/dsa/mt7530.c
@@ -25,6 +25,9 @@
#include "mt7530.h"
+#define MT7530_STATS_POLL_INTERVAL (1 * HZ)
+#define MT7530_STATS_RATE_LIMIT (HZ / 10)
+
static struct mt753x_pcs *pcs_to_mt753x_pcs(struct phylink_pcs *pcs)
{
return container_of(pcs, struct mt753x_pcs, pcs);
@@ -906,10 +909,9 @@ static void mt7530_get_rmon_stats(struct dsa_switch *ds, int port,
*ranges = mt7530_rmon_ranges;
}
-static void mt7530_get_stats64(struct dsa_switch *ds, int port,
- struct rtnl_link_stats64 *storage)
+static void mt7530_read_port_stats64(struct mt7530_priv *priv, int port,
+ struct rtnl_link_stats64 *storage)
{
- struct mt7530_priv *priv = ds->priv;
uint64_t data;
/* MIB counter doesn't provide a FramesTransmittedOK but instead
@@ -951,6 +953,54 @@ static void mt7530_get_stats64(struct dsa_switch *ds, int port,
&storage->rx_crc_errors);
}
+static void mt7530_stats_refresh(struct mt7530_priv *priv)
+{
+ struct rtnl_link_stats64 stats = {};
+ struct dsa_port *dp;
+ int port;
+
+ dsa_switch_for_each_user_port(dp, priv->ds) {
+ port = dp->index;
+
+ mt7530_read_port_stats64(priv, port, &stats);
+
+ spin_lock_bh(&priv->stats_lock);
+ priv->ports[port].stats = stats;
+ priv->stats_last = jiffies;
+ spin_unlock_bh(&priv->stats_lock);
+ }
+}
+
+static void mt7530_stats_poll(struct work_struct *work)
+{
+ struct mt7530_priv *priv = container_of(work, struct mt7530_priv,
+ stats_work.work);
+
+ mt7530_stats_refresh(priv);
+ schedule_delayed_work(&priv->stats_work,
+ MT7530_STATS_POLL_INTERVAL);
+}
+
+static void mt7530_get_stats64(struct dsa_switch *ds, int port,
+ struct rtnl_link_stats64 *storage)
+{
+ struct mt7530_priv *priv = ds->priv;
+ bool refresh;
+
+ if (priv->bus) {
+ spin_lock_bh(&priv->stats_lock);
+ *storage = priv->ports[port].stats;
+ refresh = time_after(jiffies, priv->stats_last +
+ MT7530_STATS_RATE_LIMIT);
+ spin_unlock_bh(&priv->stats_lock);
+ if (refresh)
+ mod_delayed_work(system_percpu_wq,
+ &priv->stats_work, 0);
+ } else {
+ mt7530_read_port_stats64(priv, port, storage);
+ }
+}
+
static void mt7530_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
struct ethtool_eth_ctrl_stats *ctrl_stats)
{
@@ -3137,6 +3187,12 @@ mt753x_setup(struct dsa_switch *ds)
if (ret && priv->irq_domain)
mt7530_free_mdio_irq(priv);
+ if (!ret && priv->bus) {
+ mt7530_stats_refresh(priv);
+ schedule_delayed_work(&priv->stats_work,
+ MT7530_STATS_POLL_INTERVAL);
+ }
+
return ret;
}
@@ -3395,6 +3451,9 @@ mt7530_probe_common(struct mt7530_priv *priv)
priv->ds->ops = &mt7530_switch_ops;
priv->ds->phylink_mac_ops = &mt753x_phylink_mac_ops;
mutex_init(&priv->reg_mutex);
+ spin_lock_init(&priv->stats_lock);
+ INIT_DELAYED_WORK(&priv->stats_work, mt7530_stats_poll);
+
dev_set_drvdata(dev, priv);
return 0;
@@ -3409,6 +3468,9 @@ mt7530_remove_common(struct mt7530_priv *priv)
dsa_unregister_switch(priv->ds);
+ if (priv->bus)
+ cancel_delayed_work_sync(&priv->stats_work);
+
mutex_destroy(&priv->reg_mutex);
}
EXPORT_SYMBOL_GPL(mt7530_remove_common);
diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h
index 3e0090bed298..dd33b0df3419 100644
--- a/drivers/net/dsa/mt7530.h
+++ b/drivers/net/dsa/mt7530.h
@@ -796,6 +796,7 @@ struct mt7530_fdb {
* @pvid: The VLAN specified is to be considered a PVID at ingress. Any
* untagged frames will be assigned to the related VLAN.
* @sgmii_pcs: Pointer to PCS instance for SerDes ports
+ * @stats: Cached port statistics for MDIO-connected switches
*/
struct mt7530_port {
bool enable;
@@ -803,6 +804,7 @@ struct mt7530_port {
u32 pm;
u16 pvid;
struct phylink_pcs *sgmii_pcs;
+ struct rtnl_link_stats64 stats;
};
/* Port 5 mode definitions of the MT7530 switch */
@@ -875,6 +877,9 @@ struct mt753x_info {
* @create_sgmii: Pointer to function creating SGMII PCS instance(s)
* @active_cpu_ports: Holding the active CPU ports
* @mdiodev: The pointer to the MDIO device structure
+ * @stats_lock: Protects cached per-port stats from concurrent access
+ * @stats_work: Delayed work for polling MIB counters on MDIO switches
+ * @stats_last: Jiffies timestamp of last MIB counter poll
*/
struct mt7530_priv {
struct device *dev;
@@ -900,6 +905,9 @@ struct mt7530_priv {
int (*create_sgmii)(struct mt7530_priv *priv);
u8 active_cpu_ports;
struct mdio_device *mdiodev;
+ spinlock_t stats_lock; /* protects cached stats counters */
+ struct delayed_work stats_work;
+ unsigned long stats_last;
};
struct mt7530_hw_vlan_entry {
--
2.54.0
^ permalink raw reply related
* Re: [PATCH net-next 2/2] net: bcmasp: Keep PHY link during WoL sleep cycle
From: Justin Chen @ 2026-04-30 23:02 UTC (permalink / raw)
To: Jakub Kicinski
Cc: netdev, bcm-kernel-feedback-list, pabeni, edumazet, davem,
andrew+netdev, florian.fainelli
In-Reply-To: <20260429232030.2187222-1-kuba@kernel.org>
On 4/29/26 4:20 PM, Jakub Kicinski wrote:
> This is an AI-generated review of your patch. The human sending this
> email says "Blindly sending out since there will be a repost,
> anyway."
> ---
> net: bcmasp: Keep PHY link during WoL sleep cycle
>
> We currently more or less restart all the HW on resume. Since we also
> stop the PHY, it takes a while for the PHY link to be re-negotiated on
> resume. Instead of doing a full restart, we keep the HW state and the
> PHY link, that way we can resume network traffic with a much smaller
> delay.
>
>> diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c b/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c
>> index aff0a6d84126..bab2a4f82e4e 100644
>> --- a/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c
>> +++ b/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c
> [ ... ]
>> @@ -1383,15 +1384,20 @@ int bcmasp_interface_suspend(struct bcmasp_intf *intf)
>> {
>> struct device *kdev = &intf->parent->pdev->dev;
>> struct net_device *dev = intf->ndev;
>> + bool wake;
>>
>> if (!netif_running(dev))
>> return 0;
>>
>> netif_device_detach(dev);
>>
>> - bcmasp_netif_deinit(dev);
>> + wake = device_may_wakeup(kdev) && intf->wolopts;
>>
>> - if (!intf->wolopts) {
>> + bcmasp_netif_deinit(dev, !wake);
>> +
>> + if (wake) {
>> + bcmasp_suspend_to_wol(intf);
>> + } else {
>> bcmasp_phy_hw_unprepare(intf);
>>
>> /* If Wake-on-LAN is disabled, we can safely
>> @@ -1400,9 +1406,6 @@ int bcmasp_interface_suspend(struct bcmasp_intf *intf)
>> bcmasp_core_clock_set_intf(intf, false);
>> }
>>
>> - if (device_may_wakeup(kdev) && intf->wolopts)
>> - bcmasp_suspend_to_wol(intf);
>> -
>> clk_disable_unprepare(intf->parent->clk);
>
> If wake is true, phy_stop() is skipped during bcmasp_netif_deinit().
> This leaves the phylib state machine active and polling on its delayed
> workqueue.
>
> At the end of bcmasp_interface_suspend(), the main MAC clock is
> unconditionally disabled via clk_disable_unprepare(intf->parent->clk).
>
> If the PHY state machine executes while this clock is disabled, will it
> attempt to access PHY registers over the MDIO bus, resulting in a
> synchronous external abort (bus fault) or kernel panic?
>
> Could the active state machine also concurrently invoke the MAC's
> adjust_link callback while bcmasp_interface_resume() is executing,
> causing a data race on MAC registers?
>
Just tested this. Added an infinite loop after we call
clk_disable_unprepare() and don't see the state machine running, so I
think we are good here.
>> return 0;
>> }
>> @@ -1426,8 +1429,11 @@ static void bcmasp_resume_from_wol(struct bcmasp_intf *intf)
>>
>> int bcmasp_interface_resume(struct bcmasp_intf *intf)
>> {
> [ ... ]
>> + /* The interface might be HW reset in some suspend modes, so we may
>> + * need to restore the UNIMAC/PHY if that is the case.
>> + */
>> + reg = umac_rl(intf, UMC_CMD);
>> + if (wake && (reg & UMC_CMD_RX_EN)) {
>> + umac_enable_set(intf, UMC_CMD_TX_EN, 1);
>> + bcmasp_resume_from_wol(intf);
>> + } else {
>> + bcmasp_phy_hw_prepare(intf);
>> + umac_reset_and_init(intf, dev->dev_addr);
>> + }
>
> If wake is true but the MAC hardware was reset (reg & UMC_CMD_RX_EN
> is false), the code branches to the else block and skips calling
> bcmasp_resume_from_wol().
>
> Since bcmasp_resume_from_wol() is responsible for reversing the WoL
> configuration applied during suspend (like restoring the EEE fixup state
> and masking the wakeup interrupt), does skipping this teardown leave the
> wakeup interrupt unmasked during normal operation and risk an interrupt
> storm?
>
In the case that the MAC hardware is reset, there is no WoL
configuration to reverse since it was HW reset. Interrupts are also
reset, so the HW default of everything being masked means this isn't an
issue.
>> bcmasp_netif_init(dev);
>>
>> - phy_start(dev->phydev);
>> + /* If HW was reset, we need to force a link re-negotiation */
>> + if (wake && !(reg & UMC_CMD_RX_EN)) {
>> + phy_restart_aneg(dev->phydev);
>
> If the user has explicitly configured forced speed and duplex with
> autonegotiation disabled, will this unconditional call to
> phy_restart_aneg() override the user's configuration? It appears to
> enable autonegotiation without checking if phydev->autoneg ==
> AUTONEG_ENABLE.
>
> Also, because phy_stop() was skipped during suspend, the PHY state
> machine is still actively running in the background. Does calling
> phy_restart_aneg() without holding phydev->lock create a data race
> with the concurrent PHY state machine?
>
Will fix. I think need to just put the phy state back to PHY_UP state
and have the state machine take care of relinking whatever the
configuration is set at.
Justin
>> + phy_trigger_machine(dev->phydev);
>> + } else if (!wake) {
>> + phy_start(dev->phydev);
>> + }
>>
>> netif_device_attach(dev);
^ permalink raw reply
* Re: [Intel-wired-lan] [PATCH iwl-net v2] idpf: do not perform flow ops when netdev is detached
From: Jacob Keller @ 2026-04-30 22:46 UTC (permalink / raw)
To: Li Li
Cc: Simon Horman, anthony.l.nguyen, przemyslaw.kitszel, davem, kuba,
edumazet, intel-wired-lan, netdev, linux-kernel, decot,
anjali.singhai, sridhar.samudrala, brianvv, emil.s.tantilov,
stable
In-Reply-To: <CAODvEq4R_7SXfo5QJ0z=fM5npz1ZFFcCQTTKkdoLKtXyoDnjRA@mail.gmail.com>
On 4/30/2026 1:58 PM, Li Li wrote:
> Yes, OpenOnload calls idpf_set_rxnfc directly without checking if the
> netdev is detached first. I've discussed this with the team
> internally, and we decided to fix OpenOnload directly, rather than
> adding the check in idpf.
>
> Please feel free to drop this patch, thank you!
I've dropped the patch from Intel Wired LAN patchworks. Thanks!
^ permalink raw reply
* Re: [PATCH v2 1/2] netfilter: ip_tables: guard ipt_unregister_table_pre_exit against NULL ops
From: Florian Westphal @ 2026-04-30 22:16 UTC (permalink / raw)
To: Tristan Madani; +Cc: pablo, phil, netfilter-devel, netdev, stable, linux-kernel
In-Reply-To: <177758578919.118018.11758358602621428742@gmail.com>
Tristan Madani <tristmd@gmail.com> wrote:
> Florian Westphal <fw@strlen.de> wrote:
> > Is there a reproducer for this bug?
>
> Syzkaller hit it under failslab. The race is between the lazy
> init path in ipt_register_table() and cleanup_net(). The table
> becomes visible via xt_register_table() before ops is assigned,
> so pre_exit can find it with NULL ops.
If we have races between a thread calling ipt_register_table and
the netns cleanup path there is nothing we could ever do to fix it:
we are tearing down a live network namespace.
Something else must be going on.
^ permalink raw reply
* Re: [PATCH v2 1/2] netfilter: ip_tables: guard ipt_unregister_table_pre_exit against NULL ops
From: Tristan Madani @ 2026-04-30 21:49 UTC (permalink / raw)
To: fw; +Cc: pablo, phil, netfilter-devel, netdev, stable, linux-kernel
In-Reply-To: <afNYqx41pBCyDnjR@strlen.de>
Florian Westphal <fw@strlen.de> wrote:
> Is there a reproducer for this bug?
Syzkaller hit it under failslab. The race is between the lazy
init path in ipt_register_table() and cleanup_net(). The table
becomes visible via xt_register_table() before ops is assigned,
so pre_exit can find it with NULL ops.
Cleaned crash log:
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000003: 0000 [#1] PREEMPT SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000018-0x000000000000001f]
CPU: 1 UID: 0 PID: 604 Comm: kworker/u8:19 Tainted: G E 6.14.11 #1
Workqueue: netns cleanup_net
RIP: 0010:nf_unregister_net_hook net/netfilter/core.c:531 [inline]
RIP: 0010:nf_unregister_net_hooks+0xbc/0x150 net/netfilter/core.c:613
Call Trace:
<TASK>
ipt_unregister_table_pre_exit+0x8a/0xc0 net/ipv4/netfilter/ip_tables.c:1814
iptable_mangle_net_pre_exit+0x21/0x30 net/ipv4/netfilter/iptable_mangle.c:99
ops_pre_exit_list net/core/net_namespace.c:162 [inline]
cleanup_net+0x4b9/0xbe0 net/core/net_namespace.c:632
process_one_work+0x98f/0x1750 kernel/workqueue.c:3238
worker_thread+0x679/0xf50 kernel/workqueue.c:3402
kthread+0x3f0/0x7e0 kernel/kthread.c:464
ret_from_fork+0x60/0x90 arch/x86/kernel/process.c:153
</TASK>
> I'm working on a new unreg scheme to avoid rmmod racing with
> concurrent calls into iptables set/getsockopts.
That sounds like a different issue (rmmod vs sockopt). This one
is init vs cleanup_net -- the NULL ops window exists regardless
of the unreg scheme. V2 is a minimal guard for that.
Thanks,
Tristan
^ permalink raw reply
* [PATCH net-next] net: usb: r8152: add TRENDnet TUC-ET2G v2.0
From: Aleksander Jan Bajkowski @ 2026-04-30 21:34 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, hayeswang,
hsu.chih.kai, kees, mail, linux-usb, netdev, linux-kernel
Cc: Aleksander Jan Bajkowski, Andrew Lunn
The TRENDnet TUC-ET2G V2.0 is an RTL8156B based 2.5G Ethernet controller.
Add the vendor and product ID values to the driver. This makes Ethernet
work with the adapter.
Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/usb/r8152.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index 7337bf1b7d6a..1ace1d2398c9 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -10138,6 +10138,7 @@ static const struct usb_device_id rtl8152_table[] = {
{ USB_DEVICE(VENDOR_ID_DELL, 0xb097) },
{ USB_DEVICE(VENDOR_ID_ASUS, 0x1976) },
{ USB_DEVICE(VENDOR_ID_TRENDNET, 0xe02b) },
+ { USB_DEVICE(VENDOR_ID_TRENDNET, 0xe02c) },
{}
};
--
2.53.0
^ permalink raw reply related
* [PATCH net] openvswitch: vport: fix race between tunnel creation and linking
From: Ilya Maximets @ 2026-04-30 21:32 UTC (permalink / raw)
To: netdev
Cc: Aaron Conole, Eelco Chaudron, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, dev, linux-kernel,
Ilya Maximets, Yuan Tan, Yifan Wu, Juefei Pu, Xin Liu, Yang Yang
When a tunnel vport is created it first creates the tunnel device, e.g.,
with geneve_dev_create_fb(), then it calls ovs_netdev_link() to take a
reference and link it to the device that represents openvswitch datapath.
The creation of the device is happening under RTNL, but then RTNL is
released and re-acquired to find the device by name. It is technically
possible for the tunnel device to be re-named or deleted within that
window while RTNL is not held, and some other device created in its
place. This will cause a non-tunnel device to be referenced in the
vport and tunnel-specific functions used on it, e.g. vxlan_get_options()
that directly casts the private netdev data into a struct vxlan_dev
causing an invalid memory access:
BUG: KASAN: slab-use-after-free in vxlan_get_options+0x323/0x3a0
vxlan_get_options+0x323/0x3a0
ovs_vport_cmd_new+0x6e3/0xd30
Fix that by taking a reference to the just created device before
releasing RTNL. This ensures that the device in the vport is always
the one that was just created. The search by name is only needed
for a standard vport-netdev that links pre-existing devices, so that
functionality and device type checks are moved to netdev_create().
It is also awkward that ovs_netdev_link() takes ownership of the vport
and destroys it on failure. It doesn't know the type of the port it is
dealing with, so we need to pass down the indicator that it's a tunnel,
so the link can be properly deleted on failure.
It's possible to refactor the logic to make the ovs_netdev_link() do
only the linking part and let the callers perform a proper destruction,
but it will be much more code for each legacy tunnel port type, so it
is not worth it for the bug fix.
Fixes: 614732eaa12d ("openvswitch: Use regular VXLAN net_device device")
Reported-by: Yuan Tan <tanyuan98@outlook.com>
Reported-by: Yifan Wu <yifanwucs@gmail.com>
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
Reported-by: Xin Liu <bird@lzu.edu.cn>
Reported-by: Yang Yang <n05ec@lzu.edu.cn>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
---
net/openvswitch/vport-geneve.c | 5 ++-
net/openvswitch/vport-gre.c | 5 ++-
net/openvswitch/vport-netdev.c | 58 ++++++++++++++++++++--------------
net/openvswitch/vport-netdev.h | 2 +-
net/openvswitch/vport-vxlan.c | 5 ++-
5 files changed, 48 insertions(+), 27 deletions(-)
diff --git a/net/openvswitch/vport-geneve.c b/net/openvswitch/vport-geneve.c
index b10e1602c6b14..cb5ea4424ffc8 100644
--- a/net/openvswitch/vport-geneve.c
+++ b/net/openvswitch/vport-geneve.c
@@ -97,6 +97,9 @@ static struct vport *geneve_tnl_create(const struct vport_parms *parms)
goto error;
}
+ vport->dev = dev;
+ netdev_hold(vport->dev, &vport->dev_tracker, GFP_KERNEL);
+
rtnl_unlock();
return vport;
error:
@@ -111,7 +114,7 @@ static struct vport *geneve_create(const struct vport_parms *parms)
if (IS_ERR(vport))
return vport;
- return ovs_netdev_link(vport, parms->name);
+ return ovs_netdev_link(vport, true);
}
static struct vport_ops ovs_geneve_vport_ops = {
diff --git a/net/openvswitch/vport-gre.c b/net/openvswitch/vport-gre.c
index 4014c9b5eb798..6cb5a697b396a 100644
--- a/net/openvswitch/vport-gre.c
+++ b/net/openvswitch/vport-gre.c
@@ -63,6 +63,9 @@ static struct vport *gre_tnl_create(const struct vport_parms *parms)
return ERR_PTR(err);
}
+ vport->dev = dev;
+ netdev_hold(vport->dev, &vport->dev_tracker, GFP_KERNEL);
+
rtnl_unlock();
return vport;
}
@@ -75,7 +78,7 @@ static struct vport *gre_create(const struct vport_parms *parms)
if (IS_ERR(vport))
return vport;
- return ovs_netdev_link(vport, parms->name);
+ return ovs_netdev_link(vport, true);
}
static struct vport_ops ovs_gre_vport_ops = {
diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c
index 12055af832dc0..a92ca8b37f96a 100644
--- a/net/openvswitch/vport-netdev.c
+++ b/net/openvswitch/vport-netdev.c
@@ -73,37 +73,21 @@ static struct net_device *get_dpdev(const struct datapath *dp)
return local->dev;
}
-struct vport *ovs_netdev_link(struct vport *vport, const char *name)
+struct vport *ovs_netdev_link(struct vport *vport, bool tunnel)
{
int err;
- vport->dev = dev_get_by_name(ovs_dp_get_net(vport->dp), name);
- if (!vport->dev) {
+ if (WARN_ON_ONCE(!vport->dev)) {
err = -ENODEV;
goto error_free_vport;
}
- /* Ensure that the device exists and that the provided
- * name is not one of its aliases.
- */
- if (strcmp(name, ovs_vport_name(vport))) {
- err = -ENODEV;
- goto error_put;
- }
- netdev_tracker_alloc(vport->dev, &vport->dev_tracker, GFP_KERNEL);
- if (vport->dev->flags & IFF_LOOPBACK ||
- (vport->dev->type != ARPHRD_ETHER &&
- vport->dev->type != ARPHRD_NONE) ||
- ovs_is_internal_dev(vport->dev)) {
- err = -EINVAL;
- goto error_put;
- }
rtnl_lock();
err = netdev_master_upper_dev_link(vport->dev,
get_dpdev(vport->dp),
NULL, NULL, NULL);
if (err)
- goto error_unlock;
+ goto error_put_unlock;
err = netdev_rx_handler_register(vport->dev, netdev_frame_hook,
vport);
@@ -119,10 +103,11 @@ struct vport *ovs_netdev_link(struct vport *vport, const char *name)
error_master_upper_dev_unlink:
netdev_upper_dev_unlink(vport->dev, get_dpdev(vport->dp));
-error_unlock:
- rtnl_unlock();
-error_put:
+error_put_unlock:
+ if (tunnel && vport->dev->reg_state == NETREG_REGISTERED)
+ rtnl_delete_link(vport->dev, 0, NULL);
netdev_put(vport->dev, &vport->dev_tracker);
+ rtnl_unlock();
error_free_vport:
ovs_vport_free(vport);
return ERR_PTR(err);
@@ -132,12 +117,39 @@ EXPORT_SYMBOL_GPL(ovs_netdev_link);
static struct vport *netdev_create(const struct vport_parms *parms)
{
struct vport *vport;
+ int err;
vport = ovs_vport_alloc(0, &ovs_netdev_vport_ops, parms);
if (IS_ERR(vport))
return vport;
- return ovs_netdev_link(vport, parms->name);
+ vport->dev = dev_get_by_name(ovs_dp_get_net(vport->dp), parms->name);
+ if (!vport->dev) {
+ err = -ENODEV;
+ goto error_free_vport;
+ }
+ netdev_tracker_alloc(vport->dev, &vport->dev_tracker, GFP_KERNEL);
+
+ /* Ensure that the provided name is not an alias. */
+ if (strcmp(parms->name, ovs_vport_name(vport))) {
+ err = -ENODEV;
+ goto error_put;
+ }
+
+ if (vport->dev->flags & IFF_LOOPBACK ||
+ (vport->dev->type != ARPHRD_ETHER &&
+ vport->dev->type != ARPHRD_NONE) ||
+ ovs_is_internal_dev(vport->dev)) {
+ err = -EINVAL;
+ goto error_put;
+ }
+
+ return ovs_netdev_link(vport, false);
+error_put:
+ netdev_put(vport->dev, &vport->dev_tracker);
+error_free_vport:
+ ovs_vport_free(vport);
+ return ERR_PTR(err);
}
static void vport_netdev_free(struct rcu_head *rcu)
diff --git a/net/openvswitch/vport-netdev.h b/net/openvswitch/vport-netdev.h
index c5d83a43bfc49..6c0d7366f9862 100644
--- a/net/openvswitch/vport-netdev.h
+++ b/net/openvswitch/vport-netdev.h
@@ -13,7 +13,7 @@
struct vport *ovs_netdev_get_vport(struct net_device *dev);
-struct vport *ovs_netdev_link(struct vport *vport, const char *name);
+struct vport *ovs_netdev_link(struct vport *vport, bool tunnel);
void ovs_netdev_detach_dev(struct vport *);
int __init ovs_netdev_init(void);
diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c
index 0b881b043bcf4..c1b37b50d29e1 100644
--- a/net/openvswitch/vport-vxlan.c
+++ b/net/openvswitch/vport-vxlan.c
@@ -126,6 +126,9 @@ static struct vport *vxlan_tnl_create(const struct vport_parms *parms)
goto error;
}
+ vport->dev = dev;
+ netdev_hold(vport->dev, &vport->dev_tracker, GFP_KERNEL);
+
rtnl_unlock();
return vport;
error:
@@ -140,7 +143,7 @@ static struct vport *vxlan_create(const struct vport_parms *parms)
if (IS_ERR(vport))
return vport;
- return ovs_netdev_link(vport, parms->name);
+ return ovs_netdev_link(vport, true);
}
static struct vport_ops ovs_vxlan_netdev_vport_ops = {
--
2.53.0
^ permalink raw reply related
* Re: (subset) [PATCH v2 0/4] Use the QMI service IDs from the QMI header
From: Jeff Johnson @ 2026-04-30 21:30 UTC (permalink / raw)
To: konradybcio, andersson, Daniel Lezcano
Cc: linux-kernel, Alex Elder, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Jeff Johnson,
Mathieu Poirier, Srinivas Kandagatla, Jaroslav Kysela,
Takashi Iwai, Kees Cook, Greg Kroah-Hartman, Arnd Bergmann,
Mark Brown, Wesley Cheng, netdev, linux-wireless, ath10k, ath11k,
ath12k, linux-arm-msm, linux-remoteproc, linux-sound
In-Reply-To: <20260316171419.2619620-1-daniel.lezcano@oss.qualcomm.com>
On Mon, 16 Mar 2026 18:14:10 +0100, Daniel Lezcano wrote:
> The different subsystems implementing the QMI service protocol are
> using their own definition of the service id. It is not a problem but
> it results on having those duplicated with different names but the
> same value and without consistency in their name.
>
> The QMI service IDs are defined in the qmi.h header file. Use those
> instead of defining the IDs in the protocol implementation file. It
> will result in unifying and providing a consistent way to represent
> the supported protocols.
>
> [...]
Applied, thanks!
[2/4] wifi: ath: Use the unified QMI service ID instead of defining it locally
commit: 0e39eea57626e545c596734c0d250c1735f1a7e5
Best regards,
--
Jeff Johnson <jeff.johnson@oss.qualcomm.com>
^ permalink raw reply
* Re: (subset) [PATCH v3 00/14] Remove redundant rcu_read_lock/unlock() in spin_lock
From: Jeff Johnson @ 2026-04-30 21:30 UTC (permalink / raw)
To: tj, tony.luck, jani.nikula, ap420073, jv, freude, bcrl, trondmy,
longman, kees, pengdonglin
Cc: bigeasy, hdanton, paulmck, linux-kernel, linux-rt-devel,
linux-nfs, linux-aio, linux-fsdevel, linux-security-module,
netdev, intel-gfx, linux-wireless, linux-acpi, linux-s390,
cgroups
In-Reply-To: <20250916044735.2316171-1-dolinux.peng@gmail.com>
On Tue, 16 Sep 2025 12:47:21 +0800, pengdonglin wrote:
> Since commit a8bb74acd8efe ("rcu: Consolidate RCU-sched update-side function definitions")
> there is no difference between rcu_read_lock(), rcu_read_lock_bh() and
> rcu_read_lock_sched() in terms of RCU read section and the relevant grace
> period. That means that spin_lock(), which implies rcu_read_lock_sched(),
> also implies rcu_read_lock().
>
> There is no need no explicitly start a RCU read section if one has already
> been started implicitly by spin_lock().
>
> [...]
Applied, thanks!
[14/14] wifi: ath9k: Remove redundant rcu_read_lock/unlock() in spin_lock
commit: c4f518736472c8cfbf1d304e01c631babd2bbf34
Best regards,
--
Jeff Johnson <jeff.johnson@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH v1 bpf 2/2] selftest: bpf: Add test for bpf_tcp_sock() and RAW socket.
From: Kuniyuki Iwashima @ 2026-04-30 21:14 UTC (permalink / raw)
To: Martin KaFai Lau, Daniel Borkmann, Alexei Starovoitov,
Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi
Cc: John Fastabend, Stanislav Fomichev, Song Liu, Yonghong Song,
Jiri Olsa, Eric Dumazet, Kuniyuki Iwashima, bpf, netdev
In-Reply-To: <CAAVpQUC83MxhfV-sreAPvfPJoqW=G20CPCPdRaQ6hvMt_khFtw@mail.gmail.com>
On Thu, Apr 30, 2026 at 1:32 PM Kuniyuki Iwashima <kuniyu@google.com> wrote:
>
> On Thu, Apr 30, 2026 at 11:44 AM Kuniyuki Iwashima <kuniyu@google.com> wrote:
> >
> > Let's extend sockopt_sk.c to cover bpf_tcp_sock() for the
> > wrong socket type.
> >
> > Before:
> > # ./test_progs -t sockopt_sk
> > [ 151.948613] ==================================================================
> > [ 151.951376] BUG: KASAN: slab-out-of-bounds in sol_tcp_sockopt+0xc7/0x8e0
> > [ 151.954159] Read of size 8 at addr ffff88801083d760 by task test_progs/1259
> > ...
> > run_test:FAIL:getsetsockopt unexpected error: -1 (errno 22)
> > #427 sockopt_sk:FAIL
> >
> > After:
> > #427 sockopt_sk:OK
> >
> > While at it, missing free() is fixed up.
> >
> > Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
> > ---
> > .../selftests/bpf/prog_tests/sockopt_sk.c | 17 ++++++++++++++++-
> > tools/testing/selftests/bpf/progs/sockopt_sk.c | 16 ++++++++++++++++
> > 2 files changed, 32 insertions(+), 1 deletion(-)
> >
> > diff --git a/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c b/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c
> > index 53637431ec5d..87e771c8991f 100644
> > --- a/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c
> > +++ b/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c
> > @@ -190,7 +190,7 @@ static int getsetsockopt(void)
> > fd = socket(AF_NETLINK, SOCK_RAW, 0);
> > if (fd < 0) {
> > log_err("Failed to create AF_NETLINK socket");
> > - return -1;
> > + goto err;
> > }
> >
> > buf.u32 = 1;
> > @@ -211,6 +211,21 @@ static int getsetsockopt(void)
> > }
> > ASSERT_EQ(optlen, 8, "Unexpected NETLINK_LIST_MEMBERSHIPS value");
> >
> > + /* Trick bpf_tcp_sock() with IPPROTO_TCP */
> > + close(fd);
> > + fd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
> > + if (fd < 0) {
> > + log_err("Failed to create RAW socket");
> > + goto err;
> > + }
> > +
> > + optlen = 60;
> > + err = setsockopt(fd, SOL_TCP, TCP_SAVED_SYN, &buf, optlen);
> > + if (err) {
>
> Ugh, I forgot to commit s/err/!err/ change.. :/
>
I'll include Matt's followup and extend the test
accordingly in v2.
https://lore.kernel.org/mptcp/20260430-mptcp-bpf-mptcp-sock-type-v1-1-d2ed5cda7da9@kernel.org/
^ permalink raw reply
* [PATCH 6/6] MAINTAINERS: BITOPS: include bitrev.[ch]
From: Yury Norov @ 2026-04-30 21:13 UTC (permalink / raw)
To: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Yury Norov, Rasmus Villemoes, Arnd Bergmann, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Morton, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Jinjie Ruan, linux-kernel, linux-riscv, linux-arch, netdev, bpf
Cc: Yury Norov
In-Reply-To: <20260430211351.658193-1-ynorov@nvidia.com>
Arch bitrev API is covered in MAINTAINERS under the BITOPS entry,
while generic bitrev is unmaintained. Move it under BITOPS too.
Signed-off-by: Yury Norov <ynorov@nvidia.com>
---
MAINTAINERS | 2 ++
1 file changed, 2 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 27a073f53cea..b69db2a7031f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4549,7 +4549,9 @@ F: arch/*/lib/bitops.c
F: include/asm-generic/bitops
F: include/asm-generic/bitops.h
F: include/linux/bitops.h
+F: include/linux/bitrev.h
F: include/linux/count_zeros.h
+F: lib/bitrev.c
F: lib/hweight.c
F: lib/test_bitops.c
F: lib/tests/bitops_kunit.c
--
2.51.0
^ permalink raw reply related
* [PATCH 5/6] lib: compile generic bitrev.c conditionally on GENERIC_BITREVERSE
From: Yury Norov @ 2026-04-30 21:13 UTC (permalink / raw)
To: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Yury Norov, Rasmus Villemoes, Arnd Bergmann, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Morton, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Jinjie Ruan, linux-kernel, linux-riscv, linux-arch, netdev, bpf
Cc: Yury Norov
In-Reply-To: <20260430211351.658193-1-ynorov@nvidia.com>
The file is compiled based on CONFIG_BITREVERSE=y, but everything inside
is protected with CONFIG_GENERIC_BITREVERSE.
Make it simpler by switching the Makefile to compile lib/bitrev.c based
on the proper config.
Signed-off-by: Yury Norov <ynorov@nvidia.com>
---
lib/Makefile | 2 +-
lib/bitrev.c | 3 ---
2 files changed, 1 insertion(+), 4 deletions(-)
diff --git a/lib/Makefile b/lib/Makefile
index f33a24bf1c19..23e07d19d01c 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -145,7 +145,7 @@ obj-$(CONFIG_DEBUG_PREEMPT) += smp_processor_id.o
obj-$(CONFIG_LIST_HARDENED) += list_debug.o
obj-$(CONFIG_DEBUG_OBJECTS) += debugobjects.o
-obj-$(CONFIG_BITREVERSE) += bitrev.o
+obj-$(CONFIG_GENERIC_BITREVERSE) += bitrev.o
obj-$(CONFIG_LINEAR_RANGES) += linear_ranges.o
obj-$(CONFIG_PACKING) += packing.o
obj-$(CONFIG_PACKING_KUNIT_TEST) += packing_test.o
diff --git a/lib/bitrev.c b/lib/bitrev.c
index 3a53ff67aeba..05088231f31f 100644
--- a/lib/bitrev.c
+++ b/lib/bitrev.c
@@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-only
-#ifdef CONFIG_GENERIC_BITREVERSE
#include <linux/types.h>
#include <linux/module.h>
#include <linux/bitrev.h>
@@ -43,5 +42,3 @@ const u8 byte_rev_table[256] = {
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
};
EXPORT_SYMBOL_GPL(byte_rev_table);
-
-#endif /* CONFIG_GENERIC_BITREVERSE */
--
2.51.0
^ permalink raw reply related
* [PATCH 4/6] arch/riscv: Add bitrev.h file to support rev8 and brev8
From: Yury Norov @ 2026-04-30 21:13 UTC (permalink / raw)
To: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Yury Norov, Rasmus Villemoes, Arnd Bergmann, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Morton, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Jinjie Ruan, linux-kernel, linux-riscv, linux-arch, netdev, bpf
Cc: Yury Norov, David Laight
In-Reply-To: <20260430211351.658193-1-ynorov@nvidia.com>
From: Jinjie Ruan <ruanjinjie@huawei.com>
The RISC-V Bit-manipulation Extension for Cryptography (Zbkb) provides
the 'brev8' instruction, which reverses the bits within each byte.
Combined with the 'rev8' instruction (from Zbb or Zbkb), which reverses
the byte order of a register, we can efficiently implement 16-bit,
32-bit, and (on RV64) 64-bit bit reversal.
This is significantly faster than the default software table-lookup
implementation in lib/bitrev.c, as it replaces memory accesses and
multiple arithmetic operations with just two or three hardware
instructions.
Select HAVE_ARCH_BITREVERSE as well as GENERIC_BITREVERSE,
and provide <asm/bitrev.h> to utilize these instructions when
the Zbkb extension is available at runtime via the alternatives
mechanism.
Link: https://docs.riscv.org/reference/isa/unpriv/b-st-ext.html
Suggested-by: David Laight <david.laight.linux@gmail.com>
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
Signed-off-by: Yury Norov <ynorov@nvidia.com>
---
arch/riscv/Kconfig | 2 ++
arch/riscv/include/asm/bitrev.h | 51 +++++++++++++++++++++++++++++++++
2 files changed, 53 insertions(+)
create mode 100644 arch/riscv/include/asm/bitrev.h
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index d235396c4514..d32309846fa3 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -104,6 +104,7 @@ config RISCV
select FUNCTION_ALIGNMENT_8B if DYNAMIC_FTRACE_WITH_CALL_OPS
select GENERIC_ARCH_TOPOLOGY
select GENERIC_ATOMIC64 if !64BIT
+ select GENERIC_BITREVERSE
select GENERIC_CLOCKEVENTS_BROADCAST if SMP
select GENERIC_CPU_DEVICES
select GENERIC_CPU_VULNERABILITIES
@@ -128,6 +129,7 @@ config RISCV
select HAS_IOPORT if MMU
select HAVE_ALIGNED_STRUCT_PAGE
select HAVE_ARCH_AUDITSYSCALL
+ select HAVE_ARCH_BITREVERSE if RISCV_ISA_ZBKB
select HAVE_ARCH_HUGE_VMALLOC if HAVE_ARCH_HUGE_VMAP
select HAVE_ARCH_HUGE_VMAP if MMU && 64BIT
select HAVE_ARCH_JUMP_LABEL
diff --git a/arch/riscv/include/asm/bitrev.h b/arch/riscv/include/asm/bitrev.h
new file mode 100644
index 000000000000..4b9b8d34cc3b
--- /dev/null
+++ b/arch/riscv/include/asm/bitrev.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_BITREV_H
+#define __ASM_BITREV_H
+
+#include <linux/types.h>
+#include <asm/cpufeature-macros.h>
+#include <asm/hwcap.h>
+#include <asm-generic/bitops/__bitrev.h>
+
+static __always_inline __attribute_const__ u32 __arch_bitrev32(u32 x)
+{
+ unsigned long result;
+
+ if (!riscv_has_extension_likely(RISCV_ISA_EXT_ZBKB))
+ return generic___bitrev32(x);
+
+ asm volatile(
+ ".option push\n"
+ ".option arch,+zbkb\n"
+ "rev8 %0, %1\n"
+ "brev8 %0, %0\n"
+ ".option pop"
+ : "=r" (result) : "r" ((long)x)
+ );
+
+ return result >> (__riscv_xlen - 32);
+}
+
+static __always_inline __attribute_const__ u16 __arch_bitrev16(u16 x)
+{
+ return __arch_bitrev32(x) >> 16;
+}
+
+static __always_inline __attribute_const__ u8 __arch_bitrev8(u8 x)
+{
+ unsigned long result;
+
+ if (!riscv_has_extension_likely(RISCV_ISA_EXT_ZBKB))
+ return generic___bitrev8(x);
+
+ asm volatile(
+ ".option push\n"
+ ".option arch,+zbkb\n"
+ "brev8 %0, %1\n"
+ ".option pop"
+ : "=r" (result) : "r" ((long)x)
+ );
+
+ return result;
+}
+#endif
--
2.51.0
^ permalink raw reply related
* [PATCH 3/6] bitops: Define generic __bitrev8/16/32 for reuse
From: Yury Norov @ 2026-04-30 21:13 UTC (permalink / raw)
To: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Yury Norov, Rasmus Villemoes, Arnd Bergmann, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Morton, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Jinjie Ruan, linux-kernel, linux-riscv, linux-arch, netdev, bpf
Cc: Yury Norov
In-Reply-To: <20260430211351.658193-1-ynorov@nvidia.com>
From: Jinjie Ruan <ruanjinjie@huawei.com>
Define generic __bitrev8/16/32 using the implementation
in <linux/bitrev.h>, so they can be reused in <asm/bitrev.h>,
such as RISCV.
Reviewed-by: Yury Norov <ynorov@nvidia.com>
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
Signed-off-by: Yury Norov <ynorov@nvidia.com>
---
include/asm-generic/bitops/__bitrev.h | 25 +++++++++++++++++++++++++
include/linux/bitrev.h | 20 ++++----------------
2 files changed, 29 insertions(+), 16 deletions(-)
create mode 100644 include/asm-generic/bitops/__bitrev.h
diff --git a/include/asm-generic/bitops/__bitrev.h b/include/asm-generic/bitops/__bitrev.h
new file mode 100644
index 000000000000..f06af929678d
--- /dev/null
+++ b/include/asm-generic/bitops/__bitrev.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_GENERIC_BITOPS___BITREV_H_
+#define _ASM_GENERIC_BITOPS___BITREV_H_
+
+#ifdef CONFIG_GENERIC_BITREVERSE
+#include <asm/types.h>
+
+extern u8 const byte_rev_table[256];
+static __always_inline __attribute_const__ u8 generic___bitrev8(u8 byte)
+{
+ return byte_rev_table[byte];
+}
+
+static __always_inline __attribute_const__ u16 generic___bitrev16(u16 x)
+{
+ return (generic___bitrev8(x & 0xff) << 8) | generic___bitrev8(x >> 8);
+}
+
+static __always_inline __attribute_const__ u32 generic___bitrev32(u32 x)
+{
+ return (generic___bitrev16(x & 0xffff) << 16) | generic___bitrev16(x >> 16);
+}
+#endif /* CONFIG_GENERIC_BITREVERSE */
+
+#endif /* _ASM_GENERIC_BITOPS___BITREV_H_ */
diff --git a/include/linux/bitrev.h b/include/linux/bitrev.h
index d35b8ec1c485..11620a70e776 100644
--- a/include/linux/bitrev.h
+++ b/include/linux/bitrev.h
@@ -12,22 +12,10 @@
#define __bitrev8 __arch_bitrev8
#else
-extern u8 const byte_rev_table[256];
-static inline u8 __bitrev8(u8 byte)
-{
- return byte_rev_table[byte];
-}
-
-static inline u16 __bitrev16(u16 x)
-{
- return (__bitrev8(x & 0xff) << 8) | __bitrev8(x >> 8);
-}
-
-static inline u32 __bitrev32(u32 x)
-{
- return (__bitrev16(x & 0xffff) << 16) | __bitrev16(x >> 16);
-}
-
+#include <asm-generic/bitops/__bitrev.h>
+#define __bitrev32 generic___bitrev32
+#define __bitrev16 generic___bitrev16
+#define __bitrev8 generic___bitrev8
#endif /* CONFIG_HAVE_ARCH_BITREVERSE */
#define __bitrev8x4(x) (__bitrev32(swab32(x)))
--
2.51.0
^ permalink raw reply related
* [PATCH 2/6] lib/bitrev: Introduce GENERIC_BITREVERSE and cleanup Kconfig
From: Yury Norov @ 2026-04-30 21:13 UTC (permalink / raw)
To: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Yury Norov, Rasmus Villemoes, Arnd Bergmann, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Morton, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Jinjie Ruan, linux-kernel, linux-riscv, linux-arch, netdev, bpf
Cc: Yury Norov, David Laight
In-Reply-To: <20260430211351.658193-1-ynorov@nvidia.com>
From: Jinjie Ruan <ruanjinjie@huawei.com>
Currently, the bit reversal lookup table is controlled by
!HAVE_ARCH_BITREVERSE. This makes it difficult for architectures to
provide a hardware-accelerated implementation while still falling
back to the generic table for specific configurations.
Introduce CONFIG_GENERIC_BITREVERSE to explicitly manage the generic
lookup table implementation. By using 'def_bool !HAVE_ARCH_BITREVERSE'
with a dependency on 'BITREVERSE', we ensure that:
1. The table is only compiled when needed.
2. The .config is not polluted with useless options when BITREVERSE
is disabled.
3. Avoids bloating the .data section for architectures that have
full hardware bit-reverse support and don't need the table.
Update lib/bitrev.c to use CONFIG_GENERIC_BITREVERSE instead of
checking the absence of HAVE_ARCH_BITREVERSE. This provides a
cleaner interface for architectures like RISC-V that may want to
selectively use the generic implementation as a fallback.
Suggested-by: David Laight <David.Laight@ACULAB.COM>
Suggested-by: Yury Norov <ynorov@nvidia.com>
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
Signed-off-by: Yury Norov <ynorov@nvidia.com>
---
lib/Kconfig | 18 ++++++++++++++++++
lib/bitrev.c | 4 ++--
2 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/lib/Kconfig b/lib/Kconfig
index 00a9509636c1..3ac12308eb76 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -62,6 +62,24 @@ config HAVE_ARCH_BITREVERSE
This option enables the use of hardware bit-reversal instructions on
architectures which support such operations.
+config GENERIC_BITREVERSE
+ def_bool !HAVE_ARCH_BITREVERSE
+ depends on BITREVERSE
+ help
+ This option provides the standard software-based bit reversal
+ implementation using a lookup table.
+
+ Architecture-specific implementations (HAVE_ARCH_BITREVERSE)
+ and this generic version are not necessarily mutually exclusive
+ at the configuration level, but selecting this ensures that
+ the generic `bitrev8/16/32` functions are available when the
+ CPU does not provide native instructions (like RISC-V's ZBKB
+ extension).
+
+ If you are an architecture maintainer and your CPU has native
+ bit-reversal instructions, you should select HAVE_ARCH_BITREVERSE
+ to skip this table-based implementation.
+
config ARCH_HAS_STRNCPY_FROM_USER
bool
diff --git a/lib/bitrev.c b/lib/bitrev.c
index 81b56e0a7f32..3a53ff67aeba 100644
--- a/lib/bitrev.c
+++ b/lib/bitrev.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-#ifndef CONFIG_HAVE_ARCH_BITREVERSE
+#ifdef CONFIG_GENERIC_BITREVERSE
#include <linux/types.h>
#include <linux/module.h>
#include <linux/bitrev.h>
@@ -44,4 +44,4 @@ const u8 byte_rev_table[256] = {
};
EXPORT_SYMBOL_GPL(byte_rev_table);
-#endif /* CONFIG_HAVE_ARCH_BITREVERSE */
+#endif /* CONFIG_GENERIC_BITREVERSE */
--
2.51.0
^ permalink raw reply related
* [PATCH 1/6] lib: include crc32.h conditionally on CONFIG_CRC32
From: Yury Norov @ 2026-04-30 21:13 UTC (permalink / raw)
To: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Yury Norov, Rasmus Villemoes, Arnd Bergmann, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Morton, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Jinjie Ruan, linux-kernel, linux-riscv, linux-arch, netdev, bpf
Cc: Yury Norov, Nathan Chancellor
In-Reply-To: <20260430211351.658193-1-ynorov@nvidia.com>
Currently, bitreverse API is either declared based on
CONFIG_HAVE_ARCH_BITREVERSE, wired to arch implementation, or if the
arch has no bitreverse, based on generic implementation.
So, regardless of CONFIG_BITREVERSE=n, the corresponding API is always
declared. If that happens, the functions become declared but not
implemented, which is an error.
The following patches of the series make it possible to have bitreverse
API undeclared if CONFIG_BITREVERSE=n, thus spotting the problem when
building the tinyconfig:
$ make -skj"$(nproc)" ARCH=s390 CROSS_COMPILE=s390-linux- mrproper tinyconfig fs/select.o
In file included from include/linux/crc32.h:6,
from include/linux/etherdevice.h:23,
from include/linux/if_vlan.h:11,
from include/linux/filter.h:21,
from include/net/xdp.h:10,
from include/net/busy_poll.h:19,
from fs/select.c:33:
include/linux/etherdevice.h: In function 'eth_hw_addr_crc':
include/linux/bitrev.h:16:20: error: implicit declaration of function 'generic___bitrev32' [-Wimplicit-function-declaration]
16 | #define __bitrev32 generic___bitrev32
| ^~~~~~~~~~~~~~~~~~
include/linux/bitrev.h:67:9: note: in expansion of macro '__bitrev32'
67 | __bitrev32(__x); \
| ^~~~~~~~~~
include/linux/crc32.h:107:36: note: in expansion of macro 'bitrev32'
107 | #define ether_crc(length, data) bitrev32(crc32_le(~0, data, length))
| ^~~~~~~~
include/linux/etherdevice.h:292:16: note: in expansion of macro 'ether_crc'
292 | return ether_crc(ETH_ALEN, ha->addr);
| ^~~~~~~~~
make[5]: *** [scripts/Makefile.build:289: fs/select.o] Error 1
...
The current unconditionally enabled codebase doesn't use CRC32, neither
bitrev functionality, and if generic___bitrev32 prototype is provided,
the compilation and linkage phases are passed OK.
The only header requiring the crc32 and bitreverse prototypes is
include/linux/etherdevice.h. Thus, protect inclusion of corresponding
headers in the etherdevice with CONFIG_CRC32, together with the only
function depending on it.
Reported-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Yury Norov <ynorov@nvidia.com>
---
include/linux/etherdevice.h | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h
index df8f88f63a70..d35be27a91a5 100644
--- a/include/linux/etherdevice.h
+++ b/include/linux/etherdevice.h
@@ -20,7 +20,9 @@
#include <linux/if_ether.h>
#include <linux/netdevice.h>
#include <linux/random.h>
+#ifdef CONFIG_CRC32
#include <linux/crc32.h>
+#endif
#include <linux/unaligned.h>
#include <asm/bitsperlong.h>
@@ -281,6 +283,7 @@ static inline void eth_hw_addr_random(struct net_device *dev)
dev->addr_assign_type = NET_ADDR_RANDOM;
}
+#ifdef CONFIG_CRC32
/**
* eth_hw_addr_crc - Calculate CRC from netdev_hw_addr
* @ha: pointer to hardware address
@@ -291,6 +294,7 @@ static inline u32 eth_hw_addr_crc(struct netdev_hw_addr *ha)
{
return ether_crc(ETH_ALEN, ha->addr);
}
+#endif
/**
* ether_addr_copy - Copy an Ethernet address
--
2.51.0
^ permalink raw reply related
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