* [PATCH 0/3] net: ag71xx: NAPI weight, MMIO drain, and TX OOM fix
From: Rosen Penev @ 2026-06-28 23:09 UTC (permalink / raw)
To: netdev
Cc: Chris Snook, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, open list
Drop the custom NAPI weight in favour of the core default, remove
unnecessary MMIO read-back drains from register writes for a small
performance gain, and fix a TX stall that occurs when the RX ring
runs out of buffers.
Rosen Penev (3):
net: ag71xx: Use default NAPI weight
net: ag71xx: remove MMIO read-back drain from register writes
net: ag71xx: re-enable TX interrupts on RX OOM
drivers/net/ethernet/atheros/ag71xx.c | 25 ++++++++-----------------
1 file changed, 8 insertions(+), 17 deletions(-)
--
2.54.0
^ permalink raw reply
* [PATCH net-next] net: dsa: qca8k: fall back to ethernet-ports node name for LEDs
From: Rosen Penev @ 2026-06-28 22:57 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, open list
The device tree binding allows both "ports" and "ethernet-ports" as
the container node name. Try "ethernet-ports" when "ports" is absent
so that newer DTBs with the preferred name work.
This matches the handling already present in qca8k-8xxx.c
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/dsa/qca/qca8k-leds.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/net/dsa/qca/qca8k-leds.c b/drivers/net/dsa/qca/qca8k-leds.c
index ef496e345a4e..0ada28377f46 100644
--- a/drivers/net/dsa/qca/qca8k-leds.c
+++ b/drivers/net/dsa/qca/qca8k-leds.c
@@ -457,6 +457,9 @@ qca8k_setup_led_ctrl(struct qca8k_priv *priv)
int ret;
ports = device_get_named_child_node(priv->dev, "ports");
+ if (!ports)
+ ports = device_get_named_child_node(priv->dev, "ethernet-ports");
+
if (!ports) {
dev_info(priv->dev, "No ports node specified in device tree!");
return 0;
--
2.54.0
^ permalink raw reply related
* Re: [PATCH iproute2] man: fix troff warnings and whitespace
From: patchwork-bot+netdevbpf @ 2026-06-28 22:40 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: netdev
In-Reply-To: <20260628215830.379088-1-stephen@networkplumber.org>
Hello:
This patch was applied to iproute2/iproute2.git (main)
by Stephen Hemminger <stephen@networkplumber.org>:
On Sun, 28 Jun 2026 14:58:02 -0700 you wrote:
> Fix troff warnings about line breaks around URL's.
> Remove unnecessary whitespace at end of line.
>
> Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
> ---
> man/man8/ip-link.8.in | 5 ++++-
> man/man8/tc-flower.8 | 2 --
> man/man8/tc-fq_codel.8 | 2 +-
> man/man8/tc-netem.8 | 4 ++++
> man/man8/tc-red.8 | 6 +++++-
> 5 files changed, 14 insertions(+), 5 deletions(-)
Here is the summary with links:
- [iproute2] man: fix troff warnings and whitespace
https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=ee2a4a866256
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* [PATCH net-next v3 2/2] net: pse-pd: add Realtek/Broadcom PSE MCU driver
From: Jonas Jelonek @ 2026-06-28 22:27 UTC (permalink / raw)
To: Oleksij Rempel, Kory Maincent, Andrew Lunn, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: netdev, devicetree, linux-kernel, Daniel Golle, Bjørn Mork,
Jonas Jelonek
In-Reply-To: <20260628222705.4052815-1-jelonek.jonas@gmail.com>
A range of PoE switches use a small microcontroller on the PCB to front
the actual PSE silicon. The host CPU talks to that MCU over I2C/SMBus or
UART using a fixed 12-byte request/response protocol with a trailing
checksum; the PSE chips are managed by the MCU and are not accessed
directly. The same protocol family is spoken by Realtek and Broadcom PSE
MCUs, diverging in opcode numbering and a few response layouts, which the
driver abstracts behind a per-dialect opcode table and parser hooks
selected by the compatible. The specific PSE chip behind the MCU is
detected at runtime and only influences per-chip constants (power scaling
and the per-port cap).
The driver is split into a shared core and two transport modules:
- PSE_REALTEK_MCU: protocol, message framing, dialect machinery, and the
pse_controller_ops glue.
- PSE_REALTEK_MCU_I2C / PSE_REALTEK_MCU_UART: transport modules
registering the MCU on an I2C bus or a serdev port respectively.
The realtek-pse-mcu-* files and PSE_REALTEK_MCU* symbols match the
realtek,pse-mcu-rtk / realtek,pse-mcu-brcm compatibles: all name the
Realtek PSE-MCU front-end, not the MCU silicon or the PSE chip behind
it (see the binding for the prefix rationale). Broadcom PSE MCUs speak
the same protocol family and are handled by the same shared core
through the dialect abstraction selected by the '-brcm' compatible.
Power budgeting is left to the MCU firmware; the driver advertises
PSE_BUDGET_EVAL_STRAT_DYNAMIC (controller-managed budget) accordingly.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
---
MAINTAINERS | 7 +
drivers/net/pse-pd/Kconfig | 28 +
drivers/net/pse-pd/Makefile | 3 +
drivers/net/pse-pd/realtek-pse-mcu-core.c | 1017 +++++++++++++++++++++
drivers/net/pse-pd/realtek-pse-mcu-i2c.c | 162 ++++
drivers/net/pse-pd/realtek-pse-mcu-uart.c | 155 ++++
drivers/net/pse-pd/realtek-pse-mcu.h | 87 ++
7 files changed, 1459 insertions(+)
create mode 100644 drivers/net/pse-pd/realtek-pse-mcu-core.c
create mode 100644 drivers/net/pse-pd/realtek-pse-mcu-i2c.c
create mode 100644 drivers/net/pse-pd/realtek-pse-mcu-uart.c
create mode 100644 drivers/net/pse-pd/realtek-pse-mcu.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 15011f5752a9..926c42d3bb93 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22721,6 +22721,13 @@ S: Maintained
F: include/sound/rt*.h
F: sound/soc/codecs/rt*
+REALTEK/BROADCOM PSE MCU DRIVER
+M: Jonas Jelonek <jelonek.jonas@gmail.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/net/pse-pd/realtek,pse-mcu.yaml
+F: drivers/net/pse-pd/realtek-pse-mcu*
+
REALTEK OTTO WATCHDOG
M: Sander Vanheule <sander@svanheule.net>
L: linux-watchdog@vger.kernel.org
diff --git a/drivers/net/pse-pd/Kconfig b/drivers/net/pse-pd/Kconfig
index 7ef29657ee5d..23e44dde3dbf 100644
--- a/drivers/net/pse-pd/Kconfig
+++ b/drivers/net/pse-pd/Kconfig
@@ -13,6 +13,34 @@ menuconfig PSE_CONTROLLER
if PSE_CONTROLLER
+config PSE_REALTEK_MCU
+ tristate
+ help
+ Shared core for the Realtek/Broadcom PSE MCU driver. This is
+ selected automatically by the transport options below.
+
+config PSE_REALTEK_MCU_I2C
+ tristate "Realtek/Broadcom PSE MCU driver (I2C transport)"
+ depends on I2C
+ select PSE_REALTEK_MCU
+ help
+ Driver for the microcontroller (MCU) that fronts the PSE
+ hardware on switches with Realtek or Broadcom PSE chips, attached
+ via I2C/SMBus. The MCU exposes a message-based protocol; the actual
+ PSE silicon is not accessed directly. To compile this driver as a
+ module, choose M here: the module will be called realtek-pse-mcu-i2c.
+
+config PSE_REALTEK_MCU_UART
+ tristate "Realtek/Broadcom PSE MCU driver (UART transport)"
+ depends on SERIAL_DEV_BUS
+ select PSE_REALTEK_MCU
+ help
+ Driver for the microcontroller (MCU) that fronts the PSE
+ hardware on switches with Realtek or Broadcom PSE chips, attached
+ via UART. The MCU exposes a message-based protocol; the actual PSE
+ silicon is not accessed directly. To compile this driver as a
+ module, choose M here: the module will be called realtek-pse-mcu-uart.
+
config PSE_REGULATOR
tristate "Regulator based PSE controller"
help
diff --git a/drivers/net/pse-pd/Makefile b/drivers/net/pse-pd/Makefile
index cc78f7ea7f5f..9cca5900fe34 100644
--- a/drivers/net/pse-pd/Makefile
+++ b/drivers/net/pse-pd/Makefile
@@ -3,6 +3,9 @@
obj-$(CONFIG_PSE_CONTROLLER) += pse_core.o
+obj-$(CONFIG_PSE_REALTEK_MCU) += realtek-pse-mcu-core.o
+obj-$(CONFIG_PSE_REALTEK_MCU_I2C) += realtek-pse-mcu-i2c.o
+obj-$(CONFIG_PSE_REALTEK_MCU_UART) += realtek-pse-mcu-uart.o
obj-$(CONFIG_PSE_REGULATOR) += pse_regulator.o
obj-$(CONFIG_PSE_PD692X0) += pd692x0.o
obj-$(CONFIG_PSE_SI3474) += si3474.o
diff --git a/drivers/net/pse-pd/realtek-pse-mcu-core.c b/drivers/net/pse-pd/realtek-pse-mcu-core.c
new file mode 100644
index 000000000000..718788d13d90
--- /dev/null
+++ b/drivers/net/pse-pd/realtek-pse-mcu-core.c
@@ -0,0 +1,1017 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for the microcontroller (MCU) fronting Realtek or Broadcom PSE
+ * chips. Both vendors' MCUs speak a closely related 12-byte fixed-frame
+ * management protocol; this driver covers both via a per-dialect opcode
+ * table and response parsers.
+ *
+ * Many PoE switch designs put a dedicated microcontroller in front of the
+ * actual PSE silicon: the host CPU talks to the MCU over I2C/SMBus or
+ * UART, and the MCU in turn manages the PSE chips on the board. The MCU
+ * speaks a small message-based protocol (12-byte fixed-size frames; opcode
+ * + arg + 9 payload bytes + checksum). The PSE chips themselves are not
+ * accessed directly; everything goes through MCU commands.
+ *
+ * This driver targets that architecture for the Realtek-family protocol.
+ * Two dialects are supported: Realtek MCUs managing RTL823x/RTL8239* PSE
+ * chips, and Broadcom MCUs managing BCM590xx PSE chips. The two share
+ * frame format and a sum-mod-256 checksum but diverge on opcode numbers
+ * and on a few response layouts; this is handled by the per-dialect
+ * opcode table and parser hooks.
+ *
+ * Out of scope: PSE chips that are interfaced directly from the host
+ * without a management MCU, MCU designs that speak an unrelated protocol
+ * family, and "dumb PSE" modes where no host control is wired up at all.
+ * Those, if and when they show up in the kernel, belong in separate
+ * drivers under drivers/net/pse-pd/.
+ *
+ * This core module implements the protocol, decoding/encoding of MCU
+ * responses, and the pse_controller_ops integration. Transport modules
+ * (realtek-pse-i2c, realtek-pse-uart) provide the send/recv callbacks.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/jiffies.h>
+#include <linux/minmax.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/pse-pd/pse.h>
+#include <linux/unaligned.h>
+
+#include "realtek-pse-mcu.h"
+
+#define RTPSE_MCU_DEVICE_ID_RTL8238B 0x0138
+#define RTPSE_MCU_DEVICE_ID_RTL8239 0x0039
+#define RTPSE_MCU_DEVICE_ID_RTL8239C 0x0139
+#define RTPSE_MCU_DEVICE_ID_BCM59111 0xe111
+#define RTPSE_MCU_DEVICE_ID_BCM59121 0xe121
+
+#define RTPSE_MCU_PORT_STS_DISABLED 0x00
+#define RTPSE_MCU_PORT_STS_SEARCHING 0x01
+#define RTPSE_MCU_PORT_STS_DELIVERING 0x02
+#define RTPSE_MCU_PORT_STS_FAULT 0x04
+#define RTPSE_MCU_PORT_STS_REQUESTING 0x06
+
+/* RTPSE_MCU_PORT_SET_POWER_LIMIT_TYPE values */
+#define RTPSE_MCU_PORT_PW_LIMIT_TYPE_USER 0x02
+
+#define RTPSE_MCU_MAX_PORTS 48
+#define RTPSE_MCU_PORT_MAX_PRIORITY 3
+
+enum rtpse_mcu_cmd {
+ RTPSE_MCU_CMD_SET_GLOBAL_STATE,
+ RTPSE_MCU_CMD_GET_SYSTEM_INFO,
+ RTPSE_MCU_CMD_GET_EXT_CONFIG,
+
+ RTPSE_MCU_CMD_PORT_ENABLE,
+ RTPSE_MCU_CMD_PORT_SET_POWER_LIMIT_TYPE,
+ RTPSE_MCU_CMD_PORT_SET_POWER_LIMIT,
+ RTPSE_MCU_CMD_PORT_SET_POWER_LIMIT_EXT,
+ RTPSE_MCU_CMD_PORT_SET_PRIORITY,
+ RTPSE_MCU_CMD_PORT_GET_STATUS,
+ RTPSE_MCU_CMD_PORT_GET_POWER_STATS,
+ RTPSE_MCU_CMD_PORT_GET_CONFIG,
+ RTPSE_MCU_CMD_PORT_GET_EXT_CONFIG,
+
+ RTPSE_MCU_NUM_CMDS,
+};
+
+struct rtpse_mcu_opcode {
+ u8 op;
+ bool valid;
+};
+
+/* Shorthand for the designated-initializer entries in dialect opcode tables. */
+#define RTPSE_MCU_OP(opc) { .op = (opc), .valid = true }
+
+/* Forward-declared so dialects can supply response parsers (defined below). */
+struct rtpse_mcu_info;
+struct rtpse_mcu_port_status;
+
+struct rtpse_mcu_dialect {
+ struct rtpse_mcu_opcode opcode[RTPSE_MCU_NUM_CMDS];
+
+ /*
+ * Response parsers. Each dialect must supply its own; the core calls
+ * these unconditionally rather than carrying a default that would
+ * silently mis-decode bytes from a dialect that forgot to set them.
+ */
+ int (*parse_system_info)(const u8 *payload, struct rtpse_mcu_info *info);
+ int (*parse_port_class)(const struct rtpse_mcu_port_status *status);
+ const char *(*mcu_type_str)(unsigned int mcu_type);
+};
+
+/*
+ * Per-compatible match data: selected by the DT/I2C compatible, it bundles
+ * the protocol dialect with attachment quirks that the exact MCU silicon
+ * does not determine (only its firmware protocol and the host bus do).
+ */
+struct rtpse_mcu_match_data {
+ const struct rtpse_mcu_dialect *dialect;
+ /* I2C framing must come from DT (realtek,i2c-protocol); else SMBus. */
+ bool i2c_proto_dt_required;
+};
+
+struct rtpse_mcu_chip_info {
+ const char *name;
+ u16 device_id;
+ u32 max_mW_per_port;
+ enum rtpse_mcu_cmd pw_set_cmd; /* command used by set_pw_limit */
+ u32 pw_set_lsb_mW; /* LSB of pw_set_cmd value, in mW */
+ u32 pw_read_lsb_mW; /* LSB of ext_config.max_power read-back, in mW */
+};
+
+/* Parsed MCU response structures (decoded from rtpse_mcu_msg replies) */
+
+struct rtpse_mcu_info {
+ u8 max_ports;
+ bool system_enable;
+ u16 device_id;
+ u8 sw_ver;
+ u8 mcu_type;
+ u8 config_status;
+ u8 ext_ver;
+};
+
+struct rtpse_mcu_ext_config {
+ u8 uvlo;
+ u8 ovlo;
+ bool prealloc_enable;
+ u8 num_of_pses;
+};
+
+struct rtpse_mcu_port_status {
+ u8 sts1;
+ u8 sts2;
+ u8 sts3;
+};
+
+struct rtpse_mcu_port_measurement {
+ u16 voltage_raw; /* 64.45mV/LSB */
+ u16 current_raw; /* 1mA/LSB */
+ u16 temperature_raw; /* T(mC) = 1250 * (220 - raw) */
+ u16 power_raw; /* 100mW/LSB */
+};
+
+struct rtpse_mcu_port_config {
+ bool enable;
+ u8 function_mode;
+ u8 detection_type;
+ u8 cls_type;
+ u8 disconnect_type;
+ u8 pair_type;
+};
+
+struct rtpse_mcu_port_ext_config {
+ u8 inrush_mode;
+ u8 limit_type;
+ u8 max_power;
+ u8 priority;
+ u8 chip_addr;
+ u8 channel;
+};
+
+static const struct rtpse_mcu_chip_info rtl8238b_info = {
+ .device_id = RTPSE_MCU_DEVICE_ID_RTL8238B,
+ .max_mW_per_port = 30000,
+ .name = "RTL8238B",
+ .pw_read_lsb_mW = 200,
+ .pw_set_cmd = RTPSE_MCU_CMD_PORT_SET_POWER_LIMIT,
+ .pw_set_lsb_mW = 200,
+};
+
+static const struct rtpse_mcu_chip_info rtl8239_info = {
+ .device_id = RTPSE_MCU_DEVICE_ID_RTL8239,
+ .max_mW_per_port = 90000,
+ .name = "RTL8239",
+ .pw_read_lsb_mW = 400,
+ .pw_set_cmd = RTPSE_MCU_CMD_PORT_SET_POWER_LIMIT_EXT,
+ .pw_set_lsb_mW = 400,
+};
+
+static const struct rtpse_mcu_chip_info rtl8239c_info = {
+ .device_id = RTPSE_MCU_DEVICE_ID_RTL8239C,
+ .max_mW_per_port = 90000,
+ .name = "RTL8239C",
+ .pw_read_lsb_mW = 400,
+ .pw_set_cmd = RTPSE_MCU_CMD_PORT_SET_POWER_LIMIT_EXT,
+ .pw_set_lsb_mW = 400,
+};
+
+static const struct rtpse_mcu_chip_info bcm59111_info = {
+ .device_id = RTPSE_MCU_DEVICE_ID_BCM59111,
+ .max_mW_per_port = 30000,
+ .name = "BCM59111",
+ .pw_read_lsb_mW = 200,
+ .pw_set_cmd = RTPSE_MCU_CMD_PORT_SET_POWER_LIMIT,
+ .pw_set_lsb_mW = 200,
+};
+
+static const struct rtpse_mcu_chip_info bcm59121_info = {
+ .device_id = RTPSE_MCU_DEVICE_ID_BCM59121,
+ /*
+ * BCM59121 is a 60W Type-3 part, but known boards run it at 802.3at
+ * and the BCM dialect has only the 8-bit/0.2W set command (<=51W);
+ * cap at the 30W the hardware actually offers.
+ */
+ .max_mW_per_port = 30000,
+ .name = "BCM59121",
+ .pw_read_lsb_mW = 200,
+ .pw_set_cmd = RTPSE_MCU_CMD_PORT_SET_POWER_LIMIT,
+ .pw_set_lsb_mW = 200,
+};
+
+/* Helpers and basic functions */
+
+static struct rtpse_mcu_ctrl *to_rtpse_mcu_ctrl(struct pse_controller_dev *pcdev)
+{
+ return container_of(pcdev, struct rtpse_mcu_ctrl, pcdev);
+}
+
+bool rtpse_mcu_needs_i2c_proto(const struct rtpse_mcu_match_data *match)
+{
+ return match->i2c_proto_dt_required;
+}
+EXPORT_SYMBOL_GPL(rtpse_mcu_needs_i2c_proto);
+
+static void rtpse_mcu_msg_init(struct rtpse_mcu_msg *msg, u8 opcode)
+{
+ memset(msg, 0xff, sizeof(*msg));
+ msg->opcode = opcode;
+}
+
+static u8 rtpse_mcu_checksum(const u8 *buf, size_t len)
+{
+ u8 sum = 0;
+
+ while (len--)
+ sum += *buf++;
+ return sum;
+}
+
+static int rtpse_mcu_do_xfer(struct rtpse_mcu_ctrl *pse, struct rtpse_mcu_msg *req,
+ struct rtpse_mcu_msg *resp)
+{
+ int ret;
+
+ req->checksum = rtpse_mcu_checksum((u8 *)req, RTPSE_MCU_MSG_SIZE - 1);
+
+ scoped_guard(mutex, &pse->mutex) {
+ ret = pse->transport->send(pse, req);
+ if (ret)
+ return ret;
+
+ /*
+ * The MCU needs a fixed amount of time between receiving a request
+ * and having the response ready, regardless of how the bytes get to
+ * us. Pace the transaction here so each transport can keep its recv
+ * path simple: a single bounded wait rather than a generic retry.
+ */
+ msleep(RTPSE_MCU_RESPONSE_MS);
+
+ memset(resp, 0, sizeof(*resp));
+ ret = pse->transport->recv(pse, req, resp);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Explicit MCU error opcodes (observed on the BCM dialect; harmless
+ * to check for RTL too). Catch these before the generic opcode/CRC
+ * mismatch path so callers see a meaningful errno.
+ */
+ switch (resp->opcode) {
+ case RTPSE_MCU_OPCODE_INCOMPLETE:
+ return -EBADE;
+ case RTPSE_MCU_OPCODE_BAD_CSUM:
+ return -EBADMSG;
+ case RTPSE_MCU_OPCODE_NOT_READY:
+ return -EAGAIN;
+ }
+
+ if (resp->opcode != req->opcode ||
+ resp->seq_num != req->seq_num ||
+ resp->checksum != rtpse_mcu_checksum((u8 *)resp, RTPSE_MCU_MSG_SIZE - 1))
+ return -EBADMSG;
+
+ return 0;
+}
+
+static int rtpse_mcu_port_query(struct rtpse_mcu_ctrl *pse, unsigned int port, u8 opcode,
+ struct rtpse_mcu_msg *resp)
+{
+ struct rtpse_mcu_msg req;
+ int ret;
+
+ rtpse_mcu_msg_init(&req, opcode);
+ req.payload[0] = port;
+
+ ret = rtpse_mcu_do_xfer(pse, &req, resp);
+ if (ret)
+ return ret;
+
+ if (resp->payload[0] != port)
+ return -EIO;
+
+ return 0;
+}
+
+static int rtpse_mcu_port_cmd(struct rtpse_mcu_ctrl *pse, unsigned int port, u8 opcode, u8 arg)
+{
+ struct rtpse_mcu_msg req, resp;
+ int ret;
+
+ rtpse_mcu_msg_init(&req, opcode);
+ req.payload[0] = port;
+ req.payload[1] = arg;
+
+ ret = rtpse_mcu_do_xfer(pse, &req, &resp);
+ if (ret)
+ return ret;
+
+ if (resp.payload[0] != port || resp.payload[1] != 0)
+ return -EIO;
+
+ return 0;
+}
+
+/* Global operations */
+
+static int rtpse_mcu_get_info(struct rtpse_mcu_ctrl *pse, struct rtpse_mcu_info *info)
+{
+ struct rtpse_mcu_msg req, resp;
+ const struct rtpse_mcu_opcode *opc;
+ int ret;
+
+ opc = &pse->dialect->opcode[RTPSE_MCU_CMD_GET_SYSTEM_INFO];
+ if (!opc->valid)
+ return -EOPNOTSUPP;
+
+ rtpse_mcu_msg_init(&req, opc->op);
+ ret = rtpse_mcu_do_xfer(pse, &req, &resp);
+ if (ret)
+ return ret;
+
+ return pse->dialect->parse_system_info(resp.payload, info);
+}
+
+static int rtpse_mcu_get_ext_config(struct rtpse_mcu_ctrl *pse, struct rtpse_mcu_ext_config *config)
+{
+ struct rtpse_mcu_msg req, resp;
+ const struct rtpse_mcu_opcode *opc;
+ int ret;
+
+ opc = &pse->dialect->opcode[RTPSE_MCU_CMD_GET_EXT_CONFIG];
+ if (!opc->valid)
+ return -EOPNOTSUPP;
+
+ rtpse_mcu_msg_init(&req, opc->op);
+ ret = rtpse_mcu_do_xfer(pse, &req, &resp);
+ if (ret)
+ return ret;
+
+ config->uvlo = resp.payload[0];
+ config->ovlo = resp.payload[5];
+ config->prealloc_enable = (resp.payload[1] == 0x1);
+ config->num_of_pses = resp.payload[6];
+
+ return 0;
+}
+
+static int rtpse_mcu_set_global_state(struct rtpse_mcu_ctrl *pse, bool enable)
+{
+ struct rtpse_mcu_msg req, resp;
+ const struct rtpse_mcu_opcode *opc;
+ int ret;
+
+ opc = &pse->dialect->opcode[RTPSE_MCU_CMD_SET_GLOBAL_STATE];
+ if (!opc->valid)
+ return -EOPNOTSUPP;
+
+ rtpse_mcu_msg_init(&req, opc->op);
+ req.payload[0] = enable ? 0x1 : 0x0;
+
+ ret = rtpse_mcu_do_xfer(pse, &req, &resp);
+ if (ret)
+ return ret;
+
+ return (resp.payload[0] == 0x0) ? 0 : -EIO;
+}
+
+/* Port operations */
+
+static int rtpse_mcu_port_get_status(struct rtpse_mcu_ctrl *pse, unsigned int port,
+ struct rtpse_mcu_port_status *status)
+{
+ const struct rtpse_mcu_opcode *opc;
+ struct rtpse_mcu_msg resp;
+ int ret;
+
+ opc = &pse->dialect->opcode[RTPSE_MCU_CMD_PORT_GET_STATUS];
+ if (!opc->valid)
+ return -EOPNOTSUPP;
+
+ ret = rtpse_mcu_port_query(pse, port, opc->op, &resp);
+ if (ret)
+ return ret;
+
+ status->sts1 = resp.payload[1];
+ status->sts2 = resp.payload[2];
+ status->sts3 = resp.payload[3];
+
+ return 0;
+}
+
+static int rtpse_mcu_port_get_measurement(struct rtpse_mcu_ctrl *pse, unsigned int port,
+ struct rtpse_mcu_port_measurement *measurement)
+{
+ const struct rtpse_mcu_opcode *opc;
+ struct rtpse_mcu_msg resp;
+ int ret;
+
+ opc = &pse->dialect->opcode[RTPSE_MCU_CMD_PORT_GET_POWER_STATS];
+ if (!opc->valid)
+ return -EOPNOTSUPP;
+
+ ret = rtpse_mcu_port_query(pse, port, opc->op, &resp);
+ if (ret)
+ return ret;
+
+ measurement->voltage_raw = get_unaligned_be16(&resp.payload[1]);
+ measurement->current_raw = get_unaligned_be16(&resp.payload[3]);
+ measurement->temperature_raw = get_unaligned_be16(&resp.payload[5]);
+ measurement->power_raw = get_unaligned_be16(&resp.payload[7]);
+
+ return 0;
+}
+
+static int rtpse_mcu_port_get_config(struct rtpse_mcu_ctrl *pse, unsigned int port,
+ struct rtpse_mcu_port_config *config)
+{
+ const struct rtpse_mcu_opcode *opc;
+ struct rtpse_mcu_msg resp;
+ int ret;
+
+ opc = &pse->dialect->opcode[RTPSE_MCU_CMD_PORT_GET_CONFIG];
+ if (!opc->valid)
+ return -EOPNOTSUPP;
+
+ ret = rtpse_mcu_port_query(pse, port, opc->op, &resp);
+ if (ret)
+ return ret;
+
+ config->enable = (resp.payload[1] == 1);
+ config->function_mode = resp.payload[2];
+ config->detection_type = resp.payload[3];
+ config->cls_type = resp.payload[4];
+ config->disconnect_type = resp.payload[5];
+ config->pair_type = resp.payload[6];
+
+ return 0;
+}
+
+static int rtpse_mcu_port_get_ext_config(struct rtpse_mcu_ctrl *pse, unsigned int port,
+ struct rtpse_mcu_port_ext_config *config)
+{
+ const struct rtpse_mcu_opcode *opc;
+ struct rtpse_mcu_msg resp;
+ int ret;
+
+ opc = &pse->dialect->opcode[RTPSE_MCU_CMD_PORT_GET_EXT_CONFIG];
+ if (!opc->valid)
+ return -EOPNOTSUPP;
+
+ ret = rtpse_mcu_port_query(pse, port, opc->op, &resp);
+ if (ret)
+ return ret;
+
+ config->inrush_mode = resp.payload[1];
+ config->limit_type = resp.payload[2];
+ config->max_power = resp.payload[3];
+ config->priority = resp.payload[4];
+ config->chip_addr = resp.payload[5];
+ config->channel = resp.payload[6];
+
+ return 0;
+}
+
+static int rtpse_mcu_port_set_state(struct rtpse_mcu_ctrl *pse, unsigned int port, bool enable)
+{
+ const struct rtpse_mcu_opcode *opc;
+
+ opc = &pse->dialect->opcode[RTPSE_MCU_CMD_PORT_ENABLE];
+ if (!opc->valid)
+ return -EOPNOTSUPP;
+
+ return rtpse_mcu_port_cmd(pse, port, opc->op, enable ? 0x1 : 0x0);
+}
+
+/* PSE controller ops */
+
+static int rtpse_mcu_port_get_admin_state(struct pse_controller_dev *pcdev, int id,
+ struct pse_admin_state *admin_state)
+{
+ struct rtpse_mcu_ctrl *pse = to_rtpse_mcu_ctrl(pcdev);
+ struct rtpse_mcu_port_config config;
+ int ret;
+
+ ret = rtpse_mcu_port_get_config(pse, id, &config);
+ if (ret)
+ return ret;
+
+ admin_state->c33_admin_state = config.enable ? ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED :
+ ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED;
+ return 0;
+}
+
+static int rtpse_mcu_port_get_pw_status(struct pse_controller_dev *pcdev, int id,
+ struct pse_pw_status *pw_status)
+{
+ struct rtpse_mcu_ctrl *pse = to_rtpse_mcu_ctrl(pcdev);
+ struct rtpse_mcu_port_status status;
+ int ret;
+
+ ret = rtpse_mcu_port_get_status(pse, id, &status);
+ if (ret)
+ return ret;
+
+ switch (status.sts1) {
+ case RTPSE_MCU_PORT_STS_DISABLED:
+ pw_status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED;
+ break;
+ case RTPSE_MCU_PORT_STS_SEARCHING:
+ case RTPSE_MCU_PORT_STS_REQUESTING:
+ pw_status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_SEARCHING;
+ break;
+ case RTPSE_MCU_PORT_STS_DELIVERING:
+ pw_status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING;
+ break;
+ case RTPSE_MCU_PORT_STS_FAULT:
+ pw_status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_FAULT;
+ break;
+ default:
+ pw_status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_UNKNOWN;
+ break;
+ }
+
+ return 0;
+}
+
+static int rtpse_mcu_port_get_pw_class(struct pse_controller_dev *pcdev, int id)
+{
+ struct rtpse_mcu_ctrl *pse = to_rtpse_mcu_ctrl(pcdev);
+ struct rtpse_mcu_port_status status;
+ int ret;
+
+ ret = rtpse_mcu_port_get_status(pse, id, &status);
+ if (ret)
+ return ret;
+
+ /*
+ * sts2 carries detection+classification only when sts1 is not a
+ * fault state; in fault states it encodes the fault type instead.
+ * Treat the two reserved sts1 codes (0x3, 0x5) as faults too, since
+ * the datasheet hints at "other fault" beyond the explicit 0x4.
+ */
+ switch (status.sts1) {
+ case RTPSE_MCU_PORT_STS_DISABLED:
+ case RTPSE_MCU_PORT_STS_SEARCHING:
+ case RTPSE_MCU_PORT_STS_DELIVERING:
+ case RTPSE_MCU_PORT_STS_REQUESTING:
+ return pse->dialect->parse_port_class(&status);
+ default:
+ return 0;
+ }
+}
+
+static int rtpse_mcu_port_get_actual_pw(struct pse_controller_dev *pcdev, int id)
+{
+ struct rtpse_mcu_ctrl *pse = to_rtpse_mcu_ctrl(pcdev);
+ struct rtpse_mcu_port_measurement measurement;
+ int ret;
+
+ ret = rtpse_mcu_port_get_measurement(pse, id, &measurement);
+ if (ret)
+ return ret;
+
+ /* 100mW per LSB */
+ return measurement.power_raw * 100U;
+}
+
+static int rtpse_mcu_port_get_voltage(struct pse_controller_dev *pcdev, int id)
+{
+ struct rtpse_mcu_ctrl *pse = to_rtpse_mcu_ctrl(pcdev);
+ struct rtpse_mcu_port_measurement measurement;
+ int ret;
+ u32 uV;
+
+ ret = rtpse_mcu_port_get_measurement(pse, id, &measurement);
+ if (ret)
+ return ret;
+
+ /* 64.45mV per LSB */
+ uV = (u32)measurement.voltage_raw * 64450U;
+ return min_t(u32, uV, INT_MAX);
+}
+
+static int rtpse_mcu_port_enable(struct pse_controller_dev *pcdev, int id)
+{
+ return rtpse_mcu_port_set_state(to_rtpse_mcu_ctrl(pcdev), id, true);
+}
+
+static int rtpse_mcu_port_disable(struct pse_controller_dev *pcdev, int id)
+{
+ return rtpse_mcu_port_set_state(to_rtpse_mcu_ctrl(pcdev), id, false);
+}
+
+static int rtpse_mcu_port_get_pw_limit(struct pse_controller_dev *pcdev, int id)
+{
+ struct rtpse_mcu_ctrl *pse = to_rtpse_mcu_ctrl(pcdev);
+ struct rtpse_mcu_port_ext_config config;
+ int ret;
+
+ ret = rtpse_mcu_port_get_ext_config(pse, id, &config);
+ if (ret)
+ return ret;
+
+ return config.max_power * pse->chip->pw_read_lsb_mW;
+}
+
+static int rtpse_mcu_port_set_pw_limit(struct pse_controller_dev *pcdev, int id, int max_mW)
+{
+ const struct rtpse_mcu_opcode *type_opc, *val_opc;
+ struct rtpse_mcu_ctrl *pse = to_rtpse_mcu_ctrl(pcdev);
+ const struct rtpse_mcu_chip_info *chip = pse->chip;
+ unsigned int prg_val;
+ int ret;
+
+ if (max_mW < 0 || max_mW > chip->max_mW_per_port)
+ return -ERANGE;
+
+ type_opc = &pse->dialect->opcode[RTPSE_MCU_CMD_PORT_SET_POWER_LIMIT_TYPE];
+ val_opc = &pse->dialect->opcode[chip->pw_set_cmd];
+ if (!type_opc->valid || !val_opc->valid)
+ return -EOPNOTSUPP;
+
+ /*
+ * Switch the port to user-defined limit mode first, then program the
+ * limit value. If the second cmd fails, the port is left in
+ * user-defined mode but with the previous limit value; the next
+ * successful set_pw_limit call recovers it.
+ */
+ ret = rtpse_mcu_port_cmd(pse, id, type_opc->op, RTPSE_MCU_PORT_PW_LIMIT_TYPE_USER);
+ if (ret)
+ return ret;
+
+ prg_val = min_t(unsigned int, max_mW / chip->pw_set_lsb_mW, 0xff);
+
+ return rtpse_mcu_port_cmd(pse, id, val_opc->op, prg_val);
+}
+
+static int rtpse_mcu_port_get_pw_limit_ranges(struct pse_controller_dev *pcdev, int id,
+ struct pse_pw_limit_ranges *out)
+{
+ struct ethtool_c33_pse_pw_limit_range *range;
+ struct rtpse_mcu_ctrl *pse = to_rtpse_mcu_ctrl(pcdev);
+
+ range = kzalloc_obj(*range, GFP_KERNEL);
+ if (!range)
+ return -ENOMEM;
+
+ range[0].min = 0;
+ range[0].max = pse->chip->max_mW_per_port;
+
+ out->c33_pw_limit_ranges = range;
+ return 1;
+}
+
+static int rtpse_mcu_port_get_prio(struct pse_controller_dev *pcdev, int id)
+{
+ struct rtpse_mcu_ctrl *pse = to_rtpse_mcu_ctrl(pcdev);
+ struct rtpse_mcu_port_ext_config config;
+ int ret;
+
+ ret = rtpse_mcu_port_get_ext_config(pse, id, &config);
+ if (ret)
+ return ret;
+
+ return config.priority;
+}
+
+static int rtpse_mcu_port_set_prio(struct pse_controller_dev *pcdev, int id, unsigned int prio)
+{
+ struct rtpse_mcu_ctrl *pse = to_rtpse_mcu_ctrl(pcdev);
+ const struct rtpse_mcu_opcode *opc;
+
+ if (prio > RTPSE_MCU_PORT_MAX_PRIORITY)
+ return -ERANGE;
+
+ opc = &pse->dialect->opcode[RTPSE_MCU_CMD_PORT_SET_PRIORITY];
+ if (!opc->valid)
+ return -EOPNOTSUPP;
+
+ return rtpse_mcu_port_cmd(pse, id, opc->op, prio);
+}
+
+static const struct pse_controller_ops rtpse_mcu_ops = {
+ .pi_get_admin_state = rtpse_mcu_port_get_admin_state,
+ .pi_get_pw_status = rtpse_mcu_port_get_pw_status,
+ .pi_get_pw_class = rtpse_mcu_port_get_pw_class,
+ .pi_get_actual_pw = rtpse_mcu_port_get_actual_pw,
+ .pi_enable = rtpse_mcu_port_enable,
+ .pi_disable = rtpse_mcu_port_disable,
+ .pi_get_voltage = rtpse_mcu_port_get_voltage,
+ .pi_get_pw_limit = rtpse_mcu_port_get_pw_limit,
+ .pi_set_pw_limit = rtpse_mcu_port_set_pw_limit,
+ .pi_get_pw_limit_ranges = rtpse_mcu_port_get_pw_limit_ranges,
+ .pi_get_prio = rtpse_mcu_port_get_prio,
+ .pi_set_prio = rtpse_mcu_port_set_prio,
+};
+
+static int rtpse_mcu_discover(struct rtpse_mcu_ctrl *pse, struct rtpse_mcu_info *info)
+{
+ struct rtpse_mcu_ext_config ext_config;
+ unsigned long deadline;
+ int ret;
+
+ /*
+ * The MCU may not answer on the bus yet right after power-up or
+ * enable-gpios assertion: depending on the transport it either stays
+ * silent (-ETIMEDOUT) or does not ACK its address at all (-ENXIO /
+ * -EREMOTEIO). Retry within a bounded wall-time window so a slow boot
+ * still probes, while a genuinely unresponsive MCU fails with its real
+ * error instead of deferring forever and masking it.
+ */
+ deadline = jiffies + msecs_to_jiffies(RTPSE_MCU_BOOT_TIMEOUT_MS);
+ do {
+ ret = rtpse_mcu_get_info(pse, info);
+ if (ret != -ETIMEDOUT && ret != -ENXIO && ret != -EREMOTEIO &&
+ ret != -EAGAIN)
+ break;
+ msleep(RTPSE_MCU_BOOT_RETRY_MS);
+ } while (time_before(jiffies, deadline));
+ if (ret)
+ return dev_err_probe(pse->dev, ret, "failed to read MCU info\n");
+
+ switch (info->device_id) {
+ case RTPSE_MCU_DEVICE_ID_RTL8238B:
+ pse->chip = &rtl8238b_info;
+ break;
+ case RTPSE_MCU_DEVICE_ID_RTL8239:
+ pse->chip = &rtl8239_info;
+ break;
+ case RTPSE_MCU_DEVICE_ID_RTL8239C:
+ pse->chip = &rtl8239c_info;
+ break;
+ case RTPSE_MCU_DEVICE_ID_BCM59111:
+ pse->chip = &bcm59111_info;
+ break;
+ case RTPSE_MCU_DEVICE_ID_BCM59121:
+ pse->chip = &bcm59121_info;
+ break;
+ default:
+ return dev_err_probe(pse->dev, -EINVAL, "unknown PSE id 0x%x\n",
+ info->device_id);
+ }
+
+ if (!info->max_ports || info->max_ports > RTPSE_MCU_MAX_PORTS)
+ return dev_err_probe(pse->dev, -EINVAL,
+ "MCU reports invalid port count %u\n", info->max_ports);
+
+ ret = rtpse_mcu_get_ext_config(pse, &ext_config);
+ if (ret)
+ return dev_err_probe(pse->dev, ret, "failed to read MCU ext config\n");
+
+ dev_info(pse->dev, "%s MCU, %s (id 0x%04x), %u ports across %u PSE chip(s)\n",
+ pse->dialect->mcu_type_str(info->mcu_type), pse->chip->name,
+ info->device_id, info->max_ports, ext_config.num_of_pses);
+ return 0;
+}
+
+static void rtpse_mcu_regulator_disable(void *data)
+{
+ regulator_disable(data);
+}
+
+static void rtpse_mcu_global_disable(void *data)
+{
+ struct rtpse_mcu_ctrl *pse = data;
+
+ rtpse_mcu_set_global_state(pse, false);
+}
+
+int rtpse_mcu_register(struct rtpse_mcu_ctrl *pse)
+{
+ const struct rtpse_mcu_match_data *match;
+ struct gpio_desc *enable_gpio;
+ struct rtpse_mcu_info info;
+ int ret;
+
+ BUILD_BUG_ON(sizeof(struct rtpse_mcu_msg) != RTPSE_MCU_MSG_SIZE);
+
+ ret = devm_mutex_init(pse->dev, &pse->mutex);
+ if (ret)
+ return ret;
+
+ match = device_get_match_data(pse->dev);
+ if (!match)
+ return dev_err_probe(pse->dev, -ENODEV, "missing match data\n");
+ pse->dialect = match->dialect;
+
+ /*
+ * Catch a dialect that forgot to set one of the required hooks at
+ * probe time, rather than NULL-deref'ing later from a fast path.
+ */
+ if (!pse->dialect ||
+ !pse->dialect->parse_system_info ||
+ !pse->dialect->parse_port_class ||
+ !pse->dialect->mcu_type_str)
+ return dev_err_probe(pse->dev, -EINVAL,
+ "dialect for chip is incomplete\n");
+
+ pse->poe_supply = devm_regulator_get(pse->dev, "power");
+ if (IS_ERR(pse->poe_supply))
+ return dev_err_probe(pse->dev, PTR_ERR(pse->poe_supply),
+ "failed to get PoE supply\n");
+
+ enable_gpio = devm_gpiod_get_optional(pse->dev, "enable", GPIOD_OUT_HIGH);
+ if (IS_ERR(enable_gpio))
+ return dev_err_probe(pse->dev, PTR_ERR(enable_gpio),
+ "failed to get enable gpio\n");
+
+ ret = rtpse_mcu_discover(pse, &info);
+ if (ret)
+ return ret;
+
+ ret = regulator_enable(pse->poe_supply);
+ if (ret)
+ return dev_err_probe(pse->dev, ret, "failed to enable PoE supply\n");
+
+ ret = devm_add_action_or_reset(pse->dev, rtpse_mcu_regulator_disable, pse->poe_supply);
+ if (ret)
+ return ret;
+
+ if (!info.system_enable) {
+ ret = rtpse_mcu_set_global_state(pse, true);
+ /* Dialects without a global-state concept (e.g. BCM) return
+ * -EOPNOTSUPP; treat that as "no separate enable required".
+ */
+ if (ret && ret != -EOPNOTSUPP)
+ return dev_err_probe(pse->dev, ret,
+ "failed to enable PSE system\n");
+ if (!ret) {
+ ret = devm_add_action_or_reset(pse->dev,
+ rtpse_mcu_global_disable, pse);
+ if (ret)
+ return ret;
+ }
+ }
+
+ /*
+ * Depending on the MCU firmware configuration (which might be different
+ * for every board), it isn't known whether the PoE subsystem is active or
+ * inactive by default. At this stage, the PSE chips might already deliver
+ * power to PDs without any explicit enable.
+ */
+
+ pse->pcdev.owner = THIS_MODULE;
+ pse->pcdev.ops = &rtpse_mcu_ops;
+ pse->pcdev.dev = pse->dev;
+ pse->pcdev.types = ETHTOOL_PSE_C33;
+ pse->pcdev.nr_lines = info.max_ports;
+ pse->pcdev.pis_prio_max = RTPSE_MCU_PORT_MAX_PRIORITY;
+ pse->pcdev.supp_budget_eval_strategies = PSE_BUDGET_EVAL_STRAT_DYNAMIC;
+
+ return devm_pse_controller_register(pse->dev, &pse->pcdev);
+}
+EXPORT_SYMBOL_GPL(rtpse_mcu_register);
+
+static int rtpse_mcu_rtl_parse_system_info(const u8 *payload, struct rtpse_mcu_info *info)
+{
+ info->max_ports = payload[1];
+ info->system_enable = (payload[2] == 0x1);
+ info->device_id = get_unaligned_be16(&payload[3]);
+ info->sw_ver = payload[5];
+ info->mcu_type = payload[6];
+ info->config_status = payload[7];
+ info->ext_ver = payload[8];
+ return 0;
+}
+
+static int rtpse_mcu_rtl_parse_port_class(const struct rtpse_mcu_port_status *status)
+{
+ /* Class lives in the upper nibble of sts2. */
+ return FIELD_GET(GENMASK(7, 4), status->sts2);
+}
+
+static const char *rtpse_mcu_rtl_mcu_type_str(unsigned int mcu_type)
+{
+ switch (mcu_type) {
+ case 0x00: return "GigaDevice GD32F310";
+ case 0x01: return "GigaDevice GD32F230";
+ case 0x02: return "GigaDevice GD32F303";
+ case 0x03: return "GigaDevice GD32F103";
+ case 0x04: return "GigaDevice GD32E103";
+ case 0x10: return "Nuvoton M0516";
+ case 0x11: return "Nuvoton M0564";
+ case 0x12: return "Nuvoton NUC029";
+ default: return "unknown";
+ }
+}
+
+static int rtpse_mcu_brcm_parse_system_info(const u8 *payload, struct rtpse_mcu_info *info)
+{
+ info->max_ports = payload[1];
+ /* BCM has no explicit system_enable byte; the closest analog is the
+ * "remote enable" bit in the system-status flags at payload[7].
+ */
+ info->system_enable = !!(payload[7] & BIT(2));
+ info->device_id = get_unaligned_be16(&payload[3]);
+ info->sw_ver = payload[5];
+ info->mcu_type = payload[6];
+ info->config_status = payload[7];
+ info->ext_ver = payload[8];
+ return 0;
+}
+
+static int rtpse_mcu_brcm_parse_port_class(const struct rtpse_mcu_port_status *status)
+{
+ /* BCM puts the detected class in payload[3] (== sts3) directly.
+ * Mask to the low nibble; class is 0..8 and any high bits would be
+ * noise.
+ */
+ return status->sts3 & 0x0f;
+}
+
+static const char *rtpse_mcu_brcm_mcu_type_str(unsigned int mcu_type)
+{
+ switch (mcu_type) {
+ case 0x00: return "ST Micro ST32F100";
+ case 0x01: return "Nuvoton M05xx LAN";
+ case 0x02: return "ST Micro STF030C8";
+ case 0x03: return "Nuvoton M058SAN";
+ case 0x04: return "Nuvoton NUC122";
+ default: return "unknown";
+ }
+}
+
+/* Map each logical command the core issues to its per-dialect opcode. */
+static const struct rtpse_mcu_dialect rtpse_mcu_dialect_rtk = {
+ .parse_system_info = rtpse_mcu_rtl_parse_system_info,
+ .parse_port_class = rtpse_mcu_rtl_parse_port_class,
+ .mcu_type_str = rtpse_mcu_rtl_mcu_type_str,
+ .opcode = {
+ [RTPSE_MCU_CMD_SET_GLOBAL_STATE] = RTPSE_MCU_OP(0x00),
+ [RTPSE_MCU_CMD_GET_SYSTEM_INFO] = RTPSE_MCU_OP(0x40),
+ [RTPSE_MCU_CMD_GET_EXT_CONFIG] = RTPSE_MCU_OP(0x4a),
+
+ [RTPSE_MCU_CMD_PORT_ENABLE] = RTPSE_MCU_OP(0x01),
+ [RTPSE_MCU_CMD_PORT_SET_POWER_LIMIT_TYPE] = RTPSE_MCU_OP(0x12),
+ [RTPSE_MCU_CMD_PORT_SET_POWER_LIMIT] = RTPSE_MCU_OP(0x13),
+ [RTPSE_MCU_CMD_PORT_SET_POWER_LIMIT_EXT] = RTPSE_MCU_OP(0x14),
+ [RTPSE_MCU_CMD_PORT_SET_PRIORITY] = RTPSE_MCU_OP(0x15),
+ [RTPSE_MCU_CMD_PORT_GET_STATUS] = RTPSE_MCU_OP(0x42),
+ [RTPSE_MCU_CMD_PORT_GET_POWER_STATS] = RTPSE_MCU_OP(0x44),
+ [RTPSE_MCU_CMD_PORT_GET_CONFIG] = RTPSE_MCU_OP(0x48),
+ [RTPSE_MCU_CMD_PORT_GET_EXT_CONFIG] = RTPSE_MCU_OP(0x49),
+ },
+};
+
+static const struct rtpse_mcu_dialect rtpse_mcu_dialect_brcm = {
+ .parse_system_info = rtpse_mcu_brcm_parse_system_info,
+ .parse_port_class = rtpse_mcu_brcm_parse_port_class,
+ .mcu_type_str = rtpse_mcu_brcm_mcu_type_str,
+ .opcode = {
+ [RTPSE_MCU_CMD_GET_SYSTEM_INFO] = RTPSE_MCU_OP(0x20),
+ [RTPSE_MCU_CMD_GET_EXT_CONFIG] = RTPSE_MCU_OP(0x2b),
+
+ [RTPSE_MCU_CMD_PORT_ENABLE] = RTPSE_MCU_OP(0x00),
+ [RTPSE_MCU_CMD_PORT_SET_POWER_LIMIT_TYPE] = RTPSE_MCU_OP(0x15),
+ [RTPSE_MCU_CMD_PORT_SET_POWER_LIMIT] = RTPSE_MCU_OP(0x16),
+ [RTPSE_MCU_CMD_PORT_SET_PRIORITY] = RTPSE_MCU_OP(0x1a),
+ [RTPSE_MCU_CMD_PORT_GET_STATUS] = RTPSE_MCU_OP(0x21),
+ [RTPSE_MCU_CMD_PORT_GET_POWER_STATS] = RTPSE_MCU_OP(0x30),
+ [RTPSE_MCU_CMD_PORT_GET_CONFIG] = RTPSE_MCU_OP(0x25),
+ [RTPSE_MCU_CMD_PORT_GET_EXT_CONFIG] = RTPSE_MCU_OP(0x26),
+ },
+};
+
+const struct rtpse_mcu_match_data rtpse_mcu_rtk_data = {
+ .dialect = &rtpse_mcu_dialect_rtk,
+ .i2c_proto_dt_required = true,
+};
+EXPORT_SYMBOL_GPL(rtpse_mcu_rtk_data);
+
+const struct rtpse_mcu_match_data rtpse_mcu_brcm_data = {
+ .dialect = &rtpse_mcu_dialect_brcm,
+};
+EXPORT_SYMBOL_GPL(rtpse_mcu_brcm_data);
+
+MODULE_DESCRIPTION("Realtek/Broadcom PSE MCU driver (core)");
+MODULE_AUTHOR("Jonas Jelonek <jelonek.jonas@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/pse-pd/realtek-pse-mcu-i2c.c b/drivers/net/pse-pd/realtek-pse-mcu-i2c.c
new file mode 100644
index 000000000000..2ea405cfbba1
--- /dev/null
+++ b/drivers/net/pse-pd/realtek-pse-mcu-i2c.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pse-pd/pse.h>
+
+#include "realtek-pse-mcu.h"
+
+/*
+ * The core has already waited RTPSE_MCU_RESPONSE_MS before calling us, so
+ * the response is normally ready on the very first read. For commands the
+ * MCU produces more slowly, keep polling at the typical response cadence
+ * up to the worst-case ceiling.
+ */
+#define RTPSE_MCU_I2C_RETRY_MS RTPSE_MCU_RESPONSE_MS
+#define RTPSE_MCU_I2C_MAX_TRIES (RTPSE_MCU_RESPONSE_MAX_MS / RTPSE_MCU_I2C_RETRY_MS)
+
+static int rtpse_mcu_i2c_smbus_send(struct rtpse_mcu_ctrl *pse, const struct rtpse_mcu_msg *req)
+{
+ struct i2c_client *client = to_i2c_client(pse->dev);
+
+ /* Send opcode as SMBus command byte; remaining 11 bytes as block data */
+ return i2c_smbus_write_i2c_block_data(client, req->opcode, RTPSE_MCU_MSG_SIZE - 1,
+ (u8 *)req + 1);
+}
+
+static int rtpse_mcu_i2c_smbus_recv(struct rtpse_mcu_ctrl *pse, const struct rtpse_mcu_msg *req,
+ struct rtpse_mcu_msg *resp)
+{
+ struct i2c_client *client = to_i2c_client(pse->dev);
+ int tries, ret;
+
+ for (tries = 0; tries < RTPSE_MCU_I2C_MAX_TRIES; tries++) {
+ if (tries > 0)
+ msleep(RTPSE_MCU_I2C_RETRY_MS);
+
+ /* MCU needs 0x00 as command byte for read */
+ ret = i2c_smbus_read_i2c_block_data(client, 0x00,
+ RTPSE_MCU_MSG_SIZE,
+ (u8 *)resp);
+ if (ret < 0)
+ return ret;
+ if (ret == RTPSE_MCU_MSG_SIZE && rtpse_mcu_resp_is_final(req, resp))
+ return 0;
+ }
+
+ return -ETIMEDOUT;
+}
+
+static const struct rtpse_mcu_transport_ops rtpse_mcu_i2c_smbus_ops = {
+ .send = rtpse_mcu_i2c_smbus_send,
+ .recv = rtpse_mcu_i2c_smbus_recv,
+};
+
+static int rtpse_mcu_i2c_native_send(struct rtpse_mcu_ctrl *pse, const struct rtpse_mcu_msg *req)
+{
+ struct i2c_client *client = to_i2c_client(pse->dev);
+ int ret;
+
+ ret = i2c_master_send(client, (const u8 *)req, RTPSE_MCU_MSG_SIZE);
+ if (ret < 0)
+ return ret;
+ return ret == RTPSE_MCU_MSG_SIZE ? 0 : -EIO;
+}
+
+static int rtpse_mcu_i2c_native_recv(struct rtpse_mcu_ctrl *pse, const struct rtpse_mcu_msg *req,
+ struct rtpse_mcu_msg *resp)
+{
+ struct i2c_client *client = to_i2c_client(pse->dev);
+ int tries, ret;
+
+ for (tries = 0; tries < RTPSE_MCU_I2C_MAX_TRIES; tries++) {
+ if (tries > 0)
+ msleep(RTPSE_MCU_I2C_RETRY_MS);
+
+ ret = i2c_master_recv(client, (u8 *)resp, RTPSE_MCU_MSG_SIZE);
+ if (ret < 0)
+ return ret;
+ if (ret == RTPSE_MCU_MSG_SIZE && rtpse_mcu_resp_is_final(req, resp))
+ return 0;
+ }
+
+ return -ETIMEDOUT;
+}
+
+static const struct rtpse_mcu_transport_ops rtpse_mcu_i2c_native_ops = {
+ .send = rtpse_mcu_i2c_native_send,
+ .recv = rtpse_mcu_i2c_native_recv,
+};
+
+static int rtpse_mcu_i2c_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ const struct rtpse_mcu_match_data *match;
+ struct rtpse_mcu_ctrl *pse;
+ bool use_native = false;
+ int ret;
+
+ match = device_get_match_data(dev);
+ if (!match)
+ return dev_err_probe(dev, -ENODEV, "missing match data\n");
+
+ if (rtpse_mcu_needs_i2c_proto(match)) {
+ const char *proto;
+
+ ret = device_property_read_string(dev, "realtek,i2c-protocol", &proto);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "missing required \"realtek,i2c-protocol\" property\n");
+
+ if (!strcmp(proto, "i2c"))
+ use_native = true;
+ else if (!strcmp(proto, "smbus"))
+ use_native = false;
+ else
+ return dev_err_probe(dev, -EINVAL,
+ "unknown realtek,i2c-protocol \"%s\"\n", proto);
+ }
+
+ if (use_native) {
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return dev_err_probe(dev, -EOPNOTSUPP,
+ "plain-I2C MCU protocol requires I2C-capable adapter\n");
+ } else {
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WRITE_I2C_BLOCK |
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK))
+ return dev_err_probe(dev, -EOPNOTSUPP,
+ "SMBus MCU protocol requires SMBus I2C-block support\n");
+ }
+
+ pse = devm_kzalloc(dev, sizeof(*pse), GFP_KERNEL);
+ if (!pse)
+ return -ENOMEM;
+
+ pse->dev = dev;
+ pse->transport = use_native ? &rtpse_mcu_i2c_native_ops : &rtpse_mcu_i2c_smbus_ops;
+
+ return rtpse_mcu_register(pse);
+}
+
+static const struct of_device_id rtpse_mcu_i2c_of_match[] = {
+ { .compatible = "realtek,pse-mcu-rtk", .data = &rtpse_mcu_rtk_data },
+ { .compatible = "realtek,pse-mcu-brcm", .data = &rtpse_mcu_brcm_data },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rtpse_mcu_i2c_of_match);
+
+static struct i2c_driver rtpse_mcu_i2c_driver = {
+ .driver = {
+ .name = "realtek-pse-mcu-i2c",
+ .of_match_table = rtpse_mcu_i2c_of_match,
+ },
+ .probe = rtpse_mcu_i2c_probe,
+};
+module_i2c_driver(rtpse_mcu_i2c_driver);
+
+MODULE_AUTHOR("Jonas Jelonek <jelonek.jonas@gmail.com>");
+MODULE_DESCRIPTION("Realtek/Broadcom PSE MCU driver (I2C transport)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/pse-pd/realtek-pse-mcu-uart.c b/drivers/net/pse-pd/realtek-pse-mcu-uart.c
new file mode 100644
index 000000000000..4d74abf8fd4b
--- /dev/null
+++ b/drivers/net/pse-pd/realtek-pse-mcu-uart.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/cleanup.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pse-pd/pse.h>
+#include <linux/serdev.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+
+#include "realtek-pse-mcu.h"
+
+#define RTPSE_MCU_UART_BAUD_DEFAULT 19200
+#define RTPSE_MCU_UART_TX_TIMEOUT msecs_to_jiffies(100)
+#define RTPSE_MCU_UART_RX_TIMEOUT msecs_to_jiffies(RTPSE_MCU_RESPONSE_MAX_MS)
+
+struct rtpse_mcu_uart {
+ struct rtpse_mcu_ctrl pse;
+ struct serdev_device *serdev;
+ struct completion rx_done;
+ spinlock_t rx_lock; /* protects rx_buf and rx_len */
+ size_t rx_len;
+ u8 rx_buf[RTPSE_MCU_MSG_SIZE];
+};
+
+#define to_rtpse_mcu_uart(p) container_of(p, struct rtpse_mcu_uart, pse)
+
+/*
+ * No framing is done here: a glitched frame costs one transaction, then
+ * the next _send re-frames from rx_len 0. Resync works by returning count
+ * (not take), dropping any overflow so serdev keeps no leftover to bleed
+ * into the next frame.
+ */
+static size_t rtpse_mcu_uart_receive(struct serdev_device *serdev,
+ const u8 *buf, size_t count)
+{
+ struct rtpse_mcu_uart *ctx = serdev_device_get_drvdata(serdev);
+ size_t take;
+
+ scoped_guard(spinlock_irqsave, &ctx->rx_lock) {
+ take = min(count, sizeof(ctx->rx_buf) - ctx->rx_len);
+ if (take) {
+ memcpy(ctx->rx_buf + ctx->rx_len, buf, take);
+ ctx->rx_len += take;
+ if (ctx->rx_len == sizeof(ctx->rx_buf))
+ complete(&ctx->rx_done);
+ }
+ }
+
+ /* consume all to avoid desync/misalignment */
+ return count;
+}
+
+static const struct serdev_device_ops rtpse_mcu_uart_serdev_ops = {
+ .receive_buf = rtpse_mcu_uart_receive,
+ .write_wakeup = serdev_device_write_wakeup,
+};
+
+static int rtpse_mcu_uart_send(struct rtpse_mcu_ctrl *pse, const struct rtpse_mcu_msg *req)
+{
+ struct rtpse_mcu_uart *ctx = to_rtpse_mcu_uart(pse);
+ int written;
+
+ /* clear any leftover rx state before transmitting */
+ scoped_guard(spinlock_irqsave, &ctx->rx_lock) {
+ reinit_completion(&ctx->rx_done);
+ ctx->rx_len = 0;
+ }
+
+ written = serdev_device_write(ctx->serdev, (const u8 *)req, sizeof(*req),
+ RTPSE_MCU_UART_TX_TIMEOUT);
+ if (written < 0)
+ return written;
+ if (written != sizeof(*req))
+ return -EIO;
+
+ return 0;
+}
+
+static int rtpse_mcu_uart_recv(struct rtpse_mcu_ctrl *pse,
+ const struct rtpse_mcu_msg *req,
+ struct rtpse_mcu_msg *resp)
+{
+ struct rtpse_mcu_uart *ctx = to_rtpse_mcu_uart(pse);
+
+ if (!wait_for_completion_timeout(&ctx->rx_done, RTPSE_MCU_UART_RX_TIMEOUT))
+ return -ETIMEDOUT;
+
+ scoped_guard(spinlock_irqsave, &ctx->rx_lock) {
+ if (ctx->rx_len != sizeof(*resp))
+ return -EIO;
+
+ memcpy(resp, ctx->rx_buf, sizeof(*resp));
+ }
+ return 0;
+}
+
+static const struct rtpse_mcu_transport_ops rtpse_mcu_uart_transport_ops = {
+ .send = rtpse_mcu_uart_send,
+ .recv = rtpse_mcu_uart_recv,
+};
+
+static int rtpse_mcu_uart_probe(struct serdev_device *serdev)
+{
+ u32 speed = RTPSE_MCU_UART_BAUD_DEFAULT;
+ struct device *dev = &serdev->dev;
+ struct rtpse_mcu_uart *ctx;
+ int ret;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->serdev = serdev;
+ ctx->pse.dev = dev;
+ ctx->pse.transport = &rtpse_mcu_uart_transport_ops;
+ init_completion(&ctx->rx_done);
+ spin_lock_init(&ctx->rx_lock);
+
+ serdev_device_set_drvdata(serdev, ctx);
+ serdev_device_set_client_ops(serdev, &rtpse_mcu_uart_serdev_ops);
+
+ ret = devm_serdev_device_open(dev, serdev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to open serdev\n");
+
+ fwnode_property_read_u32(dev_fwnode(dev), "current-speed", &speed);
+ serdev_device_set_baudrate(serdev, speed);
+ serdev_device_set_flow_control(serdev, false);
+ serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
+
+ return rtpse_mcu_register(&ctx->pse);
+}
+
+static const struct of_device_id rtpse_mcu_uart_of_match[] = {
+ { .compatible = "realtek,pse-mcu-rtk", .data = &rtpse_mcu_rtk_data },
+ { .compatible = "realtek,pse-mcu-brcm", .data = &rtpse_mcu_brcm_data },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rtpse_mcu_uart_of_match);
+
+static struct serdev_device_driver rtpse_mcu_uart_driver = {
+ .driver = {
+ .name = "realtek-pse-mcu-uart",
+ .of_match_table = rtpse_mcu_uart_of_match,
+ },
+ .probe = rtpse_mcu_uart_probe,
+};
+module_serdev_device_driver(rtpse_mcu_uart_driver);
+
+MODULE_AUTHOR("Jonas Jelonek <jelonek.jonas@gmail.com>");
+MODULE_DESCRIPTION("Realtek/Broadcom PSE MCU driver (UART transport)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/pse-pd/realtek-pse-mcu.h b/drivers/net/pse-pd/realtek-pse-mcu.h
new file mode 100644
index 000000000000..b9bf3b2dde08
--- /dev/null
+++ b/drivers/net/pse-pd/realtek-pse-mcu.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef _REALTEK_PSE_MCU_H
+#define _REALTEK_PSE_MCU_H
+
+#include <linux/mutex.h>
+#include <linux/pse-pd/pse.h>
+#include <linux/types.h>
+
+/*
+ * Time the MCU itself needs between accepting a request and having a
+ * response ready. These are properties of the MCU firmware, not of the
+ * underlying transport: the core paces transactions by RTPSE_MCU_RESPONSE_MS
+ * and both transports size their per-transaction recv ceiling from
+ * RTPSE_MCU_RESPONSE_MAX_MS, since some commands are documented as
+ * needing up to ~1s to produce a reply.
+ */
+#define RTPSE_MCU_RESPONSE_MS 25
+#define RTPSE_MCU_RESPONSE_MAX_MS 1000
+
+/*
+ * Total time to keep retrying the first MCU read at probe, and the pause
+ * between attempts. Right after enable-gpios is asserted the MCU may not
+ * answer on the bus yet; give it a bounded window to come up before
+ * declaring the probe failed.
+ */
+#define RTPSE_MCU_BOOT_TIMEOUT_MS 3000
+#define RTPSE_MCU_BOOT_RETRY_MS 100
+
+#define RTPSE_MCU_MSG_SIZE 12
+
+struct rtpse_mcu_msg {
+ u8 opcode;
+ u8 seq_num;
+ u8 payload[9];
+ u8 checksum;
+} __packed;
+
+/*
+ * MCU status opcodes (seen on the BCM dialect; RTL never emits them).
+ * INCOMPLETE/BAD_CSUM are terminal; NOT_READY is transient.
+ */
+#define RTPSE_MCU_OPCODE_INCOMPLETE 0xfd /* -EBADE */
+#define RTPSE_MCU_OPCODE_BAD_CSUM 0xfe /* -EBADMSG */
+#define RTPSE_MCU_OPCODE_NOT_READY 0xff /* -EAGAIN */
+
+/* A polling transport can stop here: the matching reply, or a terminal error. */
+static inline bool rtpse_mcu_resp_is_final(const struct rtpse_mcu_msg *req,
+ const struct rtpse_mcu_msg *resp)
+{
+ return resp->opcode == req->opcode ||
+ resp->opcode == RTPSE_MCU_OPCODE_INCOMPLETE ||
+ resp->opcode == RTPSE_MCU_OPCODE_BAD_CSUM;
+}
+
+/* Opaque to transports; defined in realtek-pse-core.c. */
+struct rtpse_mcu_dialect;
+struct rtpse_mcu_match_data;
+struct rtpse_mcu_chip_info;
+struct rtpse_mcu_ctrl;
+
+struct rtpse_mcu_transport_ops {
+ int (*send)(struct rtpse_mcu_ctrl *pse, const struct rtpse_mcu_msg *req);
+ int (*recv)(struct rtpse_mcu_ctrl *pse, const struct rtpse_mcu_msg *req,
+ struct rtpse_mcu_msg *resp);
+};
+
+struct rtpse_mcu_ctrl {
+ struct device *dev;
+ struct pse_controller_dev pcdev;
+ struct mutex mutex; /* serializes MCU request/response transactions */
+ const struct rtpse_mcu_dialect *dialect;
+ const struct rtpse_mcu_chip_info *chip;
+ const struct rtpse_mcu_transport_ops *transport;
+
+ struct regulator *poe_supply;
+};
+
+int rtpse_mcu_register(struct rtpse_mcu_ctrl *pse);
+
+/* Whether the I2C transport must read "realtek,i2c-protocol" from DT. */
+bool rtpse_mcu_needs_i2c_proto(const struct rtpse_mcu_match_data *match);
+
+extern const struct rtpse_mcu_match_data rtpse_mcu_rtk_data;
+extern const struct rtpse_mcu_match_data rtpse_mcu_brcm_data;
+
+#endif
--
2.51.0
^ permalink raw reply related
* [PATCH net-next v3 1/2] dt-bindings: net: pse-pd: add bindings for Realtek/Broadcom PSE MCU
From: Jonas Jelonek @ 2026-06-28 22:27 UTC (permalink / raw)
To: Oleksij Rempel, Kory Maincent, Andrew Lunn, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: netdev, devicetree, linux-kernel, Daniel Golle, Bjørn Mork,
Jonas Jelonek
In-Reply-To: <20260628222705.4052815-1-jelonek.jonas@gmail.com>
Add a binding for the microcontroller (MCU) that fronts the PSE silicon
on a range of managed switches. The host talks only to the MCU, over
I2C/SMBus or UART, using a fixed message-based protocol; the PSE chips
behind it never appear on the bus.
The compatible names the MCU front-end, not a specific part. These
boards front the PSE silicon with an MCU that presents a stable
message protocol Realtek documents. The PSE chip behind it varies
- Broadcom on older boards, Realtek on newer - and is detected at
runtime; the arrangement appears to be a Realtek MCU-based PoE design
carried across those PSE-chip generations. So the 'realtek' prefix
names that front-end (Realtek's protocol and firmware), not the
general-purpose MCU silicon or the PSE chip - the google,cros-ec-*
model. The '-rtk'/'-brcm' suffix selects the Realtek or Broadcom dialect.
A single compatible per dialect covers both the I2C/SMBus and UART
attachments: the wire protocol is identical across them and the transport
is expressed by the node's parent bus, so it is not encoded in the
compatible.
Both dialects share one protocol family and one device tree contract, so
they are documented in a single binding under the one 'realtek' prefix,
with the '-rtk'/'-brcm' suffix distinguishing the dialect.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
---
.../bindings/net/pse-pd/realtek,pse-mcu.yaml | 154 ++++++++++++++++++
1 file changed, 154 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/pse-pd/realtek,pse-mcu.yaml
diff --git a/Documentation/devicetree/bindings/net/pse-pd/realtek,pse-mcu.yaml b/Documentation/devicetree/bindings/net/pse-pd/realtek,pse-mcu.yaml
new file mode 100644
index 000000000000..3a414ef38922
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/pse-pd/realtek,pse-mcu.yaml
@@ -0,0 +1,154 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/pse-pd/realtek,pse-mcu.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek/Broadcom PSE MCU
+
+maintainers:
+ - Jonas Jelonek <jelonek.jonas@gmail.com>
+
+description: |
+ Microcontroller (MCU) that fronts the PSE hardware on switches using
+ Realtek (RTL8238B, RTL8239, RTL8239C) or Broadcom (BCM59111, BCM59121)
+ PSE chips. The MCU exposes a small message-based protocol over either
+ I2C/SMBus or UART; the actual PSE silicon is not accessed directly. The
+ Realtek and Broadcom variants share this device tree contract but use
+ different protocol opcodes, selected by the compatible.
+
+ The compatible identifies the PSE-MCU protocol dialect, not a specific
+ part. The device here is the MCU: it presents a stable message protocol
+ documented by Realtek, with the PSE silicon behind it - Broadcom on
+ older boards, Realtek on newer - detected at runtime and not described
+ here. The MCU's own silicon is general-purpose and varies across
+ boards, so the 'realtek' vendor prefix names the protocol front-end
+ (following the google,cros-ec pattern); the '-rtk'/'-brcm' suffix
+ selects the Realtek or Broadcom dialect.
+
+ A single compatible per dialect covers both the I2C/SMBus and UART
+ attachments: the wire protocol is identical across them and the
+ transport is already expressed by the node's parent bus, so it is not
+ encoded in the compatible. Transport-specific properties differ
+ accordingly - the I2C attachment carries 'reg' (and, for Realtek,
+ 'realtek,i2c-protocol'), while the UART attachment carries the serial
+ peripheral properties such as 'current-speed'.
+
+properties:
+ compatible:
+ enum:
+ - realtek,pse-mcu-rtk
+ - realtek,pse-mcu-brcm
+
+ reg:
+ maxItems: 1
+
+ power-supply:
+ description: Regulator supplying the PoE power rail.
+
+ enable-gpios:
+ maxItems: 1
+
+ realtek,i2c-protocol:
+ $ref: /schemas/types.yaml#/definitions/string
+ enum: [ i2c, smbus ]
+ description: |
+ Wire framing the MCU firmware expects on the I2C bus. "smbus" means
+ reads carry a leading command byte (0x00) and a repeated start; "i2c"
+ means bare 12-byte writes and reads with no command prefix. Only
+ applies to the Realtek I2C attachment.
+
+required:
+ - compatible
+
+allOf:
+ - $ref: pse-controller.yaml#
+ - $ref: /schemas/serial/serial-peripheral-props.yaml#
+ # The I2C attachment (identified by 'reg') cannot carry serial bus props.
+ - if:
+ required: [reg]
+ then:
+ properties:
+ current-speed: false
+ max-speed: false
+ # 'realtek,i2c-protocol' is meaningful only for the Realtek I2C attachment;
+ # the Broadcom variant and any UART attachment must not carry it.
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: realtek,pse-mcu-rtk
+ required: [reg]
+ then:
+ required:
+ - realtek,i2c-protocol
+ else:
+ properties:
+ "realtek,i2c-protocol": false
+
+unevaluatedProperties: false
+
+examples:
+ # Realtek PSE chip, I2C attachment (SMBus framing).
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethernet-pse@20 {
+ compatible = "realtek,pse-mcu-rtk";
+ reg = <0x20>;
+ realtek,i2c-protocol = "smbus";
+
+ pse-pis {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pse-pi@0 {
+ reg = <0>;
+ #pse-cells = <0>;
+ };
+ };
+ };
+ };
+
+ # Broadcom PSE chip, I2C attachment.
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethernet-pse@20 {
+ compatible = "realtek,pse-mcu-brcm";
+ reg = <0x20>;
+
+ pse-pis {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pse-pi@0 {
+ reg = <0>;
+ #pse-cells = <0>;
+ };
+ };
+ };
+ };
+
+ # Realtek PSE chip, UART attachment.
+ - |
+ serial {
+ ethernet-pse {
+ compatible = "realtek,pse-mcu-rtk";
+ current-speed = <115200>;
+
+ pse-pis {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pse-pi@0 {
+ reg = <0>;
+ #pse-cells = <0>;
+ };
+ };
+ };
+ };
--
2.51.0
^ permalink raw reply related
* [PATCH net-next v3 0/2] net: pse-pd: add Realtek/Broadcom PSE MCU support
From: Jonas Jelonek @ 2026-06-28 22:27 UTC (permalink / raw)
To: Oleksij Rempel, Kory Maincent, Andrew Lunn, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: netdev, devicetree, linux-kernel, Daniel Golle, Bjørn Mork,
Jonas Jelonek
This series adds a PSE-PD driver for the microcontroller (MCU) that
fronts the PSE silicon on a range of managed switches, together with its
DT binding.
Hardware model
==============
These boards do not expose the PSE chips to the host directly. A small
microcontroller sits on an I2C/SMBus or UART bus and manages one or more
PSE chips behind it; the host CPU only ever talks to that MCU, using a
fixed 12-byte request/response protocol with a trailing checksum. The
PSE silicon never appears on the bus.
The same protocol family is used by MCUs fronting Realtek PSE chips
(RTL8238B, RTL8239, RTL8239C) and Broadcom PSE chips (BCM59111,
BCM59121), diverging in opcode numbering and a few response layouts. The
driver abstracts that behind a per-dialect opcode table and parser hooks,
selected by the compatible. The specific PSE chip behind the MCU is
detected at runtime and only influences per-chip constants (power scaling
and the per-port cap).
Why the compatible names the protocol, not the chip
===================================================
The compatibles are "realtek,pse-mcu-rtk" and "realtek,pse-mcu-brcm".
This is a deliberate choice and the part most likely to raise questions,
so the reasoning up front.
The node names the protocol dialect, not a part:
- The DT node describes the MCU, not a PSE chip: the PSE chips are
behind the MCU and never appear on the bus, so naming the node after
one (e.g. "realtek,rtl8239") would describe hardware that isn't at
that address.
- The PSE chips are, in principle, usable without this MCU (host-driven
directly) - different hardware with a different programming model
that would warrant its own binding. Claiming the PSE-chip compatibles
here would collide with that.
- Naming the MCU silicon is equally wrong: these are ordinary
general-purpose microcontrollers (GigaDevice, Nuvoton, ...) that vary
across boards and are not dedicated to this application.
- What is fixed, and all the driver needs at DT-parse time, is the
protocol dialect, so the compatible encodes exactly that. The
"realtek" prefix names the owner of the protocol the MCU runs -
Realtek documents it and supplies the firmware - following the
"google,cros-ec-*" pattern (the prefix is the protocol/firmware owner,
not the varying controller silicon). The "-rtk"/"-brcm" suffix selects
the Realtek or Broadcom dialect; the specific PSE chip behind the MCU
is detected at runtime.
One compatible per dialect spans both transports:
- The 12-byte wire protocol is identical over I2C/SMBus and UART; only
the plumbing differs (SMBus vs native framing on I2C, baud rate on
UART), and the transport is already expressed structurally by the
node's parent bus (i2c@... vs serial@...). A "-i2c"/"-uart" suffix
would only duplicate that, for a protocol that does not change across
transports.
- This is the multi-transport model used by e.g. "bosch,bmi160" (one
compatible, separate i2c and spi drivers binding it), rather than the
cros-ec model of per-transport compatibles - cros-ec splits because
its on-wire framing genuinely differs per bus, which is not the case
here.
The binding documents both points as well.
Testing
=======
- Linksys LGS328MPCv2 (RTL8238B, I2C)
- Zyxel GS1900-10HP A1 (BCM59121, UART)
- Zyxel GS1900-10HP B1 (RTL8238B, UART)
- Zyxel GS1920-24HPv2 (BCM59121, SMBus)
- Zyxel XMG1915-10EP (RTL8239C, UART)
- Zyxel XS1930-12HP (RTL8239, SMBus)
---
v2 -> v3:
- dt-bindings: using brcm instead of bcm for Broadcom
- rename the driver files and Kconfig symbols to realtek-pse-mcu-* /
PSE_REALTEK_MCU* for consistency with the realtek,pse-mcu-* compatibles
- rename driver-internal prefix from 'rtpse_' to 'rtpse_mcu' to
emphasize this targets the MCU-centric setup (and leaves room open
for eventual directly addressable PSE chips)
- rework the vendor-prefix rationale (binding + commit message): the
prefix names the protocol/firmware owner (Realtek documents the protocol
and supplies the firmware), and -rtk/-brcm select the Realtek or Broadcom
protocol dialect
- core: reject zeroed/echo-mismatched responses via the echoed seq_num
(a BCM PORT_ENABLE on port 0 was otherwise accepted from an all-zero
frame)
- core: enable the PoE supply before global-enabling the MCU, and roll
back the global enable on probe failure or driver removal
- core: drop inline from helpers (flagged by automated check)
- uart: update the completion under rx_lock too, so a late frame can no
longer make the next transaction fail spuriously with -EIO
v2: https://lore.kernel.org/netdev/20260612132944.460646-1-jelonek.jonas@gmail.com/
v1 -> v2:
- all points flagged by Sashiko addressed:
- uart: drop frame overflow (return count, not the stored length) so
serdev retains no leftover bytes that would misalign the next response
- uart: guard rx_buf/rx_len with a spinlock to close a data race between
the async receive_buf callback and send/recv
- i2c: return terminal MCU error opcodes (0xfd/0xfe) to the core
immediately instead of polling to the 1 s timeout
- core: cap BCM59121 at 30 W (802.3at) — the basic 8-bit set command
can't program the advertised 60 W (it silently clamped to 51 W)
v1: https://lore.kernel.org/netdev/20260608205758.1830521-1-jelonek.jonas@gmail.com/
---
Jonas Jelonek (2):
dt-bindings: net: pse-pd: add bindings for Realtek/Broadcom PSE MCU
net: pse-pd: add Realtek/Broadcom PSE MCU driver
.../bindings/net/pse-pd/realtek,pse-mcu.yaml | 154 +++
MAINTAINERS | 7 +
drivers/net/pse-pd/Kconfig | 28 +
drivers/net/pse-pd/Makefile | 3 +
drivers/net/pse-pd/realtek-pse-mcu-core.c | 1017 +++++++++++++++++
drivers/net/pse-pd/realtek-pse-mcu-i2c.c | 162 +++
drivers/net/pse-pd/realtek-pse-mcu-uart.c | 155 +++
drivers/net/pse-pd/realtek-pse-mcu.h | 87 ++
8 files changed, 1613 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/pse-pd/realtek,pse-mcu.yaml
create mode 100644 drivers/net/pse-pd/realtek-pse-mcu-core.c
create mode 100644 drivers/net/pse-pd/realtek-pse-mcu-i2c.c
create mode 100644 drivers/net/pse-pd/realtek-pse-mcu-uart.c
create mode 100644 drivers/net/pse-pd/realtek-pse-mcu.h
base-commit: 805185b7c7a1069e407b6f7b3bc98e44d415f484
--
2.51.0
^ permalink raw reply
* Re: [PATCH bpf v2 0/4] bpf, sockmap: Fix sockmap leaking UDP socks
From: Michal Luczaj @ 2026-06-28 22:00 UTC (permalink / raw)
To: sun jian
Cc: Eric Dumazet, Kuniyuki Iwashima, Paolo Abeni, Willem de Bruijn,
John Fastabend, Jakub Sitnicki, Jiayuan Chen, David S. Miller,
Jakub Kicinski, Simon Horman, Alexei Starovoitov, Cong Wang,
Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman,
Kumar Kartikeya Dwivedi, Martin KaFai Lau, Song Liu,
Yonghong Song, Jiri Olsa, Emil Tsalapatis, Shuah Khan, netdev,
bpf, linux-kernel, linux-kselftest
In-Reply-To: <CABFUUZGg1B0vaw9DFX3uTHxpRyyGtvn04Ae=Ob-VmnuVJB4KLg@mail.gmail.com>
On 6/27/26 19:34, sun jian wrote:
> On Sat, Jun 27, 2026 at 4:37 AM Michal Luczaj <mhal@rbox.co> wrote:
>>
>> Fix for UDP sockets getting leaked during sockmap lookup/release.
>> Accompanied by selftests updates.
>> ...
>
> Hi Michal,
>
> I tested this series on bpf.git base commit:
> 26490a375cb9be9bac96b5171610fd85ca6c2305
> The test environment was virtme-ng x86_64.
Great, thank you.
> However, I can reproduce the same timeout in three consecutive
> runs on the unmodified base commit, so this looks pre-existing or
> environmental in my virtme-ng setup rather than a regression from
> this series.
Yup, same for me. Good thing bpf-next appears to have this fixed.
thanks,
Michal
^ permalink raw reply
* Re: [PATCH bpf v2 3/4] selftests/bpf: Adapt sockmap update error handling
From: Michal Luczaj @ 2026-06-28 21:59 UTC (permalink / raw)
To: Kuniyuki Iwashima
Cc: Eric Dumazet, Paolo Abeni, Willem de Bruijn, John Fastabend,
Jakub Sitnicki, Jiayuan Chen, David S. Miller, Jakub Kicinski,
Simon Horman, Alexei Starovoitov, Cong Wang, Daniel Borkmann,
Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis, Shuah Khan, netdev, bpf, linux-kernel,
linux-kselftest
In-Reply-To: <CAAVpQUDULzZh58oKkRaYqgM9bRToGb7wHrzT+cZNpRmAtM12Ow@mail.gmail.com>
On 6/26/26 22:58, Kuniyuki Iwashima wrote:
> On Fri, Jun 26, 2026 at 1:37 PM Michal Luczaj <mhal@rbox.co> wrote:
>>
>> Update sockmap_listen to accommodate the recent change in sockmap that
>> rejects unbound UDP sockets.
>>
>> TCP: Reject unbound and bound (unless established or listening).
>> UDP: Accept only bound sockets.
>>
>> Signed-off-by: Michal Luczaj <mhal@rbox.co>
>> ---
>> tools/testing/selftests/bpf/prog_tests/sockmap_listen.c | 17 +++++++++--------
>> 1 file changed, 9 insertions(+), 8 deletions(-)
>>
>> diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
>> index cc0c68bab907..6ee1bc6b3b23 100644
>> --- a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
>> +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
>> @@ -63,11 +63,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused,
>> errno = 0;
>> value = s;
>> err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST);
>> - if (sotype == SOCK_STREAM) {
>> - if (!err || errno != EOPNOTSUPP)
>> - FAIL_ERRNO("map_update: expected EOPNOTSUPP");
>> - } else if (err)
>> - FAIL_ERRNO("map_update: expected success");
>
> Initially I thought AF_UNIX still exercised this path but it was removed
> in f3de1cf621f7. The leftover in family_str() was a bit confusing, so please
> follow up on bpf-next.
Sure, will do.
thanks,
Michal
^ permalink raw reply
* Re: [PATCH bpf v2 2/4] selftests/bpf: Ensure UDP sockets are bound
From: Michal Luczaj @ 2026-06-28 21:58 UTC (permalink / raw)
To: Kuniyuki Iwashima
Cc: Eric Dumazet, Paolo Abeni, Willem de Bruijn, John Fastabend,
Jakub Sitnicki, Jiayuan Chen, David S. Miller, Jakub Kicinski,
Simon Horman, Alexei Starovoitov, Cong Wang, Daniel Borkmann,
Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis, Shuah Khan, netdev, bpf, linux-kernel,
linux-kselftest
In-Reply-To: <CAAVpQUCGpSWuei+cX9LL1WG=-14_FJU75G2njvvWCugsSMWDqQ@mail.gmail.com>
On 6/26/26 22:47, Kuniyuki Iwashima wrote:
> On Fri, Jun 26, 2026 at 1:37 PM Michal Luczaj <mhal@rbox.co> wrote:
>>
>> Update sockmap_basic tests to bind sockets before they are used. This
>> accommodates the recent change in sockmap that rejects unbound UDP sockets.
>>
>> Signed-off-by: Michal Luczaj <mhal@rbox.co>
>
> nit: this should be patch 1.
OK, I'll push it up.
thanks,
Michal
^ permalink raw reply
* [PATCH iproute2] man: fix troff warnings and whitespace
From: Stephen Hemminger @ 2026-06-28 21:58 UTC (permalink / raw)
To: netdev; +Cc: Stephen Hemminger
Fix troff warnings about line breaks around URL's.
Remove unnecessary whitespace at end of line.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
man/man8/ip-link.8.in | 5 ++++-
man/man8/tc-flower.8 | 2 --
man/man8/tc-fq_codel.8 | 2 +-
man/man8/tc-netem.8 | 4 ++++
man/man8/tc-red.8 | 6 +++++-
5 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in
index fc7f0248..38f5a13a 100644
--- a/man/man8/ip-link.8.in
+++ b/man/man8/ip-link.8.in
@@ -3071,7 +3071,10 @@ are supported:
locally terminated traffic for the given DSA switch user port. For a
description of which network interfaces are suitable for serving as conduit
interfaces of this user port, please see
-https://docs.kernel.org/networking/dsa/configuration.html#affinity-of-user-ports-to-cpu-ports
+.nf
+.UR https://docs.kernel.org/networking/dsa/configuration.html#affinity-of-user-ports-to-cpu-ports
+.UE
+.fi
as well as what is supported by the driver in use.
.sp
diff --git a/man/man8/tc-flower.8 b/man/man8/tc-flower.8
index adde2168..3f8ba465 100644
--- a/man/man8/tc-flower.8
+++ b/man/man8/tc-flower.8
@@ -418,8 +418,6 @@ bits is assumed.
.BI enc_tos " NUMBER"
.TQ
.BI enc_ttl " NUMBER"
-.TQ
-.BR
.TP
.BI ct_state " CT_STATE"
.TQ
diff --git a/man/man8/tc-fq_codel.8 b/man/man8/tc-fq_codel.8
index 78590636..6ed523c7 100644
--- a/man/man8/tc-fq_codel.8
+++ b/man/man8/tc-fq_codel.8
@@ -66,7 +66,7 @@ the local minimum queue delay that packets experience. Default value is 5ms.
has the same semantics as
.B codel
and is used to ensure that the measured minimum delay does not become too stale.
-The minimum delay must be experienced in the last epoch of length
+The minimum delay must be experienced in the last epoch of length
.BR interval .
It should be set on the order of the worst-case RTT through the bottleneck to
give endpoints sufficient time to react. Default value is 100ms.
diff --git a/man/man8/tc-netem.8 b/man/man8/tc-netem.8
index a4cc0d61..8d608c77 100644
--- a/man/man8/tc-netem.8
+++ b/man/man8/tc-netem.8
@@ -383,15 +383,19 @@ A filter classifies all packets going to 65.172.181.4 as being priority 3.
.IP " 1. " 4
Hemminger S. , "Network Emulation with NetEm", Open Source Development Lab,
April 2005
+.nf
.UR http://devresources.linux-foundation.org/shemminger/netem/LCA2005_paper.pdf
.UE
+.fi
.IP " 2. " 4
Salsano S., Ludovici F., Ordine A., "Definition of a general and intuitive loss
model for packet networks and its implementation in the Netem module in the
Linux kernel", available at
+.nf
.UR http://netgroup.uniroma2.it/NetemCLG
.UE
+.fi
.SH SEE ALSO
.BR tc (8)
diff --git a/man/man8/tc-red.8 b/man/man8/tc-red.8
index 662e4d8b..dd6bc6cf 100644
--- a/man/man8/tc-red.8
+++ b/man/man8/tc-red.8
@@ -132,7 +132,11 @@ With this parameter, traffic that should be marked, but is not ECN-capable, is
enqueued. Without the parameter it is early-dropped.
.TP
adaptive
-(Added in linux-3.3) Sets RED in adaptive mode as described in http://icir.org/floyd/papers/adaptiveRed.pdf
+(Added in linux-3.3) Sets RED in adaptive mode as described in
+.na
+.UR http://icir.org/floyd/papers/adaptiveRed.pdf
+.UE
+.ad
.nf
Goal of Adaptive RED is to make 'probability' dynamic value between 1% and 50% to reach the target average queue :
.B (max - min) / 2
--
2.53.0
^ permalink raw reply related
* Re: iproute2: trailing whitespace in man pages
From: Stephen Hemminger @ 2026-06-28 21:55 UTC (permalink / raw)
To: Bjarni Ingi Gislason; +Cc: netdev, Debian Bug Tracking System
In-Reply-To: <178243847243.6540.10252433987238870483.reportbug@kassi.invalid.is>
On Fri, 26 Jun 2026 01:54:52 +0000
Bjarni Ingi Gislason <bjarniig@simnet.is> wrote:
> Package: iproute2 (in Debian)
> Version: 7.1.0-1
> Severity: minor
>
> Additional remarks.
>
> Mails from me to "submit@bugs.debian.org" are no longer acknowledged. A
> Debian maintainer told me, that he would contact the mail administrator
> about me not wanting to send bugs upstream.
>
> -.-
>
> Dear Maintainer,
>
> * What led up to the situation?
>
> Checking for defects with a new version
>
> test-[g|n]roff -mandoc -t -K utf8 -rF0 -rHY=0 -rCHECKSTYLE=0 -ww -z < "man page"
>
> [Use
>
> grep -n -e ' $' -e '\\~$' -e ' \\f.$' -e ' \\"' <file>
>
> to find (most) trailing spaces.]
>
> ["test-groff" is a script in the repository for "groff"; is not shipped]
> (local copy and "troff" slightly changed by me).
>
> [The fate of "test-nroff" was decided in groff bug #55941.]
>
> * What was the outcome of this action?
>
> troff:/tmp/gz.roff.tw9zTq:10: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.tw9zTq:15: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.tw9zTq:20: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.tw9zTq:34: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.tw9zTq:36: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.tw9zTq:38: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.tw9zTq:40: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.tw9zTq:41: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.tw9zTq:42: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.tw9zTq:43: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.tw9zTq:44: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.tw9zTq:45: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.tw9zTq:46: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.tw9zTq:47: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.tw9zTq:48: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.tw9zTq:49: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.tw9zTq:50: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.tw9zTq:55: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.tw9zTq:56: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.tw9zTq:57: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/bridge.8.gz
>
> troff:/tmp/gz.roff.m7ah0F:12: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/dcb-app.8.gz
>
> troff:/tmp/gz.roff.lEZOzh:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.lEZOzh:32: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/dcb-apptrust.8.gz
>
> troff:/tmp/gz.roff.WIScEl:12: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/dcb-buffer.8.gz
>
> troff:/tmp/gz.roff.lQyzHW:11: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/dcb-dcbx.8.gz
>
> troff:/tmp/gz.roff.i604Vp:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.i604Vp:43: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/dcb-ets.8.gz
>
> troff:/tmp/gz.roff.21mI9Z:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.21mI9Z:37: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/dcb-maxrate.8.gz
>
> troff:/tmp/gz.roff.tNIhrB:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.tNIhrB:38: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/dcb-pfc.8.gz
>
> troff:/tmp/gz.roff.rSgJgv:12: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/dcb-rewr.8.gz
>
> troff:/tmp/gz.roff.Q8vb3N:11: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Q8vb3N:18: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Q8vb3N:24: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/dcb.8.gz
>
> troff:/tmp/gz.roff.IodjFN:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.IodjFN:17: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.IodjFN:32: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.IodjFN:34: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.IodjFN:36: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.IodjFN:54: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.IodjFN:123: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.IodjFN:133: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.IodjFN:149: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.IodjFN:175: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/devlink-dev.8.gz
>
> troff:/tmp/gz.roff.Nx3P7S:13: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Nx3P7S:18: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Nx3P7S:28: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Nx3P7S:32: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/devlink-dpipe.8.gz
>
> troff:/tmp/gz.roff.f7fvNK:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.f7fvNK:17: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.f7fvNK:24: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.f7fvNK:66: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.f7fvNK:68: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.f7fvNK:184: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.f7fvNK:188: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.f7fvNK:193: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.f7fvNK:197: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/devlink-health.8.gz
>
> troff:/tmp/gz.roff.hTtjSy:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.hTtjSy:17: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.hTtjSy:24: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.hTtjSy:25: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.hTtjSy:29: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.hTtjSy:50: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.hTtjSy:54: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.hTtjSy:55: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.hTtjSy:56: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.hTtjSy:68: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/devlink-lc.8.gz
>
> troff:/tmp/gz.roff.soeA6o:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.soeA6o:17: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.soeA6o:22: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.soeA6o:24: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.soeA6o:29: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.soeA6o:35: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.soeA6o:50: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.soeA6o:66: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.soeA6o:68: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.soeA6o:69: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.soeA6o:71: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.soeA6o:74: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.soeA6o:77: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.soeA6o:80: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.soeA6o:83: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.soeA6o:86: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.soeA6o:91: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.soeA6o:101: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.soeA6o:127: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.soeA6o:167: warning: treating total indentation -72u as zero [-w range]
> /usr/share/man/man8/devlink-port.8.gz
>
> troff:/tmp/gz.roff.kYqBeN:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.kYqBeN:17: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.kYqBeN:26: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/devlink-rate.8.gz
>
> troff:/tmp/gz.roff.9tHyhr:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.9tHyhr:17: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.9tHyhr:28: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.9tHyhr:48: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/devlink-region.8.gz
>
> troff:/tmp/gz.roff.9MYeZd:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.9MYeZd:17: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/devlink-resource.8.gz
>
> troff:/tmp/gz.roff.ZhPBEk:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZhPBEk:17: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZhPBEk:22: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZhPBEk:23: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZhPBEk:28: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZhPBEk:29: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZhPBEk:31: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZhPBEk:39: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZhPBEk:41: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZhPBEk:43: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZhPBEk:48: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZhPBEk:49: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZhPBEk:51: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZhPBEk:59: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZhPBEk:61: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZhPBEk:63: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZhPBEk:66: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZhPBEk:67: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZhPBEk:69: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZhPBEk:71: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/devlink-sb.8.gz
>
> troff:/tmp/gz.roff.cbJFkk:17: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.cbJFkk:79: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.cbJFkk:114: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.cbJFkk:139: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.cbJFkk:143: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/devlink-trap.8.gz
>
> troff:/tmp/gz.roff.QOaboD:10: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.QOaboD:16: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/devlink.8.gz
>
> troff:/tmp/gz.roff.m0E9ov:11: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.m0E9ov:19: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.m0E9ov:29: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/dpll.8.gz
>
> troff:/tmp/gz.roff.hhba1k:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.hhba1k:19: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.hhba1k:24: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.hhba1k:29: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.hhba1k:31: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/genl.8.gz
>
> troff:/tmp/gz.roff.UspeXf:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.UspeXf:17: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.UspeXf:27: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.UspeXf:29: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.UspeXf:31: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.UspeXf:33: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.UspeXf:39: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.UspeXf:41: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.UspeXf:43: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.UspeXf:45: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.UspeXf:47: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.UspeXf:49: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.UspeXf:51: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.UspeXf:63: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.UspeXf:65: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.UspeXf:67: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.UspeXf:69: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.UspeXf:71: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.UspeXf:76: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.UspeXf:77: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-address.8.gz
>
> troff:/tmp/gz.roff.ACnG5f:10: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ACnG5f:15: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ACnG5f:17: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ACnG5f:56: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-addrlabel.8.gz
>
> troff:/tmp/gz.roff.5eqgIA:14: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.5eqgIA:21: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.5eqgIA:27: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.5eqgIA:31: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.5eqgIA:35: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.5eqgIA:39: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.5eqgIA:48: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.5eqgIA:52: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.5eqgIA:56: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.5eqgIA:60: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-fou.8.gz
>
> troff:/tmp/gz.roff.muormS:10: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.muormS:21: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.muormS:25: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.muormS:49: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-ioam.8.gz
>
> troff:/tmp/gz.roff.YZqY4h:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.YZqY4h:18: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.YZqY4h:85: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.YZqY4h:232: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-l2tp.8.gz
>
> troff:/tmp/gz.roff.x9nPAk:10: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.x9nPAk:59: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.x9nPAk:67: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.x9nPAk:159: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.x9nPAk:164: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.x9nPAk:186: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.x9nPAk:195: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.x9nPAk:197: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.x9nPAk:223: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.x9nPAk:224: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.x9nPAk:226: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.x9nPAk:227: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.x9nPAk:228: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.x9nPAk:229: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.x9nPAk:230: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.x9nPAk:236: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.x9nPAk:237: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.x9nPAk:248: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.x9nPAk:249: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.x9nPAk:256: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-link.8.gz
>
> troff:/tmp/gz.roff.r9LukD:5: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.r9LukD:64: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.r9LukD:65: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.r9LukD:67: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.r9LukD:75: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.r9LukD:77: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.r9LukD:80: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.r9LukD:83: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.r9LukD:85: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-macsec.8.gz
>
> troff:/tmp/gz.roff.fk7Uej:12: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-maddress.8.gz
>
> troff:/tmp/gz.roff.OtuwHM:11: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.OtuwHM:17: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.OtuwHM:45: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.OtuwHM:51: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.OtuwHM:57: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.OtuwHM:58: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-monitor.8.gz
>
> troff:/tmp/gz.roff.DLgQwy:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DLgQwy:14: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DLgQwy:16: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DLgQwy:22: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DLgQwy:30: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DLgQwy:32: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DLgQwy:35: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DLgQwy:37: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DLgQwy:39: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DLgQwy:42: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DLgQwy:45: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DLgQwy:52: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DLgQwy:88: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DLgQwy:89: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DLgQwy:92: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-mptcp.8.gz
>
> troff:/tmp/gz.roff.Bf2muc:9: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Bf2muc:10: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Bf2muc:11: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Bf2muc:13: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Bf2muc:15: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Bf2muc:17: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-mroute.8.gz
>
> troff:/tmp/gz.roff.aIjjMP:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.aIjjMP:17: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.aIjjMP:18: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.aIjjMP:20: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.aIjjMP:24: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.aIjjMP:26: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.aIjjMP:27: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.aIjjMP:28: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.aIjjMP:29: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.aIjjMP:30: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.aIjjMP:35: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.aIjjMP:37: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.aIjjMP:39: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-neighbour.8.gz
>
> troff:/tmp/gz.roff.sjvvc4:9: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-netconf.8.gz
>
> troff:/tmp/gz.roff.bAu8qy:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.bAu8qy:46: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-netns.8.gz
>
> troff:/tmp/gz.roff.nM3cIw:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.nM3cIw:18: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.nM3cIw:19: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.nM3cIw:23: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.nM3cIw:28: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.nM3cIw:40: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.nM3cIw:42: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.nM3cIw:44: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.nM3cIw:46: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.nM3cIw:48: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.nM3cIw:49: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.nM3cIw:53: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.nM3cIw:59: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.nM3cIw:60: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.nM3cIw:62: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.nM3cIw:64: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.nM3cIw:65: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.nM3cIw:67: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.nM3cIw:68: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.nM3cIw:70: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-nexthop.8.gz
>
> troff:/tmp/gz.roff.Kn3w81:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Kn3w81:18: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Kn3w81:57: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Kn3w81:59: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-ntable.8.gz
>
> troff:/tmp/gz.roff.gv3LBB:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gv3LBB:18: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gv3LBB:19: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gv3LBB:33: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gv3LBB:36: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gv3LBB:38: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gv3LBB:40: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gv3LBB:42: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gv3LBB:44: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gv3LBB:46: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gv3LBB:56: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gv3LBB:60: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gv3LBB:62: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gv3LBB:64: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gv3LBB:66: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gv3LBB:68: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gv3LBB:70: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gv3LBB:72: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gv3LBB:74: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gv3LBB:84: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-route.8.gz
>
> troff:/tmp/gz.roff.6owsuu:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.6owsuu:31: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.6owsuu:34: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.6owsuu:36: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.6owsuu:38: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.6owsuu:40: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.6owsuu:42: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.6owsuu:44: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.6owsuu:46: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.6owsuu:48: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.6owsuu:49: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.6owsuu:51: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.6owsuu:53: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.6owsuu:54: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.6owsuu:55: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.6owsuu:56: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.6owsuu:57: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.6owsuu:58: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.6owsuu:59: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.6owsuu:61: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-rule.8.gz
>
> troff:/tmp/gz.roff.6yXkbF:10: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-sr.8.gz
>
> troff:/tmp/gz.roff.glO7gb:11: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.glO7gb:18: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.glO7gb:20: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.glO7gb:28: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-stats.8.gz
>
> troff:/tmp/gz.roff.XiAasB:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.XiAasB:25: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.XiAasB:26: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-tcp_metrics.8.gz
>
> troff:/tmp/gz.roff.XtavYp:10: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-token.8.gz
>
> troff:/tmp/gz.roff.TS3gWw:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.TS3gWw:18: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.TS3gWw:20: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.TS3gWw:25: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.TS3gWw:34: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.TS3gWw:39: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.TS3gWw:41: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.TS3gWw:59: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.TS3gWw:76: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.TS3gWw:81: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-tunnel.8.gz
>
> troff:/tmp/gz.roff.5llFkR:11: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.5llFkR:28: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-vrf.8.gz
>
> troff:/tmp/gz.roff.91zMPC:13: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.91zMPC:19: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.91zMPC:29: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.91zMPC:103: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.91zMPC:158: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.91zMPC:171: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.91zMPC:175: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.91zMPC:183: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.91zMPC:198: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.91zMPC:240: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.91zMPC:274: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.91zMPC:325: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.91zMPC:327: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.91zMPC:345: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.91zMPC:363: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.91zMPC:367: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.91zMPC:371: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.91zMPC:427: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.91zMPC:448: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.91zMPC:564: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip-xfrm.8.gz
>
> troff:/tmp/gz.roff.gY3yJ3:10: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gY3yJ3:16: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gY3yJ3:21: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gY3yJ3:31: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gY3yJ3:39: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gY3yJ3:169: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gY3yJ3:175: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ip.8.gz
>
> troff:/tmp/gz.roff.j0dcO5:10: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.j0dcO5:15: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.j0dcO5:111: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.j0dcO5:112: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.j0dcO5:203: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.j0dcO5:204: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/netshaper.8.gz
>
> troff:/tmp/gz.roff.EOKA9l:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.EOKA9l:17: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/rdma-dev.8.gz
>
> troff:/tmp/gz.roff.j9CeKZ:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.j9CeKZ:17: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/rdma-link.8.gz
>
> troff:/tmp/gz.roff.NBliDx:16: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/rdma-monitor.8.gz
>
> troff:/tmp/gz.roff.n7n6Ij:10: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.n7n6Ij:15: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.n7n6Ij:16: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.n7n6Ij:21: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/rdma-resource.8.gz
>
> troff:/tmp/gz.roff.zNQGc9:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.zNQGc9:40: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.zNQGc9:85: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.zNQGc9:89: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.zNQGc9:93: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.zNQGc9:97: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/rdma-statistic.8.gz
>
> troff:/tmp/gz.roff.w2n0Tg:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.w2n0Tg:17: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/rdma-system.8.gz
>
> troff:/tmp/gz.roff.G452XU:10: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.G452XU:16: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.G452XU:21: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.G452XU:26: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/rdma.8.gz
>
> troff:/tmp/gz.roff.8E9eVD:10: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/rtmon.8.gz
>
> troff:/tmp/gz.roff.cESP7P:447: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.cESP7P:607: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/ss.8.gz
>
> troff:/tmp/gz.roff.ux4fN1:9: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ux4fN1:11: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-basic.8.gz
>
> troff:/tmp/gz.roff.E5yQLK:9: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-cgroup.8.gz
>
> troff:/tmp/gz.roff.xDoFMo:10: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.xDoFMo:49: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-connmark.8.gz
>
> troff:/tmp/gz.roff.EPr6TY:15: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.EPr6TY:26: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-csum.8.gz
>
> troff:/tmp/gz.roff.ozliFK:7: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ozliFK:9: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ozliFK:11: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ozliFK:13: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ozliFK:18: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-ct.8.gz
>
> troff:/tmp/gz.roff.NlhG4f:34: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.NlhG4f:39: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.NlhG4f:51: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-dualpi2.8.gz
>
> troff:/tmp/gz.roff.AXC30K:14: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.AXC30K:20: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.AXC30K:22: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.AXC30K:33: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.AXC30K:73: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-ematch.8.gz
>
> troff:/tmp/gz.roff.Xqn4yW:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Xqn4yW:13: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Xqn4yW:21: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Xqn4yW:22: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Xqn4yW:24: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Xqn4yW:32: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Xqn4yW:34: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Xqn4yW:36: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Xqn4yW:38: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Xqn4yW:46: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Xqn4yW:47: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Xqn4yW:54: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Xqn4yW:55: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Xqn4yW:56: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.Xqn4yW:57: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-flow.8.gz
>
> troff:/tmp/gz.roff.SN8HX6:8: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.SN8HX6:9: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.SN8HX6:11: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.SN8HX6:13: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.SN8HX6:22: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.SN8HX6:24: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.SN8HX6:26: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.SN8HX6:28: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.SN8HX6:29: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.SN8HX6:30: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.SN8HX6:32: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.SN8HX6:34: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.SN8HX6:35: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.SN8HX6:36: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.SN8HX6:38: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.SN8HX6:40: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.SN8HX6:41: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.SN8HX6:42: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.SN8HX6:44: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.SN8HX6:45: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-flower.8.gz
>
> troff:/tmp/gz.roff.75BF9d:69: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-fq_codel.8.gz
>
> troff:/tmp/gz.roff.ATqeiC:9: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-fw.8.gz
>
> troff:/tmp/gz.roff.0gJmYD:11: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.0gJmYD:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.0gJmYD:21: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.0gJmYD:24: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-gact.8.gz
>
> troff:/tmp/gz.roff.1ht4Bu:9: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.1ht4Bu:11: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.1ht4Bu:13: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.1ht4Bu:15: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.1ht4Bu:16: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.1ht4Bu:17: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.1ht4Bu:19: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.1ht4Bu:22: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.1ht4Bu:26: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.1ht4Bu:32: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.1ht4Bu:36: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.1ht4Bu:59: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.1ht4Bu:67: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-ife.8.gz
>
> troff:/tmp/gz.roff.eEZB5X:8: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.eEZB5X:10: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.eEZB5X:12: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-matchall.8.gz
>
> troff:/tmp/gz.roff.BCTuKt:11: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.BCTuKt:15: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.BCTuKt:22: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.BCTuKt:26: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.BCTuKt:30: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-mirred.8.gz
>
> troff:/tmp/gz.roff.KuFNwq:8: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.KuFNwq:9: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.KuFNwq:10: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.KuFNwq:14: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.KuFNwq:19: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.KuFNwq:23: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.KuFNwq:25: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.KuFNwq:27: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.KuFNwq:31: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.KuFNwq:35: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.KuFNwq:37: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.KuFNwq:40: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.KuFNwq:57: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.KuFNwq:80: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.KuFNwq:81: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-mpls.8.gz
>
> troff:/tmp/gz.roff.9RiFlD:235: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.9RiFlD:237: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.9RiFlD:254: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.9RiFlD:274: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.9RiFlD:282: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.9RiFlD:284: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-mqprio.8.gz
>
> troff:/tmp/gz.roff.t0JWAM:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.t0JWAM:22: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.t0JWAM:23: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-nat.8.gz
>
> troff:/tmp/gz.roff.ZHcyYL:6: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZHcyYL:14: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZHcyYL:18: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZHcyYL:25: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZHcyYL:26: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZHcyYL:34: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZHcyYL:35: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZHcyYL:37: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZHcyYL:41: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZHcyYL:45: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZHcyYL:47: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZHcyYL:51: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZHcyYL:55: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZHcyYL:56: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZHcyYL:59: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZHcyYL:60: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZHcyYL:63: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZHcyYL:67: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZHcyYL:383: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZHcyYL:387: warning [page 1, line 269]: cannot adjust line in b adjust mode; overset by 4n [-w break]
> /usr/share/man/man8/tc-netem.8.gz
>
> troff:/tmp/gz.roff.DJ46bB:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DJ46bB:18: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DJ46bB:22: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DJ46bB:29: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DJ46bB:44: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DJ46bB:48: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DJ46bB:53: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DJ46bB:57: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DJ46bB:62: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DJ46bB:63: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DJ46bB:67: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DJ46bB:71: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DJ46bB:77: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DJ46bB:79: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.DJ46bB:80: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-pedit.8.gz
>
> troff:/tmp/gz.roff.HpJEdr:34: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-police.8.gz
>
> troff:/tmp/gz.roff.JYFwIE:9: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.JYFwIE:11: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.JYFwIE:13: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.JYFwIE:15: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-route.8.gz
>
> troff:/tmp/gz.roff.TQDw2w:14: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.TQDw2w:16: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.TQDw2w:19: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-sample.8.gz
>
> troff:/tmp/gz.roff.LzdwcT:69: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-skbedit.8.gz
>
> troff:/tmp/gz.roff.EeoYee:8: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.EeoYee:9: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.EeoYee:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.EeoYee:13: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.EeoYee:17: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.EeoYee:19: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.EeoYee:21: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.EeoYee:23: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.EeoYee:26: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-skbmod.8.gz
>
> troff:/tmp/gz.roff.LaXqMO:8: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.LaXqMO:12: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-tunnel_key.8.gz
>
> troff:/tmp/gz.roff.0SnZpw:9: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.0SnZpw:11: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.0SnZpw:13: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.0SnZpw:15: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.0SnZpw:17: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.0SnZpw:19: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.0SnZpw:21: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.0SnZpw:23: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.0SnZpw:25: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.0SnZpw:27: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.0SnZpw:29: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.0SnZpw:31: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.0SnZpw:32: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.0SnZpw:33: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.0SnZpw:37: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.0SnZpw:44: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.0SnZpw:46: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.0SnZpw:51: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.0SnZpw:52: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.0SnZpw:53: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-u32.8.gz
>
> troff:/tmp/gz.roff.ZLTTLY:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZLTTLY:16: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZLTTLY:20: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZLTTLY:24: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZLTTLY:28: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZLTTLY:30: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.ZLTTLY:33: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc-vlan.8.gz
>
> troff:/tmp/gz.roff.bkv7BO:128: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.bkv7BO:129: warning: ignoring escape character before ']' [-w escape]
> troff:/tmp/gz.roff.bkv7BO:717: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.bkv7BO:723: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tc.8.gz
>
> troff:/tmp/gz.roff.My0dwn:57: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tipc-bearer.8.gz
>
> troff:/tmp/gz.roff.bYd25b:18: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.bYd25b:27: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.bYd25b:28: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.bYd25b:30: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.bYd25b:32: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.bYd25b:42: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.bYd25b:53: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.bYd25b:62: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.bYd25b:66: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tipc-link.8.gz
>
> troff:/tmp/gz.roff.rs4kL6:17: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tipc-media.8.gz
>
> troff:/tmp/gz.roff.z8m3fR:15: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.z8m3fR:19: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.z8m3fR:23: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tipc-node.8.gz
>
> an.tmac:/tmp/gz.roff.9VgwLV:30: warning: cannot call .SS while an input trap is pending
> an.tmac:/tmp/gz.roff.9VgwLV:34: warning: cannot call .SS while an input trap is pending
> /usr/share/man/man8/tipc-socket.8.gz
>
> troff:/tmp/gz.roff.rBSHKg:14: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.rBSHKg:19: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/tipc.8.gz
>
> troff:/tmp/gz.roff.gPfIxK:11: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gPfIxK:12: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.gPfIxK:17: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/vdpa-dev.8.gz
>
> troff:/tmp/gz.roff.wHzGiy:11: warning: trailing whitespace [-w trail]
> troff:/tmp/gz.roff.wHzGiy:16: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/vdpa-mgmtdev.8.gz
>
> troff:/tmp/gz.roff.qSwXBM:10: warning: trailing whitespace [-w trail]
> /usr/share/man/man8/vdpa.8.gz
>
> * What outcome did you expect instead?
>
> No output (no warnings).
>
> -.-
>
> General remarks and further material, if a diff-file exist, are in the
> attachments.
>
>
> -- System Information:
> Debian Release: forky/sid
> APT prefers testing
> APT policy: (500, 'testing')
> Architecture: amd64 (x86_64)
>
> Kernel: Linux 7.0.12+deb14.1-amd64 (SMP w/2 CPU threads; PREEMPT)
> Locale: LANG=is_IS.iso88591, LC_CTYPE=is_IS.iso88591 (charmap=ISO-8859-1), LANGUAGE not set
> Shell: /bin/sh linked to /usr/bin/dash
> Init: sysvinit (via /sbin/init)
>
> Versions of packages iproute2 depends on:
> ii debconf [debconf-2.0] 1.5.92
> ii libbpf1 1:1.7.0-1
> ii libc6 2.42-17
> ii libcap2 1:2.78-1
> ii libcap2-bin 1:2.78-1
> ii libdb5.3t64 5.3.28+dfsg2-11+b1
> ii libelf1t64 0.195-1
> ii libmnl0 1.0.5-3+b2
> ii libselinux1 3.10-1
> ii libtirpc3t64 1.3.7+ds-1
> ii libxtables12 1.8.13-1
>
> iproute2 recommends no packages.
>
> Versions of packages iproute2 suggests:
> ii python3 3.13.9-3+b1
>
> -- debconf information excluded
I didn't go look at Debian packages in detail but this report looked bogus
so sent AI to investigate. It did motivate me to look and fix some whitespace
issues; but those weren't the ones in this report.
● Based on my analysis, here's what's happening with the whitespace report:
The Issue with the Report
The report appears to be overly sensitive or potentially faulty for several
reasons:
1. Testing installed packages, not source files: The report is testing the
installed Debian package man pages from /usr/share/man/, not the source files
in the git repository. The temporary files (/tmp/gz.roff.*) suggest they're
uncompressing and processing installed gzipped man pages.
2. Blank lines misinterpreted as trailing whitespace: Looking at the pattern
of warnings (often on lines like 10, 12, 15, 17, etc. - early in files),
these appear to be blank lines between sections in man pages, which are
normal and necessary for readability. Traditional troff/groff with strict
warnings enabled can flag these as "trailing whitespace" even though they're
just empty lines.
3. Already addressing real issues: Your recent commits show you've been
fixing actual whitespace problems:
- 66d16c41: Fixed whitespace in bridge code
- db4d810d: Removed trailing whitespace from CREDITS
- 328ba79f: Removed trailing whitespace from .mailmap
4. Source files appear clean: When I checked the actual source man pages in
your repository (like tc-fq_codel.8), I couldn't find actual trailing spaces.
The blank lines between sections are standard man page formatting.
5. Tool methodology questionable: The report mentions using a custom
"test-groff" script with modifications. The sheer number of warnings
(thousands across all files) and their consistent pattern (mostly early line
numbers) suggests the tool might be:
- Using overly strict warning settings
- Counting blank lines as trailing whitespace
- Processing files after some transformation that introduces artifacts
Conclusion
The methodology appears faulty because:
- It's testing packaged files, not source
- It's likely flagging normal blank lines as trailing whitespace
- The pattern of warnings (clustered on early lines) doesn't match typical
trailing whitespace issues
- Modern groff with normal settings doesn't reproduce these warnings on the
source files
The report is essentially noise - flagging standard man page formatting as
errors. Your source files appear to be properly formatted.
^ permalink raw reply
* [PATCH net v2] nfc: nci: fix use of uninitialized memory in NFC-DEP general bytes
From: Muhammad Bilal @ 2026-06-28 21:49 UTC (permalink / raw)
To: david
Cc: netdev, davem, edumazet, kuba, pabeni, horms, oe-linux-nfc,
linux-kernel, stable, Muhammad Bilal
nci_store_general_bytes_nfc_dep() derives the length of the NFC-DEP
general bytes by subtracting the fixed general-bytes offset from the ATR
length:
atr_res_len - NFC_ATR_RES_GT_OFFSET (poll, offset 15)
atr_req_len - NFC_ATR_REQ_GT_OFFSET (listen, offset 14)
It never checks that the ATR is at least that long. When a
RF_INTF_ACTIVATED_NTF reports an ATR shorter than the offset the
subtraction is negative; because min_t() casts its arguments to __u8 the
negative value becomes large and is then capped at
NFC_ATR_RES_GB_MAXSIZE / NFC_ATR_REQ_GB_MAXSIZE. remote_gb_len is thus
set to up to 47/48 even though only atr_res_len/atr_req_len bytes of the
on-stack atr_res/atr_req buffer were copied from the packet, and the
following memcpy() reads the uninitialized remainder into
ndev->remote_gb.
Zero remote_gb_len and skip storing the general bytes when the ATR is
shorter than the general-bytes offset, so that a stale remote_gb_len
from a previous activation does not survive into the new session.
Fixes: a99903ec4566 ("NFC: NCI: Handle Target mode activation")
Cc: stable@vger.kernel.org
Signed-off-by: Muhammad Bilal <meatuni001@gmail.com>
---
net/nfc/nci/ntf.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c
index 802928ca4d51e..b72545daa2051 100644
--- a/net/nfc/nci/ntf.c
+++ b/net/nfc/nci/ntf.c
@@ -654,8 +654,10 @@ static int nci_store_general_bytes_nfc_dep(struct nci_dev *ndev,
case NCI_NFC_A_PASSIVE_POLL_MODE:
case NCI_NFC_F_PASSIVE_POLL_MODE:
if (ntf->activation_params.poll_nfc_dep.atr_res_len <
- NFC_ATR_RES_GT_OFFSET)
+ NFC_ATR_RES_GT_OFFSET) {
+ ndev->remote_gb_len = 0;
break;
+ }
ndev->remote_gb_len = min_t(__u8,
(ntf->activation_params.poll_nfc_dep.atr_res_len
- NFC_ATR_RES_GT_OFFSET),
@@ -669,8 +671,10 @@ static int nci_store_general_bytes_nfc_dep(struct nci_dev *ndev,
case NCI_NFC_A_PASSIVE_LISTEN_MODE:
case NCI_NFC_F_PASSIVE_LISTEN_MODE:
if (ntf->activation_params.listen_nfc_dep.atr_req_len <
- NFC_ATR_REQ_GT_OFFSET)
+ NFC_ATR_REQ_GT_OFFSET) {
+ ndev->remote_gb_len = 0;
break;
+ }
ndev->remote_gb_len = min_t(__u8,
(ntf->activation_params.listen_nfc_dep.atr_req_len
- NFC_ATR_REQ_GT_OFFSET),
--
2.54.0
^ permalink raw reply related
* [PATCH net v2] nfc: nci: fix use of uninitialized memory in NFC-DEP general bytes
From: Muhammad Bilal @ 2026-06-28 21:45 UTC (permalink / raw)
To: david
Cc: netdev, davem, edumazet, kuba, pabeni, horms, oe-linux-nfc,
linux-kernel, stable, Muhammad Bilal
In-Reply-To: <20260628211627.131617-1-meatuni001@gmail.com>
nci_store_general_bytes_nfc_dep() derives the length of the NFC-DEP
general bytes by subtracting the fixed general-bytes offset from the ATR
length:
atr_res_len - NFC_ATR_RES_GT_OFFSET (poll, offset 15)
atr_req_len - NFC_ATR_REQ_GT_OFFSET (listen, offset 14)
It never checks that the ATR is at least that long. When a
RF_INTF_ACTIVATED_NTF reports an ATR shorter than the offset the
subtraction is negative; because min_t() casts its arguments to __u8 the
negative value becomes large and is then capped at
NFC_ATR_RES_GB_MAXSIZE / NFC_ATR_REQ_GB_MAXSIZE. remote_gb_len is thus
set to up to 47/48 even though only atr_res_len/atr_req_len bytes of the
on-stack atr_res/atr_req buffer were copied from the packet, and the
following memcpy() reads the uninitialized remainder into
ndev->remote_gb.
Zero remote_gb_len and skip storing the general bytes when the ATR is
shorter than the general-bytes offset, so that a stale remote_gb_len
from a previous activation does not survive into the new session.
Fixes: a99903ec4566 ("NFC: NCI: Handle Target mode activation")
Cc: stable@vger.kernel.org
Signed-off-by: Muhammad Bilal <meatuni001@gmail.com>
---
net/nfc/nci/ntf.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c
index 802928ca4d51e..b72545daa2051 100644
--- a/net/nfc/nci/ntf.c
+++ b/net/nfc/nci/ntf.c
@@ -654,8 +654,10 @@ static int nci_store_general_bytes_nfc_dep(struct nci_dev *ndev,
case NCI_NFC_A_PASSIVE_POLL_MODE:
case NCI_NFC_F_PASSIVE_POLL_MODE:
if (ntf->activation_params.poll_nfc_dep.atr_res_len <
- NFC_ATR_RES_GT_OFFSET)
+ NFC_ATR_RES_GT_OFFSET) {
+ ndev->remote_gb_len = 0;
break;
+ }
ndev->remote_gb_len = min_t(__u8,
(ntf->activation_params.poll_nfc_dep.atr_res_len
- NFC_ATR_RES_GT_OFFSET),
@@ -669,8 +671,10 @@ static int nci_store_general_bytes_nfc_dep(struct nci_dev *ndev,
case NCI_NFC_A_PASSIVE_LISTEN_MODE:
case NCI_NFC_F_PASSIVE_LISTEN_MODE:
if (ntf->activation_params.listen_nfc_dep.atr_req_len <
- NFC_ATR_REQ_GT_OFFSET)
+ NFC_ATR_REQ_GT_OFFSET) {
+ ndev->remote_gb_len = 0;
break;
+ }
ndev->remote_gb_len = min_t(__u8,
(ntf->activation_params.listen_nfc_dep.atr_req_len
- NFC_ATR_REQ_GT_OFFSET),
--
2.54.0
^ permalink raw reply related
* [PATCH net] nfc: nci: fix use of uninitialized memory in NFC-DEP general bytes
From: Muhammad Bilal @ 2026-06-28 21:16 UTC (permalink / raw)
To: David Heidelberg, netdev
Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, oe-linux-nfc, linux-kernel
nci_store_general_bytes_nfc_dep() derives the length of the NFC-DEP
general bytes by subtracting the fixed general-bytes offset from the ATR
length:
atr_res_len - NFC_ATR_RES_GT_OFFSET (poll, offset 15)
atr_req_len - NFC_ATR_REQ_GT_OFFSET (listen, offset 14)
It never checks that the ATR is at least that long. When a
RF_INTF_ACTIVATED_NTF reports an ATR shorter than the offset the
subtraction is negative; because min_t() casts its arguments to __u8 the
negative value becomes large and is then capped at
NFC_ATR_RES_GB_MAXSIZE / NFC_ATR_REQ_GB_MAXSIZE. remote_gb_len is thus
set to up to 47/48 even though only atr_res_len/atr_req_len bytes of the
on-stack atr_res/atr_req buffer were copied from the packet, and the
following memcpy() reads the uninitialized remainder into
ndev->remote_gb.
Skip storing the general bytes (leaving remote_gb_len at 0) when the ATR
is shorter than the general-bytes offset.
Fixes: a99903ec4566 ("NFC: NCI: Handle Target mode activation")
Cc: stable@vger.kernel.org
Signed-off-by: Muhammad Bilal <meatuni001@gmail.com>
---
net/nfc/nci/ntf.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c
index 63aa0a78472b1..802928ca4d51e 100644
--- a/net/nfc/nci/ntf.c
+++ b/net/nfc/nci/ntf.c
@@ -653,6 +653,9 @@ static int nci_store_general_bytes_nfc_dep(struct nci_dev *ndev,
switch (ntf->activation_rf_tech_and_mode) {
case NCI_NFC_A_PASSIVE_POLL_MODE:
case NCI_NFC_F_PASSIVE_POLL_MODE:
+ if (ntf->activation_params.poll_nfc_dep.atr_res_len <
+ NFC_ATR_RES_GT_OFFSET)
+ break;
ndev->remote_gb_len = min_t(__u8,
(ntf->activation_params.poll_nfc_dep.atr_res_len
- NFC_ATR_RES_GT_OFFSET),
@@ -665,6 +668,9 @@ static int nci_store_general_bytes_nfc_dep(struct nci_dev *ndev,
case NCI_NFC_A_PASSIVE_LISTEN_MODE:
case NCI_NFC_F_PASSIVE_LISTEN_MODE:
+ if (ntf->activation_params.listen_nfc_dep.atr_req_len <
+ NFC_ATR_REQ_GT_OFFSET)
+ break;
ndev->remote_gb_len = min_t(__u8,
(ntf->activation_params.listen_nfc_dep.atr_req_len
- NFC_ATR_REQ_GT_OFFSET),
--
2.54.0
^ permalink raw reply related
* Re: [PATCH] Wireguard: Fix data-race in rx/tx counter
From: Andrew Lunn @ 2026-06-28 21:02 UTC (permalink / raw)
To: Rafael Passos
Cc: Jason, andrew+netdev, davem, edumazet, kuba, linux-kernel, netdev,
pabeni, syzbot+9ca7674fa7521a3f1bc2, syzkaller-bugs, wireguard
In-Reply-To: <20260628203823.144789-1-rafael@rcpassos.me>
On Sun, Jun 28, 2026 at 05:38:23PM -0300, Rafael Passos wrote:
> fixes data-race in {rx/tx}_bytes counter for wireguard connection.
> these values were incremented inside a read_lock_bh block, but write
> protections were missing. making them atomic was the simplest way out.
> This was found by syzbot with kcsan.
>
> Reported-by: syzbot+9ca7674fa7521a3f1bc2@syzkaller.appspotmail.com
> Link: https://syzkaller.appspot.com/bug?extid=9ca7674fa7521a3f1bc2
> Signed-off-by: Rafael Passos <rafael@rcpassos.me>
> ---
>
> Hi,
>
> I am posting this patch to better ilustrate the discussion.
> If this is a non-issue, its fine.
> As I mentioned in the previous email, this issue was reported by syzbot,
> but I was not able to reproduce it.
> I am also aware atomic calls may introduce extra cost on older arm cpus.
Atomics are expensive in general, especially on high CPU count
systems.
Statistic counters tend to be very asymmetric in usage. They are
incremented frequently, maybe per packet, but reported very
infrequently, maybe every minute when an SNMP agent reads them. So the
solution to statistic counters should reflect this. Increment should
be very cheap, reporting them can be expensive.
There are a few different solutions. Per CPU counters is
one. u64_stats_sync.h may help.
Please take a look at other drivers doing statistics. This is a solved
problem, you just need to copy bits of code from somewhere else.
Andrew
^ permalink raw reply
* [PATCH net] nfc: nci: fix out-of-bounds read in activation parameter parsing
From: Muhammad Bilal @ 2026-06-28 21:00 UTC (permalink / raw)
To: David Heidelberg, netdev
Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, oe-linux-nfc, linux-kernel
nci_extract_activation_params_iso_dep() and
nci_extract_activation_params_nfc_dep() receive a pointer into the
RF_INTF_ACTIVATED_NTF notification but are not told how many bytes
remain. Each reads a one-byte length field (rats_res_len,
attrib_res_len, atr_res_len or atr_req_len) and then memcpy()s that many
bytes from the packet. The length is clamped to the destination size,
but it is never checked against the remaining activation-parameter data,
so a notification whose length field is larger than the data present
reads past the end of the buffer.
The sibling nci_extract_rf_params_*() helpers were recently given a
data_len argument and matching remaining-length checks, but the
activation-parameter helpers were not updated.
Pass the remaining length down and validate each field against it before
copying, as the rf_params helpers do.
Fixes: ac2068384034 ("NFC: Parse NCI NFC-DEP activation params")
Cc: stable@vger.kernel.org
Signed-off-by: Muhammad Bilal <meatuni001@gmail.com>
---
net/nfc/nci/ntf.c | 36 ++++++++++++++++++++++++++++++------
1 file changed, 30 insertions(+), 6 deletions(-)
diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c
index c96512bb86531..63aa0a78472b1 100644
--- a/net/nfc/nci/ntf.c
+++ b/net/nfc/nci/ntf.c
@@ -525,7 +525,7 @@ static int nci_rf_discover_ntf_packet(struct nci_dev *ndev,
static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev,
struct nci_rf_intf_activated_ntf *ntf,
- const __u8 *data)
+ const __u8 *data, ssize_t data_len)
{
struct activation_params_nfca_poll_iso_dep *nfca_poll;
struct activation_params_nfcb_poll_iso_dep *nfcb_poll;
@@ -533,9 +533,14 @@ static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev,
switch (ntf->activation_rf_tech_and_mode) {
case NCI_NFC_A_PASSIVE_POLL_MODE:
nfca_poll = &ntf->activation_params.nfca_poll_iso_dep;
+ if (data_len < 1)
+ return NCI_STATUS_RF_PROTOCOL_ERROR;
nfca_poll->rats_res_len = min_t(__u8, *data++, NFC_ATS_MAXSIZE);
+ data_len--;
pr_debug("rats_res_len %d\n", nfca_poll->rats_res_len);
if (nfca_poll->rats_res_len > 0) {
+ if (data_len < nfca_poll->rats_res_len)
+ return NCI_STATUS_RF_PROTOCOL_ERROR;
memcpy(nfca_poll->rats_res,
data, nfca_poll->rats_res_len);
}
@@ -543,9 +548,14 @@ static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev,
case NCI_NFC_B_PASSIVE_POLL_MODE:
nfcb_poll = &ntf->activation_params.nfcb_poll_iso_dep;
+ if (data_len < 1)
+ return NCI_STATUS_RF_PROTOCOL_ERROR;
nfcb_poll->attrib_res_len = min_t(__u8, *data++, 50);
+ data_len--;
pr_debug("attrib_res_len %d\n", nfcb_poll->attrib_res_len);
if (nfcb_poll->attrib_res_len > 0) {
+ if (data_len < nfcb_poll->attrib_res_len)
+ return NCI_STATUS_RF_PROTOCOL_ERROR;
memcpy(nfcb_poll->attrib_res,
data, nfcb_poll->attrib_res_len);
}
@@ -562,7 +572,7 @@ static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev,
static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev,
struct nci_rf_intf_activated_ntf *ntf,
- const __u8 *data)
+ const __u8 *data, ssize_t data_len)
{
struct activation_params_poll_nfc_dep *poll;
struct activation_params_listen_nfc_dep *listen;
@@ -571,21 +581,33 @@ static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev,
case NCI_NFC_A_PASSIVE_POLL_MODE:
case NCI_NFC_F_PASSIVE_POLL_MODE:
poll = &ntf->activation_params.poll_nfc_dep;
+ if (data_len < 1)
+ return NCI_STATUS_RF_PROTOCOL_ERROR;
poll->atr_res_len = min_t(__u8, *data++,
NFC_ATR_RES_MAXSIZE - 2);
+ data_len--;
pr_debug("atr_res_len %d\n", poll->atr_res_len);
- if (poll->atr_res_len > 0)
+ if (poll->atr_res_len > 0) {
+ if (data_len < poll->atr_res_len)
+ return NCI_STATUS_RF_PROTOCOL_ERROR;
memcpy(poll->atr_res, data, poll->atr_res_len);
+ }
break;
case NCI_NFC_A_PASSIVE_LISTEN_MODE:
case NCI_NFC_F_PASSIVE_LISTEN_MODE:
listen = &ntf->activation_params.listen_nfc_dep;
+ if (data_len < 1)
+ return NCI_STATUS_RF_PROTOCOL_ERROR;
listen->atr_req_len = min_t(__u8, *data++,
NFC_ATR_REQ_MAXSIZE - 2);
+ data_len--;
pr_debug("atr_req_len %d\n", listen->atr_req_len);
- if (listen->atr_req_len > 0)
+ if (listen->atr_req_len > 0) {
+ if (data_len < listen->atr_req_len)
+ return NCI_STATUS_RF_PROTOCOL_ERROR;
memcpy(listen->atr_req, data, listen->atr_req_len);
+ }
break;
default:
@@ -806,12 +828,14 @@ static int nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
switch (ntf.rf_interface) {
case NCI_RF_INTERFACE_ISO_DEP:
err = nci_extract_activation_params_iso_dep(ndev,
- &ntf, data);
+ &ntf, data,
+ ntf.activation_params_len);
break;
case NCI_RF_INTERFACE_NFC_DEP:
err = nci_extract_activation_params_nfc_dep(ndev,
- &ntf, data);
+ &ntf, data,
+ ntf.activation_params_len);
break;
case NCI_RF_INTERFACE_FRAME:
--
2.54.0
^ permalink raw reply related
* [PATCH] Wireguard: Fix data-race in rx/tx counter
From: Rafael Passos @ 2026-06-28 20:38 UTC (permalink / raw)
To: rafael
Cc: Jason, andrew+netdev, davem, edumazet, kuba, linux-kernel, netdev,
pabeni, syzbot+9ca7674fa7521a3f1bc2, syzkaller-bugs, wireguard
In-Reply-To: <DJFTVX3FE7OD.2O8GTO84798T@rcpassos.me>
fixes data-race in {rx/tx}_bytes counter for wireguard connection.
these values were incremented inside a read_lock_bh block, but write
protections were missing. making them atomic was the simplest way out.
This was found by syzbot with kcsan.
Reported-by: syzbot+9ca7674fa7521a3f1bc2@syzkaller.appspotmail.com
Link: https://syzkaller.appspot.com/bug?extid=9ca7674fa7521a3f1bc2
Signed-off-by: Rafael Passos <rafael@rcpassos.me>
---
Hi,
I am posting this patch to better ilustrate the discussion.
If this is a non-issue, its fine.
As I mentioned in the previous email, this issue was reported by syzbot,
but I was not able to reproduce it.
I am also aware atomic calls may introduce extra cost on older arm cpus.
I would like to hear from the community: would this an adequate solution ?
Thanks,
Rafael Passos
drivers/net/wireguard/netlink.c | 4 ++--
drivers/net/wireguard/peer.h | 2 +-
drivers/net/wireguard/receive.c | 2 +-
drivers/net/wireguard/socket.c | 2 +-
4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireguard/netlink.c b/drivers/net/wireguard/netlink.c
index 1da7e98d0d509..ec66f79e46377 100644
--- a/drivers/net/wireguard/netlink.c
+++ b/drivers/net/wireguard/netlink.c
@@ -109,9 +109,9 @@ get_peer(struct wg_peer *peer, struct sk_buff *skb, struct dump_ctx *ctx)
sizeof(last_handshake), &last_handshake) ||
nla_put_u16(skb, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL,
peer->persistent_keepalive_interval) ||
- nla_put_u64_64bit(skb, WGPEER_A_TX_BYTES, peer->tx_bytes,
+ nla_put_u64_64bit(skb, WGPEER_A_TX_BYTES, atomic64_read(&peer->tx_bytes),
WGPEER_A_UNSPEC) ||
- nla_put_u64_64bit(skb, WGPEER_A_RX_BYTES, peer->rx_bytes,
+ nla_put_u64_64bit(skb, WGPEER_A_RX_BYTES, atomic64_read(&peer->rx_bytes),
WGPEER_A_UNSPEC) ||
nla_put_u32(skb, WGPEER_A_PROTOCOL_VERSION, 1))
goto err;
diff --git a/drivers/net/wireguard/peer.h b/drivers/net/wireguard/peer.h
index 718fb42bdac7e..01c4b80086759 100644
--- a/drivers/net/wireguard/peer.h
+++ b/drivers/net/wireguard/peer.h
@@ -49,7 +49,7 @@ struct wg_peer {
struct work_struct transmit_handshake_work, clear_peer_work, transmit_packet_work;
struct cookie latest_cookie;
struct hlist_node pubkey_hash;
- u64 rx_bytes, tx_bytes;
+ atomic64_t rx_bytes, tx_bytes;
struct timer_list timer_retransmit_handshake, timer_send_keepalive;
struct timer_list timer_new_handshake, timer_zero_key_material;
struct timer_list timer_persistent_keepalive;
diff --git a/drivers/net/wireguard/receive.c b/drivers/net/wireguard/receive.c
index eb8851113654f..500d86576c692 100644
--- a/drivers/net/wireguard/receive.c
+++ b/drivers/net/wireguard/receive.c
@@ -20,7 +20,7 @@
static void update_rx_stats(struct wg_peer *peer, size_t len)
{
dev_sw_netstats_rx_add(peer->device->dev, len);
- peer->rx_bytes += len;
+ atomic64_add(len, &peer->rx_bytes);
}
#define SKB_TYPE_LE32(skb) (((struct message_header *)(skb)->data)->type)
diff --git a/drivers/net/wireguard/socket.c b/drivers/net/wireguard/socket.c
index 0028ef17dc716..9e8a49b9078f2 100644
--- a/drivers/net/wireguard/socket.c
+++ b/drivers/net/wireguard/socket.c
@@ -179,7 +179,7 @@ int wg_socket_send_skb_to_peer(struct wg_peer *peer, struct sk_buff *skb, u8 ds)
else
dev_kfree_skb(skb);
if (likely(!ret))
- peer->tx_bytes += skb_len;
+ atomic64_add(skb_len, &peer->tx_bytes);
read_unlock_bh(&peer->endpoint_lock);
return ret;
--
2.53.0
^ permalink raw reply related
* Re: [PATCH net-next v3] net: usb: rtl8150: handle link status read failures
From: Andrew Lunn @ 2026-06-28 20:17 UTC (permalink / raw)
To: Yousef Alhouseen
Cc: Petko Manolov, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, linux-usb, netdev, linux-kernel,
syzbot+9db6c624635564ad813c
In-Reply-To: <20260628165033.17842-1-alhouseenyousef@gmail.com>
On Sun, Jun 28, 2026 at 06:50:33PM +0200, Yousef Alhouseen wrote:
> set_carrier() ignores the result of the USB control transfer and tests
> the stack variable supplied as its receive buffer. If the device rejects
> or aborts the request, that variable remains uninitialized and the driver
> chooses an arbitrary carrier state.
>
> Leave the existing carrier state unchanged when the link status cannot be
> read. A transient USB error should not be treated as link loss.
>
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
If it is for net-next it does not need a Fixes tag.
Please also note that:
https://www.kernel.org/doc/html/latest/process/maintainer-netdev.html
say:
* don’t repost your patches within one 24h period
It is a good idea the read that whole document.
Andrew
^ permalink raw reply
* Re: [PATCH] net: sysfs: cleanup coding style
From: Andrew Lunn @ 2026-06-28 20:14 UTC (permalink / raw)
To: Lucas Poupeau
Cc: davem, edumazet, kuba, pabeni, horms, kuniyu, sdf, brauner,
krikku, netdev, linux-kernel
In-Reply-To: <20260628185824.231250-1-lucasp.linux@gmail.com>
On Sun, Jun 28, 2026 at 08:58:24PM +0200, Lucas Poupeau wrote:
> Replace DEVICE_ATTR() with DEVICE_ATTR_RW() where applicable to
> follow kernel coding style conventions. Add blank lines between
> function definitions and macro invocations. Fold multi-line
> attribute initializations onto single lines where it improves
> readability without exceeding the line length limit.
When you have a list like this, it means you should have a patch
series, one patch per item on the list.
Please also take a read of
https://www.kernel.org/doc/html/latest/process/maintainer-netdev.html
Andrew
---
pw-bot: cr
^ permalink raw reply
* [PATCH] net: sysfs: cleanup coding style
From: Lucas Poupeau @ 2026-06-28 18:58 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni
Cc: horms, kuniyu, sdf, brauner, krikku, netdev, linux-kernel,
Lucas Poupeau
Replace DEVICE_ATTR() with DEVICE_ATTR_RW() where applicable to
follow kernel coding style conventions. Add blank lines between
function definitions and macro invocations. Fold multi-line
attribute initializations onto single lines where it improves
readability without exceeding the line length limit.
Signed-off-by: Lucas Poupeau <lucasp.linux@gmail.com>
---
net/core/net-sysfs.c | 43 ++++++++++++++++++++-----------------------
1 file changed, 20 insertions(+), 23 deletions(-)
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index 0e71c9ed41e8..14efe81d006b 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -494,6 +494,7 @@ static ssize_t mtu_store(struct device *dev, struct device_attribute *attr,
{
return netdev_store(dev, attr, buf, len, change_mtu);
}
+
NETDEVICE_SHOW_RW(mtu, fmt_dec);
static int change_flags(struct net_device *dev, unsigned long new_flags)
@@ -506,6 +507,7 @@ static ssize_t flags_store(struct device *dev, struct device_attribute *attr,
{
return netdev_store(dev, attr, buf, len, change_flags);
}
+
NETDEVICE_SHOW_RW(flags, fmt_hex);
static ssize_t tx_queue_len_store(struct device *dev,
@@ -517,6 +519,7 @@ static ssize_t tx_queue_len_store(struct device *dev,
return netdev_store(dev, attr, buf, len, dev_change_tx_queue_len);
}
+
NETDEVICE_SHOW_RW(tx_queue_len, fmt_dec);
static int change_gro_flush_timeout(struct net_device *dev, unsigned long val)
@@ -534,6 +537,7 @@ static ssize_t gro_flush_timeout_store(struct device *dev,
return netdev_lock_store(dev, attr, buf, len, change_gro_flush_timeout);
}
+
NETDEVICE_SHOW_RW(gro_flush_timeout, fmt_ulong);
static int change_napi_defer_hard_irqs(struct net_device *dev, unsigned long val)
@@ -555,6 +559,7 @@ static ssize_t napi_defer_hard_irqs_store(struct device *dev,
return netdev_lock_store(dev, attr, buf, len,
change_napi_defer_hard_irqs);
}
+
NETDEVICE_SHOW_RW(napi_defer_hard_irqs, fmt_uint);
static ssize_t ifalias_store(struct device *dev, struct device_attribute *attr,
@@ -612,8 +617,9 @@ static ssize_t group_store(struct device *dev, struct device_attribute *attr,
{
return netdev_store(dev, attr, buf, len, change_group);
}
+
NETDEVICE_SHOW(group, fmt_dec);
-static DEVICE_ATTR(netdev_group, 0644, group_show, group_store);
+static DEVICE_ATTR_RW(netdev_group);
static int change_proto_down(struct net_device *dev, unsigned long proto_down)
{
@@ -626,6 +632,7 @@ static ssize_t proto_down_store(struct device *dev,
{
return netdev_store(dev, attr, buf, len, change_proto_down);
}
+
NETDEVICE_SHOW_RW(proto_down, fmt_dec);
static ssize_t phys_port_id_show(struct device *dev,
@@ -1125,8 +1132,7 @@ static struct rx_queue_attribute rps_cpus_attribute __ro_after_init
= __ATTR(rps_cpus, 0644, show_rps_map, store_rps_map);
static struct rx_queue_attribute rps_dev_flow_table_cnt_attribute __ro_after_init
- = __ATTR(rps_flow_cnt, 0644,
- show_rps_dev_flow_table_cnt, store_rps_dev_flow_table_cnt);
+ = __ATTR(rps_flow_cnt, 0644, show_rps_dev_flow_table_cnt, store_rps_dev_flow_table_cnt);
#endif /* CONFIG_RPS */
static struct attribute *rx_queue_default_attrs[] __ro_after_init = {
@@ -1279,8 +1285,7 @@ static int rx_queue_change_owner(struct net_device *dev, int index, kuid_t kuid,
return error;
if (dev->sysfs_rx_queue_group)
- error = sysfs_group_change_owner(
- kobj, dev->sysfs_rx_queue_group, kuid, kgid);
+ error = sysfs_group_change_owner(kobj, dev->sysfs_rx_queue_group, kuid, kgid);
return error;
}
@@ -1358,6 +1363,7 @@ struct netdev_queue_attribute {
struct netdev_queue *queue, const char *buf,
size_t len);
};
+
#define to_netdev_queue_attr(_attr) \
container_of(_attr, struct netdev_queue_attribute, attr)
@@ -1366,8 +1372,7 @@ struct netdev_queue_attribute {
static ssize_t netdev_queue_attr_show(struct kobject *kobj,
struct attribute *attr, char *buf)
{
- const struct netdev_queue_attribute *attribute
- = to_netdev_queue_attr(attr);
+ const struct netdev_queue_attribute *attribute = to_netdev_queue_attr(attr);
struct netdev_queue *queue = to_netdev_queue(kobj);
if (!attribute->show)
@@ -1380,8 +1385,7 @@ static ssize_t netdev_queue_attr_store(struct kobject *kobj,
struct attribute *attr,
const char *buf, size_t count)
{
- const struct netdev_queue_attribute *attribute
- = to_netdev_queue_attr(attr);
+ const struct netdev_queue_attribute *attribute = to_netdev_queue_attr(attr);
struct netdev_queue *queue = to_netdev_queue(kobj);
if (!attribute->store)
@@ -1499,15 +1503,12 @@ static ssize_t tx_maxrate_store(struct kobject *kobj, struct attribute *attr,
return err;
}
-static struct netdev_queue_attribute queue_tx_maxrate __ro_after_init
- = __ATTR_RW(tx_maxrate);
+static struct netdev_queue_attribute queue_tx_maxrate __ro_after_init = __ATTR_RW(tx_maxrate);
#endif
-static struct netdev_queue_attribute queue_trans_timeout __ro_after_init
- = __ATTR_RO(tx_timeout);
+static struct netdev_queue_attribute queue_trans_timeout __ro_after_init = __ATTR_RO(tx_timeout);
-static struct netdev_queue_attribute queue_traffic_class __ro_after_init
- = __ATTR_RO(traffic_class);
+static struct netdev_queue_attribute queue_traffic_class __ro_after_init = __ATTR_RO(traffic_class);
#ifdef CONFIG_BQL
/*
@@ -1565,8 +1566,7 @@ static ssize_t bql_set_hold_time(struct kobject *kobj, struct attribute *attr,
}
static struct netdev_queue_attribute bql_hold_time_attribute __ro_after_init
- = __ATTR(hold_time, 0644,
- bql_show_hold_time, bql_set_hold_time);
+ = __ATTR(hold_time, 0644, bql_show_hold_time, bql_set_hold_time);
static ssize_t bql_show_stall_thrs(struct kobject *kobj, struct attribute *attr,
struct netdev_queue *queue, char *buf)
@@ -1660,8 +1660,7 @@ static ssize_t bql_set_ ## NAME(struct kobject *kobj, \
} \
\
static struct netdev_queue_attribute bql_ ## NAME ## _attribute __ro_after_init \
- = __ATTR(NAME, 0644, \
- bql_show_ ## NAME, bql_set_ ## NAME)
+ = __ATTR(NAME, 0644, bql_show_ ## NAME, bql_set_ ## NAME)
BQL_ATTR(limit, limit);
BQL_ATTR(limit_max, max_limit);
@@ -1816,8 +1815,7 @@ static ssize_t xps_cpus_store(struct kobject *kobj, struct attribute *attr,
return err ? : len;
}
-static struct netdev_queue_attribute xps_cpus_attribute __ro_after_init
- = __ATTR_RW(xps_cpus);
+static struct netdev_queue_attribute xps_cpus_attribute __ro_after_init = __ATTR_RW(xps_cpus);
static ssize_t xps_rxqs_show(struct kobject *kobj, struct attribute *attr,
struct netdev_queue *queue, char *buf)
@@ -1886,8 +1884,7 @@ static ssize_t xps_rxqs_store(struct kobject *kobj, struct attribute *attr,
return err ? : len;
}
-static struct netdev_queue_attribute xps_rxqs_attribute __ro_after_init
- = __ATTR_RW(xps_rxqs);
+static struct netdev_queue_attribute xps_rxqs_attribute __ro_after_init = __ATTR_RW(xps_rxqs);
#endif /* CONFIG_XPS */
static struct attribute *netdev_queue_default_attrs[] __ro_after_init = {
--
2.53.0
^ permalink raw reply related
* Re: [PATCH net-next v4] vsock/virtio: rewrite MSG_ZEROCOPY flag handling
From: Michael S. Tsirkin @ 2026-06-28 18:35 UTC (permalink / raw)
To: Arseniy Krasnov
Cc: Stefan Hajnoczi, Stefano Garzarella, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Jason Wang,
Bobby Eshleman, Xuan Zhuo, Eugenio Pérez, Simon Horman, kvm,
virtualization, netdev, linux-kernel, oxffffaa, rulkc
In-Reply-To: <20260628182052.951760-1-avkrasnov@rulkc.org>
On Sun, Jun 28, 2026 at 09:20:52PM +0300, Arseniy Krasnov wrote:
> Logically it was based on TCP implementation, so to make further support
> easier, rewrite it in the TCP way (like in 'tcp_sendmsg_locked()'). By
> this way, patch also adds handling case when 'msg_ubuf' is already set.
>
> Signed-off-by: Arseniy Krasnov <avkrasnov@rulkc.org>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
> ---
> Changelog v1->v2:
> * Rebase on last 'net-next'. Don't need 'skb_zcopy_set()' now - it was
> already added.
> Changelog v2->v3:
> * Update commit message.
> * Remove one empty line.
> Changelog v3->v4:
> * Update commit message.
>
> net/vmw_vsock/virtio_transport_common.c | 47 ++++++++++++-------------
> 1 file changed, 22 insertions(+), 25 deletions(-)
>
> diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
> index 09475007165b..41c2a0b82a8e 100644
> --- a/net/vmw_vsock/virtio_transport_common.c
> +++ b/net/vmw_vsock/virtio_transport_common.c
> @@ -328,38 +328,35 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk,
> if (pkt_len == 0 && info->op == VIRTIO_VSOCK_OP_RW)
> return pkt_len;
>
> - if (info->msg) {
> - /* If zerocopy is not enabled by 'setsockopt()', we behave as
> - * there is no MSG_ZEROCOPY flag set.
> + if (info->msg && (info->msg->msg_flags & MSG_ZEROCOPY)) {
> + /* If 'info->msg' is not NULL, this is only VIRTIO_VSOCK_OP_RW.
> + * 'MSG_ZEROCOPY' flag handling here is based on the same flag
> + * handling from 'tcp_sendmsg_locked()'.
> */
> - if (!sock_flag(sk_vsock(vsk), SOCK_ZEROCOPY))
> - info->msg->msg_flags &= ~MSG_ZEROCOPY;
> + if (info->msg->msg_ubuf) {
> + uarg = info->msg->msg_ubuf;
> + can_zcopy = virtio_transport_can_zcopy(t_ops, info, pkt_len);
> + } else if (sock_flag(sk_vsock(vsk), SOCK_ZEROCOPY)) {
> + uarg = msg_zerocopy_realloc(sk_vsock(vsk), pkt_len,
> + NULL, false);
> + if (!uarg) {
> + virtio_transport_put_credit(vvs, pkt_len);
> + return -ENOMEM;
> + }
>
> - if (info->msg->msg_flags & MSG_ZEROCOPY)
> can_zcopy = virtio_transport_can_zcopy(t_ops, info, pkt_len);
> + if (!can_zcopy)
> + uarg_to_msgzc(uarg)->zerocopy = 0;
>
> + have_uref = true;
> + }
> +
> + /* 'can_zcopy' means that this transmission will be
> + * in zerocopy way (e.g. using 'frags' array).
> + */
> if (can_zcopy)
> max_skb_len = min_t(u32, VIRTIO_VSOCK_MAX_PKT_BUF_SIZE,
> (MAX_SKB_FRAGS * PAGE_SIZE));
> -
> - if (info->msg->msg_flags & MSG_ZEROCOPY &&
> - info->op == VIRTIO_VSOCK_OP_RW) {
> - uarg = info->msg->msg_ubuf;
> -
> - if (!uarg) {
> - uarg = msg_zerocopy_realloc(sk_vsock(vsk),
> - pkt_len, NULL, false);
> - if (!uarg) {
> - virtio_transport_put_credit(vvs, pkt_len);
> - return -ENOMEM;
> - }
> -
> - if (!can_zcopy)
> - uarg_to_msgzc(uarg)->zerocopy = 0;
> -
> - have_uref = true;
> - }
> - }
> }
>
> rest_len = pkt_len;
> --
> 2.25.1
^ permalink raw reply
* [PATCH net-next v4] vsock/virtio: rewrite MSG_ZEROCOPY flag handling
From: Arseniy Krasnov @ 2026-06-28 18:20 UTC (permalink / raw)
To: Stefan Hajnoczi, Stefano Garzarella, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Michael S. Tsirkin,
Jason Wang, Bobby Eshleman, Xuan Zhuo, Eugenio Pérez,
Simon Horman
Cc: kvm, virtualization, netdev, linux-kernel, oxffffaa, rulkc,
Arseniy Krasnov
Logically it was based on TCP implementation, so to make further support
easier, rewrite it in the TCP way (like in 'tcp_sendmsg_locked()'). By
this way, patch also adds handling case when 'msg_ubuf' is already set.
Signed-off-by: Arseniy Krasnov <avkrasnov@rulkc.org>
---
Changelog v1->v2:
* Rebase on last 'net-next'. Don't need 'skb_zcopy_set()' now - it was
already added.
Changelog v2->v3:
* Update commit message.
* Remove one empty line.
Changelog v3->v4:
* Update commit message.
net/vmw_vsock/virtio_transport_common.c | 47 ++++++++++++-------------
1 file changed, 22 insertions(+), 25 deletions(-)
diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
index 09475007165b..41c2a0b82a8e 100644
--- a/net/vmw_vsock/virtio_transport_common.c
+++ b/net/vmw_vsock/virtio_transport_common.c
@@ -328,38 +328,35 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk,
if (pkt_len == 0 && info->op == VIRTIO_VSOCK_OP_RW)
return pkt_len;
- if (info->msg) {
- /* If zerocopy is not enabled by 'setsockopt()', we behave as
- * there is no MSG_ZEROCOPY flag set.
+ if (info->msg && (info->msg->msg_flags & MSG_ZEROCOPY)) {
+ /* If 'info->msg' is not NULL, this is only VIRTIO_VSOCK_OP_RW.
+ * 'MSG_ZEROCOPY' flag handling here is based on the same flag
+ * handling from 'tcp_sendmsg_locked()'.
*/
- if (!sock_flag(sk_vsock(vsk), SOCK_ZEROCOPY))
- info->msg->msg_flags &= ~MSG_ZEROCOPY;
+ if (info->msg->msg_ubuf) {
+ uarg = info->msg->msg_ubuf;
+ can_zcopy = virtio_transport_can_zcopy(t_ops, info, pkt_len);
+ } else if (sock_flag(sk_vsock(vsk), SOCK_ZEROCOPY)) {
+ uarg = msg_zerocopy_realloc(sk_vsock(vsk), pkt_len,
+ NULL, false);
+ if (!uarg) {
+ virtio_transport_put_credit(vvs, pkt_len);
+ return -ENOMEM;
+ }
- if (info->msg->msg_flags & MSG_ZEROCOPY)
can_zcopy = virtio_transport_can_zcopy(t_ops, info, pkt_len);
+ if (!can_zcopy)
+ uarg_to_msgzc(uarg)->zerocopy = 0;
+ have_uref = true;
+ }
+
+ /* 'can_zcopy' means that this transmission will be
+ * in zerocopy way (e.g. using 'frags' array).
+ */
if (can_zcopy)
max_skb_len = min_t(u32, VIRTIO_VSOCK_MAX_PKT_BUF_SIZE,
(MAX_SKB_FRAGS * PAGE_SIZE));
-
- if (info->msg->msg_flags & MSG_ZEROCOPY &&
- info->op == VIRTIO_VSOCK_OP_RW) {
- uarg = info->msg->msg_ubuf;
-
- if (!uarg) {
- uarg = msg_zerocopy_realloc(sk_vsock(vsk),
- pkt_len, NULL, false);
- if (!uarg) {
- virtio_transport_put_credit(vvs, pkt_len);
- return -ENOMEM;
- }
-
- if (!can_zcopy)
- uarg_to_msgzc(uarg)->zerocopy = 0;
-
- have_uref = true;
- }
- }
}
rest_len = pkt_len;
--
2.25.1
^ permalink raw reply related
* Re: Question: bridge: clarify MST VLAN list RCU traversal contract
From: Ido Schimmel @ 2026-06-28 17:49 UTC (permalink / raw)
To: Runyu Xiao
Cc: Nikolay Aleksandrov, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, bridge, netdev,
linux-kernel, jianhao.xu
In-Reply-To: <20260627132539.3701630-1-runyu.xiao@seu.edu.cn>
On Sat, Jun 27, 2026 at 09:25:39PM +0800, Runyu Xiao wrote:
> Hi bridge maintainers,
>
> This question comes from a candidate found by our static analysis tool
> and then manually reviewed against the current tree. The audit used
> CONFIG_PROVE_RCU_LIST as target-matched triage evidence; I am asking
> for maintainer guidance because the source-level review did not prove
> a use-after-free.
>
> A CONFIG_PROVE_RCU_LIST audit flags the VLAN-list traversal in
> br_mst_info_size():
>
> net/bridge/br_mst.c:251 br_mst_info_size()
>
> The helper walks vg->vlan_list with list_for_each_entry_rcu(). In the
> direct local context, br_get_link_af_size_filtered() first enters an
> RCU read-side section, resolves the bridge port or bridge VLAN group,
> and calls br_get_num_vlan_infos(vg, filter_mask). That local RCU
> read-side section is then dropped before the later MST sizing call:
>
> net/bridge/br_netlink.c:104 rcu_read_lock()
> net/bridge/br_netlink.c:113 br_get_num_vlan_infos(vg, filter_mask)
> net/bridge/br_netlink.c:114 rcu_read_unlock()
> net/bridge/br_netlink.c:123 br_mst_info_size(vg)
>
> The helper is registered through rtnl_af_ops.get_link_af_size, and
> bridge VLAN updates appear RTNL-centered, so the broader rtnetlink
> sizing path may already provide the intended serialization. I am not
> claiming a use-after-free here. The question is only whether the
> RCU-list traversal contract around br_mst_info_size() should be made
> explicit enough for CONFIG_PROVE_RCU_LIST to see it.
>
> Would you prefer one of these directions?
>
> 1. keep the MST sizing loop inside an explicit rcu_read_lock() in
> br_get_link_af_size_filtered();
>
> 2. pass a confirmed RTNL lockdep condition to the iterator in
> br_mst_info_size();
>
> 3. document that the outer rtnetlink sizing path is the required
> protection and leave the helper unchanged;
>
> 4. use a different bridge-specific pattern.
>
> I am intentionally sending this as a maintainer question rather than a
> patch because the right contract seems to depend on the bridge/rtnetlink
> caller semantics.
I don't think anything needs to change. AFAICT, br_mst_info_size() is
only reachable via the get_link_af_size() callback and
rtnl_link_get_af_size() always invokes it from an RCU read-side critical
section.
Did you see a splat with CONFIG_PROVE_RCU_LIST?
^ permalink raw reply
* Re: [PATCH iproute-next v3] ipaddress: add support for showing IPv4 devconf attributes
From: David Ahern @ 2026-06-28 17:30 UTC (permalink / raw)
To: Fernando Fernandez Mancera, netdev
Cc: stephen, davem, edumazet, kuba, pabeni, horms
In-Reply-To: <20260614182515.8765-1-fmancera@suse.de>
On 6/14/26 12:25 PM, Fernando Fernandez Mancera wrote:
> This patch introduces support for showing IPv4 devconf attributes on
> detailed output of an interface e.g "ip -d link show dev enp1s0".
>
> Additionally, this refactors 'print_af_spec()' to sequentially process
> both AF_INET and AF_INET6 attributes rather than returning early if
> AF_INET6 is missing.
refactors should be a separate patch.
>
> Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
> ---
> v2: changed print_string to print_bool for boolean attributes
> v3: use print_bool for JSON output only
> ---
> ip/ipaddress.c | 313 ++++++++++++++++++++++++++++++++++++++++++-------
> 1 file changed, 273 insertions(+), 40 deletions(-)
>
> diff --git a/ip/ipaddress.c b/ip/ipaddress.c
> index 6017bc83..1530b836 100644
> --- a/ip/ipaddress.c
> +++ b/ip/ipaddress.c
> @@ -23,6 +23,7 @@
> #include <linux/netdevice.h>
> #include <linux/if_arp.h>
> #include <linux/if_infiniband.h>
> +#include <linux/ip.h>
> #include <linux/sockios.h>
> #include <linux/net_namespace.h>
>
> @@ -294,53 +295,285 @@ static void print_linktype(FILE *fp, struct rtattr *tb)
> close_json_object();
> }
>
> +static void print_inet(FILE *fp, struct rtattr *inet_attr)
> +{
> + struct rtattr *tb[IFLA_INET_MAX + 1];
> +
> + parse_rtattr_nested(tb, IFLA_INET_MAX, inet_attr);
> +
> + if (tb[IFLA_INET_CONF]) {
> + int *conf = RTA_DATA(tb[IFLA_INET_CONF]);
> + int max_elements = RTA_PAYLOAD(tb[IFLA_INET_CONF]) / sizeof(int);
> +
> + if (max_elements >= IPV4_DEVCONF_FORWARDING) {
> + print_bool(PRINT_JSON, "forwarding", NULL,
> + conf[IPV4_DEVCONF_FORWARDING - 1]);
> + print_string(PRINT_FP, "forwarding", "forwarding %s ",
> + conf[IPV4_DEVCONF_FORWARDING - 1] ? "on" : "off");
> + }
> +
> + if (max_elements >= IPV4_DEVCONF_MC_FORWARDING) {
> + print_bool(PRINT_JSON, "mc_forwarding", NULL,
> + conf[IPV4_DEVCONF_MC_FORWARDING - 1]);
> + print_string(PRINT_FP, "mc_forwarding", "mc_forwarding %s ",
> + conf[IPV4_DEVCONF_MC_FORWARDING - 1] ? "on" : "off");
> + }
> +
> + if (max_elements >= IPV4_DEVCONF_PROXY_ARP) {
> + print_bool(PRINT_JSON, "proxy_arp", NULL,
> + conf[IPV4_DEVCONF_PROXY_ARP - 1]);
> + print_string(PRINT_FP, "proxy_arp", "proxy_arp %s ",
> + conf[IPV4_DEVCONF_PROXY_ARP - 1] ? "on" : "off");
> + }
> +
> + if (max_elements >= IPV4_DEVCONF_ACCEPT_REDIRECTS) {
> + print_bool(PRINT_JSON, "accept_redirects", NULL,
> + conf[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1]);
> + print_string(PRINT_FP, "accept_redirects",
> + "accept_redirects %s ",
> + conf[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] ? "on" : "off");
As I stated in the last patch for devconf:
"iproute2 follows netdev with coding standards and those need to be
followed as long as humans are in the loop. Please make sure follow on
patches adhere to roughly 80 columns with a little extra if it improves
readability (and of course strings are not broken across lines)."
Use print_on_off for example for these or use a temp variable for the
attributes.
^ permalink raw reply
* Re: [PATCH iproute2-next v3] rdma: display resource limits in curr/max format
From: David Ahern @ 2026-06-28 17:22 UTC (permalink / raw)
To: Tao Cui, leonro; +Cc: linux-rdma, netdev, Tao Cui
In-Reply-To: <20260615005315.169582-1-cui.tao@linux.dev>
On 6/14/26 6:53 PM, Tao Cui wrote:
> diff --git a/rdma/include/uapi/rdma/rdma_netlink.h b/rdma/include/uapi/rdma/rdma_netlink.h
> index 4356ec4a..e5b8b065 100644
> --- a/rdma/include/uapi/rdma/rdma_netlink.h
> +++ b/rdma/include/uapi/rdma/rdma_netlink.h
> @@ -604,6 +604,11 @@ enum rdma_nldev_attr {
> RDMA_NLDEV_ATTR_FRMR_POOL_PINNED_HANDLES, /* u32 */
> RDMA_NLDEV_ATTR_FRMR_POOL_KEY_KERNEL_VENDOR_KEY, /* u64 */
>
> + /*
> + * Resource summary entry maximum value.
> + */
> + RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_MAX, /* u64 */
I do not see this uapi in Linus' tree. What is the status of the kernel
commit? Put a reference to the kernel patches in the commit message.
^ 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