lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250512012748.79749-6-damien.riegel@silabs.com>
Date: Sun, 11 May 2025 21:27:38 -0400
From: Damien Riégel <damien.riegel@...abs.com>
To: Andrew Lunn <andrew+netdev@...n.ch>,
        "David S . Miller" <davem@...emloft.net>,
        Eric Dumazet <edumazet@...gle.com>, Jakub Kicinski <kuba@...nel.org>,
        Paolo Abeni <pabeni@...hat.com>, Rob Herring <robh@...nel.org>,
        Krzysztof Kozlowski <krzk+dt@...nel.org>,
        Conor Dooley <conor+dt@...nel.org>,
        Silicon Labs Kernel Team <linux-devel@...abs.com>,
        netdev@...r.kernel.org, devicetree@...r.kernel.org,
        linux-kernel@...r.kernel.org
Subject: [RFC net-next 05/15] net: cpc: implement basic transmit path

Implement a very basic transmission path for endpoints. Transmitting
involves the following flow:
  - user call cpc_endpoint_write() with the SKB they wish to send
  - a CPC header is prepared and prepended to the frame
  - SKB is pushed to endpoint's holding queue (*)
  - SKB is dequeued from endpoint's holding queue and moved to
    interface's transmit queue (*)
  - interface is woken up by invoking its wake_tx() callback
  - interface work task is now responsible for dequeueing and
    transmitting that SKB when it's convenient for it to do so.

(*) Endpoint's holding queue currently serves no purpose but it will
    when other protocol features are implemented.

Signed-off-by: Damien Riégel <damien.riegel@...abs.com>
---
 drivers/net/cpc/Makefile    |  2 +-
 drivers/net/cpc/cpc.h       | 15 +++++++++
 drivers/net/cpc/endpoint.c  | 33 +++++++++++++++++++
 drivers/net/cpc/interface.c | 40 +++++++++++++++++++++++
 drivers/net/cpc/interface.h |  7 +++++
 drivers/net/cpc/main.c      | 50 +++++++++++++++++++++++++++++
 drivers/net/cpc/protocol.c  | 63 +++++++++++++++++++++++++++++++++++++
 drivers/net/cpc/protocol.h  | 19 +++++++++++
 8 files changed, 228 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/cpc/protocol.c
 create mode 100644 drivers/net/cpc/protocol.h

diff --git a/drivers/net/cpc/Makefile b/drivers/net/cpc/Makefile
index 81c470012c1..0e9c3f775dc 100644
--- a/drivers/net/cpc/Makefile
+++ b/drivers/net/cpc/Makefile
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
 
-cpc-y := endpoint.o header.o interface.o main.o
+cpc-y := endpoint.o header.o interface.o main.o protocol.o
 
 obj-$(CONFIG_CPC)	+= cpc.o
diff --git a/drivers/net/cpc/cpc.h b/drivers/net/cpc/cpc.h
index cbd1b3d6a03..2f54e5b660e 100644
--- a/drivers/net/cpc/cpc.h
+++ b/drivers/net/cpc/cpc.h
@@ -7,6 +7,7 @@
 #define __CPC_H
 
 #include <linux/device.h>
+#include <linux/skbuff.h>
 #include <linux/types.h>
 
 #define CPC_ENDPOINT_NAME_MAX_LEN 128
@@ -24,6 +25,8 @@ extern const struct bus_type cpc_bus;
  * @id: Endpoint id, uniquely identifies an endpoint within a CPC device.
  * @intf: Pointer to CPC device this endpoint belongs to.
  * @list_node: list_head member for linking in a CPC device.
+ * @holding_queue: Contains frames that were not pushed to the transport layer
+ *                 due to having insufficient space in the transmit window.
  *
  * Each endpoint can send and receive data without consideration of the other endpoints sharing the
  * same physical link.
@@ -36,6 +39,8 @@ struct cpc_endpoint {
 
 	struct cpc_interface *intf;
 	struct list_head list_node;
+
+	struct sk_buff_head holding_queue;
 };
 
 struct cpc_endpoint *cpc_endpoint_alloc(struct cpc_interface *intf, u8 id);
