* [PATCH net-next v2 0/4] net: move .getsockopt away from __user buffers
From: Breno Leitao @ 2026-04-01 15:44 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Kuniyuki Iwashima, Willem de Bruijn, metze, axboe,
Stanislav Fomichev
Cc: io-uring, bpf, netdev, Linus Torvalds, linux-kernel, kernel-team,
Breno Leitao
Currently, the .getsockopt callback requires __user pointers:
int (*getsockopt)(struct socket *sock, int level,
int optname, char __user *optval, int __user *optlen);
This prevents kernel callers (io_uring, BPF) from using getsockopt on
levels other than SOL_SOCKET, since they pass kernel pointers.
Following Linus' suggestion [0], this series introduces sockopt_t, a
type-safe wrapper around iov_iter, and a getsockopt_iter callback that
works with both user and kernel buffers. AF_PACKET and CAN raw are
converted as initial users, with selftests covering the trickiest
conversion patterns.
[0] https://lore.kernel.org/all/CAHk-=whmzrO-BMU=uSVXbuoLi-3tJsO=0kHj1BCPBE3F2kVhTA@mail.gmail.com/
Below are some questions raised during the RFC discussion:
1) Should optlen be an iov_iter as well?
No. optlen can remain a plain kernel int since do_sock_getsockopt_iter() syncs
it back to userspace on both success and failure. The existing callback
patterns all work with this approach:
a) Most callbacks (roughly 2/3) always write back optlen.
b) Some callbacks read optlen but never update it. The original
value is written back unchanged.
c) CAN raw updates optlen even on error (-ERANGE) to report the
required buffer size:
err = -ERANGE;
if (put_user(fsize, optlen))
err = -EFAULT;
No regression, since opt.optlen is always written back to
userspace by the wrapper.
d) Bluetooth uses put_user() with mixed sizes (u32, u16, u8) but
never updates optlen. Same as case (b).
2) Can callbacks change iov_iter direction mid-flight?
Yes. Some protocols read from and then write back to optval in the same
getsockopt call. For example, PACKET_HDRLEN reads a tpacket version from optval
and writes back the corresponding header size.
The converted callback handles this by temporarily flipping the iter direction,
reverting the position, and writing back:
case PACKET_HDRLEN:
// opt->iter.data_source is ITER_SOURCE;
if (copy_from_iter(&val, len, &opt->iter) != len)
return -EFAULT;
// unroll the bytes
iov_iter_revert(&opt->iter, len);
opt->iter.data_source = ITER_DEST;
// ... update val ...
if (copy_to_iter(&val, len, &opt->iter) != len)
return -EFAULT;
The callback needs to handle two things after reading from the iter:
reset the position with iov_iter_revert(), and flip data_source back
to ITER_DEST before writing.
- ITER_DEST — the iter is a destination (kernel writes to it).
copy_to_iter() works, copy_from_iter() refuses.
- ITER_SOURCE — the iter is a source (kernel reads from it).
copy_from_iter() works, copy_to_iter() refuses.
3) In which case iov_iter_revert() needs to be called?
When a callback needs to read from and then write back to the same
buffer in a single getsockopt call. The iter advances its position on
copy_from_iter(), so you need iov_iter_revert() to reset the position
back to the start before you can copy_to_iter() into the same location.
Without the revert, copy_to_iter() would write past the end of the
buffer since the iter already advanced during the read.
4) Do we have any selftest for this change?
Yes, I've created a commit that I am using to test it, but, I am not
sure how useful it is rigth now, so, not appending it here.
You can find it at
https://github.com/leitao/linux/commit/2d9311947061f1baa43858f597dd6c54d7ccc5d2
Note: The dance regarding changes to iov_iter_revert() (2) and
opt->iter.data_source (3) is a bit fragile. It will not be a bad idea to
creaet a helper (e.g., sockopt_read_val()) would be safer to prevent
others from getting it wrong.
I am not adding it now, so, it is easier to read the bare bones of the
change and helpers can come later.
Link: https://lore.kernel.org/all/CAHk-=whmzrO-BMU=uSVXbuoLi-3tJsO=0kHj1BCPBE3F2kVhTA@mail.gmail.com/ [0]
---
Changes in v2:
- Restore optlen even on error path (getsockopt_iter fails)
- Move af_packet.c and can instead of netlink (given these are the most
complicate ones).
- Link to v1: https://patch.msgid.link/20260130-getsockopt-v1-0-9154fcff6f95@debian.org
---
Breno Leitao (4):
net: add getsockopt_iter callback to proto_ops
net: call getsockopt_iter if available
af_packet: convert to getsockopt_iter
can: raw: convert to getsockopt_iter
include/linux/net.h | 19 +++++++++++++++++++
net/can/raw.c | 28 +++++++++++++---------------
net/packet/af_packet.c | 18 ++++++++++--------
net/socket.c | 48 +++++++++++++++++++++++++++++++++++++++++++++---
4 files changed, 87 insertions(+), 26 deletions(-)
---
base-commit: 2d9311947061f1baa43858f597dd6c54d7ccc5d2
change-id: 20260130-getsockopt-9f36625eedcb
Best regards,
--
Breno Leitao <leitao@debian.org>
^ permalink raw reply
* [PATCH net] ipvs: fix MTU check for GSO packets in tunnel mode
From: Yingnan Zhang @ 2026-04-01 15:38 UTC (permalink / raw)
To: horms, ja
Cc: pablo, fw, phil, davem, edumazet, kuba, pabeni, netdev, lvs-devel,
netfilter-devel, coreteam, linux-kernel, Yingnan Zhang
Currently, IPVS skips MTU checks for GSO packets by excluding them with
the !skb_is_gso(skb) condition. This creates problems when IPVS tunnel
mode encapsulates GSO packets with IPIP headers.
The issue manifests in two ways:
1. MTU violation after encapsulation:
When a GSO packet passes through IPVS tunnel mode, the original MTU
check is bypassed. After adding the IPIP tunnel header, the packet
size may exceed the outgoing interface MTU, leading to unexpected
fragmentation at the IP layer.
2. Fragmentation with problematic IP IDs:
When net.ipv4.vs.pmtu_disc=1 and a GSO packet with multiple segments
is fragmented after encapsulation, each segment gets a sequentially
incremented IP ID (0, 1, 2, ...). This happens because:
a) The GSO packet bypasses MTU check and gets encapsulated
b) At __ip_finish_output, the oversized GSO packet is split into
separate SKBs (one per segment), with IP IDs incrementing
c) Each SKB is then fragmented again based on the actual MTU
This sequential IP ID allocation differs from the expected behavior
and can cause issues with fragment reassembly and packet tracking.
Fix this by removing the GSO packet exception from the MTU check and
properly validating GSO packets using skb_gso_validate_network_len().
This function correctly validates whether the GSO segments will fit
within the MTU after segmentation. If validation fails, send an ICMP
Fragmentation Needed message to enable proper PMTU discovery.
Fixes: 4cdd34084d53 ("netfilter: nf_conntrack_ipv6: improve fragmentation handling")
Signed-off-by: Yingnan Zhang <342144303@qq.com>
---
net/netfilter/ipvs/ip_vs_xmit.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
index 3601eb86d..82f2e7a32 100644
--- a/net/netfilter/ipvs/ip_vs_xmit.c
+++ b/net/netfilter/ipvs/ip_vs_xmit.c
@@ -232,8 +232,15 @@ static inline bool ensure_mtu_is_adequate(struct netns_ipvs *ipvs, int skb_af,
return true;
if (unlikely(ip_hdr(skb)->frag_off & htons(IP_DF) &&
- skb->len > mtu && !skb_is_gso(skb) &&
+ skb->len > mtu &&
!ip_vs_iph_icmp(ipvsh))) {
+ if (skb_is_gso(skb)) {
+ if (skb_gso_validate_network_len(skb, mtu))
+ return true;
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
+ IP_VS_DBG(1, "frag needed for %pI4\n", &ip_hdr(skb)->saddr);
+ return false;
+ }
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
htonl(mtu));
IP_VS_DBG(1, "frag needed for %pI4\n",
--
2.51.0
^ permalink raw reply related
* Re: RFC: phylink: disable PHY autonomous EEE when MAC manages LPI
From: Andrew Lunn @ 2026-04-01 15:38 UTC (permalink / raw)
To: Russell King (Oracle); +Cc: Nicolai Buchwitz, netdev
In-Reply-To: <ac00PIpzXPjxktzC@shell.armlinux.org.uk>
On Wed, Apr 01, 2026 at 04:05:32PM +0100, Russell King (Oracle) wrote:
> On Wed, Apr 01, 2026 at 03:11:24PM +0200, Andrew Lunn wrote:
> > > Thanks Russell and Andrew. You're both heading in the same direction,
> > > so to make sure I understand correctly:
> > >
> > > 1. Add a new phylib interface (separate from phy_ethtool_set_eee) to
> > > control PHY-autonomous EEE, e.g. phy_set_autonomous_eee(phydev,
> > > enable) and phy_get_autonomous_eee(phydev) to query the current
> > > state.
> >
> > In the end, we want the MAC driver using phylib to just call the
> > phylib methods for configuring EEE, and the MAC driver should not care
> > if EEE is actually implemented in the PHY or the MAC. The adjust_link
> > callback would simply not enable LPI if the PHY is doing EEE.
>
> This won't work.
>
> If we mask out eee.tx_lpi_enabled when calling into phylib to tell
> phylib drivers to disable SmartEEE (that's what I'm calling it here
> because it's easier to type)),
We need phylib to handle some state information. Are we doing MAC EEE
or SmartEEE? We can then set phydev->enable_tx_lpi == True to indicate
if the MAC should be sending LPI indications, if and only if the
phylib knows we are doing MAC EEE and it should be enabled because the
user said so.
then phylib records that tx_lpi_enabled
> was false. This then makes the following in phy_check_link_status():
>
> phydev->enable_tx_lpi = phydev->eee_cfg.tx_lpi_enabled &&
> phydev->eee_active;
Which will need to change to take into account the state information.
However, i think this is down the road. If the MAC driver calls
phy_support_eee(), we should call the SmartEEE disable method of the
PHY driver. When we add support for SmartEEE then we need this
additional state information.
Andrew
^ permalink raw reply
* [PATCH net-next v4 5/6] enic: add type-aware alloc for WQ, RQ, CQ and INTR resources
From: Satish Kharat via B4 Relay @ 2026-04-01 15:31 UTC (permalink / raw)
To: Satish Kharat, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni
Cc: netdev, linux-kernel
In-Reply-To: <20260401-enic-sriov-v2-prep-v4-0-d5834b2ef1b9@cisco.com>
From: Satish Kharat <satishkh@cisco.com>
The existing vnic_wq_alloc(), vnic_rq_alloc(), vnic_cq_alloc() and
vnic_intr_alloc() hardcode data-path resource types (RES_TYPE_WQ,
RES_TYPE_RQ, RES_TYPE_CQ, RES_TYPE_INTR_CTRL). The upcoming admin
channel uses different BAR resource types (RES_TYPE_ADMIN_WQ/RQ/CQ,
RES_TYPE_SRIOV_INTR) for its queues.
Add _with_type() variants that accept an explicit resource type
parameter. Refactor the original functions as thin wrappers that
pass the default data-path type. No functional change.
Signed-off-by: Satish Kharat <satishkh@cisco.com>
---
drivers/net/ethernet/cisco/enic/vnic_cq.c | 14 +++++++++++---
drivers/net/ethernet/cisco/enic/vnic_cq.h | 3 +++
drivers/net/ethernet/cisco/enic/vnic_intr.c | 12 +++++++++---
drivers/net/ethernet/cisco/enic/vnic_intr.h | 2 ++
drivers/net/ethernet/cisco/enic/vnic_rq.c | 14 +++++++++++---
drivers/net/ethernet/cisco/enic/vnic_rq.h | 3 +++
drivers/net/ethernet/cisco/enic/vnic_wq.c | 14 +++++++++++---
drivers/net/ethernet/cisco/enic/vnic_wq.h | 3 +++
8 files changed, 53 insertions(+), 12 deletions(-)
diff --git a/drivers/net/ethernet/cisco/enic/vnic_cq.c b/drivers/net/ethernet/cisco/enic/vnic_cq.c
index 27c885e91552..5a0dbb816223 100644
--- a/drivers/net/ethernet/cisco/enic/vnic_cq.c
+++ b/drivers/net/ethernet/cisco/enic/vnic_cq.c
@@ -20,13 +20,14 @@ void vnic_cq_free(struct vnic_cq *cq)
cq->ctrl = NULL;
}
-int vnic_cq_alloc(struct vnic_dev *vdev, struct vnic_cq *cq, unsigned int index,
- unsigned int desc_count, unsigned int desc_size)
+int vnic_cq_alloc_with_type(struct vnic_dev *vdev, struct vnic_cq *cq,
+ unsigned int index, unsigned int desc_count,
+ unsigned int desc_size, unsigned int res_type)
{
cq->index = index;
cq->vdev = vdev;
- cq->ctrl = vnic_dev_get_res(vdev, RES_TYPE_CQ, index);
+ cq->ctrl = vnic_dev_get_res(vdev, res_type, index);
if (!cq->ctrl) {
vdev_err(vdev, "Failed to hook CQ[%d] resource\n", index);
return -EINVAL;
@@ -35,6 +36,13 @@ int vnic_cq_alloc(struct vnic_dev *vdev, struct vnic_cq *cq, unsigned int index,
return vnic_dev_alloc_desc_ring(vdev, &cq->ring, desc_count, desc_size);
}
+int vnic_cq_alloc(struct vnic_dev *vdev, struct vnic_cq *cq, unsigned int index,
+ unsigned int desc_count, unsigned int desc_size)
+{
+ return vnic_cq_alloc_with_type(vdev, cq, index, desc_count, desc_size,
+ RES_TYPE_CQ);
+}
+
void vnic_cq_init(struct vnic_cq *cq, unsigned int flow_control_enable,
unsigned int color_enable, unsigned int cq_head, unsigned int cq_tail,
unsigned int cq_tail_color, unsigned int interrupt_enable,
diff --git a/drivers/net/ethernet/cisco/enic/vnic_cq.h b/drivers/net/ethernet/cisco/enic/vnic_cq.h
index 0e37f5d5e527..d46d4d2ef6bb 100644
--- a/drivers/net/ethernet/cisco/enic/vnic_cq.h
+++ b/drivers/net/ethernet/cisco/enic/vnic_cq.h
@@ -73,6 +73,9 @@ static inline void vnic_cq_inc_to_clean(struct vnic_cq *cq)
void vnic_cq_free(struct vnic_cq *cq);
int vnic_cq_alloc(struct vnic_dev *vdev, struct vnic_cq *cq, unsigned int index,
unsigned int desc_count, unsigned int desc_size);
+int vnic_cq_alloc_with_type(struct vnic_dev *vdev, struct vnic_cq *cq,
+ unsigned int index, unsigned int desc_count,
+ unsigned int desc_size, unsigned int res_type);
void vnic_cq_init(struct vnic_cq *cq, unsigned int flow_control_enable,
unsigned int color_enable, unsigned int cq_head, unsigned int cq_tail,
unsigned int cq_tail_color, unsigned int interrupt_enable,
diff --git a/drivers/net/ethernet/cisco/enic/vnic_intr.c b/drivers/net/ethernet/cisco/enic/vnic_intr.c
index 25319f072a04..010ad8c2108d 100644
--- a/drivers/net/ethernet/cisco/enic/vnic_intr.c
+++ b/drivers/net/ethernet/cisco/enic/vnic_intr.c
@@ -19,13 +19,13 @@ void vnic_intr_free(struct vnic_intr *intr)
intr->ctrl = NULL;
}
-int vnic_intr_alloc(struct vnic_dev *vdev, struct vnic_intr *intr,
- unsigned int index)
+int vnic_intr_alloc_with_type(struct vnic_dev *vdev, struct vnic_intr *intr,
+ unsigned int index, unsigned int res_type)
{
intr->index = index;
intr->vdev = vdev;
- intr->ctrl = vnic_dev_get_res(vdev, RES_TYPE_INTR_CTRL, index);
+ intr->ctrl = vnic_dev_get_res(vdev, res_type, index);
if (!intr->ctrl) {
vdev_err(vdev, "Failed to hook INTR[%d].ctrl resource\n",
index);
@@ -35,6 +35,12 @@ int vnic_intr_alloc(struct vnic_dev *vdev, struct vnic_intr *intr,
return 0;
}
+int vnic_intr_alloc(struct vnic_dev *vdev, struct vnic_intr *intr,
+ unsigned int index)
+{
+ return vnic_intr_alloc_with_type(vdev, intr, index, RES_TYPE_INTR_CTRL);
+}
+
void vnic_intr_init(struct vnic_intr *intr, u32 coalescing_timer,
unsigned int coalescing_type, unsigned int mask_on_assertion)
{
diff --git a/drivers/net/ethernet/cisco/enic/vnic_intr.h b/drivers/net/ethernet/cisco/enic/vnic_intr.h
index 33a72aa10b26..2bc2dbffdb80 100644
--- a/drivers/net/ethernet/cisco/enic/vnic_intr.h
+++ b/drivers/net/ethernet/cisco/enic/vnic_intr.h
@@ -89,6 +89,8 @@ static inline u32 vnic_intr_legacy_pba(u32 __iomem *legacy_pba)
void vnic_intr_free(struct vnic_intr *intr);
int vnic_intr_alloc(struct vnic_dev *vdev, struct vnic_intr *intr,
unsigned int index);
+int vnic_intr_alloc_with_type(struct vnic_dev *vdev, struct vnic_intr *intr,
+ unsigned int index, unsigned int res_type);
void vnic_intr_init(struct vnic_intr *intr, u32 coalescing_timer,
unsigned int coalescing_type, unsigned int mask_on_assertion);
void vnic_intr_coalescing_timer_set(struct vnic_intr *intr,
diff --git a/drivers/net/ethernet/cisco/enic/vnic_rq.c b/drivers/net/ethernet/cisco/enic/vnic_rq.c
index 5ae80551f17c..a662d9fd1199 100644
--- a/drivers/net/ethernet/cisco/enic/vnic_rq.c
+++ b/drivers/net/ethernet/cisco/enic/vnic_rq.c
@@ -69,15 +69,16 @@ void vnic_rq_free(struct vnic_rq *rq)
rq->ctrl = NULL;
}
-int vnic_rq_alloc(struct vnic_dev *vdev, struct vnic_rq *rq, unsigned int index,
- unsigned int desc_count, unsigned int desc_size)
+int vnic_rq_alloc_with_type(struct vnic_dev *vdev, struct vnic_rq *rq,
+ unsigned int index, unsigned int desc_count,
+ unsigned int desc_size, unsigned int res_type)
{
int err;
rq->index = index;
rq->vdev = vdev;
- rq->ctrl = vnic_dev_get_res(vdev, RES_TYPE_RQ, index);
+ rq->ctrl = vnic_dev_get_res(vdev, res_type, index);
if (!rq->ctrl) {
vdev_err(vdev, "Failed to hook RQ[%d] resource\n", index);
return -EINVAL;
@@ -98,6 +99,13 @@ int vnic_rq_alloc(struct vnic_dev *vdev, struct vnic_rq *rq, unsigned int index,
return 0;
}
+int vnic_rq_alloc(struct vnic_dev *vdev, struct vnic_rq *rq, unsigned int index,
+ unsigned int desc_count, unsigned int desc_size)
+{
+ return vnic_rq_alloc_with_type(vdev, rq, index, desc_count, desc_size,
+ RES_TYPE_RQ);
+}
+
static void vnic_rq_init_start(struct vnic_rq *rq, unsigned int cq_index,
unsigned int fetch_index, unsigned int posted_index,
unsigned int error_interrupt_enable,
diff --git a/drivers/net/ethernet/cisco/enic/vnic_rq.h b/drivers/net/ethernet/cisco/enic/vnic_rq.h
index a1cdd729caec..9fc2090eac44 100644
--- a/drivers/net/ethernet/cisco/enic/vnic_rq.h
+++ b/drivers/net/ethernet/cisco/enic/vnic_rq.h
@@ -196,6 +196,9 @@ static inline int vnic_rq_fill(struct vnic_rq *rq,
void vnic_rq_free(struct vnic_rq *rq);
int vnic_rq_alloc(struct vnic_dev *vdev, struct vnic_rq *rq, unsigned int index,
unsigned int desc_count, unsigned int desc_size);
+int vnic_rq_alloc_with_type(struct vnic_dev *vdev, struct vnic_rq *rq,
+ unsigned int index, unsigned int desc_count,
+ unsigned int desc_size, unsigned int res_type);
void vnic_rq_init(struct vnic_rq *rq, unsigned int cq_index,
unsigned int error_interrupt_enable,
unsigned int error_interrupt_offset);
diff --git a/drivers/net/ethernet/cisco/enic/vnic_wq.c b/drivers/net/ethernet/cisco/enic/vnic_wq.c
index 29c7900349b2..5a20bdc62141 100644
--- a/drivers/net/ethernet/cisco/enic/vnic_wq.c
+++ b/drivers/net/ethernet/cisco/enic/vnic_wq.c
@@ -72,15 +72,16 @@ void vnic_wq_free(struct vnic_wq *wq)
wq->ctrl = NULL;
}
-int vnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, unsigned int index,
- unsigned int desc_count, unsigned int desc_size)
+int vnic_wq_alloc_with_type(struct vnic_dev *vdev, struct vnic_wq *wq,
+ unsigned int index, unsigned int desc_count,
+ unsigned int desc_size, unsigned int res_type)
{
int err;
wq->index = index;
wq->vdev = vdev;
- wq->ctrl = vnic_dev_get_res(vdev, RES_TYPE_WQ, index);
+ wq->ctrl = vnic_dev_get_res(vdev, res_type, index);
if (!wq->ctrl) {
vdev_err(vdev, "Failed to hook WQ[%d] resource\n", index);
return -EINVAL;
@@ -101,6 +102,13 @@ int vnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, unsigned int index,
return 0;
}
+int vnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, unsigned int index,
+ unsigned int desc_count, unsigned int desc_size)
+{
+ return vnic_wq_alloc_with_type(vdev, wq, index, desc_count, desc_size,
+ RES_TYPE_WQ);
+}
+
int enic_wq_devcmd2_alloc(struct vnic_dev *vdev, struct vnic_wq *wq,
unsigned int desc_count, unsigned int desc_size)
{
diff --git a/drivers/net/ethernet/cisco/enic/vnic_wq.h b/drivers/net/ethernet/cisco/enic/vnic_wq.h
index 3bb4758100ba..1d448b7a9ba2 100644
--- a/drivers/net/ethernet/cisco/enic/vnic_wq.h
+++ b/drivers/net/ethernet/cisco/enic/vnic_wq.h
@@ -165,6 +165,9 @@ static inline void vnic_wq_service(struct vnic_wq *wq,
void vnic_wq_free(struct vnic_wq *wq);
int vnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, unsigned int index,
unsigned int desc_count, unsigned int desc_size);
+int vnic_wq_alloc_with_type(struct vnic_dev *vdev, struct vnic_wq *wq,
+ unsigned int index, unsigned int desc_count,
+ unsigned int desc_size, unsigned int res_type);
void vnic_wq_init(struct vnic_wq *wq, unsigned int cq_index,
unsigned int error_interrupt_enable,
unsigned int error_interrupt_offset);
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v4 6/6] enic: detect admin channel resources for SR-IOV
From: Satish Kharat via B4 Relay @ 2026-04-01 15:31 UTC (permalink / raw)
To: Satish Kharat, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni
Cc: netdev, linux-kernel
In-Reply-To: <20260401-enic-sriov-v2-prep-v4-0-d5834b2ef1b9@cisco.com>
From: Satish Kharat <satishkh@cisco.com>
Check for the presence of admin channel BAR resources
(RES_TYPE_ADMIN_WQ, ADMIN_RQ, ADMIN_CQ, SRIOV_INTR) during resource
discovery. Set has_admin_channel when all four are available.
Use ARRAY_SIZE(enic->admin_cq) for the admin CQ count check since the
driver allocates two admin CQs (one for WQ completions, one for RQ
completions) and both must be backed by hardware resources.
Add admin WQ, RQ, CQ and INTR fields to struct enic for use by the
upcoming admin channel open/close paths.
Signed-off-by: Satish Kharat <satishkh@cisco.com>
---
drivers/net/ethernet/cisco/enic/enic.h | 7 +++++++
drivers/net/ethernet/cisco/enic/enic_res.c | 12 ++++++++++--
2 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h
index 67fd780b1fa1..08472420f3a1 100644
--- a/drivers/net/ethernet/cisco/enic/enic.h
+++ b/drivers/net/ethernet/cisco/enic/enic.h
@@ -289,6 +289,13 @@ struct enic {
u8 rss_key[ENIC_RSS_LEN];
struct vnic_gen_stats gen_stats;
enum ext_cq ext_cq;
+
+ /* Admin channel resources for SR-IOV MBOX */
+ bool has_admin_channel;
+ struct vnic_wq admin_wq;
+ struct vnic_rq admin_rq;
+ struct vnic_cq admin_cq[2];
+ struct vnic_intr admin_intr;
};
static inline struct net_device *vnic_get_netdev(struct vnic_dev *vdev)
diff --git a/drivers/net/ethernet/cisco/enic/enic_res.c b/drivers/net/ethernet/cisco/enic/enic_res.c
index bbd3143ed73e..2b7545d6a67f 100644
--- a/drivers/net/ethernet/cisco/enic/enic_res.c
+++ b/drivers/net/ethernet/cisco/enic/enic_res.c
@@ -205,10 +205,18 @@ void enic_get_res_counts(struct enic *enic)
enic->cq_count = enic->cq_avail;
enic->intr_count = enic->intr_avail;
+ enic->has_admin_channel =
+ vnic_dev_get_res_count(enic->vdev, RES_TYPE_ADMIN_WQ) >= 1 &&
+ vnic_dev_get_res_count(enic->vdev, RES_TYPE_ADMIN_RQ) >= 1 &&
+ vnic_dev_get_res_count(enic->vdev, RES_TYPE_ADMIN_CQ) >=
+ ARRAY_SIZE(enic->admin_cq) &&
+ vnic_dev_get_res_count(enic->vdev, RES_TYPE_SRIOV_INTR) >= 1;
+
dev_info(enic_get_dev(enic),
- "vNIC resources avail: wq %d rq %d cq %d intr %d\n",
+ "vNIC resources avail: wq %d rq %d cq %d intr %d admin %s\n",
enic->wq_avail, enic->rq_avail,
- enic->cq_avail, enic->intr_avail);
+ enic->cq_avail, enic->intr_avail,
+ enic->has_admin_channel ? "yes" : "no");
}
void enic_init_vnic_resources(struct enic *enic)
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v4 4/6] enic: make enic_dev_enable/disable ref-counted
From: Satish Kharat via B4 Relay @ 2026-04-01 15:31 UTC (permalink / raw)
To: Satish Kharat, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni
Cc: netdev, linux-kernel
In-Reply-To: <20260401-enic-sriov-v2-prep-v4-0-d5834b2ef1b9@cisco.com>
From: Satish Kharat <satishkh@cisco.com>
Both the data path (ndo_open/ndo_stop) and the upcoming admin channel
need to enable and disable the vNIC device independently. Without
reference counting, closing the admin channel while the netdev is up
would inadvertently disable the entire device.
Add an enable_count to struct enic, protected by the existing
devcmd_lock. enic_dev_enable() issues CMD_ENABLE_WAIT only on the
first caller (0 -> 1 transition), and enic_dev_disable() issues
CMD_DISABLE only when the last caller releases (1 -> 0 transition).
Also check the return value of enic_dev_enable() in enic_open() and
fail the open if the firmware enable command fails. Without this check,
a failed enable leaves enable_count at zero while the interface appears
up, which can cause a later admin channel enable/disable cycle to
incorrectly disable the hardware under the active data path.
Signed-off-by: Satish Kharat <satishkh@cisco.com>
---
drivers/net/ethernet/cisco/enic/enic.h | 1 +
drivers/net/ethernet/cisco/enic/enic_dev.c | 17 +++++++++++++----
drivers/net/ethernet/cisco/enic/enic_main.c | 17 ++++++++++++++++-
3 files changed, 30 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h
index 0fd9cd917132..67fd780b1fa1 100644
--- a/drivers/net/ethernet/cisco/enic/enic.h
+++ b/drivers/net/ethernet/cisco/enic/enic.h
@@ -260,6 +260,7 @@ struct enic {
u16 num_vfs;
#endif
enum enic_vf_type vf_type;
+ unsigned int enable_count;
spinlock_t enic_api_lock;
bool enic_api_busy;
struct enic_port_profile *pp;
diff --git a/drivers/net/ethernet/cisco/enic/enic_dev.c b/drivers/net/ethernet/cisco/enic/enic_dev.c
index 2cbae7c6cc3d..659787f73cf1 100644
--- a/drivers/net/ethernet/cisco/enic/enic_dev.c
+++ b/drivers/net/ethernet/cisco/enic/enic_dev.c
@@ -131,10 +131,13 @@ int enic_dev_set_ig_vlan_rewrite_mode(struct enic *enic)
int enic_dev_enable(struct enic *enic)
{
- int err;
+ int err = 0;
spin_lock_bh(&enic->devcmd_lock);
- err = vnic_dev_enable_wait(enic->vdev);
+ if (enic->enable_count == 0)
+ err = vnic_dev_enable_wait(enic->vdev);
+ if (!err)
+ enic->enable_count++;
spin_unlock_bh(&enic->devcmd_lock);
return err;
@@ -142,10 +145,16 @@ int enic_dev_enable(struct enic *enic)
int enic_dev_disable(struct enic *enic)
{
- int err;
+ int err = 0;
spin_lock_bh(&enic->devcmd_lock);
- err = vnic_dev_disable(enic->vdev);
+ if (enic->enable_count == 0) {
+ spin_unlock_bh(&enic->devcmd_lock);
+ return 0;
+ }
+ enic->enable_count--;
+ if (enic->enable_count == 0)
+ err = vnic_dev_disable(enic->vdev);
spin_unlock_bh(&enic->devcmd_lock);
return err;
diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c
index acd05350ec1a..e7125b818087 100644
--- a/drivers/net/ethernet/cisco/enic/enic_main.c
+++ b/drivers/net/ethernet/cisco/enic/enic_main.c
@@ -1750,7 +1750,11 @@ static int enic_open(struct net_device *netdev)
if (vnic_dev_get_intr_mode(enic->vdev) == VNIC_DEV_INTR_MODE_MSIX)
for (i = 0; i < enic->wq_count; i++)
napi_enable(&enic->napi[enic_cq_wq(enic, i)]);
- enic_dev_enable(enic);
+ err = enic_dev_enable(enic);
+ if (err) {
+ netdev_err(netdev, "Failed to enable device: %d\n", err);
+ goto err_out_dev_enable;
+ }
for (i = 0; i < enic->intr_count; i++)
vnic_intr_unmask(&enic->intr[i]);
@@ -1760,6 +1764,17 @@ static int enic_open(struct net_device *netdev)
return 0;
+err_out_dev_enable:
+ for (i = 0; i < enic->rq_count; i++)
+ napi_disable(&enic->napi[i]);
+ if (vnic_dev_get_intr_mode(enic->vdev) == VNIC_DEV_INTR_MODE_MSIX)
+ for (i = 0; i < enic->wq_count; i++)
+ napi_disable(&enic->napi[enic_cq_wq(enic, i)]);
+ netif_tx_disable(netdev);
+ if (!enic_is_dynamic(enic) && !enic_is_sriov_vf(enic))
+ enic_dev_del_station_addr(enic);
+ for (i = 0; i < enic->wq_count; i++)
+ vnic_wq_disable(&enic->wq[i].vwq);
err_out_free_rq:
for (i = 0; i < enic->rq_count; i++) {
ret = vnic_rq_disable(&enic->rq[i].vrq);
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v4 3/6] enic: detect SR-IOV VF type from PCI capability
From: Satish Kharat via B4 Relay @ 2026-04-01 15:31 UTC (permalink / raw)
To: Satish Kharat, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni
Cc: netdev, linux-kernel
In-Reply-To: <20260401-enic-sriov-v2-prep-v4-0-d5834b2ef1b9@cisco.com>
From: Satish Kharat <satishkh@cisco.com>
Read the VF device ID from the SR-IOV PCI capability at probe time to
determine whether the PF is configured for V1, USNIC, or V2 virtual
functions. Store the result in enic->vf_type for use by subsequent
SR-IOV operations.
The VF type is a firmware-configured property (set via UCSM, CIMC,
Intersight etc) that is immutable from the driver's perspective. Only
PFs are probed for this capability; VFs and dynamic vnics skip
detection.
Signed-off-by: Satish Kharat <satishkh@cisco.com>
---
drivers/net/ethernet/cisco/enic/enic.h | 8 +++++++
drivers/net/ethernet/cisco/enic/enic_main.c | 37 +++++++++++++++++++++++++++++
2 files changed, 45 insertions(+)
diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h
index 366c65d072fc..0fd9cd917132 100644
--- a/drivers/net/ethernet/cisco/enic/enic.h
+++ b/drivers/net/ethernet/cisco/enic/enic.h
@@ -225,6 +225,13 @@ struct enic_rq {
struct page_pool *pool;
} ____cacheline_aligned;
+enum enic_vf_type {
+ ENIC_VF_TYPE_NONE,
+ ENIC_VF_TYPE_V1,
+ ENIC_VF_TYPE_USNIC,
+ ENIC_VF_TYPE_V2,
+};
+
/* Per-instance private data structure */
struct enic {
struct net_device *netdev;
@@ -252,6 +259,7 @@ struct enic {
#ifdef CONFIG_PCI_IOV
u16 num_vfs;
#endif
+ enum enic_vf_type vf_type;
spinlock_t enic_api_lock;
bool enic_api_busy;
struct enic_port_profile *pp;
diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c
index e16dfbcd2c22..acd05350ec1a 100644
--- a/drivers/net/ethernet/cisco/enic/enic_main.c
+++ b/drivers/net/ethernet/cisco/enic/enic_main.c
@@ -67,6 +67,7 @@
#define PCI_DEVICE_ID_CISCO_VIC_ENET_DYN 0x0044 /* enet dynamic vnic */
#define PCI_DEVICE_ID_CISCO_VIC_ENET_VF 0x0071 /* enet SRIOV VF */
#define PCI_DEVICE_ID_CISCO_VIC_ENET_VF_V2 0x02b7 /* enet SRIOV V2 VF */
+#define PCI_DEVICE_ID_CISCO_VIC_ENET_VF_USNIC 0x00cf /* enet USNIC VF */
/* Supported devices */
static const struct pci_device_id enic_id_table[] = {
@@ -2621,6 +2622,41 @@ static void enic_iounmap(struct enic *enic)
iounmap(enic->bar[i].vaddr);
}
+#ifdef CONFIG_PCI_IOV
+static void enic_sriov_detect_vf_type(struct enic *enic)
+{
+ struct pci_dev *pdev = enic->pdev;
+ int pos;
+ u16 vf_dev_id;
+
+ if (enic_is_sriov_vf(enic) || enic_is_dynamic(enic))
+ return;
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
+ if (!pos) {
+ enic->vf_type = ENIC_VF_TYPE_NONE;
+ return;
+ }
+
+ pci_read_config_word(pdev, pos + PCI_SRIOV_VF_DID, &vf_dev_id);
+
+ switch (vf_dev_id) {
+ case PCI_DEVICE_ID_CISCO_VIC_ENET_VF:
+ enic->vf_type = ENIC_VF_TYPE_V1;
+ break;
+ case PCI_DEVICE_ID_CISCO_VIC_ENET_VF_USNIC:
+ enic->vf_type = ENIC_VF_TYPE_USNIC;
+ break;
+ case PCI_DEVICE_ID_CISCO_VIC_ENET_VF_V2:
+ enic->vf_type = ENIC_VF_TYPE_V2;
+ break;
+ default:
+ enic->vf_type = ENIC_VF_TYPE_NONE;
+ break;
+ }
+}
+#endif
+
static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct device *dev = &pdev->dev;
@@ -2734,6 +2770,7 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
num_pps = enic->num_vfs;
}
}
+ enic_sriov_detect_vf_type(enic);
#endif
/* Allocate structure for port profiles */
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v4 1/6] enic: extend resource discovery for SR-IOV admin channel
From: Satish Kharat via B4 Relay @ 2026-04-01 15:31 UTC (permalink / raw)
To: Satish Kharat, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni
Cc: netdev, linux-kernel
In-Reply-To: <20260401-enic-sriov-v2-prep-v4-0-d5834b2ef1b9@cisco.com>
From: Satish Kharat <satishkh@cisco.com>
VIC firmware exposes admin channel resources (WQ, RQ, CQ) for PF-VF
communication when SR-IOV is active. Add the corresponding resource
type definitions and teach the discovery and access functions to
handle them.
Signed-off-by: Satish Kharat <satishkh@cisco.com>
---
drivers/net/ethernet/cisco/enic/enic.h | 2 ++
drivers/net/ethernet/cisco/enic/vnic_dev.c | 10 ++++++++++
drivers/net/ethernet/cisco/enic/vnic_resource.h | 4 ++++
3 files changed, 16 insertions(+)
diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h
index 6959e85ab516..366c65d072fc 100644
--- a/drivers/net/ethernet/cisco/enic/enic.h
+++ b/drivers/net/ethernet/cisco/enic/enic.h
@@ -297,6 +297,8 @@ static inline struct net_device *vnic_get_netdev(struct vnic_dev *vdev)
dev_warn(&(vdev)->pdev->dev, fmt, ##__VA_ARGS__)
#define vdev_info(vdev, fmt, ...) \
dev_info(&(vdev)->pdev->dev, fmt, ##__VA_ARGS__)
+#define vdev_dbg(vdev, fmt, ...) \
+ dev_dbg(&(vdev)->pdev->dev, fmt, ##__VA_ARGS__)
#define vdev_neterr(vdev, fmt, ...) \
netdev_err(vnic_get_netdev(vdev), fmt, ##__VA_ARGS__)
diff --git a/drivers/net/ethernet/cisco/enic/vnic_dev.c b/drivers/net/ethernet/cisco/enic/vnic_dev.c
index c72452749f5e..c8d657e97094 100644
--- a/drivers/net/ethernet/cisco/enic/vnic_dev.c
+++ b/drivers/net/ethernet/cisco/enic/vnic_dev.c
@@ -77,6 +77,9 @@ static int vnic_dev_discover_res(struct vnic_dev *vdev,
u32 count = ioread32(&r->count);
u32 len;
+ vdev_dbg(vdev, "res type %u bar %u offset 0x%x count %u\n",
+ type, bar_num, bar_offset, count);
+
r++;
if (bar_num >= num_bars)
@@ -90,6 +93,9 @@ static int vnic_dev_discover_res(struct vnic_dev *vdev,
case RES_TYPE_RQ:
case RES_TYPE_CQ:
case RES_TYPE_INTR_CTRL:
+ case RES_TYPE_ADMIN_WQ:
+ case RES_TYPE_ADMIN_RQ:
+ case RES_TYPE_ADMIN_CQ:
/* each count is stride bytes long */
len = count * VNIC_RES_STRIDE;
if (len + bar_offset > bar[bar_num].len) {
@@ -102,6 +108,7 @@ static int vnic_dev_discover_res(struct vnic_dev *vdev,
case RES_TYPE_INTR_PBA_LEGACY:
case RES_TYPE_DEVCMD:
case RES_TYPE_DEVCMD2:
+ case RES_TYPE_SRIOV_INTR:
len = count;
break;
default:
@@ -135,6 +142,9 @@ void __iomem *vnic_dev_get_res(struct vnic_dev *vdev, enum vnic_res_type type,
case RES_TYPE_RQ:
case RES_TYPE_CQ:
case RES_TYPE_INTR_CTRL:
+ case RES_TYPE_ADMIN_WQ:
+ case RES_TYPE_ADMIN_RQ:
+ case RES_TYPE_ADMIN_CQ:
return (char __iomem *)vdev->res[type].vaddr +
index * VNIC_RES_STRIDE;
default:
diff --git a/drivers/net/ethernet/cisco/enic/vnic_resource.h b/drivers/net/ethernet/cisco/enic/vnic_resource.h
index b4776e334d63..d327821fa9b9 100644
--- a/drivers/net/ethernet/cisco/enic/vnic_resource.h
+++ b/drivers/net/ethernet/cisco/enic/vnic_resource.h
@@ -42,6 +42,10 @@ enum vnic_res_type {
RES_TYPE_DEPRECATED1, /* Old version of devcmd 2 */
RES_TYPE_DEPRECATED2, /* Old version of devcmd 2 */
RES_TYPE_DEVCMD2, /* Device control region */
+ RES_TYPE_SRIOV_INTR = 45, /* SR-IOV VF interrupt */
+ RES_TYPE_ADMIN_WQ = 49, /* Admin channel WQ */
+ RES_TYPE_ADMIN_RQ, /* Admin channel RQ */
+ RES_TYPE_ADMIN_CQ, /* Admin channel CQ */
RES_TYPE_MAX, /* Count of resource types */
};
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v4 0/6] enic: SR-IOV V2 preparatory infrastructure
From: Satish Kharat via B4 Relay @ 2026-04-01 15:31 UTC (permalink / raw)
To: Satish Kharat, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni
Cc: netdev, linux-kernel
This is the first of four series adding SR-IOV V2 support to the enic
driver for Cisco VIC 14xx/15xx adapters.
The existing V1 SR-IOV implementation has VFs that interact directly
with the VIC firmware, leaving the PF driver with no visibility or
control over VF behavior. V2 introduces a PF-mediated model where VFs
communicate with the PF through a mailbox over a dedicated admin
channel. This brings enic in line with the standard Linux SR-IOV
model, enabling full PF management of VFs via ip link (MAC, VLAN,
link state, spoofchk, trust, and per-VF statistics).
This preparatory series adds detection and resource helper code with
no functional change to existing driver behavior:
- Extend BAR resource discovery for admin channel resources
- Register the V2 VF PCI device ID
- Detect VF type (V1/V2/usNIC) from SR-IOV PCI capability
- Make enic_dev_enable/disable ref-counted for shared use by data
path and admin channel
- Add type-aware resource allocation for admin WQ/RQ/CQ/INTR
- Detect presence of admin channel resources at probe time
Tested on VIC 14xx and 15xx series adapters with V2 VFs under KVM
(sriov_numvfs, VF passthrough, ip link VF configuration, VF traffic).
Based in part on initial work by Christian Benvenuti.
To: Satish Kharat <satishkh@cisco.com>
To: Andrew Lunn <andrew+netdev@lunn.ch>
To: David S. Miller <davem@davemloft.net>
To: Eric Dumazet <edumazet@google.com>
To: Jakub Kicinski <kuba@kernel.org>
To: Paolo Abeni <pabeni@redhat.com>
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Changes in v4:
- Repost to fix cover letter subject accidentally sent as "Changes in v3:"
instead of "enic: SR-IOV V2 preparatory infrastructure". No code changes.
Changes in v3:
- Thanks to the reviewers for the feedback on v2, all comments have
been addressed in this revision.
- Patch 4/6: check enic_dev_enable() return value in enic_open() and
add proper error unwind path so the enable reference count stays
consistent if the firmware command fails.
- Patch 6/6: use ARRAY_SIZE(enic->admin_cq) for the admin CQ resource
count check, since the driver allocates two admin CQs and both must
be backed by hardware.
- Link to v2: https://lore.kernel.org/r/20260325-enic-sriov-v2-prep-v2-0-f72cf716ebfa@cisco.com
Changes in v2:
- Add RES_TYPE_SRIOV_INTR to the has_admin_channel check in patch 6/6
- CC all netdev maintainers per cc_maintainers CI feedback
- Link to v1: https://lore.kernel.org/r/20260325-enic-sriov-v2-prep-v1-0-48f04ce110cc@cisco.com
---
Satish Kharat (6):
enic: extend resource discovery for SR-IOV admin channel
enic: add V2 SR-IOV VF device ID
enic: detect SR-IOV VF type from PCI capability
enic: make enic_dev_enable/disable ref-counted
enic: add type-aware alloc for WQ, RQ, CQ and INTR resources
enic: detect admin channel resources for SR-IOV
drivers/net/ethernet/cisco/enic/enic.h | 18 ++++++++
drivers/net/ethernet/cisco/enic/enic_dev.c | 17 +++++--
drivers/net/ethernet/cisco/enic/enic_main.c | 59 ++++++++++++++++++++++++-
drivers/net/ethernet/cisco/enic/enic_res.c | 12 ++++-
drivers/net/ethernet/cisco/enic/vnic_cq.c | 14 ++++--
drivers/net/ethernet/cisco/enic/vnic_cq.h | 3 ++
drivers/net/ethernet/cisco/enic/vnic_dev.c | 10 +++++
drivers/net/ethernet/cisco/enic/vnic_intr.c | 12 +++--
drivers/net/ethernet/cisco/enic/vnic_intr.h | 2 +
drivers/net/ethernet/cisco/enic/vnic_resource.h | 4 ++
drivers/net/ethernet/cisco/enic/vnic_rq.c | 14 ++++--
drivers/net/ethernet/cisco/enic/vnic_rq.h | 3 ++
drivers/net/ethernet/cisco/enic/vnic_wq.c | 14 ++++--
drivers/net/ethernet/cisco/enic/vnic_wq.h | 3 ++
14 files changed, 165 insertions(+), 20 deletions(-)
---
base-commit: b1c803d5c8167026791abfaed96fd3e6a1fcd750
change-id: 20260324-enic-sriov-v2-prep-2fe1eabc3dc1
Best regards,
--
Satish Kharat <satishkh@cisco.com>
^ permalink raw reply
* [PATCH net-next v4 2/6] enic: add V2 SR-IOV VF device ID
From: Satish Kharat via B4 Relay @ 2026-04-01 15:31 UTC (permalink / raw)
To: Satish Kharat, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni
Cc: netdev, linux-kernel
In-Reply-To: <20260401-enic-sriov-v2-prep-v4-0-d5834b2ef1b9@cisco.com>
From: Satish Kharat <satishkh@cisco.com>
Register the V2 VF PCI device ID (0x02b7) so the driver binds to V2
virtual functions created via sriov_configure. Update enic_is_sriov_vf()
to recognize V2 VFs alongside the existing V1 type.
Signed-off-by: Satish Kharat <satishkh@cisco.com>
---
drivers/net/ethernet/cisco/enic/enic_main.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c
index e839081f9ee4..e16dfbcd2c22 100644
--- a/drivers/net/ethernet/cisco/enic/enic_main.c
+++ b/drivers/net/ethernet/cisco/enic/enic_main.c
@@ -66,12 +66,14 @@
#define PCI_DEVICE_ID_CISCO_VIC_ENET 0x0043 /* ethernet vnic */
#define PCI_DEVICE_ID_CISCO_VIC_ENET_DYN 0x0044 /* enet dynamic vnic */
#define PCI_DEVICE_ID_CISCO_VIC_ENET_VF 0x0071 /* enet SRIOV VF */
+#define PCI_DEVICE_ID_CISCO_VIC_ENET_VF_V2 0x02b7 /* enet SRIOV V2 VF */
/* Supported devices */
static const struct pci_device_id enic_id_table[] = {
{ PCI_VDEVICE(CISCO, PCI_DEVICE_ID_CISCO_VIC_ENET) },
{ PCI_VDEVICE(CISCO, PCI_DEVICE_ID_CISCO_VIC_ENET_DYN) },
{ PCI_VDEVICE(CISCO, PCI_DEVICE_ID_CISCO_VIC_ENET_VF) },
+ { PCI_VDEVICE(CISCO, PCI_DEVICE_ID_CISCO_VIC_ENET_VF_V2) },
{ 0, } /* end of table */
};
@@ -307,7 +309,8 @@ int enic_sriov_enabled(struct enic *enic)
static int enic_is_sriov_vf(struct enic *enic)
{
- return enic->pdev->device == PCI_DEVICE_ID_CISCO_VIC_ENET_VF;
+ return enic->pdev->device == PCI_DEVICE_ID_CISCO_VIC_ENET_VF ||
+ enic->pdev->device == PCI_DEVICE_ID_CISCO_VIC_ENET_VF_V2;
}
int enic_is_valid_vf(struct enic *enic, int vf)
--
2.43.0
^ permalink raw reply related
* Re: linux-next: manual merge of the bpf-next tree with the bpf tree
From: Alexei Starovoitov @ 2026-04-01 15:27 UTC (permalink / raw)
To: Mark Brown
Cc: Daniel Borkmann, Alexei Starovoitov, Andrii Nakryiko, bpf,
Networking, Eduard Zingerman, Linux Kernel Mailing List,
Linux Next Mailing List
In-Reply-To: <ac0Pr9K1Qbn1-Dn9@sirena.org.uk>
On Wed, Apr 1, 2026 at 5:29 AM Mark Brown <broonie@kernel.org> wrote:
>
> Hi all,
>
> Today's linux-next merge of the bpf-next tree got a conflict in:
>
> kernel/bpf/verifier.c
>
> between commit:
>
> a8502a79e832b ("bpf: Fix regsafe() for pointers to packet")
>
> from the bpf tree and commit:
>
> 022ac07508836 ("bpf: use reg->var_off instead of reg->off for pointers")
>
> from the bpf-next tree.
>
> I fixed it up (see below) and can carry the fix as necessary. This
> is now fixed as far as linux-next is concerned, but any non trivial
> conflicts should be mentioned to your upstream maintainer when your tree
> is submitted for merging. You may also want to consider cooperating
> with the maintainer of the conflicting tree to minimise any particularly
> complex conflicts.
thanks for headsup.
lgtm
we will get it resolved once bpf tree gets pulled.
^ permalink raw reply
* Re: [PATCH-next v2 2/2] ipvs: Guard access of HK_TYPE_KTHREAD cpumask with RCU
From: Waiman Long @ 2026-04-01 15:13 UTC (permalink / raw)
To: Frederic Weisbecker
Cc: Simon Horman, Julian Anastasov, David S. Miller, David Ahern,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Pablo Neira Ayuso,
Florian Westphal, Phil Sutter, Chen Ridong, Phil Auld,
linux-kernel, netdev, lvs-devel, netfilter-devel, coreteam,
sheviks
In-Reply-To: <ac0VfO3XiD_F1gv-@localhost.localdomain>
On 4/1/26 8:54 AM, Frederic Weisbecker wrote:
> Le Tue, Mar 31, 2026 at 12:50:15PM -0400, Waiman Long a écrit :
>> The ip_vs_ctl.c file and the associated ip_vs.h file are the only places
>> in the kernel where HK_TYPE_KTHREAD cpumask is being retrieved and used.
>> Now that HK_TYPE_KTHREAD/HK_TYPE_DOMAIN cpumask can be changed at run
>> time. We need to use RCU to guard access to this cpumask to avoid a
>> potential UAF problem as the returned cpumask may be freed before it
>> is being used.
>>
>> We can replace HK_TYPE_KTHREAD by HK_TYPE_DOMAIN as they are aliases
>> of each other, but keeping the HK_TYPE_KTHREAD name can highlight the
>> fact that it is the kthread initiated by ipvs that is being controlled.
>>
>> Signed-off-by: Waiman Long <longman@redhat.com>
> Oh I see you're handling a few concerns here. But it's too late, the previous
> patch broke bisection.
Good point. So I have to either reverse the patch order, or just change
HK_TYPE_KTHREAD to HK_TYPE_DOMAIN & drop the first one.
>
>> ---
>> include/net/ip_vs.h | 20 ++++++++++++++++----
>> net/netfilter/ipvs/ip_vs_ctl.c | 13 ++++++++-----
>> 2 files changed, 24 insertions(+), 9 deletions(-)
>>
>> diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
>> index 72d325c81313..7bda92fd3fe6 100644
>> --- a/include/net/ip_vs.h
>> +++ b/include/net/ip_vs.h
>> @@ -1411,7 +1411,7 @@ static inline int sysctl_run_estimation(struct netns_ipvs *ipvs)
>> return ipvs->sysctl_run_estimation;
>> }
>>
>> -static inline const struct cpumask *sysctl_est_cpulist(struct netns_ipvs *ipvs)
>> +static inline const struct cpumask *__sysctl_est_cpulist(struct netns_ipvs *ipvs)
>> {
>> if (ipvs->est_cpulist_valid)
>> return ipvs->sysctl_est_cpulist;
>> @@ -1529,7 +1529,7 @@ static inline int sysctl_run_estimation(struct netns_ipvs *ipvs)
>> return 1;
>> }
>>
>> -static inline const struct cpumask *sysctl_est_cpulist(struct netns_ipvs *ipvs)
>> +static inline const struct cpumask *__sysctl_est_cpulist(struct netns_ipvs *ipvs)
>> {
>> return housekeeping_cpumask(HK_TYPE_KTHREAD);
>> }
>> @@ -1564,6 +1564,18 @@ static inline int sysctl_svc_lfactor(struct netns_ipvs *ipvs)
>> return READ_ONCE(ipvs->sysctl_svc_lfactor);
>> }
>>
>> +static inline bool sysctl_est_cpulist_empty(struct netns_ipvs *ipvs)
>> +{
>> + guard(rcu)();
>> + return cpumask_empty(__sysctl_est_cpulist(ipvs));
>> +}
>> +
>> +static inline unsigned int sysctl_est_cpulist_weight(struct netns_ipvs *ipvs)
>> +{
>> + guard(rcu)();
>> + return cpumask_weight(__sysctl_est_cpulist(ipvs));
>> +}
>> +
>> /* IPVS core functions
>> * (from ip_vs_core.c)
>> */
>> @@ -1895,7 +1907,7 @@ static inline void ip_vs_est_stopped_recalc(struct netns_ipvs *ipvs)
>> /* Stop tasks while cpulist is empty or if disabled with flag */
>> ipvs->est_stopped = !sysctl_run_estimation(ipvs) ||
>> (ipvs->est_cpulist_valid &&
>> - cpumask_empty(sysctl_est_cpulist(ipvs)));
>> + sysctl_est_cpulist_empty(ipvs));
> It's not needed, if ipvs->est_cpulist_valid, sysctl_est_cpulist() doesn't
> refer to housekeeping.
Right.
>> #endif
>> }
>>
>> @@ -1911,7 +1923,7 @@ static inline bool ip_vs_est_stopped(struct netns_ipvs *ipvs)
>> static inline int ip_vs_est_max_threads(struct netns_ipvs *ipvs)
>> {
>> unsigned int limit = IPVS_EST_CPU_KTHREADS *
>> - cpumask_weight(sysctl_est_cpulist(ipvs));
>> + sysctl_est_cpulist_weight(ipvs);
> That probably works for callers ip_vs_start_estimator().
>
> But this is not handling the core issue that related kthreads should be updated,
> as is done in ipvs_proc_est_cpumask_set(), when HK_TYPE_DOMAIN mask changes.
If ipvs_proc_est_cpumask_set() has been called, the real affinity should
be the intersection of the given cpumask and HK_TYPE_DOMAIN cpumask. Is
that what you are referring to?
Cheers,
Longman
>
>>
>> return max(1U, limit);
>> }
>> diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
>> index 032425025d88..e253a1ceef48 100644
>> --- a/net/netfilter/ipvs/ip_vs_ctl.c
>> +++ b/net/netfilter/ipvs/ip_vs_ctl.c
>> @@ -2338,11 +2338,14 @@ static int ipvs_proc_est_cpumask_get(const struct ctl_table *table,
>>
>> mutex_lock(&ipvs->est_mutex);
>>
>> - if (ipvs->est_cpulist_valid)
>> - mask = *valp;
>> - else
>> - mask = (struct cpumask *)housekeeping_cpumask(HK_TYPE_KTHREAD);
>> - ret = scnprintf(buffer, size, "%*pbl\n", cpumask_pr_args(mask));
>> + /* HK_TYPE_KTHREAD cpumask needs RCU protection */
>> + scoped_guard(rcu) {
>> + if (ipvs->est_cpulist_valid)
>> + mask = *valp;
>> + else
>> + mask = (struct cpumask *)housekeeping_cpumask(HK_TYPE_KTHREAD);
>> + ret = scnprintf(buffer, size, "%*pbl\n", cpumask_pr_args(mask));
>> + }
> And that works.
>
> Thanks.
>
>>
>> mutex_unlock(&ipvs->est_mutex);
>>
>> --
>> 2.53.0
>>
^ permalink raw reply
* Re: [PATCH net-next v7] selftests: net: add tests for PPP
From: Jakub Kicinski @ 2026-04-01 15:10 UTC (permalink / raw)
To: Qingfang Deng
Cc: Shuah Khan, David S. Miller, Eric Dumazet, Paolo Abeni,
Simon Horman, Felix Maurer, Sebastian Andrzej Siewior,
Matthieu Baerts (NGI0), linux-kernel, linux-kselftest, linux-ppp,
netdev, Paul Mackerras
In-Reply-To: <20260330035604.133073-1-dqfext@gmail.com>
On Mon, 30 Mar 2026 11:55:44 +0800 Qingfang Deng wrote:
> Add ping and iperf3 tests for ppp_async.c and pppoe.c.
Hi! I added the new TARGET to netdev CI, the pppoe.sh test does not
seem happy:
# timeout set to 45
# selftests: net/ppp: pppoe.sh
# Plugin pppoe.so loaded.
# PPPoE plugin from pppd 2.5.1
# Send PPPOE Discovery V1T1 PADI session 0x0 length 12
# dst ff:ff:ff:ff:ff:ff src b2:36:d8:0d:61:83
# [service-name] [host-uniq 9b 08 00 00]
# Recv PPPOE Discovery V1T1 PADO session 0x0 length 73
# dst b2:36:d8:0d:61:83 src 4e:f5:66:23:13:38
# [AC-name vmksft-net-extra,debug-threads=on] [service-name] [AC-cookie e5 e4 8c f0 87 72 d8 3a 60 66 4e 32 e4 ee af 6f 9a 08 00 00] [host-uniq 9b 08 00 00]
# Send PPPOE Discovery V1T1 PADR session 0x0 length 36
# dst 4e:f5:66:23:13:38 src b2:36:d8:0d:61:83
# [service-name] [host-uniq 9b 08 00 00] [AC-cookie e5 e4 8c f0 87 72 d8 3a 60 66 4e 32 e4 ee af 6f 9a 08 00 00]
# Recv PPPOE Discovery V1T1 PADS session 0x1 length 12
# dst b2:36:d8:0d:61:83 src 4e:f5:66:23:13:38
# [service-name] [host-uniq 9b 08 00 00]
# PPP session is 1
# Connected to 4E:F5:66:23:13:38 via interface veth-client
# using channel 1
# Using interface ppp0
# Connect: ppp0 <--> veth-client
# sent [LCP ConfReq id=0x1 <mru 1492> <magic 0x6db8fab4>]
# Modem hangup
# Connection terminated.
# Send PPPOE Discovery V1T1 PADT session 0x1 length 32
# dst 4e:f5:66:23:13:38 src b2:36:d8:0d:61:83
# [host-uniq 9b 08 00 00] [AC-cookie e5 e4 8c f0 87 72 d8 3a 60 66 4e 32 e4 ee af 6f 9a 08 00 00]
# Sent PADT
# ping: connect: Network is unreachable
# iperf3: error - unable to connect to server - server may have stopped running or use a different port, firewall issue, etc.: Network is unreachable
# TEST: PPPoE [FAIL]
not ok 1 selftests: net/ppp: pppoe.sh # exit=1
^ permalink raw reply
* Re: [PATCH 6/6] net: Warn when processes listen on AF_INET sockets
From: Stephen Hemminger @ 2026-04-01 15:06 UTC (permalink / raw)
To: David Woodhouse
Cc: Eric Dumazet, Saeed Mahameed, Leon Romanovsky, Tariq Toukan,
Mark Bloch, Andrew Lunn, David S. Miller, Jakub Kicinski,
Paolo Abeni, Simon Horman, Nikolay Aleksandrov, Ido Schimmel,
Martin KaFai Lau, Daniel Borkmann, John Fastabend,
Stanislav Fomichev, Alexei Starovoitov, Andrii Nakryiko,
Eduard Zingerman, Song Liu, Yonghong Song, KP Singh, Hao Luo,
Jiri Olsa, Kuniyuki Iwashima, Willem de Bruijn, David Ahern,
Neal Cardwell, Johannes Berg, Pablo Neira Ayuso, Florian Westphal,
Phil Sutter, Guillaume Nault, Kees Cook, Alexei Lazar,
Gal Pressman, Paul Moore, netdev, linux-rdma, linux-kernel,
oss-drivers, bridge, bpf, linux-wireless, netfilter-devel,
coreteam, torvalds
In-Reply-To: <252823d75e9221647e7f8ccef6105432aabe8d6f.camel@infradead.org>
On Wed, 01 Apr 2026 10:28:23 +0100
David Woodhouse <dwmw2@infradead.org> wrote:
> > Some kernels are built without CONFIG_IPV6, so this warning would be
> > quite misleading.
>
> Maybe on this date next year, we could make it not possible to build
> the kernel *without* IPv6... ?
There are some government agencies that used to require that IPV6 was disabled
for security reasons. Yes they had broken old firewalls
^ permalink raw reply
* Re: RFC: phylink: disable PHY autonomous EEE when MAC manages LPI
From: Russell King (Oracle) @ 2026-04-01 15:05 UTC (permalink / raw)
To: Andrew Lunn; +Cc: Nicolai Buchwitz, netdev
In-Reply-To: <48abd3e3-a3ee-4e85-a3d8-20c8ceedfb77@lunn.ch>
On Wed, Apr 01, 2026 at 03:11:24PM +0200, Andrew Lunn wrote:
> > Thanks Russell and Andrew. You're both heading in the same direction,
> > so to make sure I understand correctly:
> >
> > 1. Add a new phylib interface (separate from phy_ethtool_set_eee) to
> > control PHY-autonomous EEE, e.g. phy_set_autonomous_eee(phydev,
> > enable) and phy_get_autonomous_eee(phydev) to query the current
> > state.
>
> In the end, we want the MAC driver using phylib to just call the
> phylib methods for configuring EEE, and the MAC driver should not care
> if EEE is actually implemented in the PHY or the MAC. The adjust_link
> callback would simply not enable LPI if the PHY is doing EEE.
This won't work.
If we mask out eee.tx_lpi_enabled when calling into phylib to tell
phylib drivers to disable SmartEEE (that's what I'm calling it here
because it's easier to type)), then phylib records that tx_lpi_enabled
was false. This then makes the following in phy_check_link_status():
phydev->enable_tx_lpi = phydev->eee_cfg.tx_lpi_enabled &&
phydev->eee_active;
set phydev->enable_tx_lpi to false, which in turn tells the MAC
driver or phylink not to enable LPI. So, we lose the ability to use EEE
- a regression - because we're overloading tx_lpi_enabled to mean
"disable SmartEEE" at the PHY.
To work around this, we'd need to go back to the old days of EEE and
have each and every MAC driver resolve the EEE state themselves
because phylib's resolution will now always teoo the MAC driver to
disable LPI.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply
* Re: [PATCH net-next v2 7/9] net: dsa: lan9645x: add mac table integration
From: Jens Emil Schulz Ostergaard @ 2026-04-01 15:00 UTC (permalink / raw)
To: Jakub Kicinski
Cc: UNGLinuxDriver, andrew, olteanv, davem, edumazet, pabeni, horms,
robh, krzk+dt, conor+dt, woojung.huh, linux, Steen.Hegelund,
daniel.machon, linux-kernel, netdev, devicetree
In-Reply-To: <20260329195635.2789227-1-kuba@kernel.org>
On Sun, 2026-03-29 at 12:56 -0700, Jakub Kicinski wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>
> 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: dsa: lan9645x: add mac table integration
>
> Add MAC table support, and dsa fdb callback integration. The mactable is
> keyed on (vid,mac) and each bucket has 4 slots. A mac table entry typically
> points to a PGID index, the first 9 of which represent a front port.
>
> > diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_mac.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_mac.c
> > index 000000000000..6335714dca21
> > --- /dev/null
> > +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_mac.c
> [ ... ]
> > +int lan9645x_mact_entry_add(struct lan9645x *lan9645x, int pgid,
> > + const unsigned char *mac, u16 vid)
> > +{
> > + struct lan9645x_mact_entry *entry;
> > + int err;
> > +
> > + mutex_lock(&lan9645x->mact_lock);
> > +
> > + /* Users can not move (vid,mac) to a different port, without removing
> > + * the original entry first. But we overwrite entry in HW, and update
> > + * software pgid for good measure.
> > + */
> > + entry = lan9645x_mact_entry_find(lan9645x, mac, vid);
> > + if (entry) {
> > + entry->common.pgid = pgid;
> > + goto mac_learn;
> > + }
> > +
> > + entry = lan9645x_mact_entry_alloc(lan9645x, mac, vid, pgid,
> > + ENTRYTYPE_LOCKED);
> > + if (!entry) {
> > + mutex_unlock(&lan9645x->mact_lock);
> > + return -ENOMEM;
> > + }
> > +
> > + list_add_tail(&entry->list, &lan9645x->mac_entries);
> > +
> > +mac_learn:
> > + err = __lan9645x_mact_learn(lan9645x, pgid, mac, vid, ENTRYTYPE_LOCKED);
> > + if (err)
> > + lan9645x_mact_entry_dealloc(lan9645x, entry);
>
> If a pre-existing entry is updated but the hardware learning command fails,
> will this inadvertently delete the valid software entry and cause the hardware
> and software tracking to become desynchronized?
>
I will rearrange so they stay synced.
> > +
> > + mutex_unlock(&lan9645x->mact_lock);
> > + return err;
> > +}
> > +
> > +int lan9645x_mact_entry_del(struct lan9645x *lan9645x, int pgid,
> > + const unsigned char *mac, u16 vid)
> > +{
> > + struct lan9645x_mact_entry *entry;
> > + int err;
> > +
> > + mutex_lock(&lan9645x->mact_lock);
> > +
> > + entry = lan9645x_mact_entry_find(lan9645x, mac, vid);
> > + if (!entry) {
> > + mutex_unlock(&lan9645x->mact_lock);
> > + return -ENOENT;
> > + }
> > +
> > + WARN_ON(entry->common.pgid != pgid);
> > + lan9645x_mact_entry_dealloc(lan9645x, entry);
> > + err = __lan9645x_mact_forget(lan9645x, mac, vid, ENTRYTYPE_LOCKED);
>
> Does this permanently remove the software entry even if the hardware
> command to forget the entry fails?
>
Same here, I can rearrange so they stay synced.
> > +
> > + mutex_unlock(&lan9645x->mact_lock);
> > + return err;
> > +}
> [ ... ]
> > +void lan9645x_mac_deinit(struct lan9645x *lan9645x)
> > +{
> > + mutex_destroy(&lan9645x->mact_lock);
> > +}
>
> Are the static MAC entries in the mac_entries list leaked here during driver
> teardown, or are they freed elsewhere?
>
I thought DSA core would have removed them for me, but it looks like that may not be
the case. I will add dealloc during deinit.
> > +
> > +int lan9645x_mact_dsa_dump(struct lan9645x *lan9645x, int port,
> > + dsa_fdb_dump_cb_t *cb, void *data)
> > +{
> > + struct lan9645x_mact_entry entry = {};
> > + u32 mach, macl, maca;
> > + int err = 0;
> > + u32 autoage;
> [ ... ]
> > + while (1) {
> > + /* NOTE: we rely on mach, macl and type being set correctly in
> > + * the registers from previous round, vis a vis the GET_NEXT
> > + * semantics, so locking entire loop is important.
> > + */
> > + lan_wr(ANA_MACACCESS_MAC_TABLE_CMD_SET(CMD_GET_NEXT) |
> > + ANA_MACACCESS_ENTRYTYPE_SET(entry.common.type),
> > + lan9645x, ANA_MACACCESS);
> > +
> > + if (lan9645x_mac_wait_for_completion(lan9645x, &maca))
> > + break;
>
> If the hardware completion times out, the loop breaks out and returns the
> local err variable which is initialized to 0. Will this suppress the hardware
> timeout error code and incorrectly report a successful dump?
>
Yes, I will return the error.
> > +
> > + if (ANA_MACACCESS_VALID_GET(maca) == 0)
> > + break;
> > +
> > + mach = lan_rd(lan9645x, ANA_MACHDATA);
> > + macl = lan_rd(lan9645x, ANA_MACLDATA);
> > +
> > + lan9645x_mact_parse(mach, macl, maca, &entry.common);
> > +
> > + if (ANA_MACACCESS_DEST_IDX_GET(maca) == port &&
> > + entry.common.type == ENTRYTYPE_NORMAL) {
>
> Since static FDB entries are programmed with ENTRYTYPE_LOCKED, will this
> condition exclude them from the FDB dump, preventing them from showing up
> during bridge FDB queries?
>
Yes that is intentional. This is for the dynamic entries which the bridge core does not
know about.
> > + if (entry.common.key.vid > VLAN_MAX)
> > + entry.common.key.vid = 0;
> > +
> > + err = cb(entry.common.key.mac, entry.common.key.vid,
> > + false, data);
> > + if (err)
> > + break;
> > + }
> > + }
> > +
> > + /* Remove aging filters and restore aging */
> > + lan_wr(0, lan9645x, ANA_ANAGEFIL);
> > + lan_rmw(ANA_AUTOAGE_AGE_PERIOD_SET(ANA_AUTOAGE_AGE_PERIOD_GET(autoage)),
> > + ANA_AUTOAGE_AGE_PERIOD,
> > + lan9645x, ANA_AUTOAGE);
>
> Does restoring the ANA_AUTOAGE register using the autoage value saved at the
> start of the function race with other operations setting the aging time?
>
> If the aging time is updated concurrently without taking mact_lock, its newly
> configured aging period might be overwritten here.
>
AFAIK both run under rtnl so we do not have a race. But I will take the mact_lock during
set_ageing_time.
> > +
> > + mutex_unlock(&lan9645x->mact_lock);
> > +
> > + return err;
> > +}
> > diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
> > index 046c95a72242..32c0301030a4 100644
> > --- a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
> > +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
> [ ... ]
> > +static int lan9645x_fdb_del(struct dsa_switch *ds, int port,
> > + const unsigned char *addr, u16 vid,
> > + struct dsa_db db)
> > +{
> > + struct net_device *br = lan9645x_db2bridge(db);
> > + struct dsa_port *dp = dsa_to_port(ds, port);
> > + struct lan9645x *lan9645x = ds->priv;
> > + int err;
> > +
> > + if (IS_ERR(br))
> > + return PTR_ERR(br);
> > +
> > + if (dsa_port_is_cpu(dp) && !br &&
> > + dsa_fdb_present_in_other_db(ds, port, addr, vid, db))
> > + return 0;
> > +
> > + if (!vid)
> > + vid = lan9645x_vlan_unaware_pvid(!!br);
> > +
> > + if (dsa_port_is_cpu(dp))
> > + return lan9645x_mact_forget(lan9645x, addr, vid,
> > + ENTRYTYPE_LOCKED);
> > +
> > + err = lan9645x_mact_entry_del(lan9645x, port, addr, vid);
> > + if (err == -ENOENT) {
> > + dev_dbg(lan9645x->dev,
> > + "fdb not found port=%d addr=%pM vid=%u\n", port, addr,
> > + vid);
> > + return 0;
> > + }
> > +
> > + return err;
> > +}
>
> Since lan9645x_mact_entry_del returns -ENOENT when a MAC address is not found
> in the software tracking list, and the software list only contains statically
> added entries, won't this intercept attempts to manually delete dynamically
> learned MAC addresses and return success without actually removing them from
> the hardware table?
No I do not think this is how it works. Dynamic entries are flushed with
.port_fast_age, and port_fdb_del is for static entries. We handle -ENOENT
because DSA core does not refcount fdbs for userports.
^ permalink raw reply
* Re: [PATCH net-next v2 0/7] Decouple receive and transmit enablement in team driver
From: Jakub Kicinski @ 2026-04-01 14:57 UTC (permalink / raw)
To: Marc Harvey
Cc: Jiri Pirko, Andrew Lunn, David S. Miller, Eric Dumazet,
Paolo Abeni, Shuah Khan, Simon Horman, netdev, linux-kernel,
linux-kselftest
In-Reply-To: <20260401-teaming-driver-internal-v2-0-f80c1291727b@google.com>
On Wed, 01 Apr 2026 06:05:24 +0000 Marc Harvey wrote:
> Allow independent control over receive and transmit enablement states
> for aggregated ports in the team driver.
>
> The motivation is that IEE 802.3ad LACP "independent control" can't
> be implemented for the team driver currently. This was added to the
> bonding driver in commit 240fd405528b ("bonding: Add independent
> control state machine").
>
> This series also has a few patches that add tests to show that the old
> coupled enablement still works and that the new decoupled enablement
> works as intended (4, 5, and 7).
>
> There are three patches with small fixes as well, with the goal of
> making the final decouplement patch clearer (1, 2, and 3).
Closer but the activebackup test times out (45sec is the default ksft
timeout, I think). How long does it take when you run it?
# selftests: drivers/net/team: teamd_activebackup.sh
# Setting up two-link aggregation for runner activebackup
# Conf files are /tmp/tmp.XyCJqts2JC and /tmp/tmp.kI77XQxR63
# This program is not intended to be run as root.
# This program is not intended to be run as root.
# Created team devices
# PING fd00::2 (fd00::2) 56 data bytes
# 64 bytes from fd00::2: icmp_seq=1 ttl=64 time=0.027 ms
#
# --- fd00::2 ping statistics ---
# 1 packets transmitted, 1 received, 0% packet loss, time 0ms
# rtt min/avg/max/mdev = 0.027/0.027/0.027/0.000 msPacket count for test_team2 was 138
# Packet count for eth0 was 0
# Packet count for eth1 was 124
# Packet count for eth0 was 124
# Packet count for eth1 was 0
# TEST: teamd active backup runner test [ OK ]
# Tearing down two-link aggregation
# Failed to kill daemon: Timer expired
#
not ok 1 selftests: drivers/net/team: teamd_activebackup.sh # TIMEOUT 45 seconds
^ permalink raw reply
* Re: [PATCH net-next v4 1/2] net: hsr: require valid EOT supervision TLV
From: Luka Gejak @ 2026-04-01 14:19 UTC (permalink / raw)
To: Fernando Fernandez Mancera, davem, edumazet, kuba, pabeni
Cc: netdev, fmaurer, horms
In-Reply-To: <8cf382ce-b110-4758-9de4-8944ba980ca3@suse.de>
On April 1, 2026 3:44:16 PM GMT+02:00, Fernando Fernandez Mancera <fmancera@suse.de> wrote:
>On 4/1/26 3:31 PM, Luka Gejak wrote:
>> On April 1, 2026 2:05:49 PM GMT+02:00, Fernando Fernandez Mancera <fmancera@suse.de> wrote:
>>> On 4/1/26 1:06 PM, Luka Gejak wrote:
>>>> On April 1, 2026 11:52:02 AM GMT+02:00, Fernando Fernandez Mancera <fmancera@suse.de> wrote:
>>>>> On 4/1/26 11:23 AM, luka.gejak@linux.dev wrote:
>>>>>> From: Luka Gejak <luka.gejak@linux.dev>
>>>>>>
>>>>>> Supervision frames are only valid if terminated with a zero-length EOT
>>>>>> TLV. The current check fails to reject non-EOT entries as the terminal
>>>>>> TLV, potentially allowing malformed supervision traffic.
>>>>>>
>>>>>> Fix this by strictly requiring the terminal TLV to be HSR_TLV_EOT
>>>>>> with a length of zero.
>>>>>>
>>>>>> Reviewed-by: Felix Maurer <fmaurer@redhat.com>
>>>>>> Signed-off-by: Luka Gejak <luka.gejak@linux.dev>
>>>>>> ---
>>>>>
>>>>> Hi,
>>>>>
>>>>> This has not been reviewed by Felix. Felix provided his Reviewed-by tag for the v1 which was completely different than this.
>>>>>
>>>>> Revisions of this patch:
>>>>>
>>>>> v3: https://lore.kernel.org/all/20260329112313.17164-4-luka.gejak@linux.dev/
>>>>>
>>>>> v2: https://lore.kernel.org/all/20260326154715.38405-4-luka.gejak@linux.dev/
>>>>>
>>>>> v1: https://lore.kernel.org/all/20260324143503.187642-4-luka.gejak@linux.dev/
>>>>>
>>>>> Are these contributions LLM/AI generated? I believe so based on the email history.
>>>>>
>>>>> AI generated review on rtl8723bs: https://lore.kernel.org/all/B2394A3C-25FD-4CEA-8557-3E68F1F60357@linux.dev/
>>>>>
>>>>> Another AI generated review on rtl8723bs: https://lore.kernel.org/all/3831D599-655E-40B2-9E5D-9DF956013088@linux.dev/
>>>>>
>>>>> Likely an AI generated review on a 1 year old HSR patch: https://lore.kernel.org/all/DHFG26KI6L23.1YCOVQ5SSYMO5@linux.dev/
>>>>>
>>>>> If these are indeed, AI generated contributions or reviews they should be disclosed beforehand. Also there is the Assisted-by: tag. Also note that developer must take full responsibility for the contribution which means understanding it completely.
>>>>>
>>>>> https://docs.kernel.org/process/coding-assistants.html#signed-off-by-and-developer-certificate-of-origin
>>>>>
>>>>> Thanks,
>>>>> Fernando.
>>>>
>>>> Hi Fernando,
>>>> About the Reviewed-by tag, that was my mistake. I forgot to remove it
>>>> when rebasing. And about AI. I’ve been using it to help format my
>>>> emails and translate them into English since it isn't my native
>>>> language. However, the technical logic and the code itself are my own
>>>> work, written without AI.
>>>
>>> So I don't think this change in code is related to rebasing at all. The code in this function is identical when comparing net and net-next tree so why a rebase would cause such change in the code? Am I missing something here?
>>>
>>> In addition to that, the new logic does not make any sense. What motivated the diff from v3 to v4? All that information is missing.
>>>
>>> Thanks,
>>> Fernando.
>>>
>>> Should I send v5, and if so, what should I
>>>> do besides stripping the Reviewed-by tag? I've read the documentation
>>>> on coding assistants you linked and will make sure to follow it on the
>>>> next revision.
>>>> Best regards,
>>>> Luka Gejak
>>>>
>>>
>>
>> Hi Fernando,
>> These 2 patches were in the same patch series as 2 other patches.
>> Then, when rebasing to edit 3rd patch in original patch series(3/4), I
>> forgot to remove the Reviewed-by tag(I ran git commit --amend
>> --no-edit). Then I separated 1st 2 and 2nd 2 patches into separate
>> series (one for net and other for net-next) because I was instructed
>> to do so by Jakub Kicinski. His email:
>>> I think that patches 1 and 2 need to go to net with a Fixes tag.
>>> They look like run of the mill bug fixes. 3 and 4 are logical
>>> fixes and change behavior so net-next makes sense.
>>>
>
>This is not addressing my concern. I am not complaining that it went into a separate series. That makes sense. The v3 version of the patch "net: hsr: require valid EOT supervision TLV" is now something completely different but the commit message is the same.
>
>v3: https://lore.kernel.org/all/20260329112313.17164-4-luka.gejak@linux.dev/
>
>I could guess the reason is that in order to make sure the last TLV is a EOT TLV with 0 length but all that explanation is missing on the commit message.
>
>>> FWIW AI has something to say about patch 3, I did not investigate:
>>> https://sashiko.dev/#/patchset/20260329112313.17164-2-luka.gejak@linux.dev
>>> --
>>> pw-bot: cr
>
Hi Fernando,
I apologize for the confusion. Now I get what you were talking about.
The commit message in v4 didn't reflect the logic change from v3. I
will update commit message accordingly. The motivation for the "TLV
walker" refactor in v4 was to fix two issues in v3: linearization/
paged frames and forward compatibility.
Best regards,
Luka Gejak
^ permalink raw reply
* [PATCH net v2 7/7] net/sched: netem: fix slot delay calculation overflow
From: Stephen Hemminger @ 2026-04-01 14:51 UTC (permalink / raw)
To: netdev
Cc: Stephen Hemminger, Jamal Hadi Salim, Jiri Pirko, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Yousuk Seung, Neal Cardwell, open list
In-Reply-To: <20260401145332.78285-1-stephen@networkplumber.org>
get_slot_next() computes a random delay between min_delay and
max_delay using:
get_random_u32() * (max_delay - min_delay) >> 32
This overflows signed 64-bit arithmetic when the delay range exceeds
approximately 2.1 seconds (2^31 nanoseconds), producing a negative
result that effectively disables slot-based pacing. This is a
realistic configuration for WAN emulation (e.g., slot 1s 5s).
Use mul_u64_u32_shr() which handles the widening multiply without
overflow.
Fixes: 0a9fe5c375b5 ("netem: slotting with non-uniform distribution")
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
net/sched/sch_netem.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index ca4de7ae6f2f..5f497fc544ea 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -659,9 +659,8 @@ static void get_slot_next(struct netem_sched_data *q, u64 now)
if (!q->slot_dist)
next_delay = q->slot_config.min_delay +
- (get_random_u32() *
- (q->slot_config.max_delay -
- q->slot_config.min_delay) >> 32);
+ mul_u64_u32_shr(q->slot_config.max_delay - q->slot_config.min_delay,
+ get_random_u32(), 32);
else
next_delay = tabledist(q->slot_config.dist_delay,
(s32)(q->slot_config.dist_jitter),
--
2.53.0
^ permalink raw reply related
* [PATCH net v2 6/7] net/sched: netem: check for invalid slot range
From: Stephen Hemminger @ 2026-04-01 14:51 UTC (permalink / raw)
To: netdev
Cc: Stephen Hemminger, Jamal Hadi Salim, Jiri Pirko, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Neal Cardwell, Yousuk Seung, open list
In-Reply-To: <20260401145332.78285-1-stephen@networkplumber.org>
Reject slot configuration where min_delay exceeds max_delay.
The delay range computation in get_slot_next() underflows in
this case, producing bogus results.
Fixes: 0a9fe5c375b5 ("netem: slotting with non-uniform distribution")
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
net/sched/sch_netem.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 4b27fab72fef..ca4de7ae6f2f 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -882,6 +882,17 @@ static int get_dist_table(struct disttable **tbl, const struct nlattr *attr)
return 0;
}
+static int validate_slot(const struct nlattr *attr, struct netlink_ext_ack *extack)
+{
+ const struct tc_netem_slot *c = nla_data(attr);
+
+ if (c->min_delay > c->max_delay) {
+ NL_SET_ERR_MSG(extack, "slot min delay greater than max delay");
+ return -EINVAL;
+ }
+ return 0;
+}
+
static void get_slot(struct netem_sched_data *q, const struct nlattr *attr)
{
const struct tc_netem_slot *c = nla_data(attr);
@@ -1095,6 +1106,12 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt,
goto table_free;
}
+ if (tb[TCA_NETEM_SLOT]) {
+ ret = validate_slot(tb[TCA_NETEM_SLOT], extack);
+ if (ret)
+ goto table_free;
+ }
+
sch_tree_lock(sch);
/* backup q->clg and q->loss_model */
old_clg = q->clg;
--
2.53.0
^ permalink raw reply related
* [PATCH net v2 5/7] net/sched: netem: null-terminate tfifo linear queue tail
From: Stephen Hemminger @ 2026-04-01 14:51 UTC (permalink / raw)
To: netdev
Cc: Stephen Hemminger, Jamal Hadi Salim, Jiri Pirko, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Peter Oskolkov, open list
In-Reply-To: <20260401145332.78285-1-stephen@networkplumber.org>
When tfifo_enqueue() appends a packet to the linear queue tail,
nskb->next is never set to NULL. The list terminates correctly
only by accident if the skb arrived with next already NULL.
Explicitly null-terminate the tail to prevent list corruption.
Fixes: d66280b12bd7 ("net: netem: use a list in addition to rbtree")
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
net/sched/sch_netem.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index ce12b64603b2..4b27fab72fef 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -398,6 +398,7 @@ static void tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
q->t_tail->next = nskb;
else
q->t_head = nskb;
+ nskb->next = NULL;
q->t_tail = nskb;
} else {
struct rb_node **p = &q->t_root.rb_node, *parent = NULL;
--
2.53.0
^ permalink raw reply related
* [PATCH net v2 4/7] net/sched: netem: restructure dequeue to avoid re-entrancy with child qdisc
From: Stephen Hemminger @ 2026-04-01 14:51 UTC (permalink / raw)
To: netdev
Cc: Stephen Hemminger, Jamal Hadi Salim, Jiri Pirko, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
open list
In-Reply-To: <20260401145332.78285-1-stephen@networkplumber.org>
netem_dequeue() enqueues packets into its child qdisc while being
called from the parent's dequeue path. This causes two problems:
- HFSC tracks class active/inactive state on qlen transitions.
A child enqueue during dequeue causes double-insertion into
the eltree (CVE-2025-37890, CVE-2025-38001).
- Non-work-conserving children like TBF may refuse to dequeue
packets just enqueued, causing netem to return NULL despite
having backlog. Parents like DRR then incorrectly deactivate
the class.
Split the dequeue into helpers:
netem_pull_tfifo() - remove head packet from tfifo
netem_slot_account() - update slot pacing counters
netem_dequeue_child() - batch-transfer ready packets to the
child, then dequeue from the child
netem_dequeue_direct()- dequeue from tfifo when no child
When a child qdisc is present, all time-ready packets are moved
into the child before calling its dequeue. This separates the
enqueue and dequeue phases so the parent sees consistent qlen
transitions.
Fixes: 50612537e9ab ("netem: fix classful handling")
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
net/sched/sch_netem.c | 201 +++++++++++++++++++++++++++---------------
1 file changed, 128 insertions(+), 73 deletions(-)
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 448097fc88a4..ce12b64603b2 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -688,99 +688,154 @@ static struct sk_buff *netem_peek(struct netem_sched_data *q)
return q->t_head;
}
-static void netem_erase_head(struct netem_sched_data *q, struct sk_buff *skb)
+/*
+ * Pop the head packet from the tfifo and prepare it for delivery.
+ * skb->dev shares the rbnode area and must be restored after removal.
+ */
+static struct sk_buff *netem_pull_tfifo(struct netem_sched_data *q,
+ struct Qdisc *sch)
{
- if (skb == q->t_head) {
+ struct sk_buff *skb;
+
+ if (q->t_head) {
+ skb = q->t_head;
q->t_head = skb->next;
if (!q->t_head)
q->t_tail = NULL;
} else {
- rb_erase(&skb->rbnode, &q->t_root);
+ struct rb_node *p = rb_first(&q->t_root);
+
+ if (!p)
+ return NULL;
+ skb = rb_to_skb(p);
+ rb_erase(p, &q->t_root);
}
+
+ q->t_len--;
+ skb->next = NULL;
+ skb->prev = NULL;
+ skb->dev = qdisc_dev(sch);
+
+ return skb;
}
-static struct sk_buff *netem_dequeue(struct Qdisc *sch)
+/* Update slot pacing counters after releasing a packet */
+static void netem_slot_account(struct netem_sched_data *q,
+ const struct sk_buff *skb, u64 now)
+{
+ if (!q->slot.slot_next)
+ return;
+
+ q->slot.packets_left--;
+ q->slot.bytes_left -= qdisc_pkt_len(skb);
+ if (q->slot.packets_left <= 0 || q->slot.bytes_left <= 0)
+ get_slot_next(q, now);
+}
+
+/*
+ * Transfer all time-ready packets from the tfifo into the child qdisc,
+ * then dequeue from the child. Batching the transfers avoids calling
+ * qdisc_enqueue() inside the parent's dequeue path, which confuses
+ * parents that track active/inactive state on qlen transitions (HFSC).
+ */
+static struct sk_buff *netem_dequeue_child(struct Qdisc *sch)
{
struct netem_sched_data *q = qdisc_priv(sch);
+ u64 now = ktime_get_ns();
struct sk_buff *skb;
-tfifo_dequeue:
- skb = __qdisc_dequeue_head(&sch->q);
- if (skb) {
-deliver:
- qdisc_qstats_backlog_dec(sch, skb);
- qdisc_bstats_update(sch, skb);
- return skb;
- }
- skb = netem_peek(q);
- if (skb) {
- u64 time_to_send;
- u64 now = ktime_get_ns();
-
- /* if more time remaining? */
- time_to_send = netem_skb_cb(skb)->time_to_send;
- if (q->slot.slot_next && q->slot.slot_next < time_to_send)
- get_slot_next(q, now);
-
- if (time_to_send <= now && q->slot.slot_next <= now) {
- netem_erase_head(q, skb);
- q->t_len--;
- skb->next = NULL;
- skb->prev = NULL;
- /* skb->dev shares skb->rbnode area,
- * we need to restore its value.
- */
- skb->dev = qdisc_dev(sch);
-
- if (q->slot.slot_next) {
- q->slot.packets_left--;
- q->slot.bytes_left -= qdisc_pkt_len(skb);
- if (q->slot.packets_left <= 0 ||
- q->slot.bytes_left <= 0)
- get_slot_next(q, now);
- }
+ while ((skb = netem_peek(q)) != NULL) {
+ struct sk_buff *to_free = NULL;
+ unsigned int pkt_len;
+ int err;
- if (q->qdisc) {
- unsigned int pkt_len = qdisc_pkt_len(skb);
- struct sk_buff *to_free = NULL;
- int err;
-
- err = qdisc_enqueue(skb, q->qdisc, &to_free);
- kfree_skb_list(to_free);
- if (err != NET_XMIT_SUCCESS) {
- if (net_xmit_drop_count(err))
- qdisc_qstats_drop(sch);
- sch->qstats.backlog -= pkt_len;
- sch->q.qlen--;
- qdisc_tree_reduce_backlog(sch, 1, pkt_len);
- }
- goto tfifo_dequeue;
- }
+ if (netem_skb_cb(skb)->time_to_send > now)
+ break;
+ if (q->slot.slot_next && q->slot.slot_next > now)
+ break;
+
+ skb = netem_pull_tfifo(q, sch);
+ netem_slot_account(q, skb, now);
+
+ pkt_len = qdisc_pkt_len(skb);
+ err = qdisc_enqueue(skb, q->qdisc, &to_free);
+ kfree_skb_list(to_free);
+ if (unlikely(err != NET_XMIT_SUCCESS)) {
+ if (net_xmit_drop_count(err))
+ qdisc_qstats_drop(sch);
+ sch->qstats.backlog -= pkt_len;
sch->q.qlen--;
- goto deliver;
+ qdisc_tree_reduce_backlog(sch, 1, pkt_len);
}
+ }
- if (q->qdisc) {
- skb = q->qdisc->ops->dequeue(q->qdisc);
- if (skb) {
- sch->q.qlen--;
- goto deliver;
- }
- }
+ skb = q->qdisc->ops->dequeue(q->qdisc);
+ if (skb)
+ sch->q.qlen--;
- qdisc_watchdog_schedule_ns(&q->watchdog,
- max(time_to_send,
- q->slot.slot_next));
- }
+ return skb;
+}
- if (q->qdisc) {
- skb = q->qdisc->ops->dequeue(q->qdisc);
- if (skb) {
- sch->q.qlen--;
- goto deliver;
- }
+/* Dequeue directly from the tfifo when no child qdisc is configured. */
+static struct sk_buff *netem_dequeue_direct(struct Qdisc *sch)
+{
+ struct netem_sched_data *q = qdisc_priv(sch);
+ struct sk_buff *skb;
+ u64 time_to_send;
+ u64 now;
+
+ skb = netem_peek(q);
+ if (!skb)
+ return NULL;
+
+ now = ktime_get_ns();
+ time_to_send = netem_skb_cb(skb)->time_to_send;
+
+ if (q->slot.slot_next && q->slot.slot_next < time_to_send)
+ get_slot_next(q, now);
+
+ if (time_to_send > now || q->slot.slot_next > now)
+ return NULL;
+
+ skb = netem_pull_tfifo(q, sch);
+ netem_slot_account(q, skb, now);
+ sch->q.qlen--;
+
+ return skb;
+}
+
+static struct sk_buff *netem_dequeue(struct Qdisc *sch)
+{
+ struct netem_sched_data *q = qdisc_priv(sch);
+ struct sk_buff *skb;
+
+ /* First check the reorder queue */
+ skb = __qdisc_dequeue_head(&sch->q);
+ if (skb)
+ goto deliver;
+
+ if (q->qdisc)
+ skb = netem_dequeue_child(sch);
+ else
+ skb = netem_dequeue_direct(sch);
+
+ if (skb)
+ goto deliver;
+
+ /* Nothing ready — schedule watchdog for next packet */
+ skb = netem_peek(q);
+ if (skb) {
+ u64 time_to_send = netem_skb_cb(skb)->time_to_send;
+
+ qdisc_watchdog_schedule_ns(&q->watchdog,
+ max(time_to_send, q->slot.slot_next));
}
return NULL;
+
+deliver:
+ qdisc_qstats_backlog_dec(sch, skb);
+ qdisc_bstats_update(sch, skb);
+ return skb;
}
static void netem_reset(struct Qdisc *sch)
--
2.53.0
^ permalink raw reply related
* [PATCH net v2 3/7] net/sched: netem: only reseed PRNG when seed is explicitly provided
From: Stephen Hemminger @ 2026-04-01 14:51 UTC (permalink / raw)
To: netdev
Cc: Stephen Hemminger, Jamal Hadi Salim, Jiri Pirko, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
François Michel, open list
In-Reply-To: <20260401145332.78285-1-stephen@networkplumber.org>
netem_change() unconditionally reseeds the PRNG on every tc change
command. If TCA_NETEM_PRNG_SEED is not specified, a new random seed
is generated, destroying reproducibility for users who set a
deterministic seed on a previous change.
Move the initial random seed generation to netem_init() and only
reseed in netem_change() when TCA_NETEM_PRNG_SEED is explicitly
provided by the user.
Fixes: 4072d97ddc44 ("netem: add prng attribute to netem_sched_data")
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
net/sched/sch_netem.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 6cc48b698e48..448097fc88a4 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -1111,11 +1111,10 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt,
/* capping jitter to the range acceptable by tabledist() */
q->jitter = min_t(s64, abs(q->jitter), INT_MAX);
- if (tb[TCA_NETEM_PRNG_SEED])
+ if (tb[TCA_NETEM_PRNG_SEED]) {
q->prng.seed = nla_get_u64(tb[TCA_NETEM_PRNG_SEED]);
- else
- q->prng.seed = get_random_u64();
- prandom_seed_state(&q->prng.prng_state, q->prng.seed);
+ prandom_seed_state(&q->prng.prng_state, q->prng.seed);
+ }
unlock:
sch_tree_unlock(sch);
@@ -1138,6 +1137,9 @@ static int netem_init(struct Qdisc *sch, struct nlattr *opt,
return -EINVAL;
q->loss_model = CLG_RANDOM;
+ q->prng.seed = get_random_u64();
+ prandom_seed_state(&q->prng.prng_state, q->prng.seed);
+
ret = netem_change(sch, opt, extack);
if (ret)
pr_info("netem: change failed\n");
--
2.53.0
^ permalink raw reply related
* [PATCH net v2 2/7] net/sched: netem: fix queue limit check to include reordered packets
From: Stephen Hemminger @ 2026-04-01 14:51 UTC (permalink / raw)
To: netdev
Cc: Stephen Hemminger, Jamal Hadi Salim, Jiri Pirko, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
open list
In-Reply-To: <20260401145332.78285-1-stephen@networkplumber.org>
The queue limit check in netem_enqueue() uses q->t_len which only
counts packets in the internal tfifo. Packets placed in sch->q by
the reorder path (__qdisc_enqueue_head) are not counted, allowing
the total queue occupancy to exceed sch->limit under reordering.
Include sch->q.qlen in the limit check.
Fixes: 50612537e9ab ("netem: fix classful handling")
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
net/sched/sch_netem.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 2cc3acaa4068..6cc48b698e48 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -523,7 +523,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
1<<get_random_u32_below(8);
}
- if (unlikely(q->t_len >= sch->limit)) {
+ if (unlikely(sch->q.qlen >= sch->limit)) {
/* re-link segs, so that qdisc_drop_all() frees them all */
skb->next = segs;
qdisc_drop_all(skb, sch, to_free);
--
2.53.0
^ permalink raw reply related
* [PATCH net v2 1/7] net/sched: netem: fix probability gaps in 4-state loss model
From: Stephen Hemminger @ 2026-04-01 14:51 UTC (permalink / raw)
To: netdev
Cc: Stephen Hemminger, Jamal Hadi Salim, Jiri Pirko, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
open list
In-Reply-To: <20260401145332.78285-1-stephen@networkplumber.org>
The 4-state Markov chain in loss_4state() has gaps at the boundaries
between transition probability ranges. The comparisons use:
if (rnd < a4)
else if (a4 < rnd && rnd < a1 + a4)
When rnd equals a boundary value exactly, neither branch matches and
no state transition occurs. The redundant lower-bound check (a4 < rnd)
is already implied by being in the else branch.
Remove the unnecessary lower-bound comparisons so the ranges are
contiguous and every random value produces a transition, matching
the GI (General and Intuitive) loss model specification.
This bug goes back to original implementation of this model.
Fixes: 661b79725fea ("netem: revised correlated loss generator")
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
net/sched/sch_netem.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 5de1c932944a..2cc3acaa4068 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -227,10 +227,10 @@ static bool loss_4state(struct netem_sched_data *q)
if (rnd < clg->a4) {
clg->state = LOST_IN_GAP_PERIOD;
return true;
- } else if (clg->a4 < rnd && rnd < clg->a1 + clg->a4) {
+ } else if (rnd < clg->a1 + clg->a4) {
clg->state = LOST_IN_BURST_PERIOD;
return true;
- } else if (clg->a1 + clg->a4 < rnd) {
+ } else {
clg->state = TX_IN_GAP_PERIOD;
}
@@ -247,9 +247,9 @@ static bool loss_4state(struct netem_sched_data *q)
case LOST_IN_BURST_PERIOD:
if (rnd < clg->a3)
clg->state = TX_IN_BURST_PERIOD;
- else if (clg->a3 < rnd && rnd < clg->a2 + clg->a3) {
+ else if (rnd < clg->a2 + clg->a3) {
clg->state = TX_IN_GAP_PERIOD;
- } else if (clg->a2 + clg->a3 < rnd) {
+ } else {
clg->state = LOST_IN_BURST_PERIOD;
return true;
}
--
2.53.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