* Re: [PATCH net-next v3 11/15] ibmveth: Add per-queue RX and TX statistics collection and reporting
From: Simon Horman @ 2026-07-14 12:59 UTC (permalink / raw)
To: mmc
Cc: Simon Horman, netdev, bjking1, haren, ricklind, kuba, edumazet,
pabeni, linuxppc-dev, maddy, mpe, davemarq
In-Reply-To: <20260706193603.8039-12-mmc@linux.ibm.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.
Full review at: https://sashiko.dev
---
ibmveth: Add per-queue RX and TX statistics collection and reporting
This commit introduces per-queue statistics tracking for both receive and
transmit paths in the ibmveth driver. The counters are aggregated for legacy
interfaces and exposed individually via ethtool stringsets.
> diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
> index 863e5c68b42c7..4e3f49b6346fd 100644
> --- a/drivers/net/ethernet/ibm/ibmveth.c
> +++ b/drivers/net/ethernet/ibm/ibmveth.c
[ ... ]
> @@ -1655,6 +1655,10 @@ static int ibmveth_open(struct net_device *netdev)
> if (rc)
> goto out_cleanup_rx_interrupts;
>
> + rc = ibmveth_alloc_tx_qstats(adapter);
> + if (rc)
> + goto out_free_tx_resources;
> +
> netif_tx_start_all_queues(netdev);
>
> netdev_dbg(netdev, "open complete\n");
[ ... ]
> out_cleanup_rx_interrupts:
> ibmveth_cleanup_rx_interrupts(adapter);
> out_free_tx_resources:
> ibmveth_free_tx_resources(adapter);
[Severity: High]
Does the goto out_free_tx_resources on ibmveth_alloc_tx_qstats() failure
bypass the cleanup for RX interrupts?
Since out_free_tx_resources is physically below out_cleanup_rx_interrupts in
ibmveth_open(), failing here leaves the IRQs requested and NAPI enabled. If
an interrupt subsequently fires, could the handler access the freed RX queue
memory and cause a kernel panic?
[ ... ]
> @@ -1699,6 +1699,7 @@ static int ibmveth_close(struct net_device *netdev)
> }
> }
>
> + ibmveth_free_tx_qstats(adapter);
> ibmveth_free_tx_resources(adapter);
> ibmveth_cleanup_rx_interrupts(adapter);
> ibmveth_update_rx_no_buffer(adapter);
[ ... ]
> ibmveth_free_rx_qstats(adapter);
[Severity: High]
If ibmveth_close() is called to bring down the interface, does this
unconditionally free adapter->tx_qstats and adapter->rx_qstats without a
grace period?
Since ibmveth_get_stats64() can be called concurrently under RCU, and
ibmveth_start_xmit() can run concurrently if the datapath is not completely
synchronized, could this lead to a use-after-free or NULL pointer dereference
when these functions access the freed arrays?
[ ... ]
> @@ -1967,22 +1967,107 @@ static int ibmveth_set_features(struct net_device *dev,
[ ... ]
> +static void ibmveth_aggregate_rx_qstats(struct ibmveth_adapter *adapter)
> +{
> + u64 total_invalid = 0;
> + u64 total_large = 0;
> + int i;
> +
> + if (!adapter->rx_qstats)
> + return;
> +
> + for (i = 0; i < adapter->num_rx_queues; i++) {
> + total_invalid += adapter->rx_qstats[i].invalid_buffers;
> + total_large += adapter->rx_qstats[i].large_packets;
> + }
> +
> + adapter->rx_invalid_buffer = total_invalid;
> + adapter->rx_large_packets = total_large;
> +}
[Severity: High]
Does overwriting adapter->rx_invalid_buffer and other persistent global
counters with the dynamically allocated adapter->rx_qstats values cause a
permanent loss of interface statistics?
When the interface is brought down and back up, ibmveth_close() frees the
queue stats arrays and ibmveth_open() reallocates them as zeroed. Will this
cause all historical statistics to be wiped out on every interface reset,
breaking standard persistence behavior?
[ ... ]
> +static void ibmveth_get_strings(struct net_device *dev, u32 stringset, u8 *data)
> +{
[ ... ]
> + for (i = 0; i < adapter->num_rx_queues; i++) {
> + ethtool_sprintf(&p, "rx%d_packets", i);
> + ethtool_sprintf(&p, "rx%d_bytes", i);
[Severity: Low]
Are these private ethtool stringsets duplicating standard kernel statistics?
According to the subsystem guidelines, per-queue packet and byte statistics
that have a standard UAPI representation (like struct netdev_queue_stats_rx
exported via netlink) should not be duplicated as private ethtool strings.
> diff --git a/drivers/net/ethernet/ibm/ibmveth.h b/drivers/net/ethernet/ibm/ibmveth.h
> index f7b20fd01acb5..390c660af979e 100644
> --- a/drivers/net/ethernet/ibm/ibmveth.h
> +++ b/drivers/net/ethernet/ibm/ibmveth.h
> @@ -316,9 +316,21 @@ struct ibmveth_rx_queue_stats {
> u64 no_buffer_drops;
> };
>
> +struct ibmveth_tx_queue_stats {
> + u64 packets;
> + u64 bytes;
> + u64 large_packets;
> + u64 dropped_packets;
> + u64 send_failures;
> + u64 checksum_offload;
> +};
[Severity: Medium]
Will this structure layout prevent cache line false sharing?
The commit message mentions tracking transmit counters per TX queue to avoid
cache line contention, but struct ibmveth_tx_queue_stats appears to be
exactly 48 bytes and lacks cacheline alignment annotations like
____cacheline_aligned. When allocated as a contiguous array, will multiple
CPUs processing different queues end up writing to the same hardware
cacheline?
^ permalink raw reply
* [PATCH net] dpll: use pin owner's dpll ref for pin-level attribute reporting
From: Ivan Vecera @ 2026-07-14 12:59 UTC (permalink / raw)
To: netdev
Cc: Vadim Fedorenko, Arkadiusz Kubalewski, Jiri Pirko,
Aleksandr Loktionov, Grzegorz Nitka, Jakub Kicinski, open list
Commit c191b319f208 ("dpll: allow registering FW-identified pin with a
different DPLL") relaxed dpll_pin_register() to let fwnode-identified pins
register with DPLLs from a different driver. This allows, for example, the
ICE driver to register a zl3073x-created pin with its TXC DPLL using
ice_dpll_txclk_ops, which lack frequency_get and phase_adjust_get
callbacks.
After such cross-driver registration, the pin's dpll_refs xarray contains
refs from both drivers. dpll_cmd_pin_get_one() calls
dpll_xa_ref_dpll_first() which returns the ref with the lowest DPLL id.
When the foreign DPLL (e.g. ICE TXC) has a lower id than the owner DPLL
(e.g. zl3073x), the foreign ops are used for reporting. Since those ops
lack callbacks like frequency_get, pin-level attributes are silently
omitted from the netlink response.
For example, a zl3073x output pin that should report frequency and
phase-adjust shows neither:
Before:
# dpll pin show id 45
pin id 45:
module-name: zl3073x
clock-id: 3427468959636104019
board-label: 156M25_NAC0_CLKREF_SYNC
package-label: OUT3
type: synce-eth-port
capabilities: 0x0
phase-adjust-min: -2147483648
phase-adjust-max: 2147483647
phase-adjust-gran: 800
parent-device:
...
After:
# dpll pin show id 19
pin id 19:
module-name: zl3073x
clock-id: 15964355450360090479
board-label: 156M25_NAC0_CLKREF_SYNC
package-label: OUT3
type: synce-eth-port
frequency: 156250000 Hz
frequency-supported:
156250000 Hz
capabilities: 0x0
phase-adjust-min: -2147483648
phase-adjust-max: 2147483647
phase-adjust-gran: 800
phase-adjust: 0
parent-device:
...
Fix this by:
1. Adding dpll_pin_own_dpll_ref_first() helper that returns the first ref
whose DPLL matches the pin's (module, clock_id) tuple — i.e. the DPLL
from the driver that created the pin and has the complete set of ops.
Return NULL if no owner ref is found.
2. Using dpll_pin_own_dpll_ref_first() in dpll_cmd_pin_get_one() with a
fallback to dpll_xa_ref_dpll_first() for pin-on-pin child pins whose
dpll_refs all point to a different driver's DPLLs.
3. Using dpll_pin_own_dpll_ref_first() in SET operations
(dpll_pin_freq_set, dpll_pin_esync_set, dpll_pin_ref_sync_state_set,
dpll_pin_phase_adj_set) returning -ENODEV if no owner ref exists.
Replacing the validation loops that rejected the entire operation when
any ref's ops lacked the required callback — instead validate only the
owner refs so that foreign DPLLs with incomplete ops no longer block
SET operations.
4. Guarding all SET and rollback xa_for_each loops against NULL set
callbacks so that foreign refs without the operation are safely skipped
instead of causing a NULL pointer dereference.
Fixes: c191b319f208 ("dpll: allow registering FW-identified pin with a different DPLL")
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
---
drivers/dpll/dpll_core.c | 27 +++++++++++++++++
drivers/dpll/dpll_core.h | 1 +
drivers/dpll/dpll_netlink.c | 60 ++++++++++++++++++++++++++++++-------
3 files changed, 78 insertions(+), 10 deletions(-)
diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
index bb1e8650c9d59..245f625f4b3b4 100644
--- a/drivers/dpll/dpll_core.c
+++ b/drivers/dpll/dpll_core.c
@@ -1146,6 +1146,33 @@ void *dpll_pin_on_pin_priv(struct dpll_pin *parent,
return reg->priv;
}
+/**
+ * dpll_pin_own_dpll_ref_first - find the first owner dpll ref of a pin
+ * @pin: pointer to a dpll pin
+ *
+ * Search pin's dpll_refs for a ref whose dpll matches the pin's
+ * (module, clock_id) tuple, i.e. the dpll registered by the driver
+ * that created the pin. This ensures pin-level attributes are
+ * reported and modified using the owner's ops even when the pin is
+ * also registered with dplls from other drivers.
+ *
+ * Return: pointer to the owner's dpll_pin_ref, or NULL if no
+ * owner ref is found.
+ */
+struct dpll_pin_ref *dpll_pin_own_dpll_ref_first(struct dpll_pin *pin)
+{
+ struct dpll_pin_ref *ref;
+ unsigned long i;
+
+ xa_for_each(&pin->dpll_refs, i, ref) {
+ if (ref->dpll->module == pin->module &&
+ ref->dpll->clock_id == pin->clock_id)
+ return ref;
+ }
+
+ return NULL;
+}
+
const struct dpll_pin_ops *dpll_pin_ops(struct dpll_pin_ref *ref)
{
struct dpll_pin_registration *reg;
diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h
index e245771134317..da8a369556ed1 100644
--- a/drivers/dpll/dpll_core.h
+++ b/drivers/dpll/dpll_core.h
@@ -93,6 +93,7 @@ void *dpll_pin_on_pin_priv(struct dpll_pin *parent, struct dpll_pin *pin);
const struct dpll_device_ops *dpll_device_ops(struct dpll_device *dpll);
struct dpll_device *dpll_device_get_by_id(int id);
+struct dpll_pin_ref *dpll_pin_own_dpll_ref_first(struct dpll_pin *pin);
const struct dpll_pin_ops *dpll_pin_ops(struct dpll_pin_ref *ref);
struct dpll_pin_ref *dpll_xa_ref_dpll_first(struct xarray *xa_refs);
extern struct xarray dpll_device_xa;
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index bf729cde796a7..b3f0cb7d3d349 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -696,7 +696,9 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
struct dpll_pin_ref *ref;
int ret;
- ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
+ ref = dpll_pin_own_dpll_ref_first(pin);
+ if (!ref)
+ ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
ASSERT_NOT_NULL(ref);
ret = dpll_msg_add_pin_handle(msg, pin);
@@ -1087,12 +1089,19 @@ dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
xa_for_each(&pin->dpll_refs, i, ref) {
ops = dpll_pin_ops(ref);
- if (!ops->frequency_set || !ops->frequency_get) {
- NL_SET_ERR_MSG(extack, "frequency set not supported by the device");
+ if ((!ops->frequency_set || !ops->frequency_get) &&
+ ref->dpll->module == pin->module &&
+ ref->dpll->clock_id == pin->clock_id) {
+ NL_SET_ERR_MSG(extack,
+ "frequency set not supported by the device");
return -EOPNOTSUPP;
}
}
- ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
+ ref = dpll_pin_own_dpll_ref_first(pin);
+ if (!ref) {
+ NL_SET_ERR_MSG(extack, "pin owner dpll not found");
+ return -ENODEV;
+ }
ops = dpll_pin_ops(ref);
dpll = ref->dpll;
ret = ops->frequency_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
@@ -1106,6 +1115,8 @@ dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
xa_for_each(&pin->dpll_refs, i, ref) {
ops = dpll_pin_ops(ref);
+ if (!ops->frequency_set)
+ continue;
dpll = ref->dpll;
ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
dpll, dpll_priv(dpll), freq, extack);
@@ -1125,6 +1136,8 @@ dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
if (ref == failed)
break;
ops = dpll_pin_ops(ref);
+ if (!ops->frequency_set)
+ continue;
dpll = ref->dpll;
if (ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
dpll, dpll_priv(dpll), old_freq, extack))
@@ -1148,13 +1161,19 @@ dpll_pin_esync_set(struct dpll_pin *pin, struct nlattr *a,
xa_for_each(&pin->dpll_refs, i, ref) {
ops = dpll_pin_ops(ref);
- if (!ops->esync_set || !ops->esync_get) {
+ if ((!ops->esync_set || !ops->esync_get) &&
+ ref->dpll->module == pin->module &&
+ ref->dpll->clock_id == pin->clock_id) {
NL_SET_ERR_MSG(extack,
"embedded sync feature is not supported by this device");
return -EOPNOTSUPP;
}
}
- ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
+ ref = dpll_pin_own_dpll_ref_first(pin);
+ if (!ref) {
+ NL_SET_ERR_MSG(extack, "pin owner dpll not found");
+ return -ENODEV;
+ }
ops = dpll_pin_ops(ref);
dpll = ref->dpll;
ret = ops->esync_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
@@ -1178,6 +1197,8 @@ dpll_pin_esync_set(struct dpll_pin *pin, struct nlattr *a,
void *pin_dpll_priv;
ops = dpll_pin_ops(ref);
+ if (!ops->esync_set)
+ continue;
dpll = ref->dpll;
pin_dpll_priv = dpll_pin_on_dpll_priv(dpll, pin);
ret = ops->esync_set(pin, pin_dpll_priv, dpll, dpll_priv(dpll),
@@ -1201,6 +1222,8 @@ dpll_pin_esync_set(struct dpll_pin *pin, struct nlattr *a,
if (ref == failed)
break;
ops = dpll_pin_ops(ref);
+ if (!ops->esync_set)
+ continue;
dpll = ref->dpll;
pin_dpll_priv = dpll_pin_on_dpll_priv(dpll, pin);
if (ops->esync_set(pin, pin_dpll_priv, dpll, dpll_priv(dpll),
@@ -1235,8 +1258,11 @@ dpll_pin_ref_sync_state_set(struct dpll_pin *pin,
NL_SET_ERR_MSG(extack, "reference sync pin not available");
return -EINVAL;
}
- ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
- ASSERT_NOT_NULL(ref);
+ ref = dpll_pin_own_dpll_ref_first(pin);
+ if (!ref) {
+ NL_SET_ERR_MSG(extack, "pin owner dpll not found");
+ return -ENODEV;
+ }
ops = dpll_pin_ops(ref);
if (!ops->ref_sync_set || !ops->ref_sync_get) {
NL_SET_ERR_MSG(extack, "reference sync not supported by this pin");
@@ -1255,6 +1281,8 @@ dpll_pin_ref_sync_state_set(struct dpll_pin *pin,
return 0;
xa_for_each(&pin->dpll_refs, i, ref) {
ops = dpll_pin_ops(ref);
+ if (!ops->ref_sync_set)
+ continue;
dpll = ref->dpll;
ret = ops->ref_sync_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
ref_sync_pin,
@@ -1277,6 +1305,8 @@ dpll_pin_ref_sync_state_set(struct dpll_pin *pin,
if (ref == failed)
break;
ops = dpll_pin_ops(ref);
+ if (!ops->ref_sync_set)
+ continue;
dpll = ref->dpll;
if (ops->ref_sync_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
ref_sync_pin,
@@ -1468,12 +1498,18 @@ dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr,
xa_for_each(&pin->dpll_refs, i, ref) {
ops = dpll_pin_ops(ref);
- if (!ops->phase_adjust_set || !ops->phase_adjust_get) {
+ if ((!ops->phase_adjust_set || !ops->phase_adjust_get) &&
+ ref->dpll->module == pin->module &&
+ ref->dpll->clock_id == pin->clock_id) {
NL_SET_ERR_MSG(extack, "phase adjust not supported");
return -EOPNOTSUPP;
}
}
- ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
+ ref = dpll_pin_own_dpll_ref_first(pin);
+ if (!ref) {
+ NL_SET_ERR_MSG(extack, "pin owner dpll not found");
+ return -ENODEV;
+ }
ops = dpll_pin_ops(ref);
dpll = ref->dpll;
ret = ops->phase_adjust_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
@@ -1488,6 +1524,8 @@ dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr,
xa_for_each(&pin->dpll_refs, i, ref) {
ops = dpll_pin_ops(ref);
+ if (!ops->phase_adjust_set)
+ continue;
dpll = ref->dpll;
ret = ops->phase_adjust_set(pin,
dpll_pin_on_dpll_priv(dpll, pin),
@@ -1510,6 +1548,8 @@ dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr,
if (ref == failed)
break;
ops = dpll_pin_ops(ref);
+ if (!ops->phase_adjust_set)
+ continue;
dpll = ref->dpll;
if (ops->phase_adjust_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
dpll, dpll_priv(dpll), old_phase_adj,
--
2.53.0
^ permalink raw reply related
* Re: [PATCH net-next 9/9] net: document RFC6724 rule 5.5 implementation
From: Jonathan Corbet @ 2026-07-14 13:01 UTC (permalink / raw)
To: David 'equinox' Lamparter, Paolo Abeni, Jakub Kicinski,
Ido Schimmel
Cc: David Ahern, David S. Miller, Eric Dumazet, Simon Horman,
Shuah Khan, Fernando Fernandez Mancera, Lorenzo Colitti,
Maciej Żenczykowski, Patrick Rohr, netdev, linux-doc,
linux-kselftest, David 'equinox' Lamparter
In-Reply-To: <20260714094030.136317-10-equinox@diac24.net>
David 'equinox' Lamparter <equinox@diac24.net> writes:
> RFC6724 rule 5.5 is anything but obvious, especially if trying to do it
> well. (RFC8028 and its errata kinda proves the point.)
>
> This documents what exactly the Linux kernel does for RFC6724 rule 5.5,
> especially what the routing table needs to look like for it to work.
>
> Signed-off-by: David 'equinox' Lamparter <equinox@diac24.net>
> ---
> Documentation/networking/ipv6-addrsel.rst | 75 +++++++++++++++++++++++
> MAINTAINERS | 1 +
> 2 files changed, 76 insertions(+)
> create mode 100644 Documentation/networking/ipv6-addrsel.rst
You need to add this new document to the index.rst file or it won't be
part of the docs build...you should have seen a warning when you built
the docs.
> diff --git a/Documentation/networking/ipv6-addrsel.rst b/Documentation/networking/ipv6-addrsel.rst
> new file mode 100644
> index 000000000000..bed032e69570
> --- /dev/null
> +++ b/Documentation/networking/ipv6-addrsel.rst
> @@ -0,0 +1,75 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +====================================
> +IPv6 source address selection trivia
> +====================================
> +
> +
> +RFC6724 rule 5.5 support
> +------------------------
Please stick with the section markup guidelines in
Documentation/doc-guide/sphinx.rst.
> +RFC6724 rule 5.5 is a very short paragraph in a complex RFC that has turned
> +out quite tricky, but also immensely useful in multihoming scenarios. For
> +reference, it says:
> +
> +::
You'll likely get more visually pleasing results if you just leave out
the "::" here; you don't need literal formatting.
> + Rule 5.5: Prefer addresses in a prefix advertised by the next-hop.
> + If SA or SA's prefix is assigned by the selected next-hop that will
> + be used to send to D and SB or SB's prefix is assigned by a different
> + next-hop, then prefer SA. Similarly, if SB or SB's prefix is
> + assigned by the next-hop that will be used to send to D and SA or
> + SA's prefix is assigned by a different next-hop, then prefer SB.
> +
> +The way this works on Linux is as follows:
> +
> +- prior to any source address selection happening, when receiving a RA, more
> + than the installation of a default route (or ::/128 route) needs to happen:
> + for each PIO, a source-specific (subtree) route is *additionally* installed.
> + The effect of this is that *after* a source address has been selected, one
> + of the routers that advertised it will remain in use (this is *not* RFC 6724
> + related, but rather RFC 8028.) At the same time, these extra routes serve
> + to remember which router advertised what.
> +
> +- per usual, a route lookup for the IPv6 destination address in consideration
> + is done first. This is passed around in kernel as a dst_entry.
> +
> +- the source address selection code iterates through the various rules in
> + RFC 6724.
> +
> +- if/when rule 5.5 is reached, first of all, there is a check if *any* source
> + specific routes exist in the routing table. If there are none, the entire
> + code for 5.5 is skipped because it cannot have any effect, but is not free
> + to execute (can involve multiple routing lookups.) **In applications that
> + use a lot of unbound (e.g. UDP) sockets, installing subtree routes should
> + therefore be avoided to not incur this cost on each source address selection
> + pass.** Alternatively, applications should bind their sockets to a specific
> + source address such that the selection code is never hit.
> +
> +- if subtree routes do exist, the source address selection code now repeats
> + the routing lookup done before source address selection is entered, except
> + with the source address under consideration filled in. This lookup will hit
> + the subtree routes that were installed (see first item), giving a fresh
> + dst_entry. If the new dst_entry matches the original dst_entry, that means
> + the original router has in fact sent RAs with PIOs for this source address,
> + so it is preferred. Otherwise it is not.
> +
> +
> +There are a few caveats to consider:
> +
> +- the kernel currently does not create the subtree routes mentioned in the
> + first item. This is a separate work item, partially done at the time of
> + writing this. But this can equally well be performed in userspace processing
> + of RAs, e.g. NetworkManager or plain static configuration.
> +
> +- since addresses can also be acquired from DHCPv6, even RA/PIO combinations
> + that didn't result in the creation of any addresses (e.g. A=0) should have
> + subtree routes added. Those routes *may* be relevant for DHCPv6-generated
> + addresses.
> +
> +- the "announce check" lookup does not backtrack. Only the destination prefix
> + that provided the "unspecific" (::/128) match is checked for source prefixes
> + to see what routers advertised what. This means that for e.g. RIOs, subtree
> + routes also have to be created. (Backtracking for this case would further
> + increase the cost of source address selection, for a pretty rare situation
> + that has an easy fix/workaround.)
> diff --git a/MAINTAINERS b/MAINTAINERS
> index f3218abefd0c..4edf48362a07 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -18912,6 +18912,7 @@ F: Documentation/netlink/specs/rt-addr.yaml
> F: Documentation/netlink/specs/rt-neigh.yaml
> F: Documentation/netlink/specs/rt-route.yaml
> F: Documentation/netlink/specs/rt-rule.yaml
> +F: Documentation/networking/ipv6-addrsel.rst
It seems weird to add this one file here - I wonder why there isn't just
an entry for Documentation/networking ?
Thanks,
jon
^ permalink raw reply
* Re: [PATCH net-next v3 12/15] ibmveth: Expose per-queue buffer pool details via sysfs
From: Simon Horman @ 2026-07-14 13:01 UTC (permalink / raw)
To: mmc
Cc: Simon Horman, netdev, bjking1, haren, ricklind, kuba, edumazet,
pabeni, linuxppc-dev, maddy, mpe, davemarq
In-Reply-To: <20260706193603.8039-13-mmc@linux.ibm.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.
Full review at: https://sashiko.dev
---
ibmveth: Expose per-queue buffer pool details via sysfs
This commit adds a read-only sysfs attribute buffer_pools to the VIO device
to expose per-queue buffer pool details dynamically. It outputs runtime per-queue
buffer pressure metrics like size, active, and available buffers.
> diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
> index 4e3f49b6346fd..ecc472ee8f71f 100644
> --- a/drivers/net/ethernet/ibm/ibmveth.c
> +++ b/drivers/net/ethernet/ibm/ibmveth.c
> @@ -2896,6 +2896,52 @@ static const struct net_device_ops ibmveth_netdev_ops = {
[ ... ]
> +static ssize_t buffer_pools_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct net_device *netdev = dev_get_drvdata(dev);
> + struct ibmveth_adapter *adapter = netdev_priv(netdev);
> + int len = 0;
> + int i, j;
> +
> + len += scnprintf(buf + len, PAGE_SIZE - len,
> + "Queue Pool Size BuffSize Active Available\n");
> + len += scnprintf(buf + len, PAGE_SIZE - len,
> + "----- ---- ---- -------- ------ ---------\n");
[Severity: Medium]
Does this buffer_pools_show() implementation violate the sysfs "one value
per file" design rule?
The attribute appears to dump a complex, multi-line ASCII table with headers
and columns rather than exposing a single value per sysfs file.
[ ... ]
> @@ -3056,6 +3102,14 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id)
>
> netdev_dbg(netdev, "registered\n");
>
> + rc = sysfs_create_group(&dev->dev.kobj, &ibmveth_attr_group);
> + if (rc) {
> + netdev_err(netdev, "failed to create sysfs attributes rc=%d\n", rc);
> + unregister_netdev(netdev);
> + free_netdev(netdev);
> + return rc;
> + }
[Severity: High]
If userspace brings up the interface immediately after register_netdev()
succeeds, could adapter->work be scheduled before sysfs_create_group() fails?
If this race occurs, unregister_netdev() does not synchronously cancel the
work.
Could the global workqueue execute the pending adapter->work item on freed
memory after free_netdev() is called in this ibmveth_probe() error path?
[Severity: High]
This is a pre-existing issue in older error paths of ibmveth_probe(), but it
is duplicated here: does this error path leak the adapter->rx_buff_pool
kobjects?
Since kobject_put() is not called on the pool kobjects before free_netdev()
is executed, the kobjects remain attached to the VIO device with dangling
pointers to the freed adapter memory.
Could subsequent sysfs accesses to those pool kobjects trigger a
use-after-free?
^ permalink raw reply
* [PATCH net] mctp: serial: reject zero-length frames to prevent rx buffer overflow
From: Doruk Tan Ozturk @ 2026-07-14 13:03 UTC (permalink / raw)
To: jk, matt, andrew+netdev, davem, edumazet, kuba, pabeni
Cc: netdev, linux-kernel, stable
The MCTP serial receive state machine reads a frame length byte in
mctp_serial_push_header() case 2 and validates it upper-bound-only:
if (c > MCTP_SERIAL_FRAME_MTU) {
dev->rxstate = STATE_ERR;
} else {
dev->rxlen = c;
dev->rxpos = 0;
dev->rxstate = STATE_DATA;
...
}
A length of zero passes this check, so rxlen is set to 0 and the state
machine advances to STATE_DATA. In mctp_serial_push() STATE_DATA, the
incoming byte is stored and rxpos incremented before the terminator is
tested:
dev->rxbuf[dev->rxpos] = c;
dev->rxpos++;
dev->rxstate = STATE_DATA;
if (dev->rxpos == dev->rxlen) {
dev->rxpos = 0;
dev->rxstate = STATE_TRAILER;
}
With rxlen == 0 the "rxpos == rxlen" terminator can never fire (rxpos is
already 1 on the first data byte), so subsequent bytes are written past
the end of the fixed 74-byte rxbuf, which is the last member of the
netdev private area. Every following data byte is an attacker-controlled
1-byte out-of-bounds heap write, and the overflow continues until a
frame (0x7e) or escape byte resets the parser -- effectively unbounded.
Reaching this requires CAP_NET_ADMIN to attach the N_MCTP line
discipline and bring the resulting mctpserialN netdev up, after which
the bytes arrive via the tty receive path.
Reject a zero-length frame in the header parser, matching the existing
upper-bound rejection.
KASAN, on a frame of 0x7e 0x01 0x00 followed by data bytes:
UBSAN: array-index-out-of-bounds in drivers/net/mctp/mctp-serial.c:370
index 74 is out of range for type 'u8 [74]'
BUG: KASAN: slab-out-of-bounds in mctp_serial_tty_receive_buf
Write of size 1 at addr ... by task kworker/u16:0
mctp_serial_tty_receive_buf
tty_ldisc_receive_buf
flush_to_ldisc
Allocated by task 152:
alloc_netdev_mqs
mctp_serial_open
Found by 0sec (https://0sec.ai).
Fixes: a0c2ccd9b5ad ("mctp: Add MCTP-over-serial transport binding")
Cc: stable@vger.kernel.org
Assisted-by: 0sec
Signed-off-by: Doruk Tan Ozturk <doruk@0sec.ai>
---
drivers/net/mctp/mctp-serial.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/mctp/mctp-serial.c b/drivers/net/mctp/mctp-serial.c
index 26c9a33fd636..1e3d285c0500 100644
--- a/drivers/net/mctp/mctp-serial.c
+++ b/drivers/net/mctp/mctp-serial.c
@@ -313,7 +313,7 @@ static void mctp_serial_push_header(struct mctp_serial *dev, u8 c)
}
break;
case 2:
- if (c > MCTP_SERIAL_FRAME_MTU) {
+ if (c == 0 || c > MCTP_SERIAL_FRAME_MTU) {
dev->rxstate = STATE_ERR;
} else {
dev->rxlen = c;
--
2.43.0
^ permalink raw reply related
* [PATCH v2 1/2] s390/ptff: Export ptff_function_mask[]
From: Sven Schnelle @ 2026-07-14 13:03 UTC (permalink / raw)
To: Richard Cochran
Cc: netdev, linux-s390, linux-kernel, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Heiko Carstens, Vasily Gorbik,
Alexander Gordeev
In-Reply-To: <20260714130342.1971700-1-svens@linux.ibm.com>
Export the ptff_function_mask to make ptff_query() usable in modules.
Signed-off-by: Sven Schnelle <svens@linux.ibm.com>
---
arch/s390/kernel/time.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c
index bd0df61d1907..2b989bebd220 100644
--- a/arch/s390/kernel/time.c
+++ b/arch/s390/kernel/time.c
@@ -65,6 +65,7 @@ ATOMIC_NOTIFIER_HEAD(s390_epoch_delta_notifier);
EXPORT_SYMBOL(s390_epoch_delta_notifier);
unsigned char ptff_function_mask[16];
+EXPORT_SYMBOL(ptff_function_mask);
static unsigned long lpar_offset;
static unsigned long initial_leap_seconds;
--
2.55.0
^ permalink raw reply related
* [PATCH v2 0/2] Add missing facility check to ptp_s390 driver
From: Sven Schnelle @ 2026-07-14 13:03 UTC (permalink / raw)
To: Richard Cochran
Cc: netdev, linux-s390, linux-kernel, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Heiko Carstens, Vasily Gorbik,
Alexander Gordeev
This patchset adds a missing facility check and a check that the 'query
physical clock' (PTFF QPT) function is actually available. If it's not
present, no qpt ptp device will be registered. In order to use ptff_query()
in a module, the first patch adds a EXPORT_SYMBOL() to export
ptff_function_mask.
Changes in v2:
- drop CC check
- add PTFF QAF check
- add patch to export ptff_function_mask
Sven Schnelle (2):
s390/ptff: Export ptff_function_mask[]
ptp: ptp_s390: Add missing facility check
arch/s390/kernel/time.c | 1 +
drivers/ptp/ptp_s390.c | 6 +++++-
2 files changed, 6 insertions(+), 1 deletion(-)
--
2.55.0
^ permalink raw reply
* [PATCH v2 2/2] ptp: ptp_s390: Add missing facility check
From: Sven Schnelle @ 2026-07-14 13:03 UTC (permalink / raw)
To: Richard Cochran
Cc: netdev, linux-s390, linux-kernel, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Heiko Carstens, Vasily Gorbik,
Alexander Gordeev
In-Reply-To: <20260714130342.1971700-1-svens@linux.ibm.com>
Only register the physical clock when facility 28 is installed
and PTFF QAF returns that PTFF QPT is available.
Fixes: 2d7de7a3010d ("s390/time: Add PtP driver")
Signed-off-by: Sven Schnelle <svens@linux.ibm.com>
Cc: stable@kernel.org
---
drivers/ptp/ptp_s390.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/ptp/ptp_s390.c b/drivers/ptp/ptp_s390.c
index 29618eb9bf44..02d624d89a0a 100644
--- a/drivers/ptp/ptp_s390.c
+++ b/drivers/ptp/ptp_s390.c
@@ -107,6 +107,9 @@ static __init int ptp_s390_init(void)
if (IS_ERR(ptp_stcke_clock))
return PTR_ERR(ptp_stcke_clock);
+ if (!test_facility(28) || !ptff_query(PTFF_QPT))
+ return 0;
+
ptp_qpt_clock = ptp_clock_register(&ptp_s390_qpt_info, NULL);
if (IS_ERR(ptp_qpt_clock)) {
ptp_clock_unregister(ptp_stcke_clock);
@@ -117,7 +120,8 @@ static __init int ptp_s390_init(void)
static __exit void ptp_s390_exit(void)
{
- ptp_clock_unregister(ptp_qpt_clock);
+ if (ptp_qpt_clock)
+ ptp_clock_unregister(ptp_qpt_clock);
ptp_clock_unregister(ptp_stcke_clock);
}
--
2.55.0
^ permalink raw reply related
* Re: [PATCH net-next v8 1/2] dinghai: add ZTE network driver support
From: Jiri Pirko @ 2026-07-14 13:06 UTC (permalink / raw)
To: han.junyang
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, horms, linux-kernel,
netdev, ran.ming, han.chengfei, zhang.yanze
In-Reply-To: <20260713222420704lmwNL8xT8mgOhTKbqAfRX@zte.com.cn>
Mon, Jul 13, 2026 at 04:24:20PM +0200, han.junyang@zte.com.cn wrote:
>From: Junyang Han <han.junyang@zte.com.cn>
>
>Add basic framework for ZTE DingHai ethernet PF driver, including
>Kconfig/Makefile build support and PCIe device probe/remove skeleton.
>
>Signed-off-by: Junyang Han <han.junyang@zte.com.cn>
>---
> MAINTAINERS | 6 +
> drivers/net/ethernet/Kconfig | 1 +
> drivers/net/ethernet/Makefile | 1 +
> drivers/net/ethernet/zte/Kconfig | 20 +++
> drivers/net/ethernet/zte/Makefile | 6 +
> drivers/net/ethernet/zte/dinghai/Kconfig | 34 ++++
> drivers/net/ethernet/zte/dinghai/Makefile | 10 ++
> drivers/net/ethernet/zte/dinghai/en_pf.c | 195 ++++++++++++++++++++++
> drivers/net/ethernet/zte/dinghai/en_pf.h | 52 ++++++
> 9 files changed, 325 insertions(+)
> create mode 100644 drivers/net/ethernet/zte/Kconfig
> create mode 100644 drivers/net/ethernet/zte/Makefile
> create mode 100644 drivers/net/ethernet/zte/dinghai/Kconfig
> create mode 100644 drivers/net/ethernet/zte/dinghai/Makefile
> create mode 100644 drivers/net/ethernet/zte/dinghai/en_pf.c
> create mode 100644 drivers/net/ethernet/zte/dinghai/en_pf.h
>
>diff --git a/MAINTAINERS b/MAINTAINERS
>index 2fb1c75afd16..73692b09bf7b 100644
>--- a/MAINTAINERS
>+++ b/MAINTAINERS
>@@ -29440,6 +29440,12 @@ S: Maintained
> T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
> F: sound/hda/codecs/senarytech.c
>
>+ZTE DINGHAI ETHERNET DRIVER
>+M: Junyang Han <han.junyang@zte.com.cn>
>+L: netdev@vger.kernel.org
>+S: Maintained
>+F: drivers/net/ethernet/zte/
>+
> THE REST
> M: Linus Torvalds <torvalds@linux-foundation.org>
> L: linux-kernel@vger.kernel.org
>diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
>index b8f70e2a1763..c2b6996b0cfe 100644
>--- a/drivers/net/ethernet/Kconfig
>+++ b/drivers/net/ethernet/Kconfig
>@@ -188,5 +188,6 @@ source "drivers/net/ethernet/wangxun/Kconfig"
> source "drivers/net/ethernet/wiznet/Kconfig"
> source "drivers/net/ethernet/xilinx/Kconfig"
> source "drivers/net/ethernet/xircom/Kconfig"
>+source "drivers/net/ethernet/zte/Kconfig"
>
> endif # ETHERNET
>diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
>index 57344fec6ce0..a34bcbd4df4e 100644
>--- a/drivers/net/ethernet/Makefile
>+++ b/drivers/net/ethernet/Makefile
>@@ -104,3 +104,4 @@ obj-$(CONFIG_NET_VENDOR_XIRCOM) += xircom/
> obj-$(CONFIG_NET_VENDOR_SYNOPSYS) += synopsys/
> obj-$(CONFIG_NET_VENDOR_PENSANDO) += pensando/
> obj-$(CONFIG_OA_TC6) += oa_tc6.o
>+obj-$(CONFIG_NET_VENDOR_ZTE) += zte/
>diff --git a/drivers/net/ethernet/zte/Kconfig b/drivers/net/ethernet/zte/Kconfig
>new file mode 100644
>index 000000000000..b95c2fc7db77
>--- /dev/null
>+++ b/drivers/net/ethernet/zte/Kconfig
>@@ -0,0 +1,20 @@
>+# SPDX-License-Identifier: GPL-2.0-only
>+#
>+# ZTE driver configuration
>+#
>+
>+config NET_VENDOR_ZTE
>+ bool "ZTE devices"
>+ default y
>+ help
>+ If you have a network (Ethernet) card belonging to this class, say Y.
>+ Note that the answer to this question doesn't directly affect the
>+ kernel: saying N will just cause the configurator to skip all
>+ the questions about Zte cards. If you say Y, you will be asked
>+ for your specific card in the following questions.
>+
>+if NET_VENDOR_ZTE
>+
>+source "drivers/net/ethernet/zte/dinghai/Kconfig"
>+
>+endif # NET_VENDOR_ZTE
>diff --git a/drivers/net/ethernet/zte/Makefile b/drivers/net/ethernet/zte/Makefile
>new file mode 100644
>index 000000000000..cd9929b61559
>--- /dev/null
>+++ b/drivers/net/ethernet/zte/Makefile
>@@ -0,0 +1,6 @@
>+# SPDX-License-Identifier: GPL-2.0-only
>+#
>+# Makefile for the ZTE device drivers
>+#
>+
>+obj-$(CONFIG_DINGHAI) += dinghai/
>diff --git a/drivers/net/ethernet/zte/dinghai/Kconfig b/drivers/net/ethernet/zte/dinghai/Kconfig
>new file mode 100644
>index 000000000000..121be3bf7707
>--- /dev/null
>+++ b/drivers/net/ethernet/zte/dinghai/Kconfig
>@@ -0,0 +1,34 @@
>+# SPDX-License-Identifier: GPL-2.0-only
>+#
>+# ZTE DingHai Ethernet driver configuration
>+#
>+
>+config DINGHAI
>+ bool "ZTE DingHai Ethernet driver"
>+ depends on PCI
>+ select NET_DEVLINK
>+ help
>+ This driver supports ZTE DingHai Ethernet devices.
>+
>+ DingHai is a high-performance Ethernet controller that supports
>+ multiple features including hardware offloading, SR-IOV, and
>+ advanced virtualization capabilities.
>+
>+ If you say Y here, you can select specific driver variants below.
>+
>+ If unsure, say N.
>+
>+if DINGHAI
>+
>+config DINGHAI_PF
>+ tristate "ZTE DingHai PF (Physical Function) driver"
>+ help
>+ This driver supports ZTE DingHai PCI Express Ethernet
>+ adapters (PF).
>+
>+ To compile this driver as a module, choose M here. The module
>+ will be named dinghai10e.
>+
>+ If unsure, say N.
>+
>+endif # DINGHAI
>diff --git a/drivers/net/ethernet/zte/dinghai/Makefile b/drivers/net/ethernet/zte/dinghai/Makefile
>new file mode 100644
>index 000000000000..f55a8de518be
>--- /dev/null
>+++ b/drivers/net/ethernet/zte/dinghai/Makefile
>@@ -0,0 +1,10 @@
>+# SPDX-License-Identifier: GPL-2.0-only
>+#
>+# Makefile for ZTE DingHai Ethernet driver
>+#
>+
>+ccflags-y += -I$(src)
>+
>+obj-$(CONFIG_DINGHAI_PF) += dinghai10e.o
>+dinghai10e-y := en_pf.o
>+
>diff --git a/drivers/net/ethernet/zte/dinghai/en_pf.c b/drivers/net/ethernet/zte/dinghai/en_pf.c
>new file mode 100644
>index 000000000000..034b8519a71c
>--- /dev/null
>+++ b/drivers/net/ethernet/zte/dinghai/en_pf.c
>@@ -0,0 +1,195 @@
>+// SPDX-License-Identifier: GPL-2.0-only
>+/*
>+ * ZTE DingHai Ethernet driver
>+ * Copyright (c) 2022-2026, ZTE Corporation.
>+ */
>+
>+#include <linux/module.h>
>+#include <linux/pci.h>
>+#include <net/devlink.h>
>+#include <linux/dma-mapping.h>
>+#include "en_pf.h"
>+
>+MODULE_AUTHOR("Junyang Han <han.junyang@zte.com.cn>");
>+MODULE_DESCRIPTION("ZTE DingHai series Ethernet driver");
>+MODULE_LICENSE("GPL");
>+
>+static const struct devlink_ops dh_pf_devlink_ops = {};
>+
>+static const struct pci_device_id dh_pf_pci_table[] = {
>+ { PCI_DEVICE(ZXDH_PF_VENDOR_ID, ZXDH_PF_DEVICE_ID) },
>+ { PCI_DEVICE(ZXDH_PF_VENDOR_ID, ZXDH_VF_DEVICE_ID) },
>+ { }
>+};
>+
>+MODULE_DEVICE_TABLE(pci, dh_pf_pci_table);
>+
>+void *dh_core_alloc_priv(struct dh_core_dev *dh_dev, size_t size)
>+{
>+ void *priv = kzalloc(size, GFP_KERNEL);
>+
>+ if (priv)
>+ dh_dev->priv = priv;
>+ return priv;
>+}
>+
>+void dh_core_free_priv(struct dh_core_dev *dh_dev)
>+{
>+ kfree(dh_dev->priv);
>+}
>+
>+static int dh_pf_pci_init(struct dh_core_dev *dev)
>+{
>+ struct zxdh_pf_device *pf_dev = dev->priv;
>+ int ret;
>+
>+ pci_set_drvdata(dev->pdev, dev);
>+
>+ ret = pci_enable_device(dev->pdev);
>+ if (ret) {
>+ dev_err(dev->device, "pci_enable_device failed: %d\n", ret);
>+ return ret;
>+ }
>+
>+ ret = dma_set_mask_and_coherent(dev->device, DMA_BIT_MASK(64));
>+ if (ret) {
>+ ret = dma_set_mask_and_coherent(dev->device, DMA_BIT_MASK(32));
>+ if (ret) {
>+ dev_err(dev->device, "dma_set_mask_and_coherent failed: %d\n", ret);
>+ goto err_pci;
>+ }
>+ }
>+
>+ ret = pci_request_selected_regions(dev->pdev,
>+ pci_select_bars(dev->pdev, IORESOURCE_MEM),
>+ "dh-pf");
>+ if (ret) {
>+ dev_err(dev->device, "pci_request_selected_regions failed: %d\n", ret);
>+ goto err_pci;
>+ }
>+
>+ pci_set_master(dev->pdev);
>+ ret = pci_save_state(dev->pdev);
>+ if (ret) {
>+ dev_err(dev->device, "pci_save_state failed: %d\n", ret);
>+ goto err_pci_save_state;
>+ }
>+
>+ if (!(pci_resource_flags(dev->pdev, 0) & IORESOURCE_MEM)) {
>+ ret = -ENODEV;
>+ dev_err(dev->device, "BAR 0 is not an MMIO resource\n");
>+ goto err_pci_save_state;
>+ }
>+
>+ pf_dev->pci_ioremap_addr[0] =
>+ ioremap(pci_resource_start(dev->pdev, 0),
>+ pci_resource_len(dev->pdev, 0));
>+ if (!pf_dev->pci_ioremap_addr[0]) {
>+ ret = -ENOMEM;
>+ dev_err(dev->device, "dh pf pci ioremap failed\n");
>+ goto err_pci_save_state;
>+ }
>+
>+ return 0;
>+
>+err_pci_save_state:
>+ pci_release_selected_regions(dev->pdev,
>+ pci_select_bars(dev->pdev, IORESOURCE_MEM));
>+err_pci:
>+ pci_disable_device(dev->pdev);
>+ return ret;
>+}
>+
>+void dh_pf_pci_close(struct dh_core_dev *dev)
>+{
>+ struct zxdh_pf_device *pf_dev = dev->priv;
>+
>+ iounmap(pf_dev->pci_ioremap_addr[0]);
>+ pci_release_selected_regions(dev->pdev,
>+ pci_select_bars(dev->pdev, IORESOURCE_MEM));
>+ pci_disable_device(dev->pdev);
>+}
>+
>+static int dh_pf_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>+{
>+ struct zxdh_pf_device *pf_dev;
>+ struct dh_core_dev *dh_dev;
>+ struct devlink *devlink;
>+ int ret;
>+
>+ devlink = devlink_alloc(&dh_pf_devlink_ops, sizeof(struct dh_core_dev),
>+ &pdev->dev);
>+ if (!devlink) {
>+ dev_err(&pdev->dev, "dh_pf devlink alloc failed\n");
>+ return -ENOMEM;
>+ }
>+
>+ dh_dev = devlink_priv(devlink);
>+ dh_dev->device = &pdev->dev;
>+ dh_dev->pdev = pdev;
>+ dh_dev->devlink = devlink;
>+
>+ pf_dev = dh_core_alloc_priv(dh_dev, sizeof(*pf_dev));
Does not make any sense to me. Why you don't just merge struct
dh_core_dev and zxdh_pf_device. Why dh_core_dev contains *priv pointer?
What puzzles me is the naming of funct structs:
dh_core*
dh_pf*
zxdh_pf*
Could they all be dh_pf* perhaps?
Then inconsistencies like "_dev", "_device" etc. Please try to be
consitent with names. Makes the code easier to follow.
>+ if (!pf_dev) {
>+ dev_err(&pdev->dev, "dh_pf_dev alloc failed\n");
>+ ret = -ENOMEM;
>+ goto err_pf_dev;
>+ }
>+
>+ pf_dev->bar_chan_valid = false;
>+ pf_dev->vepa = false;
>+ mutex_init(&dh_dev->lock);
>+ mutex_init(&pf_dev->irq_lock);
>+
>+ dh_dev->coredev_type = GET_COREDEV_TYPE(pdev);
>+
>+ ret = dh_pf_pci_init(dh_dev);
>+ if (ret) {
>+ dev_err(&pdev->dev, "dh_pf_pci_init failed: %d\n", ret);
>+ goto err_cfg_init;
>+ }
>+
>+ devlink_register(devlink);
>+
>+ return 0;
>+
>+err_cfg_init:
>+ mutex_destroy(&pf_dev->irq_lock);
>+ mutex_destroy(&dh_dev->lock);
>+ dh_core_free_priv(dh_dev);
>+err_pf_dev:
>+ devlink_free(devlink);
>+ return ret;
>+}
>+
>+static void dh_pf_remove(struct pci_dev *pdev)
>+{
>+ struct dh_core_dev *dh_dev = pci_get_drvdata(pdev);
>+ struct devlink *devlink = priv_to_devlink(dh_dev);
>+ struct zxdh_pf_device *pf_dev = dh_dev->priv;
>+
>+ devlink_unregister(devlink);
>+ dh_pf_pci_close(dh_dev);
>+ mutex_destroy(&pf_dev->irq_lock);
>+ mutex_destroy(&dh_dev->lock);
>+ dh_core_free_priv(dh_dev);
>+ devlink_free(devlink);
>+ pci_set_drvdata(pdev, NULL);
>+}
>+
>+static void dh_pf_shutdown(struct pci_dev *pdev)
>+{
>+ if (system_state == SYSTEM_POWER_OFF)
>+ pci_set_power_state(pdev, PCI_D3hot);
>+ pci_disable_device(pdev);
>+}
>+
>+static struct pci_driver dh_pf_driver = {
>+ .name = "dinghai10e",
>+ .id_table = dh_pf_pci_table,
>+ .probe = dh_pf_probe,
>+ .remove = dh_pf_remove,
>+ .shutdown = dh_pf_shutdown,
>+};
>+
>+module_pci_driver(dh_pf_driver);
>diff --git a/drivers/net/ethernet/zte/dinghai/en_pf.h b/drivers/net/ethernet/zte/dinghai/en_pf.h
>new file mode 100644
>index 000000000000..87fa29b2d13d
>--- /dev/null
>+++ b/drivers/net/ethernet/zte/dinghai/en_pf.h
>@@ -0,0 +1,52 @@
>+/* SPDX-License-Identifier: GPL-2.0-only */
>+/*
>+ * ZTE DingHai Ethernet driver - PF header
>+ * Copyright (c) 2022-2026, ZTE Corporation.
>+ */
>+
>+#ifndef __ZXDH_EN_PF_H__
>+#define __ZXDH_EN_PF_H__
>+
>+#include <linux/types.h>
>+#include <linux/pci.h>
>+#include <linux/mutex.h>
>+#include <linux/device.h>
>+#include <linux/slab.h>
>+
>+#define ZXDH_PF_VENDOR_ID 0x1cf2
>+#define ZXDH_PF_DEVICE_ID 0x8040
>+#define ZXDH_VF_DEVICE_ID 0x8041
>+
>+enum dh_coredev_type {
>+ DH_COREDEV_PF,
>+ DH_COREDEV_VF,
>+ DH_COREDEV_SF,
>+ DH_COREDEV_MPF
>+};
>+
>+struct devlink;
>+
>+struct dh_core_dev {
>+ struct device *device;
>+ enum dh_coredev_type coredev_type;
>+ struct pci_dev *pdev;
>+ struct devlink *devlink;
>+ struct mutex lock; /* Protects device configuration */
>+ void *priv;
>+};
>+
>+struct zxdh_pf_device {
>+ void __iomem *pci_ioremap_addr[6];
>+ bool bar_chan_valid;
>+ bool vepa;
>+ struct mutex irq_lock; /* Protects IRQ operations */
>+};
>+
>+#define GET_COREDEV_TYPE(pdev) \
>+ ((pdev)->device == ZXDH_VF_DEVICE_ID ? DH_COREDEV_VF : DH_COREDEV_PF)
>+
>+void *dh_core_alloc_priv(struct dh_core_dev *dh_dev, size_t size);
>+void dh_core_free_priv(struct dh_core_dev *dh_dev);
>+void dh_pf_pci_close(struct dh_core_dev *dev);
>+
>+#endif /* __ZXDH_EN_PF_H__ */
>--
>2.27.0
>
^ permalink raw reply
* Re: [PATCH net-next v3 13/15] ibmveth: Implement incremental MQ RX queue resize
From: Simon Horman @ 2026-07-14 13:03 UTC (permalink / raw)
To: mmc
Cc: Simon Horman, netdev, bjking1, haren, ricklind, kuba, edumazet,
pabeni, linuxppc-dev, maddy, mpe, davemarq
In-Reply-To: <20260706193603.8039-14-mmc@linux.ibm.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.
Full review at: https://sashiko.dev
---
Date: Mon Jul 6 19:36:01 2026 +0000
ibmveth: Implement incremental MQ RX queue resize
Add helpers to register/deregister individual RX queues and scale
queue count up or down while the netdev stays up. Surviving queues
keep PHYP handles, buffer pools, and IRQ state across resize.
> diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
> index ecc472ee8f71f..ac4d89a66a8db 100644
> --- a/drivers/net/ethernet/ibm/ibmveth.c
> +++ b/drivers/net/ethernet/ibm/ibmveth.c
[ ... ]
> @@ -1080,6 +1132,138 @@ static void ibmveth_free_buffer_pools(struct ibmveth_adapter *adapter)
> +static int
> +ibmveth_alloc_single_rx_queue(struct ibmveth_adapter *adapter, int queue_idx,
> + int rxq_entries)
> +{
[ ... ]
> + for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++) {
> + adapter->rx_buff_pool[queue_idx][i].size =
> + adapter->rx_buff_pool[0][i].size;
> + adapter->rx_buff_pool[queue_idx][i].buff_size =
> + adapter->rx_buff_pool[0][i].buff_size;
> + adapter->rx_buff_pool[queue_idx][i].threshold =
> + adapter->rx_buff_pool[0][i].threshold;
> + adapter->rx_buff_pool[queue_idx][i].active =
> + adapter->rx_buff_pool[0][i].active;
> + }
[Severity: Critical]
Does this loop miss initializing pool->index?
If pool->index remains 0 for all buffer pools on the new queue, when
replenishing, 0 is shifted into the correlator.
Then when receiving, ibmveth_remove_buffer_from_pool() will extract pool=0,
which fetches from the wrong pool and could corrupt the free map of pool 0,
leading to a use-after-free or leak.
[ ... ]
> @@ -1192,6 +1376,49 @@ static int ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter,
> +static int
> +ibmveth_drain_rx_queue(struct ibmveth_adapter *adapter, int queue_index)
> +{
[ ... ]
> + while (drained < limit &&
> + ibmveth_rxq_pending_buffer(adapter, queue_index)) {
> + rc = ibmveth_rxq_harvest_buffer(adapter, queue_index, true);
Simon says: I think we can consider the issue below in the context of
possible follow-up, because I believe the use of this pattern
predates this patch.
[Severity: High]
Is a read memory barrier needed between reading the toggle bit in
ibmveth_rxq_pending_buffer() and reading the correlator in
ibmveth_rxq_harvest_buffer()?
Since the hypervisor could be actively writing new descriptors while this is
called, could a weakly-ordered CPU reorder the correlator load before the
toggle bit load, causing us to recycle the wrong buffer?
[ ... ]
> @@ -1491,6 +1718,175 @@ ibmveth_deregister_single_rx_queue(struct ibmveth_adapter *adapter,
> +static int
> +ibmveth_resize_rx_queues_incremental(struct ibmveth_adapter *adapter,
> + int new_count, int rxq_entries)
> +{
[ ... ]
> + rc = ibmveth_enable_irq(adapter, i);
> + if (rc) {
> + netdev_err(netdev,
> + "Failed to enable IRQ for queue %d: %d\n",
> + i, rc);
> + ibmveth_cleanup_single_rx_interrupt(adapter, i);
> + ibmveth_deregister_single_rx_queue(adapter, i);
> + ibmveth_free_single_rx_queue(adapter, i);
> + goto cleanup_new_queues;
> + }
> +
> + napi_enable(&adapter->napi[i]);
> + }
> +
> + adapter->num_rx_queues = new_count;
[Severity: High]
What happens if an interrupt fires on the newly added queue before
adapter->num_rx_queues is updated?
If an interrupt fires between ibmveth_enable_irq() and the update of
adapter->num_rx_queues, ibmveth_interrupt() might evaluate:
WARN_ON(qindex < 0 || qindex >= adapter->num_rx_queues)
to true and return IRQ_NONE without masking the interrupt.
Could this lead to an unhandled interrupt storm?
[ ... ]
> + } else {
> + netdev_dbg(netdev, "Scale-down: removing queues %d-%d\n",
> + new_count, old_count - 1);
> +
> + for (i = new_count; i < old_count; i++)
> + napi_disable(&adapter->napi[i]);
> +
> + for (i = new_count; i < old_count; i++)
> + ibmveth_drain_rx_queue(adapter, i);
> +
> + synchronize_net();
[Severity: High]
Can disabling NAPI before disabling the hardware interrupts create an
interrupt storm window?
If the hypervisor asserts an interrupt during synchronize_net(), the hardware
IRQ handler runs. Since NAPI is disabled, it returns IRQ_HANDLED without
masking the interrupt source at the hypervisor, leading immediately to an
IRQ storm and CPU lockup.
> +
> + rc = netif_set_real_num_rx_queues(netdev, new_count);
> + if (rc) {
> + netdev_err(netdev, "Failed to set real RX queues to %d: %d\n",
> + new_count, rc);
> + for (i = new_count; i < old_count; i++)
> + napi_enable(&adapter->napi[i]);
> + return rc;
> + }
[Severity: High]
Is it possible for this error path to permanently stall the RX queues?
It calls napi_enable() but doesn't call ibmveth_replenish_task(). Since the
queues were completely drained of their buffers by ibmveth_drain_rx_queue(),
will the hypervisor have any buffers to place incoming packets?
Without buffers, no interrupts will fire for these queues, and NAPI will
never run again.
^ permalink raw reply
* Re: [PATCH net-next v3 14/15] ibmveth: Wire ethtool set_channels to MQ RX queue resize
From: Simon Horman @ 2026-07-14 13:07 UTC (permalink / raw)
To: mmc
Cc: Simon Horman, netdev, bjking1, haren, ricklind, kuba, edumazet,
pabeni, linuxppc-dev, maddy, mpe, davemarq
In-Reply-To: <20260706193603.8039-15-mmc@linux.ibm.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.
Full review at: https://sashiko.dev
---
ibmveth: Wire ethtool set_channels to MQ RX queue resize
Expose incremental RX resize through ethtool channel control.
get_channels() reports rx_count from adapter->num_rx_queues and max_rx
as IBMVETH_MAX_RX_QUEUES when MQ firmware is enabled, else 1.
set_channels() validates rx_count is within 1..IBMVETH_MAX_RX_QUEUES.
> diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
> index ac4d89a66a8db..50a332ab83fd3 100644
> --- a/drivers/net/ethernet/ibm/ibmveth.c
> +++ b/drivers/net/ethernet/ibm/ibmveth.c
> @@ -2534,19 +2534,69 @@ static int ibmveth_set_channels(struct net_device *netdev,
> struct ethtool_channels *channels)
> {
> struct ibmveth_adapter *adapter = netdev_priv(netdev);
[ ... ]
> - if (!(netdev->flags & IFF_UP))
> + if (!(netdev->flags & IFF_UP)) {
> + if (goal_rx > 1 && !adapter->multi_queue) {
[ ... ]
> + /* Stash desired RX count; open() publishes it via
> + * netif_set_real_num_rx_queues() after queue registration.
> + */
> + if (goal_rx != adapter->num_rx_queues)
> + adapter->num_rx_queues = goal_rx;
> +
> return netif_set_real_num_tx_queues(netdev, goal);
[Severity: Medium]
If netif_set_real_num_tx_queues() fails and returns an error, the TX
configuration command will fail, but the RX queue count has already been
permanently modified in adapter->num_rx_queues. Will this leave the device in
an inconsistent state where the new RX count is applied on the next open
despite the command failing?
> + }
[ ... ]
> + if (goal_rx != old_rx) {
> + rc = ibmveth_resize_rx_queues_incremental(adapter, goal_rx,
> + rxq_entries);
[Severity: High]
Does this new ethtool scale-down path expose an interrupt storm?
Looking at ibmveth_resize_rx_queues_incremental(), when scaling down queues it
disables NAPI before disabling the hardware interrupt:
drivers/net/ethernet/ibm/ibmveth.c:ibmveth_resize_rx_queues_incremental() {
...
for (i = new_count; i < old_count; i++)
napi_disable(&adapter->napi[i]);
for (i = new_count; i < old_count; i++)
ibmveth_drain_rx_queue(adapter, i);
synchronize_net();
...
/* Disable hypervisor interrupts and wait for handlers to complete
* before updating num_rx_queues.
*/
for (i = new_count; i < old_count; i++) {
ibmveth_disable_irq(adapter, i);
...
}
If the hypervisor delivers a packet during this window, ibmveth_interrupt()
will execute. Since NAPI is disabled, napi_schedule_prep() fails, causing the
handler to return IRQ_HANDLED without disabling the hardware interrupt. Will
this leave the virtual interrupt asserted and force an infinite interrupt loop?
> + if (rc) {
> + netdev_err(netdev, "Failed to resize RX queues: %d\n", rc);
> + return rc;
> + }
> + }
>
> /* We have IBMVETH_MAX_QUEUES netdev_queue's allocated
> * but we may need to alloc/free the ltb's.
> */
[Severity: High]
This is a pre-existing issue, but does this function use an uninitialized
variable in the error path?
When decreasing the number of TX queues (goal < old), the allocation loop for
(i = old; i < goal; i++) is skipped, leaving 'i' uninitialized. If
netif_set_real_num_tx_queues() then fails, the error path assigns old = i;.
The cleanup loop then uses this garbage value:
for (i = old; i > goal; i--) {
if (adapter->tx_ltb_ptr[i - 1])
Will this access adapter->tx_ltb_ptr out of bounds and pass arbitrary pointers
to ibmveth_free_tx_ltb()?
> @@ -2580,7 +2630,7 @@ static int ibmveth_set_channels(struct net_device *netdev,
>
> netif_tx_wake_all_queues(netdev);
>
> - return rc;
> + return 0;
[Severity: High]
Does this unconditional return of 0 mask TX queue configuration failures?
If applying the new TX queue count via netif_set_real_num_tx_queues() fails,
the code handles it locally, but the function exits with 0 regardless. Will
this mislead userspace into believing the TX channel count was successfully
modified?
> }
^ permalink raw reply
* Re: [PATCH net] i40e: xsk: fix multi-buffer XDP_PASS skb construction
From: Maciej Fijalkowski @ 2026-07-14 13:07 UTC (permalink / raw)
To: Chenguang Zhao
Cc: anthony.l.nguyen, przemyslaw.kitszel, andrew+netdev, davem,
edumazet, kuba, pabeni, intel-wired-lan, netdev, Chenguang Zhao
In-Reply-To: <20260714025112.284724-1-chenguang.zhao@linux.dev>
On Tue, Jul 14, 2026 at 10:51:12AM +0800, Chenguang Zhao wrote:
> From: Chenguang Zhao <zhaochenguang@kylinos.cn>
>
> When AF_XDP ZC receives a multi-buffer frame and the XDP program
> returns XDP_PASS, i40e_construct_skb_zc() copies frags into a new
> skb. The copy used skb_frag_page() as the memcpy source (page
> metadata instead of packet data) and passed a virtual address to
> __skb_fill_page_desc_noacc(), which expects a struct page *.
>
> Use skb_frag_address() for the copy, attach frags with
> skb_add_rx_frag() so len/data_len/truesize are updated, and on
> dev_alloc_page() failure free the skb via the shared out path so
> xsk_buff_free() still runs and previously attached pages are
> released by kfree_skb.
>
> Fixes: 1c9ba9c14658 ("i40e: xsk: add RX multi-buffer support")
> Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
> ---
> - Fix memcpy source: use skb_frag_address() instead of skb_frag_page(),
> which was copying page metadata rather than packet data.
>
> - Fix frag attachment: pass the allocated struct page * to the skb frag
> helper instead of the page virtual address.
>
> - Use skb_add_rx_frag() so skb->len, data_len and truesize are updated
> when attaching copied frags.
>
> - On mid-loop dev_alloc_page() failure, go through the shared out path
> so previously attached pages are released via kfree_skb and
> xsk_buff_free() is still called.
Hi!
I assume this is a fix, so you should include your target tree in patch
subject (net) plus Fixes: tag.
However, could you take a look if we could use xdp_build_skb_from_zc() and
wipe out i40e_construct_skb_zc() altogether?
>
> drivers/net/ethernet/intel/i40e/i40e_xsk.c | 13 +++++--------
> 1 file changed, 5 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_xsk.c b/drivers/net/ethernet/intel/i40e/i40e_xsk.c
> index 9f47388eaba5..a4247710c85b 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_xsk.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_xsk.c
> @@ -318,22 +318,19 @@ static struct sk_buff *i40e_construct_skb_zc(struct i40e_ring *rx_ring,
> goto out;
>
> for (int i = 0; i < nr_frags; i++) {
> - struct skb_shared_info *skinfo = skb_shinfo(skb);
> skb_frag_t *frag = &sinfo->frags[i];
> + unsigned int frag_size = skb_frag_size(frag);
> struct page *page;
> - void *addr;
>
> page = dev_alloc_page();
> if (!page) {
> dev_kfree_skb(skb);
> - return NULL;
> + skb = NULL;
> + goto out;
> }
> - addr = page_to_virt(page);
>
> - memcpy(addr, skb_frag_page(frag), skb_frag_size(frag));
> -
> - __skb_fill_page_desc_noacc(skinfo, skinfo->nr_frags++,
> - addr, 0, skb_frag_size(frag));
> + memcpy(page_to_virt(page), skb_frag_address(frag), frag_size);
> + skb_add_rx_frag(skb, i, page, 0, frag_size, PAGE_SIZE);
> }
>
> out:
> --
> 2.25.1
>
>
^ permalink raw reply
* Re: [PATCH net-next v8 2/2] dinghai: add hardware register access and PCI? capability scanning
From: Jiri Pirko @ 2026-07-14 13:10 UTC (permalink / raw)
To: han.junyang
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, horms, linux-kernel,
netdev, ran.ming, han.chengfei, zhang.yanze
In-Reply-To: <20260713222804514zLeRvL3Su1xlklpOdahhF@zte.com.cn>
Mon, Jul 13, 2026 at 04:28:04PM +0200, han.junyang@zte.com.cn wrote:
[..]
>@@ -48,5 +85,34 @@ struct zxdh_pf_device {
> void *dh_core_alloc_priv(struct dh_core_dev *dh_dev, size_t size);
> void dh_core_free_priv(struct dh_core_dev *dh_dev);
> void dh_pf_pci_close(struct dh_core_dev *dev);
>+int zxdh_pf_pci_find_capability(struct pci_dev *pdev, u8 cfg_type,
>+ u32 ioresource_types, int *bars);
>+void __iomem *zxdh_pf_map_capability(struct dh_core_dev *dh_dev, int off,
>+ size_t minlen, u32 align,
>+ u32 start, u32 size,
>+ size_t *len, resource_size_t *pa,
>+ u32 *bar_off);
>+int zxdh_pf_common_cfg_init(struct dh_core_dev *dh_dev);
>+int zxdh_pf_notify_cfg_init(struct dh_core_dev *dh_dev);
>+int zxdh_pf_device_cfg_init(struct dh_core_dev *dh_dev);
>+void zxdh_pf_modern_cfg_uninit(struct dh_core_dev *dh_dev);
>+int zxdh_pf_modern_cfg_init(struct dh_core_dev *dh_dev);
>+u16 zxdh_pf_get_queue_notify_off(struct dh_core_dev *dh_dev,
>+ u16 phy_index, u16 index);
>+void __iomem *zxdh_pf_map_vq_notify(struct dh_core_dev *dh_dev,
>+ u16 phy_index, u16 index,
>+ resource_size_t *pa);
>+void zxdh_pf_unmap_vq_notify(struct dh_core_dev *dh_dev, void __iomem *priv);
>+void zxdh_pf_set_status(struct dh_core_dev *dh_dev, u8 status);
>+u8 zxdh_pf_get_status(struct dh_core_dev *dh_dev);
>+u8 zxdh_pf_get_cfg_gen(struct dh_core_dev *dh_dev);
>+void zxdh_pf_get_vf_mac(struct dh_core_dev *dh_dev, u8 *mac, int vf_id);
>+void zxdh_pf_set_vf_mac_reg(struct zxdh_pf_device *pf_dev,
>+ u8 *mac, int vf_id);
>+void zxdh_pf_set_vf_mac(struct dh_core_dev *dh_dev, u8 *mac, int vf_id);
I don't follow. You introduce functions yet you never use them? When you
introduce some function, you need to use it in the same patchset.
Honestly, I would expect quite different patchset quality in V8 :/
>+void zxdh_set_mac(struct dh_core_dev *dh_dev, u8 *mac);
>+void zxdh_get_mac(struct dh_core_dev *dh_dev, u8 *mac);
>+u64 zxdh_pf_get_features(struct dh_core_dev *dh_dev);
>+void zxdh_pf_set_features(struct dh_core_dev *dh_dev, u64 features);
>
> #endif /* __ZXDH_EN_PF_H__ */
>--
>2.27.0
>
^ permalink raw reply
* [PATCH net-next v3] net: phy: motorcomm: Enable optional clock for YT8531
From: Yanan He @ 2026-07-14 13:11 UTC (permalink / raw)
To: Frank, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni
Cc: netdev, linux-kernel, Yanan He
Some boards feed the YT8531 PHY from an SoC-provided external
reference clock described by the common ethernet-phy "clocks" property.
Enable the optional PHY clock during probe so boards can model this
clock as a PHY input instead of keeping the clock alive from the MAC
driver.
This is needed on the Alientek DLRV1126, where the PHY reference clock
is provided by CLK_GMAC_ETHERNET_OUT.
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Yanan He <grumpycat921013@gmail.com>
---
v3:
- Split the Motorcomm PHY change out of the Alientek DLRV1126 board
support series as requested by Andrew.
- Keep the clock modeled as an optional PHY input clock.
v2:
- Model CLK_GMAC_ETHERNET_OUT as a PHY clock instead of keeping it alive
from dwmac-rk.
---
drivers/net/phy/motorcomm.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c
index 5071605a1a11..3396a38cfc0f 100644
--- a/drivers/net/phy/motorcomm.c
+++ b/drivers/net/phy/motorcomm.c
@@ -6,6 +6,7 @@
* Author: Frank <Frank.Sae@motor-comm.com>
*/
+#include <linux/clk.h>
#include <linux/etherdevice.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -1180,9 +1181,15 @@ static int yt8521_probe(struct phy_device *phydev)
static int yt8531_probe(struct phy_device *phydev)
{
struct device *dev = &phydev->mdio.dev;
+ struct clk *clk;
u16 mask, val;
u32 freq;
+ clk = devm_clk_get_optional_enabled(dev, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk),
+ "failed to get and enable PHY clock\n");
+
if (device_property_read_u32(dev, "motorcomm,clk-out-frequency-hz", &freq))
freq = YTPHY_DTS_OUTPUT_CLK_DIS;
---
base-commit: f6f3b36c15ed44de1fbb44e645e4fae8c4a4453e
change-id: 20260714-motorcomm-yt8531-clk-55f95c34fb49
Best regards,
--
Yanan He <grumpycat921013@gmail.com>
^ permalink raw reply related
* Re: [PATCH net-next v3 15/15] ibmveth: Fix MQ RX poll and shutdown hangs after queue resize
From: Simon Horman @ 2026-07-14 13:10 UTC (permalink / raw)
To: mmc
Cc: Simon Horman, netdev, bjking1, haren, ricklind, kuba, edumazet,
pabeni, linuxppc-dev, maddy, mpe, davemarq
In-Reply-To: <20260706193603.8039-16-mmc@linux.ibm.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.
Full review at: https://sashiko.dev
---
ibmveth: Fix MQ RX poll and shutdown hangs after queue resize
This commit fixes polling hangs in the ibmveth driver after queue resize
by skipping over bad correlators instead of retrying endlessly. It also
ensures NAPI completes when the interface is down or being disabled.
> diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
> index 50a332ab83fd3..eb84b4e5f69fd 100644
> --- a/drivers/net/ethernet/ibm/ibmveth.c
> +++ b/drivers/net/ethernet/ibm/ibmveth.c
[ ... ]
> @@ -2959,8 +2978,23 @@ static int ibmveth_poll(struct napi_struct *napi, int budget)
> __sum16 iph_check = 0;
>
> skb = ibmveth_rxq_get_buffer(adapter, queue_index);
> - if (unlikely(!skb))
> - break;
> + if (unlikely(!skb)) {
> + if (net_ratelimit())
> + netdev_err(netdev,
[Severity: High]
This isn't a bug introduced by this patch, but can the unvalidated packet
lengths and offsets from hardware descriptors trigger an skb_over_panic()
here?
Further down in ibmveth_poll(), the driver reads length and offset directly
from the hardware receive descriptor and passes them to skb_reserve() and
skb_put() on the reused SKB:
int length = ibmveth_rxq_frame_length(adapter, queue_index);
int offset = ibmveth_rxq_frame_offset(adapter, queue_index);
...
} else {
rc = ibmveth_rxq_harvest_buffer(adapter, queue_index, false);
if (unlikely(rc))
break;
skb_reserve(skb, offset);
}
skb_put(skb, length);
If a faulty hypervisor provides an abnormally large length or offset,
skb_put() will extend the tail past the end of the buffer, which triggers an
skb_over_panic() and causes a crash.
Should the driver validate that offset + length is within the known buffer
size before modifying the boundaries?
[ ... ]
> @@ -3034,8 +3068,11 @@ static int ibmveth_poll(struct napi_struct *napi, int budget)
>
> ibmveth_replenish_task(adapter, queue_index);
>
> - if (frames_processed == budget)
> + if (frames_processed == budget) {
> + if (!netif_running(netdev) || napi_disable_pending(napi))
> + napi_complete_done(napi, frames_processed);
> goto out;
> + }
[Severity: High]
Does calling napi_complete_done() here conflict with the NAPI core contract?
If the polling loop exhausts its budget (frames_processed == budget), returning
the full budget after calling napi_complete_done() might cause issues with the
NAPI state machine. The NAPI core (__napi_poll) will operate on the instance
assuming it still holds the NAPIF_STATE_SCHED bit.
If napi_disable_pending() is true, the core calls napi_complete(), which will
hit a WARN_ON_ONCE.
If napi_disable_pending() is false but !netif_running() is true, the core will
add the NAPI instance back to the poll_list. Since the driver cleared
NAPIF_STATE_SCHED, a subsequent interrupt can trigger another napi_schedule(),
causing a double list_add_tail() and potentially leading to list corruption.
^ permalink raw reply
* Re: [PATCH net] amt: refresh cached IP header pointers after MLD/IGMP checks
From: Taehee Yoo @ 2026-07-14 13:16 UTC (permalink / raw)
To: Xiang Mei (Microsoft)
Cc: Jakub Kicinski, Andrew Lunn, David S . Miller, Eric Dumazet,
Paolo Abeni, netdev, linux-kernel, AutonomousCodeSecurity,
tgopinath, kys
In-Reply-To: <20260710014109.536177-1-xmei5@asu.edu>
On Fri, Jul 10, 2026 at 10:41 AM Xiang Mei (Microsoft) <xmei5@asu.edu> wrote:
>
Hi Xiang Mei,
Sorry for the late reply.
> amt_dev_xmit() and amt_update_handler() cache iph/ip6h before calling
> ip_mc_check_igmp() / ipv6_mc_check_mld(), which may pskb_may_pull() a
> non-linear skb and reallocate skb->head. The stale pointers are then
> read (amt_dev_xmit: group.ip{4,6} = iph/ip6h->daddr; amt_update_handler:
> ip{,v6}_eth_mc_map()), a slab-use-after-free. tx is reachable by an
> unprivileged user whose amt device has SG enabled; rx by a tunnel peer
> sending a membership update fragmented across several IP fragments.
>
> Reload iph/ip6h after the check returns, like commit f0e42f0c4337
> ("ipv6: sit: reload inner IPv6 header after GSO offloads").
>
> tx path:
> BUG: KASAN: slab-use-after-free in amt_dev_xmit (drivers/net/amt.c:1238)
> Read of size 16 by task exploit
> amt_dev_xmit (drivers/net/amt.c:1238)
> dev_hard_start_xmit (net/core/dev.c:3905)
> __dev_queue_xmit (net/core/dev.c:4872)
> packet_sendmsg (net/packet/af_packet.c:3114)
>
> rx path:
> BUG: KASAN: slab-use-after-free in amt_rcv (drivers/net/amt.c:2530)
> Read of size 4 by task exploit
> amt_rcv (drivers/net/amt.c:2530)
> udp_queue_rcv_one_skb (net/ipv4/udp.c:2388)
> ip_local_deliver (net/ipv4/ip_input.c:262)
> ip_rcv (net/ipv4/ip_input.c:612)
>
> Fixes: cbc21dc1cfe9 ("amt: add data plane of amt interface")
> Fixes: bc54e49c140b ("amt: add multicast(IGMP) report message handler")
> Fixes: b75f7095d4d4 ("amt: add mld report message handler")
> Reported-by: AutonomousCodeSecurity@microsoft.com
> Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
Thanks a lot for this work.
However Michael Bommarito is already working on this.
https://lore.kernel.org/netdev/20260711151934.2955226-2-michael.bommarito@gmail.com/
Thanks,
Taehee Yoo
> ---
> drivers/net/amt.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/drivers/net/amt.c b/drivers/net/amt.c
> index 951dd10e192b..8bc61c539b0a 100644
> --- a/drivers/net/amt.c
> +++ b/drivers/net/amt.c
> @@ -1210,6 +1210,7 @@ static netdev_tx_t amt_dev_xmit(struct sk_buff *skb, struct net_device *dev)
> } else {
> data = true;
> }
> + iph = ip_hdr(skb);
> v6 = false;
> group.ip4 = iph->daddr;
> #if IS_ENABLED(CONFIG_IPV6)
> @@ -1234,6 +1235,7 @@ static netdev_tx_t amt_dev_xmit(struct sk_buff *skb, struct net_device *dev)
> } else {
> data = true;
> }
> + ip6h = ipv6_hdr(skb);
> v6 = true;
> group.ip6 = ip6h->daddr;
> #endif
> @@ -2498,6 +2500,7 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb)
> netdev_dbg(amt->dev, "Invalid IGMP\n");
> return true;
> }
> + iph = ip_hdr(skb);
>
> spin_lock_bh(&tunnel->lock);
> amt_igmp_report_handler(amt, skb, tunnel);
> @@ -2517,6 +2520,7 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb)
> netdev_dbg(amt->dev, "Invalid MLD\n");
> return true;
> }
> + ip6h = ipv6_hdr(skb);
>
> spin_lock_bh(&tunnel->lock);
> amt_mld_report_handler(amt, skb, tunnel);
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH] virtio_net: fix infinite loop in virtnet_poll_cleantx when device is broken
From: Michael S. Tsirkin @ 2026-07-14 13:17 UTC (permalink / raw)
To: Jinqian Yang
Cc: jasowang, xuanzhuo, eperezma, andrew+netdev, davem, edumazet,
kuba, pabeni, netdev, virtualization, linux-kernel, liuyonglong,
wangzhou1, linuxarm
In-Reply-To: <20260713132025.703147-1-yangjinqian1@huawei.com>
On Mon, Jul 13, 2026 at 09:20:25PM +0800, Jinqian Yang wrote:
> virtnet_poll_cleantx() contains a do-while loop that cleans up
> transmitted TX buffers and calls virtqueue_enable_cb_delayed() to check
> whether more buffers need processing. When the virtio backend stops
> responding during guest reboot, used->idx is never updated, so
> virtqueue_enable_cb_delayed() always returns false and the loop never
> terminates. Then it will block reboot process, and the guest will hang.
>
> The problem occurs during guest reboot under network traffic:
>
> 1. kernel_restart() -> device_shutdown() traverses the device list
> 2. virtio_dev_shutdown() calls virtio_break_device() which sets
> vq->broken = true
> 3. virtio_dev_shutdown() then calls virtio_synchronize_cbs() to wait
> for in-flight callbacks to complete
> 4. A virtio interrupt fires, softirq is deferred to ksoftirqd which
> calls net_rx_action() -> virtnet_poll() -> virtnet_poll_cleantx()
> 5. virtnet_poll_cleantx() enters the do-while loop and never exits
> because the QEMU backend has stopped updating used->idx, despite
> vq->broken having been set to true in step 2.
>
> Since the loop runs inside ksoftirqd (a SCHED_OTHER kthread), it is
> visible to the scheduler and does not trigger a hard lockup. However,
> the kthread never leaves the loop, so RCU detects it as a CPU stall
> and reports it periodically. Meanwhile, the reboot process remains
> blocked in device_shutdown() because virtio_dev_shutdown() cannot
> complete its synchronization step, and the guest hangs permanently.
>
> This can be reproduced on a guest with a virtio-net device: run iperf3
> traffic in the guest, then trigger reboot. The reboot occasionally hangs
> permanently with RCU stall on ksoftirqd.
>
> Observed on ARM64 KVM guest:
>
> CPU#1 RCU stall (ksoftirqd/1), repeated periodically:
> virtqueue_enable_cb_delayed_split <- virtnet_poll <- __napi_poll <-
> net_rx_action <- handle_softirqs <- run_ksoftirqd <-
> smpboot_thread_fn <- kthread
>
> Fix by adding a virtqueue_is_broken() check to the loop condition, so
> that the loop exits immediately when the device is broken, allowing
> the device shutdown to proceed.
>
> Signed-off-by: Jinqian Yang <yangjinqian1@huawei.com>
I'd expect lots of drivers have this issue? Wouldn't it make more sense
to check virtqueue_is_broken in
virtqueue_enable_cb_delayed/virtqueue_enable_cb? This way it works for
all drivers.
> ---
> drivers/net/virtio_net.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> index 7d2eeb9b1226..c8d2d420c31d 100644
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c
> @@ -2970,7 +2970,8 @@ static void virtnet_poll_cleantx(struct receive_queue *rq, int budget)
> do {
> virtqueue_disable_cb(sq->vq);
> free_old_xmit(sq, txq, !!budget);
> - } while (unlikely(!virtqueue_enable_cb_delayed(sq->vq)));
> + } while (!virtqueue_is_broken(sq->vq) &&
> + unlikely(!virtqueue_enable_cb_delayed(sq->vq)));
>
> if (sq->vq->num_free >= MAX_SKB_FRAGS + 2)
> virtnet_tx_wake_queue(vi, sq);
> --
> 2.33.0
^ permalink raw reply
* [PATCH net v3] tipc: clear sock->sk on the failed-insert path in tipc_sk_create()
From: Daehyeon Ko @ 2026-07-14 13:19 UTC (permalink / raw)
To: netdev
Cc: Jon Maloy, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Tung Quang Nguyen, Breno Leitao,
tipc-discussion, linux-kernel, Daehyeon Ko, stable
When tipc_sk_create() fails to insert the new socket (tipc_sk_insert()
returns non-zero), its error path frees the sk with sk_free() but leaves
sock->sk pointing at the freed object:
if (tipc_sk_insert(tsk)) {
sk_free(sk);
pr_warn("Socket create failed; port number exhausted\n");
return -EINVAL;
}
This is harmless for plain socket(): the syscall layer clears sock->ops
before releasing, so tipc_release() is never called. It is not harmless
on the accept() path. tipc_accept() creates the pre-allocated child
socket with tipc_sk_create(net, new_sock, 0, kern); on failure it leaves
new_sock->sk dangling and new_sock->ops non-NULL, and do_accept() then
fput()s the new file, so __sock_release() -> tipc_release() runs
lock_sock(new_sock->sk) on the freed sk -- a use-after-free write of the
sk_lock spinlock.
tipc_release() already guards this exact "failed accept() releases a
pre-allocated child" case with "if (sk == NULL) return 0;", but the
guard is bypassed because tipc_sk_create() left sock->sk non-NULL
(dangling) rather than NULL.
Clear sock->sk on the failed-insert path so the existing tipc_release()
NULL check fires and the use-after-free is avoided.
The tipc_sk_insert() failure is reached when the per-netns socket
rhashtable hits its max_size (tsk_rht_params.max_size = 1048576, ~2M
elements) -- i.e. once a netns holds ~2M TIPC sockets every insert
returns -E2BIG.
BUG: KASAN: slab-use-after-free in lock_sock_nested (net/core/sock.c:3839)
Write of size 8 at addr ffff8880047cdc38 by task init/1
lock_sock_nested (net/core/sock.c:3839)
tipc_release (net/tipc/socket.c:638)
__sock_release (net/socket.c:710)
sock_close (net/socket.c:1501)
__fput (fs/file_table.c:512)
Allocated by task 1:
sk_alloc (net/core/sock.c:2308)
tipc_sk_create (net/tipc/socket.c:487)
tipc_accept (net/tipc/socket.c:2744)
do_accept (net/socket.c:2034)
Freed by task 1:
__sk_destruct (net/core/sock.c:2391)
tipc_sk_create (net/tipc/socket.c:504)
tipc_accept (net/tipc/socket.c:2744)
do_accept (net/socket.c:2034)
Fixes: 00aff3590fc0 ("net: tipc: fix possible refcount leak in tipc_sk_create()")
Cc: stable@vger.kernel.org
Reviewed-by: Tung Nguyen <tung.quang.nguyen@est.tech>
Reviewed-by: Breno Leitao <leitao@debian.org>
Signed-off-by: Daehyeon Ko <4ncienth@gmail.com>
---
v3: correct the Fixes: tag to 00aff3590fc0 ("net: tipc: fix possible
refcount leak in tipc_sk_create()") -- that commit added the sk_free()
on the insert-failure path; before it the path leaked sk rather than
freeing it, so the use-after-free only exists since then (v5.19+).
Thanks Breno. Collect Reviewed-by from Tung and Breno. No code change.
v2: https://lore.kernel.org/netdev/20260713082342.3803379-1-4ncienth@gmail.com/
v1: https://lore.kernel.org/netdev/20260710014440.2055584-1-4ncienth@gmail.com/
net/tipc/socket.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index e564341e0216..55e695748332 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -502,6 +502,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock,
tipc_set_sk_state(sk, TIPC_OPEN);
if (tipc_sk_insert(tsk)) {
sk_free(sk);
+ sock->sk = NULL;
pr_warn("Socket create failed; port number exhausted\n");
return -EINVAL;
}
--
2.54.0
^ permalink raw reply related
* RE: Ethtool is missing C2C link modes
From: D H, Siddaraju @ 2026-07-14 13:29 UTC (permalink / raw)
To: Maxime Chevallier, David Laight, Andrew Lunn
Cc: Michal Kubecek, netdev@vger.kernel.org, Chintalapalle, Balaji,
Das, Shubham, Srinivasan, Vijay, Samudrala, Sridhar,
Keller, Jacob E, Nguyen, Anthony L, singhai.anjali55@gmail.com,
Brandeburg, Jesse
In-Reply-To: <bb7bb5c6-5643-4e5b-b26b-9363127bb2d8@bootlin.com>
Thank you Andrew, David for bringing up all your questions.
We feel we are trending towards Maxime's intermediate link & reporting
changes but for completeness, we will start answering the open questions,
so you guys can also come up to speed and help make better design choices.
The typical AUI-C2C looks like this
RS <-MII-> PCS - PMA <-AUI-C2C-> PMA ... PMA <-AUI-C2C-> PMA - PMD <-MDI->
Andrew: Is C2C that different to CR, KR, DR?
Siddaraju: Chip-to-Chip(C2C) is simplified version of the complex KR.
IEEE defined C2C to connect chips that cannot do KR/CR/.., especially
the complex ANLT state-machine to negotiate, adjust equalizers, etc.
Andrew: Why break the pattern? NNbaseXY vs NN-AUI-C2C
Siddaraju: baseR is an end-to-end physical layer type that clearly defines
the interface & components stating from MAC RS(Reconciliation Sublayer)
till the PMD + MDI signallings. AUI is an intermediate electrical interface
layer(simplified KR) that connects 2 PMAs (PMA <=== AUI ===> PMA).
AUI is a split function design approach where subfunction implementation
from multiple vendors come together to exploit their domain expertise
to produce a best Ethernet interconnect.
Andrew: Lane numbering at the end-to-end and other patterns..
Siddaraju: The names were just a clean copy-paste from IEEE specs Andrew.
That was to give a clear pointings to specs and avoid confusions with
rename/rewordings. Yes, will follow the conventions for the ethtool
patch sets.
Andrew: I _think_ ethtool reports the media, not some intermediary format.
Is ETHTOOL_LINK_MODE_25000baseC2C_Full_BIT actually needed?
Siddaraju: We are here to find out that Andrew :).
If we are following the strict convention that ethtool link modes
report & represents the end-to-end flow "ONLY", then yes, AUI doesn't
fit there. We are exploring phy_interface_t for intermediate interfaces
and we hope we don't hit any roadblocks. Let's see.
David: fixed, *** more of a DT parameter than an ethtool one.
Siddaraju: I don't think so David, AUI-C2C is not a fixed/one-time config.
All the HW offload chips that we came across so far and connects over
AUI are multi-speed capable and depending on the MDI line rate,
the AUI also needs reconfig/runtime update to match the speed.
Say if the SFP is 25G, AUI needs to be set to 25G-AUI-C2C and
if SFP is 50G, we need 50G-AUI1-C2C.
David: Remember the history......
Siddaraju: Thank you David for highlighting this. We are in alignment with
you and we are following a strict process to review and gain as much as
history and conventions especially when dealing with Ethernet. We are
reaching to multiple folks internally and Vijay Srinivasan(also CC'ed)
is one such key contributors for us.
From SW point of view, we are reaching to our internal Linux mailing
threads and finally coming here for final guidance.
Eric: what about AOC, ACC, AEC, etc..?
Siddaraju: that's next for us Eric. We will definitely keep this in mind
especially while modelling these offloaders.
Maxime: there's ongoing work[1] to *support nics with multiple connectors..
Siddaraju: Thanks Maksim. We are exploring and evaluating this. Meanwhile,
hope the above C2C related details helps you make more finer
recommendation and we hope to hear more.
- Thank you,
Siddaraju D H
^ permalink raw reply
* Re: [PATCH v2 1/2] s390/ptff: Export ptff_function_mask[]
From: Heiko Carstens @ 2026-07-14 13:36 UTC (permalink / raw)
To: Sven Schnelle
Cc: Richard Cochran, netdev, linux-s390, linux-kernel,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Vasily Gorbik, Alexander Gordeev
In-Reply-To: <20260714130342.1971700-2-svens@linux.ibm.com>
On Tue, Jul 14, 2026 at 03:03:41PM +0200, Sven Schnelle wrote:
> Export the ptff_function_mask to make ptff_query() usable in modules.
>
> Signed-off-by: Sven Schnelle <svens@linux.ibm.com>
> ---
> arch/s390/kernel/time.c | 1 +
> 1 file changed, 1 insertion(+)
Acked-by: Heiko Carstens <hca@linux.ibm.com>
^ permalink raw reply
* Re: [PATCH v2 2/2] ptp: ptp_s390: Add missing facility check
From: Heiko Carstens @ 2026-07-14 13:38 UTC (permalink / raw)
To: Sven Schnelle
Cc: Richard Cochran, netdev, linux-s390, linux-kernel,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Vasily Gorbik, Alexander Gordeev
In-Reply-To: <20260714130342.1971700-3-svens@linux.ibm.com>
On Tue, Jul 14, 2026 at 03:03:42PM +0200, Sven Schnelle wrote:
> Only register the physical clock when facility 28 is installed
> and PTFF QAF returns that PTFF QPT is available.
>
> Fixes: 2d7de7a3010d ("s390/time: Add PtP driver")
> Signed-off-by: Sven Schnelle <svens@linux.ibm.com>
> Cc: stable@kernel.org
> ---
> drivers/ptp/ptp_s390.c | 6 +++++-
> 1 file changed, 5 insertions(+), 1 deletion(-)
Reviewed-by: Heiko Carstens <hca@linux.ibm.com>
^ permalink raw reply
* [PATCH v3 net-next] net: neigh: avoid calling neigh_forced_gc on every alloc when table is full
From: Vimal Agrawal @ 2026-07-14 13:39 UTC (permalink / raw)
To: pabeni, netdev; +Cc: kuba, kuniyu, edumazet, vimal.agrawal
In-Reply-To: <bc55e708-2f2f-46a1-b930-1cb62b05ac1d@redhat.com>
Once the neighbour table exceeds gc_thresh3, neigh_forced_gc() is called
on every allocation attempt with no rate limiting. In workloads with mostly
active/reachable entries, the GC walk traverses a large portion of the
neighbour table without reclaiming entries, holding tbl->lock for an
extended period. This causes severe lock contention and allocation
latencies exceeding 16ms under sustained neighbour creation.
Add a pre-lock check in neigh_forced_gc() to skip the GC run if one was
performed within the last 50 ms, but only when gc_thresh3 is configured
at or above NEIGH_FORCED_GC_LARGE_TABLE_THRESH (16384). This avoids
repeated full table scans and lock acquisitions on the hot allocation path
while leaving default-sized and test deployments (gc_thresh3 < 16384)
completely unaffected.
Profiling of neigh_create() shows ~3 orders of magnitude latency
improvement with this change.
Link: https://lore.kernel.org/netdev/CALkUMdSCpx_ywYCx_ePLdm6yioO1nQWx7sSM=AEgsq0kywHxTw@mail.gmail.com/
Signed-off-by: Vimal Agrawal <vimal.agrawal@sophos.com>
---
Thank you for catching this. The v2 rate limit was unconditional, so it
fired even in the selftest which sets gc_thresh3=10, preventing
forced_gc_runs from being incremented.
v3 restricts rate limiting to tables where gc_thresh3 >= 16384. Selftests
and default deployments (gc_thresh3=1024) are unaffected.
v3: Restrict rate limiting to tables with gc_thresh3 >= 16384 to avoid
breaking selftests and default deployments (gc_thresh3=1024).
v2: Changed rate-limit window from 1s (HZ) to 50ms (msecs_to_jiffies(50))
based on profiling data showing 44% -> 2.56% CPU reduction.
net/core/neighbour.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 1349c0eed..9438c5821 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -250,6 +250,8 @@ bool neigh_remove_one(struct neighbour *n)
return retval;
}
+#define NEIGH_FORCED_GC_LARGE_TABLE_THRESH 16384
+
static int neigh_forced_gc(struct neigh_table *tbl)
{
int max_clean = atomic_read(&tbl->gc_entries) -
@@ -260,6 +262,15 @@ static int neigh_forced_gc(struct neigh_table *tbl)
int shrunk = 0;
int loop = 0;
+ /*
+ * For large neighbor tables, repeated forced GC passes can spend
+ * significant CPU scanning neighbor entries when most remain active.
+ * Rate-limit consecutive forced GC passes to reduce CPU overhead.
+ */
+ if (READ_ONCE(tbl->gc_thresh3) >= NEIGH_FORCED_GC_LARGE_TABLE_THRESH &&
+ time_before(jiffies, READ_ONCE(tbl->last_flush) + msecs_to_jiffies(50)))
+ return 0;
+
NEIGH_CACHE_STAT_INC(tbl, forced_gc_runs);
spin_lock_bh(&tbl->lock);
--
2.43.0
^ permalink raw reply related
* Re: [PATCH net v3] tun/tap & vhost-net: make qdisc backpressure opt-in via IFF_BACKPRESSURE
From: Michael S. Tsirkin @ 2026-07-14 13:40 UTC (permalink / raw)
To: Simon Schippers
Cc: Willem de Bruijn, Jason Wang, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, netdev, Simon Horman,
Jonathan Corbet, Shuah Khan, Andrew Lunn, Tim Gebauer,
Brett Sheffield, linux-doc, linux-kernel
In-Reply-To: <20260709095511.168235-1-simon.schippers@tu-dortmund.de>
On Thu, Jul 09, 2026 at 11:55:11AM +0200, Simon Schippers wrote:
> Commit 1d6e569b7d0c ("tun/tap & vhost-net: avoid ptr_ring tail-drop
> when a qdisc is present") did not show a relevant performance regression
> in my testing but on Brett Sheffield's librecast testbed it shows a
> significant performance drop in a IPv6 multicast testcase. The regression
> can be pinpointed when multiple iperf3 TCP threads are sending. For 8
> threads the performance dropped from 13.5 Gbit/s to 9.13 Gbit/s. This is
> the reason why this patch makes the qdisc backpressure behavior opt-in.
>
> One option to accomplish the opt-in would be to set the default qdisc to
> noqueue at init. However this may also break userspace as users might
> have chosen a custom qdisc even though most of the qdiscs did nothing
> for tun/tap in the past due to missing backpressure...
>
> This is the reason why in this patch, the flag IFF_BACKPRESSURE is
> introduced instead which is required to enable the backpressure logic.
> This means the stopping logic in tun_net_xmit() and the waking logic in
> __tun_wake_queue() are skipped if the flag is disabled. Setting
> IFF_BACKPRESSURE makes an attached qdisc effective by stopping the queue
> instead of tail-dropping when the internal ring is full.
>
> To avoid a possible stall due to disabling IFF_BACKPRESSURE, the new
> helper tun_force_wake_queue() is implemented. The helper safely wakes the
> respective netdev queue and resets cons_cnt while the consumer_lock and
> the producer_lock of the ring are held. The helper is run in tun_attach()
> when a queue (re)attaches, in tun_set_iff() for attached tfiles, and
> in tun_queue_resize().
>
> The documentation in tuntap.rst is updated accordingly.
>
> Fixes: 1d6e569b7d0c ("tun/tap & vhost-net: avoid ptr_ring tail-drop when a qdisc is present")
> Reported-by: Brett Sheffield <brett@librecast.net>
> Closes: https://lore.kernel.org/netdev/akVnoOYQOrt8k-Gu@karahi.librecast.net/T/#u
> Signed-off-by: Simon Schippers <simon.schippers@tu-dortmund.de>
The patch itself is fine:
Acked-by: Michael S. Tsirkin <mst@redhat.com>
What bothers me is the timing: we
are past freeze and this new interface hasn't been tested
much.
We can either apply this, or revert the original patch for now
and reapply with this for next.
> ---
> V2 -> V3:
> - As suggested by MST: Clarify in tuntap.rst and the UAPI header what
> enabling IFF_BACKPRESSURE opts into: an attached qdisc becomes effective
> instead of the driver tail-dropping when the internal ring is full.
> - Avoid lines over 75 characters.
> - Update comment in tun_net_xmit() to include IFF_BACKPRESSURE.
> - Brett: Update in commit message that the referenced tests were TCP.
>
> V1 -> V2:
> - Sashiko: Ensure detached queues are woken on re-attach by calling the
> new tun_force_wake_queue() helper from tun_attach(), and reuse it
> across the existing wake paths.
> - Specify the failing test case in the commit message.
>
> V1: https://lore.kernel.org/netdev/20260704112058.95421-1-simon.schippers@tu-dortmund.de/T/#u
> V2: https://lore.kernel.org/netdev/20260706094242.115992-1-simon.schippers@tu-dortmund.de/T/#u
>
> Documentation/networking/tuntap.rst | 22 +++++++++++++
> drivers/net/tun.c | 51 ++++++++++++++++++++---------
> include/uapi/linux/if_tun.h | 4 +++
> tools/include/uapi/linux/if_tun.h | 1 +
> 4 files changed, 62 insertions(+), 16 deletions(-)
>
> diff --git a/Documentation/networking/tuntap.rst b/Documentation/networking/tuntap.rst
> index 4d7087f727be..5921a924c2ae 100644
> --- a/Documentation/networking/tuntap.rst
> +++ b/Documentation/networking/tuntap.rst
> @@ -206,6 +206,28 @@ enable is true we enable it, otherwise we disable it::
> return ioctl(fd, TUNSETQUEUE, (void *)&ifr);
> }
>
> +3.4 qdisc backpressure
> +----------------------
> +
> +Starting with Linux 7.2, IFF_BACKPRESSURE can be set to enable qdisc
> +backpressure. Without it, TX drops occur when the internal ring buffer
> +is full, so any attached qdisc is effectively bypassed and applications
> +only learn about congestion through those drops.
> +
> +With it, the kernel stops instead, letting the qdisc hold and schedule
> +packets, so its AQM, shaping and fairness actually apply. This helps
> +protocols like TCP, which cut throughput in reaction to packet drops.
> +With IFF_BACKPRESSURE, drops then only occur as a rare race. Backpressure
> +requires a qdisc to be attached and has no effect with noqueue.
> +
> +The txqueuelen can be reduced alongside this flag to further shift
> +buffering into the qdisc and reduce bufferbloat, but comes at possible
> +performance cost.
> +
> +When running multiple network streams in parallel through a single
> +TUN/TAP queue, the flag may reduce performance due to the extra overhead
> +of the backpressure mechanism.
> +
> Universal TUN/TAP device driver Frequently Asked Question
> =========================================================
>
> diff --git a/drivers/net/tun.c b/drivers/net/tun.c
> index ffbe6f13fb1f..5941e8f302ea 100644
> --- a/drivers/net/tun.c
> +++ b/drivers/net/tun.c
> @@ -98,7 +98,8 @@ static void tun_default_link_ksettings(struct net_device *dev,
> #define TUN_FASYNC IFF_ATTACH_QUEUE
>
> #define TUN_FEATURES (IFF_NO_PI | IFF_ONE_QUEUE | IFF_VNET_HDR | \
> - IFF_MULTI_QUEUE | IFF_NAPI | IFF_NAPI_FRAGS)
> + IFF_MULTI_QUEUE | IFF_NAPI | IFF_NAPI_FRAGS | \
> + IFF_BACKPRESSURE)
>
> #define GOODCOPY_LEN 128
>
> @@ -694,6 +695,20 @@ static void tun_detach_all(struct net_device *dev)
> module_put(THIS_MODULE);
> }
>
> +static void tun_force_wake_queue(struct tun_struct *tun,
> + struct tun_file *tfile)
> +{
> + /* Ensure that the producer can not stop the
> + * queue concurrently by taking locks.
> + */
> + spin_lock_bh(&tfile->tx_ring.consumer_lock);
> + spin_lock(&tfile->tx_ring.producer_lock);
> + netif_wake_subqueue(tun->dev, tfile->queue_index);
> + tfile->cons_cnt = 0;
> + spin_unlock(&tfile->tx_ring.producer_lock);
> + spin_unlock_bh(&tfile->tx_ring.consumer_lock);
> +}
> +
> static int tun_attach(struct tun_struct *tun, struct file *file,
> bool skip_filter, bool napi, bool napi_frags,
> bool publish_tun)
> @@ -737,11 +752,9 @@ static int tun_attach(struct tun_struct *tun, struct file *file,
> goto out;
> }
>
> - spin_lock(&tfile->tx_ring.consumer_lock);
> - tfile->cons_cnt = 0;
> - spin_unlock(&tfile->tx_ring.consumer_lock);
> tfile->queue_index = tun->numqueues;
> tfile->socket.sk->sk_shutdown &= ~RCV_SHUTDOWN;
> + tun_force_wake_queue(tun, tfile);
>
> if (tfile->detached) {
> /* Re-attach detached tfile, updating XDP queue_index */
> @@ -1077,7 +1090,8 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
>
> spin_lock(&tfile->tx_ring.producer_lock);
> ret = __ptr_ring_produce(&tfile->tx_ring, skb);
> - if (!qdisc_txq_has_no_queue(queue) &&
> + if ((tun->flags & IFF_BACKPRESSURE) &&
> + !qdisc_txq_has_no_queue(queue) &&
> __ptr_ring_check_produce(&tfile->tx_ring) == -ENOSPC) {
> netif_tx_stop_queue(queue);
> /* Paired with smp_mb() in __tun_wake_queue() */
> @@ -1088,8 +1102,8 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
> spin_unlock(&tfile->tx_ring.producer_lock);
>
> if (ret) {
> - /* This should be a rare case if a qdisc is present, but
> - * can happen due to lltx.
> + /* This should be a rare case if IFF_BACKPRESSURE is enabled and
> + * a qdisc is present, but can happen due to lltx.
> * Since skb_tx_timestamp(), skb_orphan(),
> * run_ebpf_filter() and pskb_trim() could have tinkered
> * with the SKB, returning NETDEV_TX_BUSY is unsafe and
> @@ -2151,8 +2165,12 @@ static ssize_t tun_put_user(struct tun_struct *tun,
> static void __tun_wake_queue(struct tun_struct *tun,
> struct tun_file *tfile, int consumed)
> {
> - struct netdev_queue *txq = netdev_get_tx_queue(tun->dev,
> - tfile->queue_index);
> + struct netdev_queue *txq;
> +
> + if (!(tun->flags & IFF_BACKPRESSURE))
> + return;
> +
> + txq = netdev_get_tx_queue(tun->dev, tfile->queue_index);
>
> /* Paired with smp_mb__after_atomic() in tun_net_xmit() */
> smp_mb();
> @@ -2764,7 +2782,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
> struct tun_struct *tun;
> struct tun_file *tfile = file->private_data;
> struct net_device *dev;
> - int err;
> + int err, i;
>
> if (tfile->detached)
> return -EINVAL;
> @@ -2893,8 +2911,12 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
> /* Make sure persistent devices do not get stuck in
> * xoff state.
> */
> - if (netif_running(tun->dev))
> - netif_tx_wake_all_queues(tun->dev);
> + if (netif_running(tun->dev)) {
> + for (i = 0; i < tun->numqueues; i++) {
> + tfile = rtnl_dereference(tun->tfiles[i]);
> + tun_force_wake_queue(tun, tfile);
> + }
> + }
>
> strscpy(ifr->ifr_name, tun->dev->name);
> return 0;
> @@ -3693,10 +3715,7 @@ static int tun_queue_resize(struct tun_struct *tun)
> if (!ret) {
> for (i = 0; i < tun->numqueues; i++) {
> tfile = rtnl_dereference(tun->tfiles[i]);
> - spin_lock(&tfile->tx_ring.consumer_lock);
> - netif_wake_subqueue(tun->dev, tfile->queue_index);
> - tfile->cons_cnt = 0;
> - spin_unlock(&tfile->tx_ring.consumer_lock);
> + tun_force_wake_queue(tun, tfile);
> }
> }
>
> diff --git a/include/uapi/linux/if_tun.h b/include/uapi/linux/if_tun.h
> index 79d53c7a1ebd..a0ddc50a7534 100644
> --- a/include/uapi/linux/if_tun.h
> +++ b/include/uapi/linux/if_tun.h
> @@ -69,6 +69,10 @@
> #define IFF_NAPI_FRAGS 0x0020
> /* Used in TUNSETIFF to bring up tun/tap without carrier */
> #define IFF_NO_CARRIER 0x0040
> +/* Stop the queue instead of dropping when the internal ring is full, so an
> + * attached qdisc applies backpressure instead of being bypassed.
> + */
> +#define IFF_BACKPRESSURE 0x0080
> #define IFF_NO_PI 0x1000
> /* This flag has no real effect */
> #define IFF_ONE_QUEUE 0x2000
> diff --git a/tools/include/uapi/linux/if_tun.h b/tools/include/uapi/linux/if_tun.h
> index 2ec07de1d73b..97b670f5bc0a 100644
> --- a/tools/include/uapi/linux/if_tun.h
> +++ b/tools/include/uapi/linux/if_tun.h
> @@ -67,6 +67,7 @@
> #define IFF_TAP 0x0002
> #define IFF_NAPI 0x0010
> #define IFF_NAPI_FRAGS 0x0020
> +#define IFF_BACKPRESSURE 0x0080
> #define IFF_NO_PI 0x1000
> /* This flag has no real effect */
> #define IFF_ONE_QUEUE 0x2000
> --
> 2.43.0
^ permalink raw reply
* Re: [PATCH 1/2] dt-bindings: net: qcom,bam-dmux: Add qcom,shikra-bam-dmux compatible
From: Vishnu Santhosh @ 2026-07-14 14:03 UTC (permalink / raw)
To: Stephan Gerhold
Cc: Stephan Gerhold, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Loic Poulain, Sergey Ryazanov, Johannes Berg,
linux-arm-msm, netdev, devicetree, linux-kernel, chris.lew,
Deepak Kumar Singh, Konrad Dybcio
In-Reply-To: <alXhHH0mGvFeUMd7@linaro.org>
On 14-07-2026 12:41 pm, Stephan Gerhold wrote:
> On Tue, Jul 14, 2026 at 11:02:31AM +0530, Vishnu Santhosh wrote:
>> On platforms where the modem DMAs into the BAM-DMUX RX data buffers and
>> the XPU enforces per-region access control, each individually
>> DMA-mapped RX buffer consumes an XPU resource group. With only ~16
>> groups available, the per-buffer mappings exhaust the table and inbound
>> transfers fault.
>>
>> Add qcom,shikra-bam-dmux as an additional compatible for the Shikra SoC,
>> paired with the generic qcom,bam-dmux fallback, so the driver can match
>> on it via its of_device_id table.
>>
>> Co-developed-by: Deepak Kumar Singh <deepak.singh@oss.qualcomm.com>
>> Signed-off-by: Deepak Kumar Singh <deepak.singh@oss.qualcomm.com>
>> Signed-off-by: Vishnu Santhosh <vishnu.santhosh@oss.qualcomm.com>
> IMHO (the DT maintainers might disagree) this is not a different
> BAM DMUX hardware block on Shikra, just a different firmware
> configuration. I'm sure the existing SoCs also have those XPU blocks,
> they just don't make use of it.
>
> Personally, I would rather describe this with an optional "qcom,vmid"
> property similar to your bam-dma patch, allowed for all SoCs.
>
> Thanks,
> Stephan
Thanks Stephan. Adding Konrad (Cc), since this compatible-string /
match-data approach was his suggestion on an earlier revision of this
series send for internal review, specifically because BAM-DMUX is a
singleton per SoC.
Konrad, given Stephan's point, would you still prefer the
compatible-string / match-data approach, or does the optional
qcom,vmid property (matching the bam-dma series) make more sense here?
Happy to go either way once you've both aligned.
Thanks,
Vishnu
^ permalink raw reply
* [PATCH v3 net 0/6] xsk: fix AF_XDP multi-buffer Tx descriptor reclaim
From: Maciej Fijalkowski @ 2026-07-14 14:07 UTC (permalink / raw)
To: netdev
Cc: bpf, magnus.karlsson, stfomichev, kuba, pabeni, horms, bjorn,
kerneljasonxing, Maciej Fijalkowski
v2: https://lore.kernel.org/netdev/20260710194424.84844-1-maciej.fijalkowski@intel.com/
v2->v3:
* Added a preceding patch that sizes the pool-wide temporary Tx descriptor
array to the larger of the first Tx ring and the device's
xdp_zc_max_segs capability. This guarantees that the shared-UMEM path can
inspect one maximum-sized valid packet even when the socket that creates
the pool has a smaller Tx ring. Consequently, this patch now records the
actual allocated size in pool->tx_descs_nentries rather than the first
socket's Tx ring size.
* Fixed a possible infinite retry loop when a shared-UMEM socket contains
an incomplete multi-buffer packet. Pass the original descriptor budget
to xskq_cons_read_desc_batch() instead of the number of descriptors
currently available, so the parser can distinguish producer exhaustion
from actual budget exhaustion.
* Moved the completion-ring space check to the common batched Tx path, so
it is performed exactly once for both singular and shared-SG pools. Keep
the legacy shared non-SG fallback outside this handling, as it reserves
completion entries one descriptor at a time.
* Reworked xsk_tx_peek_release_desc_batch() to use a common singular/shared
batched flow. Resolve an empty Tx socket list before checking CQ space,
select the shared-SG walker through an explicit shared-pool condition,
and commit the resulting batch through one common path.
* Simplified xsk_tx_commit_batch() by moving the cached CQ producer
snapshot into the helper instead of passing it from each caller.
v1: https://lore.kernel.org/netdev/20260623133240.1048434-1-maciej.fijalkowski@intel.com/
v1->v2:
* Reduced the series from seven to five patches by squashing the three
generic Tx drain and reclaim changes into a single patch. The resulting
patch handles overflow, invalid descriptors in the middle of a packet,
and reclaim of the offending descriptor as one coherent change. This
so it will be less likely to have things reported by Sashiko that are
fixed in later commits;
* Reworked the zero-copy implementation substantially:
* removed the bind-transition mechanism, including tx_share_pending,
xp_prepare_xsk_tx_share(), xp_finish_xsk_tx_share(),
synchronize_net(), and the transient bind() -EAGAIN behavior;
* added packet-framed parsing for shared-UMEM SG pools, allowing
per-socket drain state to be resumed by both singular and shared Tx
paths;
* retained the legacy one-descriptor fallback for shared non-SG pools;
* preserved the existing per-socket fairness quota while allowing the
shared walker to consume multiple complete packets and continue
filling the requested batch across fairness rounds;
* made the fairness quota large enough to process one maximum-sized
valid multi-buffer packet;
* extended the parser result with consumed-descriptor and
budget-limited accounting needed by the shared walker;
* recorded the size of the pool's temporary Tx descriptor array and
capped batch processing at that size;
* kept reclaim-only descriptors ordered after preceding driver-visible
descriptors and protected the delayed-reclaim state with
READ_ONCE()/WRITE_ONCE().
* Rewrote the zero-copy patch description to cover oversized packets,
continuation draining across calls, shared-UMEM SG handling, and CQ
publication ordering.
* Corrected the too-many-frags selftest description to state that the
invalid packet contains max_frags + 1 fragments and terminates at an
explicit packet boundary.
Hi,
This series fixes several AF_XDP multi-buffer Tx paths where descriptors
consumed from the Tx ring are not consistently returned to userspace
through the completion ring when the packet is later dropped as invalid.
The affected cases are invalid or oversized multi-buffer Tx packets in
both the generic and zero-copy paths. In these cases, the kernel can
consume one or more Tx descriptors while building or validating a
multi-buffer packet, then drop the packet before it reaches the device.
Userspace still owns the UMEM buffers only after the corresponding
addresses are returned through the CQ. Missing completions therefore
make userspace lose track of those buffers.
The generic path fixes cover following related cases:
* partially built multi-buffer skbs dropped by xsk_drop_skb();
continuation descriptors left in the Tx ring after xsk_build_skb()
reports overflow;
* invalid descriptors encountered in the middle of a multi-buffer
packet, including the offending invalid descriptor itself.
The zero-copy path is handled separately. The batched Tx parser now
distinguishes descriptors that can be passed to the driver from
descriptors that are consumed only because they belong to an invalid
multi-buffer packet. Reclaim-only descriptors are written to the CQ
address area and published in completion order, after any earlier
driver-visible Tx descriptors.
The last two patches update xskxceiver so the tests account invalid
multi-buffer Tx packets as descriptors that must be reclaimed, while
still not expecting those invalid packets on the Rx side.
This is a follow-up to Jason's changes [0] which were addressing generic
xmit only and this set allows me to pass full xskxceiver test suite run
against ice driver.
Thanks,
Maciej
[0]: https://lore.kernel.org/netdev/20260520004244.55663-1-kerneljasonxing@gmail.com/
Jason Xing (2):
xsk: fix buffer leak in xsk_drop_skb() for AF_XDP multi-buffer Tx
xsk: drain continuation descs after overflow in xsk_build_skb()
Maciej Fijalkowski (4):
xsk: provide sufficient space in pool->tx_descs
xsk: reclaim invalid multi-buffer Tx descs in ZC path
selftests/xsk: fix too-many-frags multi-buffer Tx test
selftests/xsk: account invalid multi-buffer Tx descriptors
include/net/xdp_sock.h | 1 +
include/net/xsk_buff_pool.h | 9 +-
net/xdp/xsk.c | 254 ++++++++++++++++--
net/xdp/xsk_buff_pool.c | 13 +-
net/xdp/xsk_queue.h | 76 ++++--
.../selftests/bpf/prog_tests/test_xsk.c | 48 ++--
6 files changed, 329 insertions(+), 72 deletions(-)
--
2.43.0
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox