* [PATCH net-next 02/12] net: mctp: usb: Use packet-length max for maximum packet-size check
From: Jeremy Kerr @ 2026-06-30 3:21 UTC (permalink / raw)
To: Matt Johnston, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Greg Kroah-Hartman
Cc: netdev, linux-usb
In-Reply-To: <20260630-dev-mctp-usb-1-1-v1-0-86a311fc67b7@codeconstruct.com.au>
The max packet size is smaller than the max transfer size, as we only
have a u8 length field in the transport header.
Add a define for the maximum representable length, and use that for our
check. Use this for the MTU maximum calculation too.
Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
---
drivers/net/mctp/mctp-usb.c | 2 +-
include/linux/usb/mctp-usb.h | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/net/mctp/mctp-usb.c b/drivers/net/mctp/mctp-usb.c
index 545eff06322c..c6e36b63e87a 100644
--- a/drivers/net/mctp/mctp-usb.c
+++ b/drivers/net/mctp/mctp-usb.c
@@ -76,7 +76,7 @@ static netdev_tx_t mctp_usb_start_xmit(struct sk_buff *skb,
plen = skb->len;
- if (plen + sizeof(*hdr) > MCTP_USB_1_0_XFER_SIZE)
+ if (plen + sizeof(*hdr) > MCTP_USB_1_0_PKTLEN_MAX)
goto err_drop;
rc = skb_cow_head(skb, sizeof(*hdr));
diff --git a/include/linux/usb/mctp-usb.h b/include/linux/usb/mctp-usb.h
index 47e2e3931d63..2bece8afd1c7 100644
--- a/include/linux/usb/mctp-usb.h
+++ b/include/linux/usb/mctp-usb.h
@@ -25,7 +25,8 @@ struct mctp_usb_hdr {
#define MCTP_USB_1_0_XFER_SIZE 512
#define MCTP_USB_BTU 68
#define MCTP_USB_MTU_MIN MCTP_USB_BTU
-#define MCTP_USB_1_0_MTU_MAX (U8_MAX - sizeof(struct mctp_usb_hdr))
+#define MCTP_USB_1_0_PKTLEN_MAX U8_MAX
+#define MCTP_USB_1_0_MTU_MAX (MCTP_USB_1_0_PKTLEN_MAX - sizeof(struct mctp_usb_hdr))
#define MCTP_USB_DMTF_ID 0x1ab4
#endif /* __LINUX_USB_MCTP_USB_H */
--
2.47.3
^ permalink raw reply related
* [PATCH net-next 01/12] net: mctp: usb: Include version indicator in max packet size defines
From: Jeremy Kerr @ 2026-06-30 3:21 UTC (permalink / raw)
To: Matt Johnston, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Greg Kroah-Hartman
Cc: netdev, linux-usb
In-Reply-To: <20260630-dev-mctp-usb-1-1-v1-0-86a311fc67b7@codeconstruct.com.au>
DSP0283 v1.1.0 will introduce larger maximum packet sizes. In
preparation, indicate that the current maxima are specific to v1.0.x.
Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
---
drivers/net/mctp/mctp-usb.c | 8 ++++----
include/linux/usb/mctp-usb.h | 5 +++--
2 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/drivers/net/mctp/mctp-usb.c b/drivers/net/mctp/mctp-usb.c
index fade65f2f269..545eff06322c 100644
--- a/drivers/net/mctp/mctp-usb.c
+++ b/drivers/net/mctp/mctp-usb.c
@@ -76,7 +76,7 @@ static netdev_tx_t mctp_usb_start_xmit(struct sk_buff *skb,
plen = skb->len;
- if (plen + sizeof(*hdr) > MCTP_USB_XFER_SIZE)
+ if (plen + sizeof(*hdr) > MCTP_USB_1_0_XFER_SIZE)
goto err_drop;
rc = skb_cow_head(skb, sizeof(*hdr));
@@ -128,7 +128,7 @@ static int mctp_usb_rx_queue(struct mctp_usb *mctp_usb, gfp_t gfp)
struct sk_buff *skb;
int rc;
- skb = __netdev_alloc_skb(mctp_usb->netdev, MCTP_USB_XFER_SIZE, gfp);
+ skb = __netdev_alloc_skb(mctp_usb->netdev, MCTP_USB_1_0_XFER_SIZE, gfp);
if (!skb) {
rc = -ENOMEM;
goto err_retry;
@@ -136,7 +136,7 @@ static int mctp_usb_rx_queue(struct mctp_usb *mctp_usb, gfp_t gfp)
usb_fill_bulk_urb(mctp_usb->rx_urb, mctp_usb->usbdev,
usb_rcvbulkpipe(mctp_usb->usbdev, mctp_usb->ep_in),
- skb->data, MCTP_USB_XFER_SIZE,
+ skb->data, MCTP_USB_1_0_XFER_SIZE,
mctp_usb_in_complete, skb);
rc = usb_submit_urb(mctp_usb->rx_urb, gfp);
@@ -301,7 +301,7 @@ static void mctp_usb_netdev_setup(struct net_device *dev)
dev->mtu = MCTP_USB_MTU_MIN;
dev->min_mtu = MCTP_USB_MTU_MIN;
- dev->max_mtu = MCTP_USB_MTU_MAX;
+ dev->max_mtu = MCTP_USB_1_0_MTU_MAX;
dev->hard_header_len = sizeof(struct mctp_usb_hdr);
dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN;
diff --git a/include/linux/usb/mctp-usb.h b/include/linux/usb/mctp-usb.h
index a2f6f1e04efb..47e2e3931d63 100644
--- a/include/linux/usb/mctp-usb.h
+++ b/include/linux/usb/mctp-usb.h
@@ -21,10 +21,11 @@ struct mctp_usb_hdr {
u8 len;
} __packed;
-#define MCTP_USB_XFER_SIZE 512
+/* max transfer size for DSP0283 v1.0 */
+#define MCTP_USB_1_0_XFER_SIZE 512
#define MCTP_USB_BTU 68
#define MCTP_USB_MTU_MIN MCTP_USB_BTU
-#define MCTP_USB_MTU_MAX (U8_MAX - sizeof(struct mctp_usb_hdr))
+#define MCTP_USB_1_0_MTU_MAX (U8_MAX - sizeof(struct mctp_usb_hdr))
#define MCTP_USB_DMTF_ID 0x1ab4
#endif /* __LINUX_USB_MCTP_USB_H */
--
2.47.3
^ permalink raw reply related
* [PATCH net-next 00/12] net: mctp: usb: Add support for MCTP-over-USB v1.1
From: Jeremy Kerr @ 2026-06-30 3:21 UTC (permalink / raw)
To: Matt Johnston, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Greg Kroah-Hartman
Cc: netdev, linux-usb
Version 1.1.0 of DSP0283 (MCTP over USB transport binding) has been
released, this patch series updates our current v1.0.1 support for the
changes in v1.1.x.
The major change in v1.1 is the introduction of "packet spanning" mode,
where a single MCTP packet may be split over multiple USB packets
(themselves forming a single USB bulk transfer). This relaxes the
requirement for USB high-speed mode, as we can now send MCTP packets
contained over multiple 64-byte full-speed USB bulk transfers, and gives
us an increase in the maximum MCTP packet size - we now have 13 bits of
packet length (previously 8) in the transport header.
Handling packet spanning introduces some complexity in the transmit and
receive paths, as we lose some constraints on where packet boundaries
may correspond to USB transfer boundaries, and may need to retain state
across separate transfers. To contain this complexity, we introduce a
new library for the transfer packing- and unpacking implementations,
"mctp-usblib". The host driver is a consumer of this library, and a
future gadget driver can use the same implementations. We can now also
implement tests on the API boundary of the library.
The series implements an incremental shift to mctp-usblib, then
implements packet spanning mode in the new library. We have a few
changes to prepare for this, in altering a few constants and
behaviours as v1.0-specific. Once packet spanning is implemented in
mctp-usblib, we enable it in the host-side driver.
Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
---
Jeremy Kerr (12):
net: mctp: usb: Include version indicator in max packet size defines
net: mctp: usb: Use packet-length max for maximum packet-size check
net: mctp: usblib: Move RX transfer processing to a new mctp-usblib
net: mctp: usblib: Move TX transfer processing to mctp-usblib
net: mctp: usb: Allow for multiple urb submissions from a packet tx
net: mctp: usblib: Add support for multi-packet transmit
net: mctp: usb: Accommodate DSP0283 v1.1 header format
net: mctp: usblib: Implement receive-side packet spanning
net: mctp: usblib: Implement transmit-side packet spanning
net: mctp: usblib: Add initial kunit tests
net: mctp: usb: enable v1.1 packet spanning
net: mctp: usb: Allow multiple urbs in flight
drivers/net/mctp/Kconfig | 16 ++
drivers/net/mctp/Makefile | 1 +
drivers/net/mctp/mctp-usb.c | 273 ++++++++----------
drivers/net/mctp/mctp-usblib-test.c | 330 +++++++++++++++++++++
drivers/net/mctp/mctp-usblib.c | 554 ++++++++++++++++++++++++++++++++++++
include/linux/usb/mctp-usb.h | 90 +++++-
6 files changed, 1112 insertions(+), 152 deletions(-)
---
base-commit: b85966adbf5de0668a815c6e3527f87e0c387fb4
change-id: 20260604-dev-mctp-usb-1-1-6fd854ad13e8
Best regards,
--
Jeremy Kerr <jk@codeconstruct.com.au>
^ permalink raw reply
* [PATCH net-next 04/12] net: mctp: usblib: Move TX transfer processing to mctp-usblib
From: Jeremy Kerr @ 2026-06-30 3:21 UTC (permalink / raw)
To: Matt Johnston, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Greg Kroah-Hartman
Cc: netdev, linux-usb
In-Reply-To: <20260630-dev-mctp-usb-1-1-v1-0-86a311fc67b7@codeconstruct.com.au>
With the RX processing in mctp-usblib, add TX processing alongside.
To accommodate packed transfers in DSP0283, where a transfer may contain
multiple MCTP packets, we move to a split process for the transmit API:
* push: create a new transmit context, and add a skb to it.
* send: callback to the driver implementation to send the (possibly
multi-packet) USB transfer
* complete: update skb accounting and release the tx context
The actual multi-packet transfer implementation will be added in the
next change; no tx context persists beyond the single send at present.
Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
---
drivers/net/mctp/mctp-usb.c | 125 ++++++++++++++--------------
drivers/net/mctp/mctp-usblib.c | 180 +++++++++++++++++++++++++++++++++++++++++
include/linux/usb/mctp-usb.h | 38 +++++++++
3 files changed, 280 insertions(+), 63 deletions(-)
diff --git a/drivers/net/mctp/mctp-usb.c b/drivers/net/mctp/mctp-usb.c
index 531b7c994afb..385350792dd4 100644
--- a/drivers/net/mctp/mctp-usb.c
+++ b/drivers/net/mctp/mctp-usb.c
@@ -29,90 +29,82 @@ struct mctp_usb {
u8 ep_out;
struct mctp_usblib_rx rx;
-
- struct urb *tx_urb;
struct urb *rx_urb;
/* enforces atomic access to rx_stopped and requeuing the retry work */
spinlock_t rx_lock;
bool rx_stopped;
struct delayed_work rx_retry_work;
+
+ struct mctp_usblib_tx tx;
+ /* protects tx_urb */
+ spinlock_t tx_lock;
+ struct urb *tx_urb;
};
static void mctp_usb_out_complete(struct urb *urb)
{
- struct sk_buff *skb = urb->context;
- struct net_device *netdev = skb->dev;
- int status;
+ struct mctp_usblib_tx_ctx *tx_ctx = urb->context;
+ struct mctp_usb *mctp_usb = mctp_usblib_tx_ctx_priv(tx_ctx);
+ struct net_device *netdev = mctp_usb->netdev;
+ unsigned long flags;
- status = urb->status;
+ mctp_usblib_tx_send_complete(tx_ctx, netdev, urb->status == 0);
- switch (status) {
- case -ENOENT:
- case -ECONNRESET:
- case -ESHUTDOWN:
- case -EPROTO:
- dev_dstats_tx_dropped(netdev);
- break;
- case 0:
- dev_dstats_tx_add(netdev, skb->len);
- netif_wake_queue(netdev);
- consume_skb(skb);
- return;
- default:
- netdev_dbg(netdev, "unexpected tx urb status: %d\n", status);
- dev_dstats_tx_dropped(netdev);
- }
+ spin_lock_irqsave(&mctp_usb->tx_lock, flags);
+ mctp_usb->tx_urb = NULL;
+ spin_unlock_irqrestore(&mctp_usb->tx_lock, flags);
- kfree_skb(skb);
+ usb_free_urb(urb);
+
+ netif_wake_queue(netdev);
}
-static netdev_tx_t mctp_usb_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
+static int mctp_usb_tx_send(struct mctp_usblib_tx_ctx *tx_ctx,
+ void *data, size_t len)
{
- struct mctp_usb *mctp_usb = netdev_priv(dev);
- struct mctp_usb_hdr *hdr;
- unsigned int plen;
+ struct mctp_usb *mctp_usb = mctp_usblib_tx_ctx_priv(tx_ctx);
+ unsigned long flags;
struct urb *urb;
int rc;
- plen = skb->len;
-
- if (plen + sizeof(*hdr) > MCTP_USB_1_0_PKTLEN_MAX)
- goto err_drop;
-
- rc = skb_cow_head(skb, sizeof(*hdr));
- if (rc)
- goto err_drop;
-
- hdr = skb_push(skb, sizeof(*hdr));
- if (!hdr)
- goto err_drop;
-
- hdr->id = cpu_to_be16(MCTP_USB_DMTF_ID);
- hdr->rsvd = 0;
- hdr->len = plen + sizeof(*hdr);
-
- urb = mctp_usb->tx_urb;
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb)
+ return -ENOMEM;
usb_fill_bulk_urb(urb, mctp_usb->usbdev,
usb_sndbulkpipe(mctp_usb->usbdev, mctp_usb->ep_out),
- skb->data, skb->len,
- mctp_usb_out_complete, skb);
+ data, len, mctp_usb_out_complete, tx_ctx);
- /* Stops TX queue first to prevent race condition with URB complete */
- netif_stop_queue(dev);
+ netif_stop_queue(mctp_usb->netdev);
+
+ spin_lock_irqsave(&mctp_usb->tx_lock, flags);
rc = usb_submit_urb(urb, GFP_ATOMIC);
+ if (!rc)
+ mctp_usb->tx_urb = urb;
+ spin_unlock_irqrestore(&mctp_usb->tx_lock, flags);
+
if (rc) {
- netif_wake_queue(dev);
- goto err_drop;
+ netdev_dbg(mctp_usb->netdev, "TX urb submit failed, %d\n", rc);
+ usb_free_urb(urb);
+ netif_start_queue(mctp_usb->netdev);
}
- return NETDEV_TX_OK;
+ return rc;
+}
+
+static const struct mctp_usblib_tx_ops tx_ops = {
+ .send = mctp_usb_tx_send,
+};
+
+static netdev_tx_t mctp_usb_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct mctp_usb *mctp_usb = netdev_priv(dev);
+ bool more = netdev_xmit_more();
+
+ mctp_usblib_tx_push(dev, &mctp_usb->tx, skb, more);
-err_drop:
- dev_dstats_tx_dropped(dev);
- kfree_skb(skb);
return NETDEV_TX_OK;
}
@@ -220,7 +212,12 @@ static int mctp_usb_stop(struct net_device *dev)
flush_delayed_work(&mctp_usb->rx_retry_work);
usb_kill_urb(mctp_usb->rx_urb);
+
+ spin_lock_irqsave(&mctp_usb->tx_lock, flags);
usb_kill_urb(mctp_usb->tx_urb);
+ spin_unlock_irqrestore(&mctp_usb->tx_lock, flags);
+
+ mctp_usblib_tx_cancel(&mctp_usb->tx, dev);
return 0;
}
@@ -275,31 +272,33 @@ static int mctp_usb_probe(struct usb_interface *intf,
dev->usbdev = interface_to_usbdev(intf);
dev->intf = intf;
spin_lock_init(&dev->rx_lock);
+ spin_lock_init(&dev->tx_lock);
usb_set_intfdata(intf, dev);
mctp_usblib_rx_init(&dev->rx);
+ mctp_usblib_tx_init(&dev->tx, &tx_ops, dev);
dev->ep_in = ep_in->bEndpointAddress;
dev->ep_out = ep_out->bEndpointAddress;
- dev->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
dev->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->tx_urb || !dev->rx_urb) {
+ if (!dev->rx_urb) {
rc = -ENOMEM;
- goto err_free_urbs;
+ goto err_fini_rxtx;
}
INIT_DELAYED_WORK(&dev->rx_retry_work, mctp_usb_rx_retry_work);
rc = mctp_register_netdev(netdev, NULL, MCTP_PHYS_BINDING_USB);
if (rc)
- goto err_free_urbs;
+ goto err_free_urb;
return 0;
-err_free_urbs:
- usb_free_urb(dev->tx_urb);
+err_free_urb:
usb_free_urb(dev->rx_urb);
+err_fini_rxtx:
+ mctp_usblib_tx_fini(&dev->tx);
mctp_usblib_rx_fini(&dev->rx);
free_netdev(netdev);
return rc;
@@ -311,7 +310,7 @@ static void mctp_usb_disconnect(struct usb_interface *intf)
mctp_unregister_netdev(dev->netdev);
mctp_usblib_rx_fini(&dev->rx);
- usb_free_urb(dev->tx_urb);
+ mctp_usblib_tx_fini(&dev->tx);
usb_free_urb(dev->rx_urb);
free_netdev(dev->netdev);
}
diff --git a/drivers/net/mctp/mctp-usblib.c b/drivers/net/mctp/mctp-usblib.c
index 9b86eb4310ce..56eca496bbe4 100644
--- a/drivers/net/mctp/mctp-usblib.c
+++ b/drivers/net/mctp/mctp-usblib.c
@@ -162,6 +162,186 @@ void mctp_usblib_rx_cancel(struct mctp_usblib_rx *rx)
}
EXPORT_SYMBOL_GPL(mctp_usblib_rx_cancel);
+/* transmit context: encapsulates one transfer */
+struct mctp_usblib_tx_ctx {
+ struct mctp_usblib_tx *tx;
+ struct sk_buff *skb;
+ unsigned int len;
+ enum mctp_usblib_tx_buf_type {
+ TX_SINGLE,
+ } buf_type;
+};
+
+void mctp_usblib_tx_init(struct mctp_usblib_tx *tx,
+ const struct mctp_usblib_tx_ops *ops,
+ void *priv)
+{
+ memset(tx, 0, sizeof(*tx));
+ tx->ops = *ops;
+ tx->priv = priv;
+}
+EXPORT_SYMBOL_GPL(mctp_usblib_tx_init);
+
+void mctp_usblib_tx_fini(struct mctp_usblib_tx *tx)
+{
+}
+EXPORT_SYMBOL_GPL(mctp_usblib_tx_fini);
+
+void *mctp_usblib_tx_ctx_priv(struct mctp_usblib_tx_ctx *tx_ctx)
+{
+ return tx_ctx->tx->priv;
+}
+EXPORT_SYMBOL_GPL(mctp_usblib_tx_ctx_priv);
+
+static struct mctp_usblib_tx_ctx *
+mctp_usblib_tx_ctx_create(struct mctp_usblib_tx *tx, struct sk_buff *skb)
+{
+ struct mctp_usblib_tx_ctx *ctx;
+
+ ctx = kzalloc_obj(*ctx, GFP_ATOMIC);
+ if (!ctx)
+ return NULL;
+
+ ctx->tx = tx;
+ ctx->buf_type = TX_SINGLE;
+ ctx->skb = skb;
+ ctx->len += skb->len;
+
+ return ctx;
+}
+
+static int mctp_usblib_tx_send(struct mctp_usblib_tx_ctx *ctx)
+{
+ struct mctp_usblib_tx *tx = ctx->tx;
+ void *buf = ctx->skb->data;
+
+ return tx->ops.send(ctx, buf, ctx->len);
+}
+
+static void mctp_usblib_tx_ctx_free(struct mctp_usblib_tx_ctx *ctx)
+{
+ if (ctx)
+ dev_kfree_skb_any(ctx->skb);
+ kfree(ctx);
+}
+
+static void mctp_usblib_tx_stats_update(struct mctp_usblib_tx_ctx *ctx,
+ struct net_device *dev,
+ bool ok)
+{
+ struct pcpu_dstats *dstats = get_cpu_ptr(dev->dstats);
+ unsigned long flags;
+
+ flags = u64_stats_update_begin_irqsave(&dstats->syncp);
+ if (ok) {
+ u64_stats_inc(&dstats->tx_packets);
+ u64_stats_add(&dstats->tx_bytes, ctx->len);
+ } else {
+ u64_stats_inc(&dstats->tx_drops);
+ }
+ u64_stats_update_end_irqrestore(&dstats->syncp, flags);
+ put_cpu_ptr(dev->dstats);
+}
+
+static void mctp_usblib_tx_stats_single_drop(struct net_device *dev)
+{
+ struct pcpu_dstats *dstats = get_cpu_ptr(dev->dstats);
+ unsigned long flags;
+
+ flags = u64_stats_update_begin_irqsave(&dstats->syncp);
+ u64_stats_inc(&dstats->tx_drops);
+ u64_stats_update_end_irqrestore(&dstats->syncp, flags);
+ put_cpu_ptr(dev->dstats);
+}
+
+/*
+ * Completion for the ->send() op. This will update netdev stats and
+ * free the tx context.
+ *
+ * Likely called from (atomic) URB completion context.
+ */
+void mctp_usblib_tx_send_complete(struct mctp_usblib_tx_ctx *tx_ctx,
+ struct net_device *dev, bool ok)
+{
+ mctp_usblib_tx_ctx_free(tx_ctx);
+}
+EXPORT_SYMBOL_GPL(mctp_usblib_tx_send_complete);
+
+/* Prepare a skb for push() */
+static int mctp_usblib_tx_skb_prepare(struct sk_buff *skb)
+{
+ struct mctp_usb_hdr *hdr;
+ unsigned long plen;
+ int rc;
+
+ plen = skb->len;
+ if (plen + sizeof(*hdr) > MCTP_USB_1_0_PKTLEN_MAX)
+ return -EMSGSIZE;
+
+ rc = skb_cow_head(skb, sizeof(*hdr));
+ if (rc)
+ return rc;
+
+ hdr = skb_push(skb, sizeof(*hdr));
+ if (!hdr)
+ return -ENOMEM;
+
+ hdr->id = cpu_to_be16(MCTP_USB_DMTF_ID);
+ hdr->rsvd = 0;
+ hdr->len = plen + sizeof(*hdr);
+
+ return 0;
+}
+
+/*
+ * Push a new skb to the transfer. At present, no send must be in progress,
+ * as we only handle single-packet USB transfers.
+ *
+ * Takes ownership of @skb, including on error.
+ */
+int mctp_usblib_tx_push(struct net_device *dev,
+ struct mctp_usblib_tx *tx,
+ struct sk_buff *skb, bool more)
+{
+ struct mctp_usblib_tx_ctx *ctx;
+ int rc;
+
+ if (!skb)
+ return 0;
+
+ rc = mctp_usblib_tx_skb_prepare(skb);
+ if (rc)
+ goto err_drop_single;
+
+ ctx = mctp_usblib_tx_ctx_create(tx, skb);
+ if (!ctx) {
+ rc = -ENOMEM;
+ goto err_drop_single;
+ }
+
+ rc = mctp_usblib_tx_send(ctx);
+ if (rc) {
+ mctp_usblib_tx_stats_update(ctx, dev, false);
+ mctp_usblib_tx_ctx_free(ctx);
+ }
+
+ return rc;
+
+err_drop_single:
+ mctp_usblib_tx_stats_single_drop(dev);
+ kfree_skb(skb);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(mctp_usblib_tx_push);
+
+/* Cancel a tx: any un-sent context is released. */
+void mctp_usblib_tx_cancel(struct mctp_usblib_tx *tx,
+ struct net_device *dev)
+{
+ /* nothing to do at present, no ctx is persistent */
+}
+EXPORT_SYMBOL_GPL(mctp_usblib_tx_cancel);
+
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jeremy Kerr <jk@codeconstruct.com.au>");
MODULE_DESCRIPTION("MCTP USB transport library");
diff --git a/include/linux/usb/mctp-usb.h b/include/linux/usb/mctp-usb.h
index 595e6af16dd0..9fe314c2551e 100644
--- a/include/linux/usb/mctp-usb.h
+++ b/include/linux/usb/mctp-usb.h
@@ -55,4 +55,42 @@ int mctp_usblib_rx_complete(struct net_device *netdev,
void mctp_usblib_rx_cancel(struct mctp_usblib_rx *rx);
+/*
+ * TX handle: created by mctp_usblib_tx_push() during the tx path, and
+ * may persist across multiple packet transmits.
+ *
+ * Currently though, there is a 1:1 mapping between packets and transfers, so
+ * the tx context will be cleared over each transmit. This will change in
+ * future.
+ */
+struct mctp_usblib_tx_ctx;
+
+struct mctp_usblib_tx_ops {
+ /* Start a USB TX for @data. On returning success, the implementation
+ * must arrange for mctp_usblib_tx_send_complete() to be called at some
+ * later point (eg., on urb completion).
+ */
+ int (*send)(struct mctp_usblib_tx_ctx *tx_ctx, void *data, size_t len);
+};
+
+struct mctp_usblib_tx {
+ struct mctp_usblib_tx_ops ops;
+ void *priv;
+};
+
+void mctp_usblib_tx_init(struct mctp_usblib_tx *tx,
+ const struct mctp_usblib_tx_ops *ops, void *priv);
+void mctp_usblib_tx_fini(struct mctp_usblib_tx *tx);
+
+void *mctp_usblib_tx_ctx_priv(struct mctp_usblib_tx_ctx *tx_ctx);
+
+int mctp_usblib_tx_push(struct net_device *dev,
+ struct mctp_usblib_tx *tx,
+ struct sk_buff *skb, bool more);
+
+void mctp_usblib_tx_send_complete(struct mctp_usblib_tx_ctx *tx_ctx,
+ struct net_device *dev, bool ok);
+
+void mctp_usblib_tx_cancel(struct mctp_usblib_tx *tx, struct net_device *dev);
+
#endif /* __LINUX_USB_MCTP_USB_H */
--
2.47.3
^ permalink raw reply related
* [PATCH net-next 03/12] net: mctp: usblib: Move RX transfer processing to a new mctp-usblib
From: Jeremy Kerr @ 2026-06-30 3:21 UTC (permalink / raw)
To: Matt Johnston, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Greg Kroah-Hartman
Cc: netdev, linux-usb
In-Reply-To: <20260630-dev-mctp-usb-1-1-v1-0-86a311fc67b7@codeconstruct.com.au>
The processing of USB receive transfers is common to both sides of a
MCTP over USB transport. In order to support a future gadget driver,
move the current host-side driver into a new common file, mctp-usblib.
This currently handles the submit-complete-packetise process of the
receive path of the USB transport. We'll add transmit handling in an
upcoming change.
Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
---
drivers/net/mctp/Kconfig | 11 +++
drivers/net/mctp/Makefile | 1 +
drivers/net/mctp/mctp-usb.c | 100 +++++-------------------
drivers/net/mctp/mctp-usblib.c | 167 +++++++++++++++++++++++++++++++++++++++++
include/linux/usb/mctp-usb.h | 26 +++++++
5 files changed, 225 insertions(+), 80 deletions(-)
diff --git a/drivers/net/mctp/Kconfig b/drivers/net/mctp/Kconfig
index cf325ab0b1ef..a564a792801d 100644
--- a/drivers/net/mctp/Kconfig
+++ b/drivers/net/mctp/Kconfig
@@ -47,9 +47,20 @@ config MCTP_TRANSPORT_I3C
A MCTP protocol network device is created for each I3C bus
having a "mctp-controller" devicetree property.
+config MCTP_TRANSPORT_USBLIB
+ tristate "MCTP over USB common library"
+ depends on USB
+ help
+ Common protocol handling functions for MCTP-over-USB transport
+ implementations, suitable for use in either host- or gadget-side
+ transport driver
+
+ This will be automatically enabled by the transport driver.
+
config MCTP_TRANSPORT_USB
tristate "MCTP USB transport"
depends on USB
+ select MCTP_TRANSPORT_USBLIB
help
Provides a driver to access MCTP devices over USB transport,
defined by DMTF specification DSP0283.
diff --git a/drivers/net/mctp/Makefile b/drivers/net/mctp/Makefile
index c36006849a1e..c870b62d3f1c 100644
--- a/drivers/net/mctp/Makefile
+++ b/drivers/net/mctp/Makefile
@@ -2,3 +2,4 @@ obj-$(CONFIG_MCTP_SERIAL) += mctp-serial.o
obj-$(CONFIG_MCTP_TRANSPORT_I2C) += mctp-i2c.o
obj-$(CONFIG_MCTP_TRANSPORT_I3C) += mctp-i3c.o
obj-$(CONFIG_MCTP_TRANSPORT_USB) += mctp-usb.o
+obj-$(CONFIG_MCTP_TRANSPORT_USBLIB) += mctp-usblib.o
diff --git a/drivers/net/mctp/mctp-usb.c b/drivers/net/mctp/mctp-usb.c
index c6e36b63e87a..531b7c994afb 100644
--- a/drivers/net/mctp/mctp-usb.c
+++ b/drivers/net/mctp/mctp-usb.c
@@ -28,6 +28,8 @@ struct mctp_usb {
u8 ep_in;
u8 ep_out;
+ struct mctp_usblib_rx rx;
+
struct urb *tx_urb;
struct urb *rx_urb;
@@ -125,24 +127,23 @@ static const unsigned long RX_RETRY_DELAY = HZ / 4;
static int mctp_usb_rx_queue(struct mctp_usb *mctp_usb, gfp_t gfp)
{
unsigned long flags;
- struct sk_buff *skb;
+ size_t len;
+ void *buf;
int rc;
- skb = __netdev_alloc_skb(mctp_usb->netdev, MCTP_USB_1_0_XFER_SIZE, gfp);
- if (!skb) {
- rc = -ENOMEM;
+ rc = mctp_usblib_rx_prepare(mctp_usb->netdev, &mctp_usb->rx,
+ &buf, &len, gfp);
+ if (rc)
goto err_retry;
- }
usb_fill_bulk_urb(mctp_usb->rx_urb, mctp_usb->usbdev,
usb_rcvbulkpipe(mctp_usb->usbdev, mctp_usb->ep_in),
- skb->data, MCTP_USB_1_0_XFER_SIZE,
- mctp_usb_in_complete, skb);
+ buf, len, mctp_usb_in_complete, mctp_usb);
rc = usb_submit_urb(mctp_usb->rx_urb, gfp);
if (rc) {
netdev_dbg(mctp_usb->netdev, "rx urb submit failure: %d\n", rc);
- kfree_skb(skb);
+ mctp_usblib_rx_cancel(&mctp_usb->rx);
if (rc == -ENOMEM)
goto err_retry;
}
@@ -159,92 +160,27 @@ static int mctp_usb_rx_queue(struct mctp_usb *mctp_usb, gfp_t gfp)
static void mctp_usb_in_complete(struct urb *urb)
{
- struct sk_buff *skb = urb->context;
- struct net_device *netdev = skb->dev;
- struct mctp_usb *mctp_usb = netdev_priv(netdev);
- struct mctp_skb_cb *cb;
- unsigned int len;
+ struct mctp_usb *mctp_usb = urb->context;
+ struct net_device *netdev = mctp_usb->netdev;
int status;
status = urb->status;
switch (status) {
+ default:
+ netdev_dbg(netdev, "unexpected rx urb status: %d\n", status);
+ fallthrough;
case -ENOENT:
case -ECONNRESET:
case -ESHUTDOWN:
case -EPROTO:
- kfree_skb(skb);
+ mctp_usblib_rx_cancel(&mctp_usb->rx);
return;
case 0:
break;
- default:
- netdev_dbg(netdev, "unexpected rx urb status: %d\n", status);
- kfree_skb(skb);
- return;
}
- len = urb->actual_length;
- __skb_put(skb, len);
-
- while (skb) {
- struct sk_buff *skb2 = NULL;
- struct mctp_usb_hdr *hdr;
- u8 pkt_len; /* length of MCTP packet, no USB header */
-
- skb_reset_mac_header(skb);
- hdr = skb_pull_data(skb, sizeof(*hdr));
- if (!hdr)
- break;
-
- if (be16_to_cpu(hdr->id) != MCTP_USB_DMTF_ID) {
- netdev_dbg(netdev, "rx: invalid id %04x\n",
- be16_to_cpu(hdr->id));
- break;
- }
-
- if (hdr->len <
- sizeof(struct mctp_hdr) + sizeof(struct mctp_usb_hdr)) {
- netdev_dbg(netdev, "rx: short packet (hdr) %d\n",
- hdr->len);
- break;
- }
-
- /* we know we have at least sizeof(struct mctp_usb_hdr) here */
- pkt_len = hdr->len - sizeof(struct mctp_usb_hdr);
- if (pkt_len > skb->len) {
- netdev_dbg(netdev,
- "rx: short packet (xfer) %d, actual %d\n",
- hdr->len, skb->len);
- break;
- }
-
- if (pkt_len < skb->len) {
- /* more packets may follow - clone to a new
- * skb to use on the next iteration
- */
- skb2 = skb_clone(skb, GFP_ATOMIC);
- if (skb2) {
- if (!skb_pull(skb2, pkt_len)) {
- kfree_skb(skb2);
- skb2 = NULL;
- }
- }
- skb_trim(skb, pkt_len);
- }
-
- dev_dstats_rx_add(netdev, skb->len);
-
- skb->protocol = htons(ETH_P_MCTP);
- skb_reset_network_header(skb);
- cb = __mctp_cb(skb);
- cb->halen = 0;
- netif_rx(skb);
-
- skb = skb2;
- }
-
- if (skb)
- kfree_skb(skb);
+ mctp_usblib_rx_complete(netdev, &mctp_usb->rx, urb->actual_length);
mctp_usb_rx_queue(mctp_usb, GFP_ATOMIC);
}
@@ -341,6 +277,8 @@ static int mctp_usb_probe(struct usb_interface *intf,
spin_lock_init(&dev->rx_lock);
usb_set_intfdata(intf, dev);
+ mctp_usblib_rx_init(&dev->rx);
+
dev->ep_in = ep_in->bEndpointAddress;
dev->ep_out = ep_out->bEndpointAddress;
@@ -362,6 +300,7 @@ static int mctp_usb_probe(struct usb_interface *intf,
err_free_urbs:
usb_free_urb(dev->tx_urb);
usb_free_urb(dev->rx_urb);
+ mctp_usblib_rx_fini(&dev->rx);
free_netdev(netdev);
return rc;
}
@@ -371,6 +310,7 @@ static void mctp_usb_disconnect(struct usb_interface *intf)
struct mctp_usb *dev = usb_get_intfdata(intf);
mctp_unregister_netdev(dev->netdev);
+ mctp_usblib_rx_fini(&dev->rx);
usb_free_urb(dev->tx_urb);
usb_free_urb(dev->rx_urb);
free_netdev(dev->netdev);
diff --git a/drivers/net/mctp/mctp-usblib.c b/drivers/net/mctp/mctp-usblib.c
new file mode 100644
index 000000000000..9b86eb4310ce
--- /dev/null
+++ b/drivers/net/mctp/mctp-usblib.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mctp-usblib.c - MCTP-over-USB (DMTF DSP0283) transport helper library
+ *
+ * DSP0283 is available at:
+ * https://www.dmtf.org/sites/default/files/standards/documents/DSP0283_1.0.1.pdf
+ *
+ * Copyright (C) 2024-2026 Code Construct Pty Ltd
+ */
+
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/usb/mctp-usb.h>
+#include <net/mctp.h>
+
+void mctp_usblib_rx_init(struct mctp_usblib_rx *rx)
+{
+ memset(rx, 0, sizeof(*rx));
+}
+EXPORT_SYMBOL_GPL(mctp_usblib_rx_init);
+
+void mctp_usblib_rx_fini(struct mctp_usblib_rx *rx)
+{
+ kfree_skb(rx->skb);
+}
+EXPORT_SYMBOL_GPL(mctp_usblib_rx_fini);
+
+/*
+ * Prepare a transfer buffer for future completion; *bufp and *lenp will
+ * be populated on success.
+ */
+int mctp_usblib_rx_prepare(struct net_device *netdev,
+ struct mctp_usblib_rx *rx,
+ void **bufp, size_t *lenp, gfp_t gfp)
+{
+ const unsigned int len = MCTP_USB_1_0_XFER_SIZE;
+ struct sk_buff *skb;
+
+ skb = __netdev_alloc_skb(netdev, len, gfp);
+ if (!skb)
+ return -ENOMEM;
+
+ rx->skb = skb;
+
+ *bufp = skb_tail_pointer(skb);
+ *lenp = skb_tailroom(skb);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mctp_usblib_rx_prepare);
+
+static void mctp_usblib_rx(struct net_device *netdev, struct sk_buff *skb)
+{
+ struct pcpu_dstats *dstats = this_cpu_ptr(netdev->dstats);
+ struct mctp_skb_cb *cb;
+ unsigned long flags;
+
+ /* we're called from an URB completion handler, and cannot assume local
+ * irqs are always disabled
+ */
+ flags = u64_stats_update_begin_irqsave(&dstats->syncp);
+ u64_stats_inc(&dstats->rx_packets);
+ u64_stats_add(&dstats->rx_bytes, skb->len + sizeof(struct mctp_usb_hdr));
+ u64_stats_update_end_irqrestore(&dstats->syncp, flags);
+
+ skb_reset_mac_header(skb);
+ skb->protocol = htons(ETH_P_MCTP);
+ skb_reset_network_header(skb);
+ cb = __mctp_cb(skb);
+ cb->halen = 0;
+ netif_rx(skb);
+}
+
+/*
+ * Receive a USB completion of @len bytes of incoming data. We will then split
+ * this into packets and netif_rx() each. Intended to be called in atomic
+ * contexts - ie., URB completion.
+ *
+ * Assumes @netdev uses dstats.
+ */
+int mctp_usblib_rx_complete(struct net_device *netdev,
+ struct mctp_usblib_rx *rx, size_t len)
+{
+ struct sk_buff *skb = rx->skb;
+ int rc = 0;
+
+ __skb_put(skb, len);
+
+ while (skb) {
+ struct sk_buff *skb2 = NULL;
+ struct mctp_usb_hdr *hdr;
+ /* length of MCTP packet, no USB header */
+ u8 pkt_len;
+
+ skb_reset_mac_header(skb);
+ hdr = skb_pull_data(skb, sizeof(*hdr));
+ if (!hdr) {
+ rc = -ENOMSG;
+ break;
+ }
+
+ if (be16_to_cpu(hdr->id) != MCTP_USB_DMTF_ID) {
+ netdev_dbg(netdev, "rx: invalid id %04x\n",
+ be16_to_cpu(hdr->id));
+ rc = -EPROTO;
+ break;
+ }
+
+ if (hdr->len <
+ sizeof(struct mctp_hdr) + sizeof(struct mctp_usb_hdr)) {
+ netdev_dbg(netdev, "rx: short packet (hdr) %d\n",
+ hdr->len);
+ rc = -EPROTO;
+ break;
+ }
+
+ /* we know we have at least sizeof(struct mctp_usb_hdr) here */
+ pkt_len = hdr->len - sizeof(struct mctp_usb_hdr);
+ if (pkt_len > skb->len) {
+ rc = -EPROTO;
+ netdev_dbg(netdev,
+ "rx: short packet (xfer) %d, actual %d\n",
+ hdr->len, skb->len);
+ break;
+ }
+
+ if (pkt_len < skb->len) {
+ /* more packets may follow - clone to a new
+ * skb to use on the next iteration
+ */
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2) {
+ if (!skb_pull(skb2, pkt_len)) {
+ dev_kfree_skb_any(skb2);
+ skb2 = NULL;
+ }
+ }
+ skb_trim(skb, pkt_len);
+ }
+
+ mctp_usblib_rx(netdev, skb);
+ skb = skb2;
+ }
+
+ if (skb)
+ dev_kfree_skb_any(skb);
+
+ rx->skb = NULL;
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(mctp_usblib_rx_complete);
+
+/*
+ * Cancel a rx context; subsequent prepare/complete calls will not be a
+ * continuation of any data already received.
+ */
+void mctp_usblib_rx_cancel(struct mctp_usblib_rx *rx)
+{
+ dev_kfree_skb_any(rx->skb);
+ rx->skb = NULL;
+}
+EXPORT_SYMBOL_GPL(mctp_usblib_rx_cancel);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jeremy Kerr <jk@codeconstruct.com.au>");
+MODULE_DESCRIPTION("MCTP USB transport library");
diff --git a/include/linux/usb/mctp-usb.h b/include/linux/usb/mctp-usb.h
index 2bece8afd1c7..595e6af16dd0 100644
--- a/include/linux/usb/mctp-usb.h
+++ b/include/linux/usb/mctp-usb.h
@@ -13,6 +13,8 @@
#ifndef __LINUX_USB_MCTP_USB_H
#define __LINUX_USB_MCTP_USB_H
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
#include <linux/types.h>
struct mctp_usb_hdr {
@@ -29,4 +31,28 @@ struct mctp_usb_hdr {
#define MCTP_USB_1_0_MTU_MAX (MCTP_USB_1_0_PKTLEN_MAX - sizeof(struct mctp_usb_hdr))
#define MCTP_USB_DMTF_ID 0x1ab4
+/* mctp-usblib */
+
+/*
+ * RX handle: drivers will typically create one on init, which persists for
+ * the life of the driver. The same handle is used for progressive
+ * prepare -> complete operations (for each incoming USB transfer), which
+ * result in netif_rx()-ing the MCTP packets received
+ */
+struct mctp_usblib_rx {
+ struct sk_buff *skb;
+};
+
+void mctp_usblib_rx_init(struct mctp_usblib_rx *rx);
+void mctp_usblib_rx_fini(struct mctp_usblib_rx *rx);
+
+int mctp_usblib_rx_prepare(struct net_device *netdev,
+ struct mctp_usblib_rx *rx,
+ void **bufp, size_t *lenp, gfp_t gfp);
+
+int mctp_usblib_rx_complete(struct net_device *netdev,
+ struct mctp_usblib_rx *rx, size_t len);
+
+void mctp_usblib_rx_cancel(struct mctp_usblib_rx *rx);
+
#endif /* __LINUX_USB_MCTP_USB_H */
--
2.47.3
^ permalink raw reply related
* Re: [PATCH net-next v1] ice: use dev_err_probe in all appropriate places in ice_probe()
From: Przemek Kitszel @ 2026-06-30 3:43 UTC (permalink / raw)
To: Rongguang Wei, intel-wired-lan
Cc: anthony.l.nguyen, andrew+netdev, netdev, Rongguang Wei
In-Reply-To: <20260630032537.42605-1-clementwei90@163.com>
On 6/30/26 05:25, Rongguang Wei wrote:
> From: Rongguang Wei <weirongguang@kylinos.cn>
>
> Use dev_err_probe() can conveniently combines printing
s/combines/combine/
you could also wrap at 75 chars, to have better formatting.
> an error message with returning the errno and also
> simplify the code.
>
> Signed-off-by: Rongguang Wei <weirongguang@kylinos.cn>
thank you,
Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
> ---
> drivers/net/ethernet/intel/ice/ice_main.c | 24 ++++++++---------------
> 1 file changed, 8 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
> index e2fbe111f849..81959eaec708 100644
> --- a/drivers/net/ethernet/intel/ice/ice_main.c
> +++ b/drivers/net/ethernet/intel/ice/ice_main.c
> @@ -5167,10 +5167,8 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
> struct ice_hw *hw;
> int err;
>
> - if (pdev->is_virtfn) {
> - dev_err(dev, "can't probe a virtual function\n");
> - return -EINVAL;
> - }
> + if (pdev->is_virtfn)
> + return dev_err_probe(dev, -EINVAL, "can't probe a virtual function\n");
>
> /* when under a kdump kernel initiate a reset before enabling the
> * device in order to clear out any pending DMA transactions. These
> @@ -5194,10 +5192,8 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
> return err;
>
> err = pcim_iomap_regions(pdev, BIT(ICE_BAR0), dev_driver_string(dev));
> - if (err) {
> - dev_err(dev, "BAR0 I/O map error %d\n", err);
> - return err;
> - }
> + if (err)
> + return dev_err_probe(dev, err, "BAR0 I/O map error %d\n", err);
>
> pf = ice_allocate_pf(dev);
> if (!pf)
> @@ -5208,10 +5204,8 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
>
> /* set up for high or low DMA */
> err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
> - if (err) {
> - dev_err(dev, "DMA configuration failed: 0x%x\n", err);
> - return err;
> - }
> + if (err)
> + return dev_err_probe(dev, err, "DMA configuration failed: 0x%x\n", err);
>
> pci_set_master(pdev);
> pf->pdev = pdev;
> @@ -5246,10 +5240,8 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
> return ice_probe_recovery_mode(pf);
>
> err = ice_init_hw(hw);
> - if (err) {
> - dev_err(dev, "ice_init_hw failed: %d\n", err);
> - return err;
> - }
> + if (err)
> + return dev_err_probe(dev, err, "ice_init_hw failed: %d\n", err);
>
> ice_init_dev_hw(pf);
>
^ permalink raw reply
* [PATCH net v2 1/1] tcp: bound SYN-ACK timers to reqsk timeout range
From: Ren Wei @ 2026-06-30 3:49 UTC (permalink / raw)
To: netdev
Cc: edumazet, ncardwell, kuniyu, davem, pabeni, horms, chia-yu.chang,
ij, bronzed_45_vested, fmancera, idosch, yuuchihsu, yuantan098,
yifanwucs, tomapufckgml, bird, roxy520tt, n05ec
From: Zhiling Zou <roxy520tt@gmail.com>
tcp_synack_retries supplies the SYN-ACK retry limit used by request
socket timers. The same effective limit can also come from TCP_SYNCNT
through icsk_syn_retries, while TCP_DEFER_ACCEPT can keep an ACKed
request alive until rskq_defer_accept is reached.
The request socket timeout counter is incremented before it is used to
compute the next timeout. tcp_reqsk_timeout() and the Fast Open SYN-ACK
timer shift req->timeout by req->num_timeout. Excessive retry or
defer-accept limits can therefore drive these timer paths into invalid
shift counts before the request expires.
Limit tcp_synack_retries to the request socket timer range, clamp the
effective retry and defer-accept limits in the regular request socket
timer path, clamp the Fast Open retry limit, and make the request
socket timeout helper saturate before shifting.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Reported-by: Yuan Tan <yuantan098@gmail.com>
Reported-by: Yifan Wu <yifanwucs@gmail.com>
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
Reported-by: Xin Liu <bird@lzu.edu.cn>
Assisted-by: Codex:gpt-5.4
Signed-off-by: Zhiling Zou <roxy520tt@gmail.com>
Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
---
Changes in v2:
- Keep the existing max_retries calculation in tcp_fastopen_synack_timer()
and only add the clamp, avoiding code churn.
- v1 Link: https://lore.kernel.org/all/02e24eb83639e9d7ecc623f000c60254bb5c40a5.1782643946.git.roxy520tt@gmail.com/
include/net/tcp.h | 19 +++++++++++++++----
net/ipv4/inet_connection_sock.c | 6 +++++-
net/ipv4/sysctl_net_ipv4.c | 2 ++
net/ipv4/tcp_timer.c | 3 ++-
4 files changed, 24 insertions(+), 6 deletions(-)
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 6d376ea4d1c0..656f1bd0fa1a 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -183,6 +183,7 @@ static_assert((1 << ATO_BITS) > TCP_DELACK_MAX);
#define MAX_TCP_KEEPINTVL 32767
#define MAX_TCP_KEEPCNT 127
#define MAX_TCP_SYNCNT 127
+#define MAX_TCP_SYNACK_RETRIES 63
/* Ensure that TCP PAWS checks are relaxed after ~2147 seconds
* to avoid overflows. This assumes a clock smaller than 1 Mhz.
@@ -882,12 +883,22 @@ static inline u32 __tcp_set_rto(const struct tcp_sock *tp)
return usecs_to_jiffies((tp->srtt_us >> 3) + tp->rttvar_us);
}
-static inline unsigned long tcp_reqsk_timeout(struct request_sock *req)
+static inline unsigned long tcp_reqsk_timeout_sk(const struct sock *sk,
+ struct request_sock *req)
{
- u64 timeout = (u64)req->timeout << req->num_timeout;
+ u64 timeout = req->timeout;
+ u32 rto_max = tcp_rto_max(sk);
+
+ if (req->num_timeout >= BITS_PER_TYPE(u64) ||
+ timeout > U64_MAX >> req->num_timeout)
+ return rto_max;
+
+ return (unsigned long)min_t(u64, timeout << req->num_timeout, rto_max);
+}
- return (unsigned long)min_t(u64, timeout,
- tcp_rto_max(req->rsk_listener));
+static inline unsigned long tcp_reqsk_timeout(struct request_sock *req)
+{
+ return tcp_reqsk_timeout_sk(req->rsk_listener, req);
}
u32 tcp_delack_max(const struct sock *sk);
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 56902bba5483..b74212bae3dd 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -1056,6 +1056,8 @@ static void reqsk_timer_handler(struct timer_list *t)
net = sock_net(sk_listener);
max_syn_ack_retries = READ_ONCE(icsk->icsk_syn_retries) ? :
READ_ONCE(net->ipv4.sysctl_tcp_synack_retries);
+ max_syn_ack_retries = min_t(int, max_syn_ack_retries,
+ MAX_TCP_SYNACK_RETRIES);
/* Normally all the openreqs are young and become mature
* (i.e. converted to established socket) for first timeout.
* If synack was not acknowledged for 1 second, it means
@@ -1086,7 +1088,9 @@ static void reqsk_timer_handler(struct timer_list *t)
}
}
- syn_ack_recalc(req, max_syn_ack_retries, READ_ONCE(queue->rskq_defer_accept),
+ syn_ack_recalc(req, max_syn_ack_retries,
+ min_t(u8, READ_ONCE(queue->rskq_defer_accept),
+ MAX_TCP_SYNACK_RETRIES),
&expire, &resend);
tcp_syn_ack_timeout(req);
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index ca1180dba1de..f9d233b98bbc 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -35,6 +35,7 @@ static int ip_ttl_min = 1;
static int ip_ttl_max = 255;
static int tcp_syn_retries_min = 1;
static int tcp_syn_retries_max = MAX_TCP_SYNCNT;
+static int tcp_synack_retries_max = MAX_TCP_SYNACK_RETRIES;
static int tcp_syn_linear_timeouts_max = MAX_TCP_SYNCNT;
static unsigned long ip_ping_group_range_min[] = { 0, 0 };
static unsigned long ip_ping_group_range_max[] = { GID_T_MAX, GID_T_MAX };
@@ -1034,6 +1035,7 @@ static struct ctl_table ipv4_net_table[] = {
.maxlen = sizeof(u8),
.mode = 0644,
.proc_handler = proc_dou8vec_minmax,
+ .extra2 = &tcp_synack_retries_max
},
#ifdef CONFIG_SYN_COOKIES
{
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index bf171b5e1eb3..bbedf2b9e1bc 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -467,6 +467,7 @@ static void tcp_fastopen_synack_timer(struct sock *sk, struct request_sock *req)
*/
max_retries = READ_ONCE(icsk->icsk_syn_retries) ? :
READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_synack_retries) + 1;
+ max_retries = min_t(int, max_retries, MAX_TCP_SYNACK_RETRIES);
if (req->num_timeout >= max_retries) {
tcp_write_err(sk);
@@ -488,7 +489,7 @@ static void tcp_fastopen_synack_timer(struct sock *sk, struct request_sock *req)
if (!tp->retrans_stamp)
tp->retrans_stamp = tcp_time_stamp_ts(tp);
tcp_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
- req->timeout << req->num_timeout, false);
+ tcp_reqsk_timeout_sk(sk, req), false);
}
static bool tcp_rtx_probe0_timed_out(const struct sock *sk,
--
2.43.0
^ permalink raw reply related
* [PATCH 00/10] net: emac: various cleanups, fixes, and feature additions
From: Rosen Penev @ 2026-06-30 4:16 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, open list
This series targets the IBM EMAC (Ethernet Media Access Controller)
driver used on PowerPC 4xx SoCs. It removes unused infrastructure,
fixes sparse warnings, replaces legacy helpers, streamlines
synchronization, fixes DMA API usage, and adds BQL support along
with ndo_get_stats64 conversion.
Rosen Penev (10):
net: emac: remove emac_xaht_base()
net: emac: fix sparse __iomem warnings in IAHT register access
net: emac: use DMA-specific and SMP memory barriers
net: emac: mal: replace of_get_property with of_property_read_u32
net: emac: mal: replace busy-wait in mal_poll_disable with wait_event
net: emac: batch stats, eliminate modulo, tighten barrier in RX poll
net: emac: fix DMA API mapping and unmapping correctness
net: emac: replace #ifdef CONFIG_PPC_DCR_NATIVE with IS_ENABLED()
net: emac: add Byte Queue Limits (BQL) support
net: emac: use ndo_get_stats64 instead of ndo_get_stats
drivers/net/ethernet/ibm/emac/core.c | 253 ++++++++++++++++----------
drivers/net/ethernet/ibm/emac/core.h | 17 +-
drivers/net/ethernet/ibm/emac/mal.c | 37 ++--
drivers/net/ethernet/ibm/emac/mal.h | 3 +
drivers/net/ethernet/ibm/emac/rgmii.c | 2 +-
drivers/net/ethernet/ibm/emac/tah.c | 2 +-
drivers/net/ethernet/ibm/emac/zmii.c | 4 +-
7 files changed, 188 insertions(+), 130 deletions(-)
--
2.54.0
^ permalink raw reply
* [PATCH 01/10] net: emac: remove emac_xaht_base()
From: Rosen Penev @ 2026-06-30 4:16 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, open list
In-Reply-To: <20260630041634.284127-1-rosenp@gmail.com>
Unused function. It's also missing __iomem.
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/ethernet/ibm/emac/core.h | 8 --------
1 file changed, 8 deletions(-)
diff --git a/drivers/net/ethernet/ibm/emac/core.h b/drivers/net/ethernet/ibm/emac/core.h
index 89fa1683ec3c..46c5512c8e00 100644
--- a/drivers/net/ethernet/ibm/emac/core.h
+++ b/drivers/net/ethernet/ibm/emac/core.h
@@ -420,14 +420,6 @@ static inline u32 __iomem *emac_gaht_base(struct emac_instance *dev)
return emac_xaht_base(dev) + EMAC_XAHT_REGS(dev);
}
-static inline u32 *emac_iaht_base(struct emac_instance *dev)
-{
- /* IAHT registers always come before an identical number of
- * GAHT registers.
- */
- return emac_xaht_base(dev);
-}
-
/* Ethtool get_regs complex data.
* We want to get not just EMAC registers, but also MAL, ZMII, RGMII, TAH
* when available.
--
2.54.0
^ permalink raw reply related
* [PATCH 02/10] net: emac: fix sparse __iomem warnings in IAHT register access
From: Rosen Penev @ 2026-06-30 4:16 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, open list
In-Reply-To: <20260630041634.284127-1-rosenp@gmail.com>
Annotate iaht1/iaht2 in the EMAC4 register union with __iomem so
sparse does not warn about address-space mismatches, and simplify
emac_xaht_base() to return &p->u1.emac4sync.iaht1 (or the EMAC4
variant) directly instead of computing the offset by hand.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/ethernet/ibm/emac/core.h | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ethernet/ibm/emac/core.h b/drivers/net/ethernet/ibm/emac/core.h
index 46c5512c8e00..296da4bf3781 100644
--- a/drivers/net/ethernet/ibm/emac/core.h
+++ b/drivers/net/ethernet/ibm/emac/core.h
@@ -399,17 +399,13 @@ static inline int emac_has_feature(struct emac_instance *dev,
static inline u32 __iomem *emac_xaht_base(struct emac_instance *dev)
{
struct emac_regs __iomem *p = dev->emacp;
- int offset;
/* The first IAHT entry always is the base of the block of
* IAHT and GAHT registers.
*/
if (emac_has_feature(dev, EMAC_FTR_EMAC4SYNC))
- offset = offsetof(struct emac_regs, u1.emac4sync.iaht1);
- else
- offset = offsetof(struct emac_regs, u0.emac4.iaht1);
-
- return (u32 __iomem *)((__force ptrdiff_t)p + offset);
+ return &p->u1.emac4sync.iaht1;
+ return &p->u0.emac4.iaht1;
}
static inline u32 __iomem *emac_gaht_base(struct emac_instance *dev)
--
2.54.0
^ permalink raw reply related
* [PATCH 03/10] net: emac: use DMA-specific and SMP memory barriers
From: Rosen Penev @ 2026-06-30 4:16 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, open list
In-Reply-To: <20260630041634.284127-1-rosenp@gmail.com>
Replace generic wmb()/mb() barriers with more specific variants:
- dma_wmb() for ordering descriptor field writes before hardware
ownership bit (ctrl) hand-off in TX/RX paths
- dma_rmb() for ordering descriptor ctrl read after ownership
observation in the RX poll path
- smp_wmb() for CPU-to-CPU ordering (link_polling flag,
platform_set_drvdata visibility)
- dma_rmb() for the RX descriptor ownership transfer read
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/ethernet/ibm/emac/core.c | 16 ++++++++--------
drivers/net/ethernet/ibm/emac/mal.c | 2 +-
drivers/net/ethernet/ibm/emac/rgmii.c | 2 +-
drivers/net/ethernet/ibm/emac/tah.c | 2 +-
drivers/net/ethernet/ibm/emac/zmii.c | 4 ++--
5 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c
index 1d46cf6c2c12..5e7b85d28bde 100644
--- a/drivers/net/ethernet/ibm/emac/core.c
+++ b/drivers/net/ethernet/ibm/emac/core.c
@@ -1185,7 +1185,7 @@ __emac_prepare_rx_skb(struct sk_buff *skb, struct emac_instance *dev, int slot)
dev->rx_desc[slot].data_ptr =
dma_map_single(&dev->ofdev->dev, skb->data - NET_IP_ALIGN,
dev->rx_sync_size, DMA_FROM_DEVICE) + NET_IP_ALIGN;
- wmb();
+ dma_wmb();
dev->rx_desc[slot].ctrl = MAL_RX_CTRL_EMPTY |
(slot == (NUM_RX_BUFF - 1) ? MAL_RX_CTRL_WRAP : 0);
@@ -1263,7 +1263,7 @@ static int emac_open(struct net_device *ndev)
link_poll_interval = PHY_POLL_LINK_OFF;
}
dev->link_polling = 1;
- wmb();
+ smp_wmb();
schedule_delayed_work(&dev->link_work, link_poll_interval);
emac_print_link_status(dev);
} else
@@ -1464,7 +1464,7 @@ static netdev_tx_t emac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
skb->data, len,
DMA_TO_DEVICE);
dev->tx_desc[slot].data_len = (u16) len;
- wmb();
+ dma_wmb();
dev->tx_desc[slot].ctrl = ctrl;
return emac_xmit_finish(dev, len);
@@ -1560,7 +1560,7 @@ emac_start_xmit_sg(struct sk_buff *skb, struct net_device *ndev)
/* Send the packet out */
if (dev->tx_slot == NUM_TX_BUFF - 1)
ctrl |= MAL_TX_CTRL_WRAP;
- wmb();
+ dma_wmb();
dev->tx_desc[dev->tx_slot].ctrl = ctrl;
dev->tx_slot = (slot + 1) % NUM_TX_BUFF;
@@ -1671,7 +1671,7 @@ static inline void emac_recycle_rx_skb(struct emac_instance *dev, int slot,
DMA_FROM_DEVICE);
dev->rx_desc[slot].data_len = 0;
- wmb();
+ dma_wmb();
dev->rx_desc[slot].ctrl = MAL_RX_CTRL_EMPTY |
(slot == (NUM_RX_BUFF - 1) ? MAL_RX_CTRL_WRAP : 0);
}
@@ -1754,7 +1754,7 @@ static int emac_poll_rx(void *param, int budget)
break;
skb = dev->rx_skb[slot];
- mb();
+ dma_rmb();
len = dev->rx_desc[slot].data_len;
if (unlikely(!MAL_IS_SINGLE_RX(ctrl)))
@@ -1845,7 +1845,7 @@ static int emac_poll_rx(void *param, int budget)
}
if (unlikely(budget && test_bit(MAL_COMMAC_RX_STOPPED, &dev->commac.flags))) {
- mb();
+ dma_rmb();
if (!(dev->rx_desc[slot].ctrl & MAL_RX_CTRL_EMPTY)) {
DBG2(dev, "rx restart" NL);
received = 0;
@@ -3167,7 +3167,7 @@ static int emac_probe(struct platform_device *ofdev)
/* Set our drvdata last as we don't want them visible until we are
* fully initialized
*/
- wmb();
+ smp_wmb();
platform_set_drvdata(ofdev, dev);
printk(KERN_INFO "%s: EMAC-%d %pOF, MAC %pM\n",
diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c
index 4025bc36ae16..99615c8a6c3e 100644
--- a/drivers/net/ethernet/ibm/emac/mal.c
+++ b/drivers/net/ethernet/ibm/emac/mal.c
@@ -691,7 +691,7 @@ static int mal_probe(struct platform_device *ofdev)
mal->num_tx_chans, mal->num_rx_chans);
/* Advertise this instance to the rest of the world */
- wmb();
+ smp_wmb();
platform_set_drvdata(ofdev, mal);
return 0;
diff --git a/drivers/net/ethernet/ibm/emac/rgmii.c b/drivers/net/ethernet/ibm/emac/rgmii.c
index b544dd8633b7..093aa4f129e3 100644
--- a/drivers/net/ethernet/ibm/emac/rgmii.c
+++ b/drivers/net/ethernet/ibm/emac/rgmii.c
@@ -255,7 +255,7 @@ static int rgmii_probe(struct platform_device *ofdev)
ofdev->dev.of_node,
(dev->flags & EMAC_RGMII_FLAG_HAS_MDIO) ? "" : "out");
- wmb();
+ smp_wmb();
platform_set_drvdata(ofdev, dev);
return 0;
diff --git a/drivers/net/ethernet/ibm/emac/tah.c b/drivers/net/ethernet/ibm/emac/tah.c
index ed07532aaf85..077da56fa449 100644
--- a/drivers/net/ethernet/ibm/emac/tah.c
+++ b/drivers/net/ethernet/ibm/emac/tah.c
@@ -112,7 +112,7 @@ static int tah_probe(struct platform_device *ofdev)
tah_reset(ofdev);
printk(KERN_INFO "TAH %pOF initialized\n", ofdev->dev.of_node);
- wmb();
+ smp_wmb();
return 0;
}
diff --git a/drivers/net/ethernet/ibm/emac/zmii.c b/drivers/net/ethernet/ibm/emac/zmii.c
index a3839cf02ec4..5144ee94a7d2 100644
--- a/drivers/net/ethernet/ibm/emac/zmii.c
+++ b/drivers/net/ethernet/ibm/emac/zmii.c
@@ -258,8 +258,8 @@ static int zmii_probe(struct platform_device *ofdev)
/* Disable all inputs by default */
out_be32(&dev->base->fer, 0);
- printk(KERN_INFO "ZMII %pOF initialized\n", ofdev->dev.of_node);
- wmb();
+ dev_info(&ofdev->dev, "ZMII initialized\n");
+ smp_wmb();
platform_set_drvdata(ofdev, dev);
return 0;
--
2.54.0
^ permalink raw reply related
* [PATCH 04/10] net: emac: mal: replace of_get_property with of_property_read_u32
From: Rosen Penev @ 2026-06-30 4:16 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, open list
In-Reply-To: <20260630041634.284127-1-rosenp@gmail.com>
Replace the deprecated of_get_property() calls with the typed
of_property_read_u32() helper in mal_probe(). This is both safer
and more idiomatic for modern DT API usage.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/ethernet/ibm/emac/mal.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c
index 99615c8a6c3e..82d502d576ee 100644
--- a/drivers/net/ethernet/ibm/emac/mal.c
+++ b/drivers/net/ethernet/ibm/emac/mal.c
@@ -519,7 +519,7 @@ static int mal_probe(struct platform_device *ofdev)
int err = 0, i, bd_size;
int index = mal_count++;
unsigned int dcr_base;
- const u32 *prop;
+ u32 val;
u32 cfg;
unsigned long irqflags;
irq_handler_t hdlr_serr, hdlr_txde, hdlr_rxde;
@@ -535,23 +535,23 @@ static int mal_probe(struct platform_device *ofdev)
MAL_DBG(mal, "probe" NL);
- prop = of_get_property(ofdev->dev.of_node, "num-tx-chans", NULL);
- if (prop == NULL) {
+ err = of_property_read_u32(ofdev->dev.of_node, "num-tx-chans", &val);
+ if (err) {
printk(KERN_ERR
"mal%d: can't find MAL num-tx-chans property!\n",
index);
return -ENODEV;
}
- mal->num_tx_chans = prop[0];
+ mal->num_tx_chans = val;
- prop = of_get_property(ofdev->dev.of_node, "num-rx-chans", NULL);
- if (prop == NULL) {
+ err = of_property_read_u32(ofdev->dev.of_node, "num-rx-chans", &val);
+ if (err) {
printk(KERN_ERR
"mal%d: can't find MAL num-rx-chans property!\n",
index);
return -ENODEV;
}
- mal->num_rx_chans = prop[0];
+ mal->num_rx_chans = val;
dcr_base = dcr_resource_start(ofdev->dev.of_node, 0);
if (dcr_base == 0) {
--
2.54.0
^ permalink raw reply related
* RE: [PATCH net v2 1/2] net: ethernet: oa_tc6: Protect skb pointer used by two different kernel instances
From: Selvamani Rajagopal @ 2026-06-30 4:16 UTC (permalink / raw)
To: Jakub Kicinski, Selvamani Rajagopal via B4 Relay
Cc: Parthiban Veerasooran, Andrew Lunn, Piergiorgio Beruto,
David S. Miller, Eric Dumazet, Paolo Abeni,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Andrew Lunn
In-Reply-To: <20260629191553.0a305168@kernel.org>
> -----Original Message-----
> From: Jakub Kicinski <kuba@kernel.org>
> Subject: Re: [PATCH net v2 1/2] net: ethernet: oa_tc6: Protect skb pointer used by two
> different kernel instances
>
>
> On Fri, 26 Jun 2026 08:35:18 -0700 Selvamani Rajagopal via B4 Relay
> wrote:
> > Threaded IRQ uses waiting_tx_skb. Transmit path also uses
> > this pointer without any mutual exclusion protection. As a
> > result, it might leak skb buffer, particularly threaded IRQ
> > runs in the middle of tranmsmit path, near skb_linearize.
>
> Can you say more ? only xmit sets waiting_tx_skb, the IRQ
> clears it. So why is IRQ racing with xmit leading to drops?
I believe xmit path and IRQ thread would run in different kernel instances. Imagine oa_tc6_try_spi_transfer
call fails in threaded IRQ. It would set disable_irq. If xmit function didn't see that when it checked, but it is set
before placing skb buffer in the waiting_tx_skb pointer (due to skb_linearize for example), the skb would be stuck
in waiting_tx_skb.
Also, See the Sashiko review that gave a SMP use-case. If you search for CPU0 0r CPU1, you would find the use case.
https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260611-level-trigger-v5-0-4533a9e85ce2%40onsemi.com
^ permalink raw reply
* [PATCH 05/10] net: emac: mal: replace busy-wait in mal_poll_disable with wait_event
From: Rosen Penev @ 2026-06-30 4:16 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, open list
In-Reply-To: <20260630041634.284127-1-rosenp@gmail.com>
Replace the msleep(1) busy-wait loop in mal_poll_disable() with
a proper wait_event/wake_up mechanism. Add wait_queue_head_t to
struct mal_commac, initialize it in mal_poll_add(), and wake
waiters in mal_poll_enable().
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/ethernet/ibm/emac/mal.c | 7 +++++--
drivers/net/ethernet/ibm/emac/mal.h | 3 +++
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c
index 82d502d576ee..d12a376f69fd 100644
--- a/drivers/net/ethernet/ibm/emac/mal.c
+++ b/drivers/net/ethernet/ibm/emac/mal.c
@@ -176,6 +176,8 @@ void mal_poll_add(struct mal_instance *mal, struct mal_commac *commac)
MAL_DBG(mal, "poll_add(%p)" NL, commac);
+ init_waitqueue_head(&commac->poll_wait);
+
/* starts disabled */
set_bit(MAL_COMMAC_POLL_DISABLED, &commac->flags);
@@ -371,8 +373,8 @@ static irqreturn_t mal_int(int irq, void *dev_instance)
void mal_poll_disable(struct mal_instance *mal, struct mal_commac *commac)
{
/* Spinlock-type semantics: only one caller disable poll at a time */
- while (test_and_set_bit(MAL_COMMAC_POLL_DISABLED, &commac->flags))
- msleep(1);
+ wait_event(commac->poll_wait,
+ !test_and_set_bit(MAL_COMMAC_POLL_DISABLED, &commac->flags));
/* Synchronize with the MAL NAPI poller */
napi_synchronize(&mal->napi);
@@ -382,6 +384,7 @@ void mal_poll_enable(struct mal_instance *mal, struct mal_commac *commac)
{
smp_wmb();
clear_bit(MAL_COMMAC_POLL_DISABLED, &commac->flags);
+ wake_up(&commac->poll_wait);
/* Feels better to trigger a poll here to catch up with events that
* may have happened on this channel while disabled. It will most
diff --git a/drivers/net/ethernet/ibm/emac/mal.h b/drivers/net/ethernet/ibm/emac/mal.h
index e0ddc41186a2..bd52bb41adee 100644
--- a/drivers/net/ethernet/ibm/emac/mal.h
+++ b/drivers/net/ethernet/ibm/emac/mal.h
@@ -19,6 +19,8 @@
#ifndef __IBM_NEWEMAC_MAL_H
#define __IBM_NEWEMAC_MAL_H
+#include <linux/wait.h>
+
/*
* There are some variations on the MAL, we express them in this driver as
* MAL Version 1 and 2 though that doesn't match any IBM terminology.
@@ -172,6 +174,7 @@ struct mal_commac {
void *dev;
struct list_head poll_list;
long flags;
+ wait_queue_head_t poll_wait;
#define MAL_COMMAC_RX_STOPPED 0
#define MAL_COMMAC_POLL_DISABLED 1
u32 tx_chan_mask;
--
2.54.0
^ permalink raw reply related
* [PATCH 06/10] net: emac: batch stats, eliminate modulo, tighten barrier in RX poll
From: Rosen Penev @ 2026-06-30 4:16 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, open list
In-Reply-To: <20260630041634.284127-1-rosenp@gmail.com>
Three small hot-path changes in emac_poll_rx():
- Batch per-packet 64-bit stat updates into local accumulators and
write dev->stats once after the poll loop, avoiding expensive
load-linked/store-conditional sequences on 32-bit PPC for every
received packet.
- Replace slot = (slot + 1) % NUM_RX_BUFF with a simple
if (++slot == NUM_RX_BUFF) branch, avoiding a div/mul by a
non-power-of-2 constant.
- Use dma_rmb() instead of mb() when ordering the ctrl vs. data_len
read of the coherent RX descriptor. The device writes the
descriptor fields in-order and clears MAL_RX_CTRL_EMPTY last;
a read barrier is sufficient.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/ethernet/ibm/emac/core.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c
index 5e7b85d28bde..ced9690cddc3 100644
--- a/drivers/net/ethernet/ibm/emac/core.c
+++ b/drivers/net/ethernet/ibm/emac/core.c
@@ -1741,6 +1741,7 @@ static int emac_poll_rx(void *param, int budget)
{
struct emac_instance *dev = param;
int slot = dev->rx_slot, received = 0;
+ u64 packets = 0, bytes = 0;
DBG2(dev, "poll_rx(%d)" NL, budget);
@@ -1797,10 +1798,11 @@ static int emac_poll_rx(void *param, int budget)
napi_gro_receive(&dev->mal->napi, skb);
next:
- ++dev->stats.rx_packets;
+ ++packets;
skip:
- dev->stats.rx_bytes += len;
- slot = (slot + 1) % NUM_RX_BUFF;
+ bytes += len;
+ if (++slot == NUM_RX_BUFF)
+ slot = 0;
--budget;
++received;
continue;
@@ -1864,6 +1866,9 @@ static int emac_poll_rx(void *param, int budget)
emac_rx_enable(dev);
dev->rx_slot = 0;
}
+
+ dev->stats.rx_packets += packets;
+ dev->stats.rx_bytes += bytes;
return received;
}
--
2.54.0
^ permalink raw reply related
* [PATCH 07/10] net: emac: fix DMA API mapping and unmapping correctness
From: Rosen Penev @ 2026-06-30 4:16 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, open list
In-Reply-To: <20260630041634.284127-1-rosenp@gmail.com>
Add missing dma_mapping_error() checks after every dma_map_single()
and skb_frag_dma_map() call. Without these, CONFIG_DMA_API_DEBUG
emits:
DMA-API: emac ... device driver failed to check map error
Also fix emac_recycle_rx_skb() which called dma_map_single() but
discarded the returned DMA address -- the descriptor kept a stale
(unmapped) address after the old mapping was freed.
The RX descriptors are allocated in coherent (uncached) DMA memory.
Add a shadow rx_dma[] array in regular cached memory to store the
raw DMA address of each RX slot, avoiding a slow uncached read of
dev->rx_desc[slot].data_ptr on the per-packet hot path. This
prevents a measurable throughput regression on non-coherent PowerPC
platforms where the original fix added such a read.
In emac_recycle_rx_skb(), use dma_sync_single_for_device() with the
actual received length (cache-line-aligned) instead of destroying
and recreating the mapping. The mapping is long-lived; only the
bytes touched by skb_copy_from_linear_data_offset() need
synchronization.
- __emac_prepare_rx_skb: check map error, free skb on failure
- emac_resize_rx_ring: check map error, invalidate slot on failure
- emac_recycle_rx_skb: map first, check error, then unmap old;
use dma_sync_single_for_device() with SKB_DATA_ALIGN(len + NET_IP_ALIGN)
- emac_start_xmit: check map error, free skb on failure
- emac_start_xmit_sg: check map error on both data and frag maps,
undo partial descriptor setup on frag failure
- emac_poll_rx: use rx_dma[] shadow array, unmap old skb before
passing to napi_gro_receive
- emac_clean_rx_ring: use rx_dma[] shadow array for consistency
There's a small performance decrease tested with iperf3
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 574 MBytes 481 Mbits/sec 1 sender
[ 5] 0.00-10.00 sec 572 MBytes 479 Mbits/sec receiver
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 558 MBytes 468 Mbits/sec 0 sender
[ 5] 0.00-10.00 sec 556 MBytes 466 Mbits/sec receiver
but probably worth it. For whatever reason after this patch, ath9k
stopped throwing DMA errors with CONFIG_DMA_API_DEBUG.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/ethernet/ibm/emac/core.c | 92 +++++++++++++++++++++++-----
drivers/net/ethernet/ibm/emac/core.h | 1 +
2 files changed, 77 insertions(+), 16 deletions(-)
diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c
index ced9690cddc3..aed1ad21e2ea 100644
--- a/drivers/net/ethernet/ibm/emac/core.c
+++ b/drivers/net/ethernet/ibm/emac/core.c
@@ -52,7 +52,15 @@
#include "core.h"
/*
- * Lack of dma_unmap_???? calls is intentional.
+ * Note on dma_unmap calls:
+ *
+ * RX buffers are properly unmapped before being remapped or passed to the
+ * network stack. See emac_recycle_rx_skb() and emac_poll_rx().
+ *
+ * TX buffers still lack dma_unmap calls for the reasons explained in the
+ * original note below (a single skb may be split across multiple BDs on
+ * TAH-equipped EMACs, making per-fragment tracking complex).
+ * The original rationale is kept for the TX path only:
*
* API-correct usage requires additional support state information to be
* maintained for every RX and TX buffer descriptor (BD). Unfortunately, due to
@@ -1058,6 +1066,7 @@ static int emac_resize_rx_ring(struct emac_instance *dev, int new_mtu)
/* Second pass, allocate new skbs */
for (i = 0; i < NUM_RX_BUFF; ++i) {
struct sk_buff *skb;
+ dma_addr_t dma;
skb = netdev_alloc_skb_ip_align(dev->ndev, rx_skb_size);
if (!skb) {
@@ -1066,12 +1075,24 @@ static int emac_resize_rx_ring(struct emac_instance *dev, int new_mtu)
}
BUG_ON(!dev->rx_skb[i]);
+ dma_unmap_single(&dev->ofdev->dev,
+ dev->rx_dma[i],
+ dev->rx_sync_size, DMA_FROM_DEVICE);
dev_kfree_skb(dev->rx_skb[i]);
- dev->rx_desc[i].data_ptr =
- dma_map_single(&dev->ofdev->dev, skb->data - NET_IP_ALIGN,
- rx_sync_size, DMA_FROM_DEVICE)
- + NET_IP_ALIGN;
+ dma = dma_map_single(&dev->ofdev->dev, skb->data - NET_IP_ALIGN,
+ rx_sync_size, DMA_FROM_DEVICE);
+ if (dma_mapping_error(&dev->ofdev->dev, dma)) {
+ dev_kfree_skb(skb);
+ dev->rx_skb[i] = NULL;
+ dev->rx_dma[i] = 0;
+ dev->rx_desc[i].data_ptr = 0;
+ dev->rx_desc[i].ctrl = 0;
+ ret = -ENOMEM;
+ goto oom;
+ }
+ dev->rx_desc[i].data_ptr = dma + NET_IP_ALIGN;
+ dev->rx_dma[i] = dma;
dev->rx_skb[i] = skb;
}
skip:
@@ -1150,9 +1171,13 @@ static void emac_clean_rx_ring(struct emac_instance *dev)
for (i = 0; i < NUM_RX_BUFF; ++i)
if (dev->rx_skb[i]) {
+ dma_unmap_single(&dev->ofdev->dev,
+ dev->rx_dma[i],
+ dev->rx_sync_size, DMA_FROM_DEVICE);
dev->rx_desc[i].ctrl = 0;
dev_kfree_skb(dev->rx_skb[i]);
dev->rx_skb[i] = NULL;
+ dev->rx_dma[i] = 0;
dev->rx_desc[i].data_ptr = 0;
}
@@ -1176,15 +1201,23 @@ static void emac_clear_mal_desc(struct mal_descriptor *desc, int count)
static int
__emac_prepare_rx_skb(struct sk_buff *skb, struct emac_instance *dev, int slot)
{
+ dma_addr_t dma;
+
if (unlikely(!skb))
return -ENOMEM;
dev->rx_skb[slot] = skb;
dev->rx_desc[slot].data_len = 0;
- dev->rx_desc[slot].data_ptr =
- dma_map_single(&dev->ofdev->dev, skb->data - NET_IP_ALIGN,
- dev->rx_sync_size, DMA_FROM_DEVICE) + NET_IP_ALIGN;
+ dma = dma_map_single(&dev->ofdev->dev, skb->data - NET_IP_ALIGN,
+ dev->rx_sync_size, DMA_FROM_DEVICE);
+ if (dma_mapping_error(&dev->ofdev->dev, dma)) {
+ dev->rx_skb[slot] = NULL;
+ dev_kfree_skb(skb);
+ return -ENOMEM;
+ }
+ dev->rx_desc[slot].data_ptr = dma + NET_IP_ALIGN;
+ dev->rx_dma[slot] = dma;
dma_wmb();
dev->rx_desc[slot].ctrl = MAL_RX_CTRL_EMPTY |
(slot == (NUM_RX_BUFF - 1) ? MAL_RX_CTRL_WRAP : 0);
@@ -1463,6 +1496,12 @@ static netdev_tx_t emac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
dev->tx_desc[slot].data_ptr = dma_map_single(&dev->ofdev->dev,
skb->data, len,
DMA_TO_DEVICE);
+ if (dma_mapping_error(&dev->ofdev->dev,
+ dev->tx_desc[slot].data_ptr)) {
+ dev->tx_skb[slot] = NULL;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
dev->tx_desc[slot].data_len = (u16) len;
dma_wmb();
dev->tx_desc[slot].ctrl = ctrl;
@@ -1530,8 +1569,12 @@ emac_start_xmit_sg(struct sk_buff *skb, struct net_device *ndev)
/* skb data */
dev->tx_skb[slot] = NULL;
chunk = min(len, MAL_MAX_TX_SIZE);
- dev->tx_desc[slot].data_ptr = pd =
- dma_map_single(&dev->ofdev->dev, skb->data, len, DMA_TO_DEVICE);
+ pd = dma_map_single(&dev->ofdev->dev, skb->data, len, DMA_TO_DEVICE);
+ if (dma_mapping_error(&dev->ofdev->dev, pd)) {
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+ dev->tx_desc[slot].data_ptr = pd;
dev->tx_desc[slot].data_len = (u16) chunk;
len -= chunk;
if (unlikely(len))
@@ -1547,6 +1590,18 @@ emac_start_xmit_sg(struct sk_buff *skb, struct net_device *ndev)
pd = skb_frag_dma_map(&dev->ofdev->dev, frag, 0, len,
DMA_TO_DEVICE);
+ if (dma_mapping_error(&dev->ofdev->dev, pd)) {
+ /* Undo partial descriptor setup and drop packet */
+ while (slot != dev->tx_slot) {
+ dev->tx_desc[slot].ctrl = 0;
+ --dev->tx_cnt;
+ if (--slot < 0)
+ slot = NUM_TX_BUFF - 1;
+ }
+ ++dev->estats.tx_undo;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
slot = emac_xmit_split(dev, slot, pd, len, i == nr_frags - 1,
ctrl);
@@ -1661,14 +1716,14 @@ static void emac_poll_tx(void *param)
static inline void emac_recycle_rx_skb(struct emac_instance *dev, int slot,
int len)
{
- struct sk_buff *skb = dev->rx_skb[slot];
-
DBG2(dev, "recycle %d %d" NL, slot, len);
- if (len)
- dma_map_single(&dev->ofdev->dev, skb->data - NET_IP_ALIGN,
- SKB_DATA_ALIGN(len + NET_IP_ALIGN),
- DMA_FROM_DEVICE);
+ if (len) {
+ dma_sync_single_for_device(&dev->ofdev->dev,
+ dev->rx_dma[slot],
+ SKB_DATA_ALIGN(len + NET_IP_ALIGN),
+ DMA_FROM_DEVICE);
+ }
dev->rx_desc[slot].data_len = 0;
dma_wmb();
@@ -1808,12 +1863,17 @@ static int emac_poll_rx(void *param, int budget)
continue;
sg:
if (ctrl & MAL_RX_CTRL_FIRST) {
+ dma_addr_t old_dma = dev->rx_dma[slot];
+
BUG_ON(dev->rx_sg_skb);
if (unlikely(emac_alloc_rx_skb_napi(dev, slot))) {
DBG(dev, "rx OOM %d" NL, slot);
++dev->estats.rx_dropped_oom;
emac_recycle_rx_skb(dev, slot, 0);
} else {
+ dma_unmap_single(&dev->ofdev->dev, old_dma,
+ dev->rx_sync_size,
+ DMA_FROM_DEVICE);
dev->rx_sg_skb = skb;
skb_put(skb, len);
}
diff --git a/drivers/net/ethernet/ibm/emac/core.h b/drivers/net/ethernet/ibm/emac/core.h
index 296da4bf3781..0719f98f3325 100644
--- a/drivers/net/ethernet/ibm/emac/core.h
+++ b/drivers/net/ethernet/ibm/emac/core.h
@@ -246,6 +246,7 @@ struct emac_instance {
struct sk_buff *tx_skb[NUM_TX_BUFF];
struct sk_buff *rx_skb[NUM_RX_BUFF];
+ dma_addr_t rx_dma[NUM_RX_BUFF];
/* Stats
*/
--
2.54.0
^ permalink raw reply related
* [PATCH 08/10] net: emac: replace #ifdef CONFIG_PPC_DCR_NATIVE with IS_ENABLED()
From: Rosen Penev @ 2026-06-30 4:16 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, open list
In-Reply-To: <20260630041634.284127-1-rosenp@gmail.com>
Convert compile-time #ifdef blocks to IS_ENABLED() conditionals
for better compile coverage and more idiomatic kernel code.
Affected functions: emac_rx_clk_tx, emac_rx_clk_default,
emac_reset, emac_init_phy in core.c, and mal_txeob/mal_rxeob
in mal.c.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/ethernet/ibm/emac/core.c | 41 ++++++++++++----------------
drivers/net/ethernet/ibm/emac/mal.c | 14 ++++------
2 files changed, 23 insertions(+), 32 deletions(-)
diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c
index aed1ad21e2ea..dba3cdfea340 100644
--- a/drivers/net/ethernet/ibm/emac/core.c
+++ b/drivers/net/ethernet/ibm/emac/core.c
@@ -139,20 +139,18 @@ static inline void emac_report_timeout_error(struct emac_instance *dev,
*/
static inline void emac_rx_clk_tx(struct emac_instance *dev)
{
-#ifdef CONFIG_PPC_DCR_NATIVE
- if (emac_has_feature(dev, EMAC_FTR_440EP_PHY_CLK_FIX))
+ if (IS_ENABLED(CONFIG_PPC_DCR_NATIVE) &&
+ emac_has_feature(dev, EMAC_FTR_440EP_PHY_CLK_FIX))
dcri_clrset(SDR0, SDR0_MFR,
0, SDR0_MFR_ECS >> dev->cell_index);
-#endif
}
static inline void emac_rx_clk_default(struct emac_instance *dev)
{
-#ifdef CONFIG_PPC_DCR_NATIVE
- if (emac_has_feature(dev, EMAC_FTR_440EP_PHY_CLK_FIX))
+ if (IS_ENABLED(CONFIG_PPC_DCR_NATIVE) &&
+ emac_has_feature(dev, EMAC_FTR_440EP_PHY_CLK_FIX))
dcri_clrset(SDR0, SDR0_MFR,
SDR0_MFR_ECS >> dev->cell_index, 0);
-#endif
}
/* PHY polling intervals */
@@ -339,7 +337,7 @@ static int emac_reset(struct emac_instance *dev)
{
struct emac_regs __iomem *p = dev->emacp;
int n = 20;
- bool __maybe_unused try_internal_clock = false;
+ bool try_internal_clock = false;
DBG(dev, "reset" NL);
@@ -351,8 +349,6 @@ static int emac_reset(struct emac_instance *dev)
emac_tx_disable(dev);
}
-#ifdef CONFIG_PPC_DCR_NATIVE
-do_retry:
/*
* PPC460EX/GT Embedded Processor Advanced User's Manual
* section 28.10.1 Mode Register 0 (EMACx_MR0) states:
@@ -370,7 +366,9 @@ static int emac_reset(struct emac_instance *dev)
* driver will temporarily switch to the internal clock, after
* the first reset fails.
*/
- if (emac_has_feature(dev, EMAC_FTR_460EX_PHY_CLK_FIX)) {
+retry:
+ if (IS_ENABLED(CONFIG_PPC_DCR_NATIVE) &&
+ emac_has_feature(dev, EMAC_FTR_460EX_PHY_CLK_FIX)) {
if (try_internal_clock || (dev->phy_address == 0xffffffff &&
dev->phy_map == 0xffffffff)) {
/* No PHY: select internal loop clock before reset */
@@ -382,19 +380,18 @@ static int emac_reset(struct emac_instance *dev)
SDR0_ETH_CFG_ECS << dev->cell_index, 0);
}
}
-#endif
out_be32(&p->mr0, EMAC_MR0_SRST);
while ((in_be32(&p->mr0) & EMAC_MR0_SRST) && n)
--n;
-#ifdef CONFIG_PPC_DCR_NATIVE
- if (emac_has_feature(dev, EMAC_FTR_460EX_PHY_CLK_FIX)) {
+ if (IS_ENABLED(CONFIG_PPC_DCR_NATIVE) &&
+ emac_has_feature(dev, EMAC_FTR_460EX_PHY_CLK_FIX)) {
if (!n && !try_internal_clock) {
/* first attempt has timed out. */
n = 20;
try_internal_clock = true;
- goto do_retry;
+ goto retry;
}
if (try_internal_clock || (dev->phy_address == 0xffffffff &&
@@ -404,7 +401,6 @@ static int emac_reset(struct emac_instance *dev)
SDR0_ETH_CFG_ECS << dev->cell_index, 0);
}
}
-#endif
if (n) {
dev->reset_failed = 0;
@@ -2754,18 +2750,16 @@ static int emac_init_phy(struct emac_instance *dev)
dev->phy.mdio_write = emac_mdio_write;
/* Enable internal clock source */
-#ifdef CONFIG_PPC_DCR_NATIVE
- if (emac_has_feature(dev, EMAC_FTR_440GX_PHY_CLK_FIX))
+ if (IS_ENABLED(CONFIG_PPC_DCR_NATIVE) &&
+ emac_has_feature(dev, EMAC_FTR_440GX_PHY_CLK_FIX))
dcri_clrset(SDR0, SDR0_MFR, 0, SDR0_MFR_ECS);
-#endif
/* PHY clock workaround */
emac_rx_clk_tx(dev);
/* Enable internal clock source on 440GX*/
-#ifdef CONFIG_PPC_DCR_NATIVE
- if (emac_has_feature(dev, EMAC_FTR_440GX_PHY_CLK_FIX))
+ if (IS_ENABLED(CONFIG_PPC_DCR_NATIVE) &&
+ emac_has_feature(dev, EMAC_FTR_440GX_PHY_CLK_FIX))
dcri_clrset(SDR0, SDR0_MFR, 0, SDR0_MFR_ECS);
-#endif
/* Configure EMAC with defaults so we can at least use MDIO
* This is needed mostly for 440GX
*/
@@ -2825,10 +2819,9 @@ static int emac_init_phy(struct emac_instance *dev)
}
/* Enable external clock source */
-#ifdef CONFIG_PPC_DCR_NATIVE
- if (emac_has_feature(dev, EMAC_FTR_440GX_PHY_CLK_FIX))
+ if (IS_ENABLED(CONFIG_PPC_DCR_NATIVE) &&
+ emac_has_feature(dev, EMAC_FTR_440GX_PHY_CLK_FIX))
dcri_clrset(SDR0, SDR0_MFR, SDR0_MFR_ECS, 0);
-#endif
mutex_unlock(&emac_phy_map_lock);
if (i == 0x20) {
printk(KERN_WARNING "%pOF: can't find PHY!\n", np);
diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c
index d12a376f69fd..2adfd9d9bdb1 100644
--- a/drivers/net/ethernet/ibm/emac/mal.c
+++ b/drivers/net/ethernet/ibm/emac/mal.c
@@ -282,11 +282,10 @@ static irqreturn_t mal_txeob(int irq, void *dev_instance)
mal_schedule_poll(mal);
set_mal_dcrn(mal, MAL_TXEOBISR, r);
-#ifdef CONFIG_PPC_DCR_NATIVE
- if (mal_has_feature(mal, MAL_FTR_CLEAR_ICINTSTAT))
+ if (IS_ENABLED(CONFIG_PPC_DCR_NATIVE) &&
+ mal_has_feature(mal, MAL_FTR_CLEAR_ICINTSTAT))
mtdcri(SDR0, DCRN_SDR_ICINTSTAT,
- (mfdcri(SDR0, DCRN_SDR_ICINTSTAT) | ICINTSTAT_ICTX));
-#endif
+ (mfdcri(SDR0, DCRN_SDR_ICINTSTAT) | ICINTSTAT_ICTX));
return IRQ_HANDLED;
}
@@ -302,11 +301,10 @@ static irqreturn_t mal_rxeob(int irq, void *dev_instance)
mal_schedule_poll(mal);
set_mal_dcrn(mal, MAL_RXEOBISR, r);
-#ifdef CONFIG_PPC_DCR_NATIVE
- if (mal_has_feature(mal, MAL_FTR_CLEAR_ICINTSTAT))
+ if (IS_ENABLED(CONFIG_PPC_DCR_NATIVE) &&
+ mal_has_feature(mal, MAL_FTR_CLEAR_ICINTSTAT))
mtdcri(SDR0, DCRN_SDR_ICINTSTAT,
- (mfdcri(SDR0, DCRN_SDR_ICINTSTAT) | ICINTSTAT_ICRX));
-#endif
+ (mfdcri(SDR0, DCRN_SDR_ICINTSTAT) | ICINTSTAT_ICRX));
return IRQ_HANDLED;
}
--
2.54.0
^ permalink raw reply related
* [PATCH 09/10] net: emac: add Byte Queue Limits (BQL) support
From: Rosen Penev @ 2026-06-30 4:16 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, open list
In-Reply-To: <20260630041634.284127-1-rosenp@gmail.com>
Add BQL to the TX path to improve tail latency under high throughput:
- Call netdev_tx_sent_queue() before ringing the TX doorbell
- Call netdev_tx_completed_queue() with byte/packet counts after
TX completions
- Call netdev_reset_queue() on close and full TX reset
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/ethernet/ibm/emac/core.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c
index dba3cdfea340..da5f3d436aa3 100644
--- a/drivers/net/ethernet/ibm/emac/core.c
+++ b/drivers/net/ethernet/ibm/emac/core.c
@@ -751,6 +751,7 @@ static void emac_full_tx_reset(struct emac_instance *dev)
mal_disable_tx_channel(dev->mal, dev->mal_tx_chan);
emac_clean_tx_ring(dev);
dev->tx_cnt = dev->tx_slot = dev->ack_slot = 0;
+ netdev_reset_queue(dev->ndev);
emac_configure(dev);
@@ -1428,6 +1429,7 @@ static int emac_close(struct net_device *ndev)
emac_clean_tx_ring(dev);
emac_clean_rx_ring(dev);
+ netdev_reset_queue(ndev);
netif_carrier_off(ndev);
return 0;
@@ -1448,6 +1450,9 @@ static inline netdev_tx_t emac_xmit_finish(struct emac_instance *dev, int len)
{
struct emac_regs __iomem *p = dev->emacp;
struct net_device *ndev = dev->ndev;
+ struct netdev_queue *txq = netdev_get_tx_queue(ndev, 0);
+
+ netdev_tx_sent_queue(txq, len);
/* Send the packet out. If the if makes a significant perf
* difference, then we can store the TMR0 value in "dev"
@@ -1666,6 +1671,7 @@ static void emac_parse_tx_error(struct emac_instance *dev, u16 ctrl)
static void emac_poll_tx(void *param)
{
struct emac_instance *dev = param;
+ struct netdev_queue *txq = netdev_get_tx_queue(dev->ndev, 0);
u32 bad_mask;
DBG2(dev, "poll_tx, %d %d" NL, dev->tx_cnt, dev->ack_slot);
@@ -1679,6 +1685,7 @@ static void emac_poll_tx(void *param)
if (dev->tx_cnt) {
u16 ctrl;
int slot = dev->ack_slot, n = 0;
+ unsigned int bytes = 0;
again:
ctrl = dev->tx_desc[slot].ctrl;
if (!(ctrl & MAL_TX_CTRL_READY)) {
@@ -1686,6 +1693,7 @@ static void emac_poll_tx(void *param)
++n;
if (skb) {
+ bytes += skb->len;
dev_kfree_skb(skb);
dev->tx_skb[slot] = NULL;
}
@@ -1699,6 +1707,7 @@ static void emac_poll_tx(void *param)
}
if (n) {
dev->ack_slot = slot;
+ netdev_tx_completed_queue(txq, n, bytes);
if (netif_queue_stopped(dev->ndev) &&
dev->tx_cnt < EMAC_TX_WAKEUP_THRESH)
netif_wake_queue(dev->ndev);
--
2.54.0
^ permalink raw reply related
* [PATCH 10/10] net: emac: use ndo_get_stats64 instead of ndo_get_stats
From: Rosen Penev @ 2026-06-30 4:16 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, open list
In-Reply-To: <20260630041634.284127-1-rosenp@gmail.com>
Replace the legacy emac_stats() callback with emac_get_stats64()
that fills struct rtnl_link_stats64 directly from the driver's
u64 counters, avoiding truncation of 64-bit values on 32-bit
architectures.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/ethernet/ibm/emac/core.c | 84 ++++++++++++++--------------
1 file changed, 41 insertions(+), 43 deletions(-)
diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c
index da5f3d436aa3..c62abc8aa471 100644
--- a/drivers/net/ethernet/ibm/emac/core.c
+++ b/drivers/net/ethernet/ibm/emac/core.c
@@ -2026,57 +2026,55 @@ static irqreturn_t emac_irq(int irq, void *dev_instance)
return IRQ_HANDLED;
}
-static struct net_device_stats *emac_stats(struct net_device *ndev)
+static void emac_get_stats64(struct net_device *ndev,
+ struct rtnl_link_stats64 *stats)
{
struct emac_instance *dev = netdev_priv(ndev);
struct emac_stats *st = &dev->stats;
struct emac_error_stats *est = &dev->estats;
- struct net_device_stats *nst = &ndev->stats;
unsigned long flags;
DBG2(dev, "stats" NL);
- /* Compute "legacy" statistics */
spin_lock_irqsave(&dev->lock, flags);
- nst->rx_packets = (unsigned long)st->rx_packets;
- nst->rx_bytes = (unsigned long)st->rx_bytes;
- nst->tx_packets = (unsigned long)st->tx_packets;
- nst->tx_bytes = (unsigned long)st->tx_bytes;
- nst->rx_dropped = (unsigned long)(est->rx_dropped_oom +
- est->rx_dropped_error +
- est->rx_dropped_resize +
- est->rx_dropped_mtu);
- nst->tx_dropped = (unsigned long)est->tx_dropped;
-
- nst->rx_errors = (unsigned long)est->rx_bd_errors;
- nst->rx_fifo_errors = (unsigned long)(est->rx_bd_overrun +
- est->rx_fifo_overrun +
- est->rx_overrun);
- nst->rx_frame_errors = (unsigned long)(est->rx_bd_alignment_error +
- est->rx_alignment_error);
- nst->rx_crc_errors = (unsigned long)(est->rx_bd_bad_fcs +
- est->rx_bad_fcs);
- nst->rx_length_errors = (unsigned long)(est->rx_bd_runt_packet +
- est->rx_bd_short_event +
- est->rx_bd_packet_too_long +
- est->rx_bd_out_of_range +
- est->rx_bd_in_range +
- est->rx_runt_packet +
- est->rx_short_event +
- est->rx_packet_too_long +
- est->rx_out_of_range +
- est->rx_in_range);
-
- nst->tx_errors = (unsigned long)(est->tx_bd_errors + est->tx_errors);
- nst->tx_fifo_errors = (unsigned long)(est->tx_bd_underrun +
- est->tx_underrun);
- nst->tx_carrier_errors = (unsigned long)est->tx_bd_carrier_loss;
- nst->collisions = (unsigned long)(est->tx_bd_excessive_deferral +
- est->tx_bd_excessive_collisions +
- est->tx_bd_late_collision +
- est->tx_bd_multple_collisions);
+ stats->rx_packets = st->rx_packets;
+ stats->rx_bytes = st->rx_bytes;
+ stats->tx_packets = st->tx_packets;
+ stats->tx_bytes = st->tx_bytes;
+ stats->rx_dropped = est->rx_dropped_oom +
+ est->rx_dropped_error +
+ est->rx_dropped_resize +
+ est->rx_dropped_mtu;
+ stats->tx_dropped = est->tx_dropped;
+
+ stats->rx_errors = est->rx_bd_errors;
+ stats->rx_fifo_errors = est->rx_bd_overrun +
+ est->rx_fifo_overrun +
+ est->rx_overrun;
+ stats->rx_frame_errors = est->rx_bd_alignment_error +
+ est->rx_alignment_error;
+ stats->rx_crc_errors = est->rx_bd_bad_fcs +
+ est->rx_bad_fcs;
+ stats->rx_length_errors = est->rx_bd_runt_packet +
+ est->rx_bd_short_event +
+ est->rx_bd_packet_too_long +
+ est->rx_bd_out_of_range +
+ est->rx_bd_in_range +
+ est->rx_runt_packet +
+ est->rx_short_event +
+ est->rx_packet_too_long +
+ est->rx_out_of_range +
+ est->rx_in_range;
+
+ stats->tx_errors = est->tx_bd_errors + est->tx_errors;
+ stats->tx_fifo_errors = est->tx_bd_underrun +
+ est->tx_underrun;
+ stats->tx_carrier_errors = est->tx_bd_carrier_loss;
+ stats->collisions = est->tx_bd_excessive_deferral +
+ est->tx_bd_excessive_collisions +
+ est->tx_bd_late_collision +
+ est->tx_bd_multple_collisions;
spin_unlock_irqrestore(&dev->lock, flags);
- return nst;
}
static struct mal_commac_ops emac_commac_ops = {
@@ -3040,7 +3038,7 @@ static int emac_init_config(struct emac_instance *dev)
static const struct net_device_ops emac_netdev_ops = {
.ndo_open = emac_open,
.ndo_stop = emac_close,
- .ndo_get_stats = emac_stats,
+ .ndo_get_stats64 = emac_get_stats64,
.ndo_set_rx_mode = emac_set_multicast_list,
.ndo_eth_ioctl = emac_ioctl,
.ndo_tx_timeout = emac_tx_timeout,
@@ -3052,7 +3050,7 @@ static const struct net_device_ops emac_netdev_ops = {
static const struct net_device_ops emac_gige_netdev_ops = {
.ndo_open = emac_open,
.ndo_stop = emac_close,
- .ndo_get_stats = emac_stats,
+ .ndo_get_stats64 = emac_get_stats64,
.ndo_set_rx_mode = emac_set_multicast_list,
.ndo_eth_ioctl = emac_ioctl,
.ndo_tx_timeout = emac_tx_timeout,
--
2.54.0
^ permalink raw reply related
* [PATCH] usb: atm: ueagle: fix use-after-free in uea_upload_pre_firmware()
From: Deepanshu Kartikey @ 2026-06-30 4:17 UTC (permalink / raw)
To: castet.matthieu, stf_xl, 3chas3, gregkh
Cc: linux-atm-general, netdev, linux-usb, linux-kernel,
Deepanshu Kartikey, syzbot+3d45d763d18796f97412
uea_load_firmware() calls request_firmware_nowait() passing a raw
struct usb_device pointer as context, without holding a reference
to it.
If the USB device is disconnected before the firmware workqueue
fires, the usb_device and its usb_interface objects are freed while
uea_upload_pre_firmware() is still pending on the workqueue. When
the callback eventually runs, it accesses the freed memory causing
a slab-use-after-free:
BUG: KASAN: slab-use-after-free in __intf_to_usbdev
include/linux/usb.h:752 [inline]
BUG: KASAN: slab-use-after-free in uea_upload_pre_firmware+0x8d/0x640
drivers/usb/atm/ueagle-atm.c:598
Read of size 8 at addr ffff88802b0710b8 by task kworker/0:2/1664
Fix by calling usb_get_dev() before queuing the firmware request to
pin the usb_device in memory for the lifetime of the async operation,
and usb_put_dev() in the callback once it is finished with the
pointer. On the error path where request_firmware_nowait() itself
fails, drop the reference immediately since the callback will never
fire.
Reported-by: syzbot+3d45d763d18796f97412@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=3d45d763d18796f97412
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
drivers/usb/atm/ueagle-atm.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index d610cdcef7d0..686cc58fb89f 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -663,6 +663,7 @@ static void uea_upload_pre_firmware(const struct firmware *fw_entry,
uea_err(usb, "firmware is corrupted\n");
err:
release_firmware(fw_entry);
+ usb_put_dev(usb);
}
/*
@@ -693,12 +694,14 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
break;
}
+ usb_get_dev(usb);
ret = request_firmware_nowait(THIS_MODULE, 1, fw_name, &usb->dev,
GFP_KERNEL, usb,
uea_upload_pre_firmware);
- if (ret)
+ if (ret) {
uea_err(usb, "firmware %s is not available\n", fw_name);
- else
+ usb_put_dev(usb);
+ } else
uea_info(usb, "loading firmware %s\n", fw_name);
return ret;
--
2.43.0
^ permalink raw reply related
* RE: [PATCH net v5 1/4] net: ethernet: oa_tc6: Interrupt is active low, level triggered.
From: Selvamani Rajagopal @ 2026-06-30 4:21 UTC (permalink / raw)
To: Parthiban.Veerasooran@microchip.com
Cc: andrew@lunn.ch, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org, Conor.Dooley@microchip.com,
devicetree@vger.kernel.org, andrew+netdev@lunn.ch,
davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, robh@kernel.org, krzk+dt@kernel.org,
conor+dt@kernel.org, Piergiorgio Beruto
In-Reply-To: <d15eaa01-3312-420f-a34a-d810710e5b12@microchip.com>
> -----Original Message-----
> From: Parthiban.Veerasooran@microchip.com <Parthiban.Veerasooran@microchip.com>
> Subject: Re: [PATCH net v5 1/4] net: ethernet: oa_tc6: Interrupt is active low, level
> triggered.
>
>
> Sorry for the delayed response. I see you already shared the patches for
> the fixes. Today I will test the below patch series and share the
> feedback ASAP.
>
>
Parthiban.
No worries. Let us hope this address the NULL pointer reference (and traffic recovers gracefully)
Sincerely
Selva
^ permalink raw reply
* Re: [PATCH net] selftests: net: bump default cmd() timeout to 20 seconds
From: Pavan Chebbi @ 2026-06-30 4:31 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, shuah,
petrm, leitao, dw, noren, gal, linux-kselftest
In-Reply-To: <20260629233348.2145841-1-kuba@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 2772 bytes --]
On Tue, Jun 30, 2026 at 5:04 AM Jakub Kicinski <kuba@kernel.org> wrote:
>
> We always used 5 sec as the default command timeout. But soon after
> it was introduced, David effectively made us ignore the timeout
> (it was passed to process.communicate() as the wrong argument).
> Gal recently fixed that, but turns out the 5 sec is not enough
> for a lot of tests and setups. The fix regressed regressions.
>
> In particular running reconfig commands (e.g. XDP attach) on mlx5
> with 32 rings and 9k MTU, on a heavily-debug-enabled kernel takes
> more than 5 sec. The XDP installation command will time out after
> 5 sec but since the sleeps in the kernel are non interruptible
> the command finishes anyway, leaving the XDP program attached,
> but with non-zero exit code. defer()ed cleanups are not installed,
> breaking the environment for subsequent tests.
>
> Since "install XDP" is a pretty normal command a "point fix"
> does not seem appropriate. 32 rings is a fairly reasonable
> config, too, so we should just increase the timeout to 20 sec.
>
> There's no real reason behind the value of 20.
>
> Fixes: 1cf270424218 ("net: selftest: add test for netdev netlink queue-get API")
> Fixes: f0bd19316663 ("selftests: net: fix timeout passed as positional argument to communicate()")
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
> ---
> CC: shuah@kernel.org
> CC: petrm@nvidia.com
> CC: leitao@debian.org
> CC: dw@davidwei.uk
> CC: noren@nvidia.com
> CC: gal@nvidia.com
> CC: linux-kselftest@vger.kernel.org
> ---
> tools/testing/selftests/net/lib/py/utils.py | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
Reviewed-by: Pavan Chebbi <pavan.chebbi@broadcom.com>
> diff --git a/tools/testing/selftests/net/lib/py/utils.py b/tools/testing/selftests/net/lib/py/utils.py
> index 308c91833239..9b40049e2dbb 100644
> --- a/tools/testing/selftests/net/lib/py/utils.py
> +++ b/tools/testing/selftests/net/lib/py/utils.py
> @@ -44,7 +44,7 @@ import time
> Use bkg() instead to run a command in the background.
> """
> def __init__(self, comm, shell=None, fail=True, expect_fail=False, ns=None,
> - background=False, host=None, timeout=5, ksft_ready=None,
> + background=False, host=None, timeout=20, ksft_ready=None,
> ksft_wait=None):
> if ns:
> if hasattr(ns, 'user_ns_path'):
> @@ -113,7 +113,7 @@ import time
>
> return stdout, stderr
>
> - def process(self, terminate=True, fail=None, expect_fail=False, timeout=5):
> + def process(self, terminate=True, fail=None, expect_fail=False, timeout=20):
> if fail is None:
> fail = not terminate
>
> --
> 2.54.0
>
>
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5469 bytes --]
^ permalink raw reply
* Re: [PATCH net] selftests: drv-net: tso: don't touch dangerous feature bits
From: Pavan Chebbi @ 2026-06-30 4:33 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, shuah,
daniel.zahka, linux-kselftest
In-Reply-To: <20260629233923.2151144-1-kuba@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 1024 bytes --]
On Tue, Jun 30, 2026 at 5:09 AM Jakub Kicinski <kuba@kernel.org> wrote:
>
> query_nic_features() detects which offloads depend on tx-gso-partial
> by enabling everything, turning tx-gso-partial off, and seeing which
> active features drop out. Enabling all hw features is dangerous:
> we may end up enabling rx-fcs and loopback for example. For the
> ice driver we end up getting into problems with feature dependencies
> so the cleanup isn't successful either, and the test exits with
> rx-fcs and loopback enabled.
>
> Scope the feature probing just to segmentation bits.
>
> Fixes: 266b835e5e84 ("selftests: drv-net: tso: enable test cases based on hw_features")
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
> ---
> CC: shuah@kernel.org
> CC: daniel.zahka@gmail.com
> CC: linux-kselftest@vger.kernel.org
> ---
> tools/testing/selftests/drivers/net/hw/tso.py | 16 ++++++----------
> 1 file changed, 6 insertions(+), 10 deletions(-)
>
Reviewed-by: Pavan Chebbi <pavan.chebbi@broadcom.com>
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5469 bytes --]
^ permalink raw reply
* RE: [PATCH net v2 2/2] net: ethernet: oa_tc6: Improvement in buffer overflow handling
From: Selvamani Rajagopal @ 2026-06-30 4:41 UTC (permalink / raw)
To: Jakub Kicinski, Selvamani Rajagopal via B4 Relay
Cc: Parthiban Veerasooran, Andrew Lunn, Piergiorgio Beruto,
David S. Miller, Eric Dumazet, Paolo Abeni,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Andrew Lunn
In-Reply-To: <20260629192959.445776c9@kernel.org>
> -----Original Message-----
> From: Jakub Kicinski <kuba@kernel.org>
> Sent: Monday, June 29, 2026 7:30 PM
> Subject: Re: [PATCH net v2 2/2] net: ethernet: oa_tc6: Improvement in buffer overflow handling
>
>
> This sounds rather scary. The driver seems to have no length
> information to confirm it got all the chunks. So if it missed
> a middle chunk we will never know? At the very least this seems
> like something we should increment rx_error for, not just rx_dropped?
If middle chunk is lost, we won't know. If we look at prcs_rx_chunk_payload, it relies on start_valid
and end_valid bits only. start_byte_offset and end_byte_offset are always associated with the above
two bits.
When rx buffer overflow occurs, we don't know whether to trust the data chunk or lost, if lost,
how many lost, so, we drop everything and start looking for new frame indicated by "start_bit"
That's why drop count is incremented.
If we dig deeper, though we increment the dropped count by one. It is possible that we may have lost
more than one frame.
Function process_spi_data_rx_buf bails out of the "for loop" with number_of_rx_chunks,
after EAGAIN error is seen. We don't go through all the rx chunks that are already received, when
we start looking for next data chunks with "start_bit".
That improvement is for another time. I didn't want to complicate the changes for this effort.
>
> Regarding the patch itself, I'm not clear on why we need to look
> for new frame. Will we not notice the start bit immediately and
> call oa_tc6_allocate_rx_skb() (if there is indeed a start bit in the
> stream?)
>
The chunk with "start_bit" could be next chunk or "nth" chunk. We don't know.
We keep throwing away incoming data chunks until we see a chunk with data_bit.
> So handling skb-already-exists in oa_tc6_allocate_rx_skb() seems
> like enough to start a new frame.
I don't think we can rely on this. What if we get two continuous data chunks with "start_bit" set
without end_valid bit? Later skb would overwrite the previous one. (either due to corruption or lost chunks)
>
> Sashiko has another comment:
Let me read at the Sashiko review
>
> https://sashiko.dev/#/patchset/20260626-fix-race-condition-and-crash-v2-1-
> b6c5c10e604f@onsemi.com
> <https://sashiko.dev/#/patchset/20260626-fix-race-condition-and-crash-v2-1-b6c5c10e604f@onsemi.com
> =sashiko.dev>
> --
> pw-bot: cr
^ permalink raw reply
* Re: [PATCH net] net: usb: net1080: validate packet_len before pad-byte access in rx_fixup
From: Xiang Mei @ 2026-06-30 4:51 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Paolo Abeni, netdev,
linux-usb, linux-kernel, Weiming Shi
In-Reply-To: <20260629185415.56ec8b67@kernel.org>
On Mon, Jun 29, 2026 at 6:54 PM Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Sat, 27 Jun 2026 14:28:54 -0700 Xiang Mei wrote:
> > if ((packet_len & 0x01) == 0) {
>
> just add "skb->len &&" to this condition?
>
Thanks for the review; your one-line patch is clearer.
v2 has been sent.
Xiang
> > + if (packet_len >= skb->len) {
> > + dev->net->stats.rx_frame_errors++;
> > + netdev_dbg(dev->net, "bad packet len %d (expected %d)\n",
> > + skb->len, packet_len);
> > + nc_ensure_sync(dev);
> > + return 0;
> > + }
> --
> pw-bot: cr
^ 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