@@ -44,6 +49,8 @@ struct cpc_endpoint *cpc_endpoint_new(struct cpc_interface *intf, u8 id, const c
 
 void cpc_endpoint_unregister(struct cpc_endpoint *ep);
 
+int cpc_endpoint_write(struct cpc_endpoint *ep, struct sk_buff *skb);
+
 /**
  * cpc_endpoint_from_dev() - Upcast from a device pointer.
  * @dev: Reference to a device.
@@ -137,4 +144,12 @@ static inline struct cpc_driver *cpc_driver_from_drv(const struct device_driver
 	return container_of(drv, struct cpc_driver, driver);
 }
 
+/*---------------------------------------------------------------------------*/
+
+struct sk_buff *cpc_skb_alloc(size_t payload_len, gfp_t priority);
+void cpc_skb_set_ctx(struct sk_buff *skb,
+		     void (*destructor)(struct sk_buff *skb),
+		     void *ctx);
+void *cpc_skb_get_ctx(struct sk_buff *skb);
+
 #endif
diff --git a/drivers/net/cpc/endpoint.c b/drivers/net/cpc/endpoint.c
index 98e49614320..4e98955be30 100644
--- a/drivers/net/cpc/endpoint.c
+++ b/drivers/net/cpc/endpoint.c
@@ -6,7 +6,9 @@
 #include <linux/string.h>
 
 #include "cpc.h"
+#include "header.h"
 #include "interface.h"
+#include "protocol.h"
 
 /**
  * cpc_ep_release() - Actual release of the CPC endpoint.
@@ -18,6 +20,8 @@ static void cpc_ep_release(struct device *dev)
 {
 	struct cpc_endpoint *ep = cpc_endpoint_from_dev(dev);
 
+	skb_queue_purge(&ep->holding_queue);
+
 	cpc_interface_put(ep->intf);
 	kfree(ep);
 }
@@ -51,6 +55,8 @@ struct cpc_endpoint *cpc_endpoint_alloc(struct cpc_interface *intf, u8 id)
 	ep->dev.bus = &cpc_bus;
 	ep->dev.release = cpc_ep_release;
 
+	skb_queue_head_init(&ep->holding_queue);
+
 	device_initialize(&ep->dev);
 
 	return ep;
@@ -165,3 +171,30 @@ void cpc_endpoint_unregister(struct cpc_endpoint *ep)
 	device_del(&ep->dev);
 	put_device(&ep->dev);
 }
+
+/**
+ * cpc_endpoint_write - Write a DATA frame.
+ * @ep: Endpoint handle.
+ * @skb: Frame to send.
+ *
+ * @return: 0 on success, otherwise a negative error code.
+ */
+int cpc_endpoint_write(struct cpc_endpoint *ep, struct sk_buff *skb)
+{
+	struct cpc_header hdr;
+	int err;
+
+	if (ep->intf->ops->csum)
+		ep->intf->ops->csum(skb);
+
+	memset(&hdr, 0, sizeof(hdr));
+	hdr.ctrl = cpc_header_get_ctrl(CPC_FRAME_TYPE_DATA, true);
+	hdr.ep_id = ep->id;
+	hdr.recv_wnd = CPC_HEADER_MAX_RX_WINDOW;
+	hdr.seq = 0;
+	hdr.dat.payload_len = skb->len;
+
+	err = __cpc_protocol_write(ep, &hdr, skb);
+
+	return err;
+}
diff --git a/drivers/net/cpc/interface.c b/drivers/net/cpc/interface.c
index 6b3fc16f212..1dd87deed59 100644
--- a/drivers/net/cpc/interface.c
+++ b/drivers/net/cpc/interface.c
@@ -58,6 +58,8 @@ struct cpc_interface *cpc_interface_alloc(struct device *parent,
 	mutex_init(&intf->lock);
 	INIT_LIST_HEAD(&intf->eps);
 
+	skb_queue_head_init(&intf->tx_queue);
+
 	intf->ops = ops;
 
 	intf->dev.parent = parent;
@@ -154,3 +156,41 @@ struct cpc_endpoint *cpc_interface_get_endpoint(struct cpc_interface *intf, u8 e
 
 	return ep;
 }
+
+/**
+ * cpc_interface_send_frame() - Queue a socket buffer for transmission.
+ * @intf: Interface to send SKB over.
+ * @ops: SKB to send.
+ *
+ * Queue SKB in interface's transmit queue and signal the interface. Interface is expected to use
+ * cpc_interface_dequeue() to get the next SKB to transmit.
+ */
+void cpc_interface_send_frame(struct cpc_interface *intf, struct sk_buff *skb)
+{
+	skb_queue_tail(&intf->tx_queue, skb);
+	intf->ops->wake_tx(intf);
+}
+
+/**
+ * cpc_interface_dequeue() - Get the next SKB that was queued for transmission.
+ * @intf: Interface.
+ *
+ * Get an SKB that was previously queued by cpc_interface_send_frame().
+ *
+ * Return: An SKB, or %NULL if queue was empty.
+ */
+struct sk_buff *cpc_interface_dequeue(struct cpc_interface *intf)
+{
+	return skb_dequeue(&intf->tx_queue);
+}
+
+/**
+ * cpc_interface_tx_queue_empty() - Check if transmit queue is empty.
+ * @intf: Interface.
+ *
+ * Return: True if transmit queue is empty, false otherwise.
+ */
+bool cpc_interface_tx_queue_empty(struct cpc_interface *intf)
+{
+	return skb_queue_empty_lockless(&intf->tx_queue);
+}
diff --git a/drivers/net/cpc/interface.h b/drivers/net/cpc/interface.h
index d6b6d9ce5de..1b501b1f6dc 100644
--- a/drivers/net/cpc/interface.h
+++ b/drivers/net/cpc/interface.h
@@ -22,6 +22,7 @@ struct cpc_interface_ops;
  * @index: Device index.
  * @lock: Protect access to endpoint list.
  * @eps: List of endpoints managed by this device.
+ * @tx_queue: Transmit queue to be consumed by the interface.
  */
 struct cpc_interface {
 	struct device dev;
@@ -35,6 +36,8 @@ struct cpc_interface {
 
 	struct mutex lock;	/* Protect eps from concurrent access. */
 	struct list_head eps;
+
+	struct sk_buff_head tx_queue;
 };
 
 /**
@@ -58,6 +61,10 @@ void cpc_interface_unregister(struct cpc_interface *intf);
 
 struct cpc_endpoint *cpc_interface_get_endpoint(struct cpc_interface *intf, u8 ep_id);
 
+void cpc_interface_send_frame(struct cpc_interface *intf, struct sk_buff *skb);
+struct sk_buff *cpc_interface_dequeue(struct cpc_interface *intf);
+bool cpc_interface_tx_queue_empty(struct cpc_interface *intf);
+
 /**
  * cpc_interface_get() - Get a reference to interface and return its pointer.
  * @intf: Interface to get.
diff --git a/drivers/net/cpc/main.c b/drivers/net/cpc/main.c
index dcbe6dcb651..8feb0613252 100644
--- a/drivers/net/cpc/main.c
+++ b/drivers/net/cpc/main.c
@@ -7,6 +7,56 @@
 #include <linux/module.h>
 
 #include "cpc.h"
+#include "header.h"
+
+/**
+ * cpc_skb_alloc() - Allocate an skb with a specific headroom for CPC headers.
+ * @payload_len: Length of the payload.
+ * @priority: GFP priority to use for memory allocation.
+ *
+ * Return: Pointer to the skb on success, otherwise NULL.
+ */
+struct sk_buff *cpc_skb_alloc(size_t payload_len, gfp_t priority)
+{
+	struct sk_buff *skb;
+
+	skb = alloc_skb(payload_len + CPC_HEADER_SIZE, priority);
+	if (skb)
+		skb_reserve(skb, CPC_HEADER_SIZE);
+
+	return skb;
+}
+
+/**
+ * cpc_skb_set_ctx() - Set the skb context.
+ * @skb: Frame.
+ * @destructor: Destructor callback.
+ * @ctx: Context pointer, might be NULL.
+ */
+void cpc_skb_set_ctx(struct sk_buff *skb,
+		     void (*destructor)(struct sk_buff *skb),
+		     void *ctx)
+{
+	skb->destructor = destructor;
+
+	if (ctx)
+		memcpy(&skb->cb[0], &ctx, sizeof(void *));
+}
+
+/**
+ * cpc_skb_get_ctx() - Get the skb context.
+ * @skb: Frame.
+ *
+ * Return: Context pointer.
+ */
+void *cpc_skb_get_ctx(struct sk_buff *skb)
+{
+	void *ctx;
+
+	memcpy(&ctx, &skb->cb[0], sizeof(void *));
+
+	return ctx;
+}
 
 static int cpc_bus_match(struct device *dev, const struct device_driver *driver)
 {
diff --git a/drivers/net/cpc/protocol.c b/drivers/net/cpc/protocol.c
new file mode 100644
index 00000000000..692d3e07939
--- /dev/null
+++ b/drivers/net/cpc/protocol.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025, Silicon Laboratories, Inc.
+ */
+
+#include <linux/mutex.h>
+#include <linux/skbuff.h>
+
+#include "cpc.h"
+#include "header.h"
+#include "interface.h"
+#include "protocol.h"
+
+static int __cpc_protocol_queue_tx_frame(struct cpc_endpoint *ep, struct sk_buff *skb)
+{
+	struct cpc_interface *intf = ep->intf;
+	struct sk_buff *cloned_skb;
+
+	cloned_skb = skb_clone(skb, GFP_KERNEL);
+	if (!cloned_skb)
+		return -ENOMEM;
+
+	cpc_interface_send_frame(intf, cloned_skb);
+
+	return 0;
+}
+
+static void __cpc_protocol_process_pending_tx_frames(struct cpc_endpoint *ep)
+{
+	struct sk_buff *skb;
+	int err;
+
+	while ((skb = skb_dequeue(&ep->holding_queue))) {
+		err = __cpc_protocol_queue_tx_frame(ep, skb);
+		if (err < 0) {
+			skb_queue_head(&ep->holding_queue, skb);
+			return;
+		}
+	}
+}
+
+/**
+ * __cpc_protocol_write() - Write a frame.
+ * @ep: Endpoint handle.
+ * @hdr: Header to write.
+ * @skb: Payload to write.
+ *
+ * Context: Expect endpoint's lock to be held.
+ *
+ * Return: 0 on success, otherwise a negative error code.
+ */
+int __cpc_protocol_write(struct cpc_endpoint *ep,
+			 struct cpc_header *hdr,
+			 struct sk_buff *skb)
+{
+	memcpy(skb_push(skb, sizeof(*hdr)), hdr, sizeof(*hdr));
+
+	skb_queue_tail(&ep->holding_queue, skb);
+
+	__cpc_protocol_process_pending_tx_frames(ep);
+
+	return 0;
+}
diff --git a/drivers/net/cpc/protocol.h b/drivers/net/cpc/protocol.h
new file mode 100644
index 00000000000..b51f0191be4
--- /dev/null
+++ b/drivers/net/cpc/protocol.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2025, Silicon Laboratories, Inc.
+ */
+
+#ifndef CPC_PROTOCOL_H
+#define CPC_PROTOCOL_H
+
+#include <linux/skbuff.h>
+#include <linux/timer.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+struct cpc_endpoint;
+struct cpc_header;
+
+int __cpc_protocol_write(struct cpc_endpoint *ep, struct cpc_header *hdr, struct sk_buff *skb);
+
+#endif
-- 
2.49.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