1b8564b19SJeremy Kerr // SPDX-License-Identifier: GPL-2.0
2b8564b19SJeremy Kerr /*
3b8564b19SJeremy Kerr * mctp-usblib.c - MCTP-over-USB (DMTF DSP0283) transport helper library
4b8564b19SJeremy Kerr *
5b8564b19SJeremy Kerr * DSP0283 is available at:
6d52fc7f0SJeremy Kerr * https://www.dmtf.org/sites/default/files/standards/documents/DSP0283_1.1.0.pdf
7b8564b19SJeremy Kerr *
8b8564b19SJeremy Kerr * Copyright (C) 2024-2026 Code Construct Pty Ltd
9b8564b19SJeremy Kerr */
10b8564b19SJeremy Kerr
11b8564b19SJeremy Kerr #include <linux/module.h>
12b8564b19SJeremy Kerr #include <linux/netdevice.h>
13b8564b19SJeremy Kerr #include <linux/skbuff.h>
14d52fc7f0SJeremy Kerr #include <linux/usb/ch9.h>
15b8564b19SJeremy Kerr #include <linux/usb/mctp-usb.h>
16b8564b19SJeremy Kerr #include <net/mctp.h>
17b8564b19SJeremy Kerr
mctp_usblib_rx_init(struct mctp_usblib_rx * rx,u16 ep_pktlen,bool span)18d52fc7f0SJeremy Kerr int mctp_usblib_rx_init(struct mctp_usblib_rx *rx, u16 ep_pktlen, bool span)
19b8564b19SJeremy Kerr {
20d52fc7f0SJeremy Kerr if (!ep_pktlen)
21d52fc7f0SJeremy Kerr return -EINVAL;
22d52fc7f0SJeremy Kerr
23d52fc7f0SJeremy Kerr if (ep_pktlen & ~USB_ENDPOINT_MAXP_MASK)
24d52fc7f0SJeremy Kerr return -EINVAL;
25d52fc7f0SJeremy Kerr
26b8564b19SJeremy Kerr memset(rx, 0, sizeof(*rx));
27d52fc7f0SJeremy Kerr rx->span = span;
28d52fc7f0SJeremy Kerr rx->ep_pktlen = ep_pktlen;
29d52fc7f0SJeremy Kerr
30d52fc7f0SJeremy Kerr return 0;
31b8564b19SJeremy Kerr }
32b8564b19SJeremy Kerr EXPORT_SYMBOL_GPL(mctp_usblib_rx_init);
33b8564b19SJeremy Kerr
mctp_usblib_rx_fini(struct mctp_usblib_rx * rx)34b8564b19SJeremy Kerr void mctp_usblib_rx_fini(struct mctp_usblib_rx *rx)
35b8564b19SJeremy Kerr {
36b8564b19SJeremy Kerr kfree_skb(rx->skb);
37b8564b19SJeremy Kerr }
38b8564b19SJeremy Kerr EXPORT_SYMBOL_GPL(mctp_usblib_rx_fini);
39b8564b19SJeremy Kerr
40b8564b19SJeremy Kerr /*
41b8564b19SJeremy Kerr * Prepare a transfer buffer for future completion; *bufp and *lenp will
42b8564b19SJeremy Kerr * be populated on success.
43b8564b19SJeremy Kerr */
mctp_usblib_rx_prepare(struct net_device * netdev,struct mctp_usblib_rx * rx,void ** bufp,size_t * lenp,gfp_t gfp)44b8564b19SJeremy Kerr int mctp_usblib_rx_prepare(struct net_device *netdev,
45b8564b19SJeremy Kerr struct mctp_usblib_rx *rx,
46b8564b19SJeremy Kerr void **bufp, size_t *lenp, gfp_t gfp)
47b8564b19SJeremy Kerr {
48d52fc7f0SJeremy Kerr struct sk_buff *skb = rx->skb;
49d52fc7f0SJeremy Kerr unsigned int len = 0;
50b8564b19SJeremy Kerr
51d52fc7f0SJeremy Kerr if (skb && skb->len >= MCTP_USB_1_1_PKTLEN_MAX) {
52d52fc7f0SJeremy Kerr /* something must have gone terribly wrong. clear and restart */
53d52fc7f0SJeremy Kerr mctp_usblib_rx_cancel(rx);
54d52fc7f0SJeremy Kerr skb = NULL;
55d52fc7f0SJeremy Kerr }
56d52fc7f0SJeremy Kerr
57d52fc7f0SJeremy Kerr len = rx->span ? roundup(MCTP_USB_1_1_PKTLEN_MAX, rx->ep_pktlen)
58d52fc7f0SJeremy Kerr : MCTP_USB_1_0_XFER_SIZE;
59d52fc7f0SJeremy Kerr
60d52fc7f0SJeremy Kerr if (!skb) {
61b8564b19SJeremy Kerr skb = __netdev_alloc_skb(netdev, len, gfp);
62b8564b19SJeremy Kerr if (!skb)
63b8564b19SJeremy Kerr return -ENOMEM;
64b8564b19SJeremy Kerr
65d52fc7f0SJeremy Kerr } else if (skb->cloned || skb_tailroom(skb) < rx->ep_pktlen) {
66d52fc7f0SJeremy Kerr /* We always need to realloc if ->cloned, as we cannot
67d52fc7f0SJeremy Kerr * resubmit the (now-shared) skb buffer for possible DMA.
68d52fc7f0SJeremy Kerr *
69d52fc7f0SJeremy Kerr * Otherwise (if we have an un-cloned SKB): just ensure we
70d52fc7f0SJeremy Kerr * have sufficient space to prevent babble. Since we allocated
71d52fc7f0SJeremy Kerr * for max size in the last prepare (and have not consumed any
72d52fc7f0SJeremy Kerr * of that space for a prior MCTP packet, because !cloned), we
73d52fc7f0SJeremy Kerr * have sufficient data to finish the current MCTP packet.
74d52fc7f0SJeremy Kerr */
75d52fc7f0SJeremy Kerr struct sk_buff *skb2;
76d52fc7f0SJeremy Kerr
77d52fc7f0SJeremy Kerr skb2 = skb_copy_expand(skb, 0, len, gfp);
78d52fc7f0SJeremy Kerr if (!skb2)
79d52fc7f0SJeremy Kerr return -ENOMEM;
80d52fc7f0SJeremy Kerr dev_kfree_skb_any(skb);
81d52fc7f0SJeremy Kerr skb = skb2;
82d52fc7f0SJeremy Kerr }
83d52fc7f0SJeremy Kerr
84b8564b19SJeremy Kerr rx->skb = skb;
85b8564b19SJeremy Kerr
86d52fc7f0SJeremy Kerr /* Spanning mode allows ZLPs, so we don't require exactly one
87d52fc7f0SJeremy Kerr * transfer packet. If we have extra tailroom, may as well use it,
88d52fc7f0SJeremy Kerr * and we have ensured that the tailroom >= ep_pktlen.
89d52fc7f0SJeremy Kerr */
90d52fc7f0SJeremy Kerr if (rx->span)
91d52fc7f0SJeremy Kerr len = rounddown(skb_tailroom(skb), rx->ep_pktlen);
92d52fc7f0SJeremy Kerr
93b8564b19SJeremy Kerr *bufp = skb_tail_pointer(skb);
94b8564b19SJeremy Kerr *lenp = len;
95b8564b19SJeremy Kerr
96b8564b19SJeremy Kerr return 0;
97b8564b19SJeremy Kerr }
98b8564b19SJeremy Kerr EXPORT_SYMBOL_GPL(mctp_usblib_rx_prepare);
99b8564b19SJeremy Kerr
mctp_usblib_rx(struct net_device * netdev,struct sk_buff * skb)100b8564b19SJeremy Kerr static void mctp_usblib_rx(struct net_device *netdev, struct sk_buff *skb)
101b8564b19SJeremy Kerr {
102b8564b19SJeremy Kerr struct pcpu_dstats *dstats = this_cpu_ptr(netdev->dstats);
103b8564b19SJeremy Kerr struct mctp_skb_cb *cb;
104b8564b19SJeremy Kerr unsigned long flags;
105b8564b19SJeremy Kerr
106d52fc7f0SJeremy Kerr skb_reset_mac_header(skb);
107d52fc7f0SJeremy Kerr skb_pull(skb, sizeof(struct mctp_usb_hdr));
108d52fc7f0SJeremy Kerr
109b8564b19SJeremy Kerr /* we're called from an URB completion handler, and cannot assume local
110b8564b19SJeremy Kerr * irqs are always disabled
111b8564b19SJeremy Kerr */
112b8564b19SJeremy Kerr flags = u64_stats_update_begin_irqsave(&dstats->syncp);
113b8564b19SJeremy Kerr u64_stats_inc(&dstats->rx_packets);
114b8564b19SJeremy Kerr u64_stats_add(&dstats->rx_bytes, skb->len);
115b8564b19SJeremy Kerr u64_stats_update_end_irqrestore(&dstats->syncp, flags);
116b8564b19SJeremy Kerr
117b8564b19SJeremy Kerr skb->protocol = htons(ETH_P_MCTP);
118b8564b19SJeremy Kerr skb_reset_network_header(skb);
119b8564b19SJeremy Kerr cb = __mctp_cb(skb);
120b8564b19SJeremy Kerr cb->halen = 0;
121b8564b19SJeremy Kerr netif_rx(skb);
122b8564b19SJeremy Kerr }
123b8564b19SJeremy Kerr
mctp_usblib_rx_stats_single_drop(struct net_device * dev)124b8564b19SJeremy Kerr static void mctp_usblib_rx_stats_single_drop(struct net_device *dev)
125b8564b19SJeremy Kerr {
126b8564b19SJeremy Kerr struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
127b8564b19SJeremy Kerr unsigned long flags;
128b8564b19SJeremy Kerr
129b8564b19SJeremy Kerr flags = u64_stats_update_begin_irqsave(&dstats->syncp);
130b8564b19SJeremy Kerr u64_stats_inc(&dstats->rx_drops);
131b8564b19SJeremy Kerr u64_stats_update_end_irqrestore(&dstats->syncp, flags);
132b8564b19SJeremy Kerr }
133b8564b19SJeremy Kerr
134b8564b19SJeremy Kerr /*
135b8564b19SJeremy Kerr * Receive a USB completion of @len bytes of incoming data. We will then split
136b8564b19SJeremy Kerr * this into packets and netif_rx() each. Intended to be called in atomic
137b8564b19SJeremy Kerr * contexts - ie., URB completion.
138b8564b19SJeremy Kerr *
139b8564b19SJeremy Kerr * Assumes @netdev uses dstats.
140b8564b19SJeremy Kerr */
mctp_usblib_rx_complete(struct net_device * netdev,struct mctp_usblib_rx * rx,size_t len)141b8564b19SJeremy Kerr int mctp_usblib_rx_complete(struct net_device *netdev,
142b8564b19SJeremy Kerr struct mctp_usblib_rx *rx, size_t len)
143b8564b19SJeremy Kerr {
144b8564b19SJeremy Kerr struct sk_buff *skb = rx->skb;
145b8564b19SJeremy Kerr int rc = 0;
146b8564b19SJeremy Kerr
147b8564b19SJeremy Kerr __skb_put(skb, len);
148b8564b19SJeremy Kerr
149d52fc7f0SJeremy Kerr for (;;) {
150b8564b19SJeremy Kerr struct mctp_usb_hdr *hdr;
151d52fc7f0SJeremy Kerr struct sk_buff *skb2;
152d52fc7f0SJeremy Kerr /* length of MCTP packet, including USB header */
153d52fc7f0SJeremy Kerr u16 pkt_len;
154b8564b19SJeremy Kerr
155d52fc7f0SJeremy Kerr /* no header yet, resubmit for the rest of the packet */
156d52fc7f0SJeremy Kerr if (skb->len < sizeof(*hdr)) {
157d52fc7f0SJeremy Kerr if (!rx->span) {
158d52fc7f0SJeremy Kerr netdev_dbg(netdev,
159d52fc7f0SJeremy Kerr "rx: tiny xfer (%d) in non-span mode",
160d52fc7f0SJeremy Kerr skb->len);
161b8564b19SJeremy Kerr rc = -ENOMSG;
162d52fc7f0SJeremy Kerr goto err_reset;
163d52fc7f0SJeremy Kerr }
164b8564b19SJeremy Kerr break;
165b8564b19SJeremy Kerr }
166b8564b19SJeremy Kerr
167d52fc7f0SJeremy Kerr hdr = (struct mctp_usb_hdr *)skb->data;
168d52fc7f0SJeremy Kerr
169b8564b19SJeremy Kerr if (be16_to_cpu(hdr->id) != MCTP_USB_DMTF_ID) {
170d52fc7f0SJeremy Kerr /* By resetting here, will start the next IN transfer
171d52fc7f0SJeremy Kerr * at the beginning of the new skb. This will mean
172d52fc7f0SJeremy Kerr * we re-sync when we next see a spanned packet aligned
173d52fc7f0SJeremy Kerr * with the start of a transfer.
174d52fc7f0SJeremy Kerr *
175d52fc7f0SJeremy Kerr * In non-spanning mode, this just means we'll drop
176d52fc7f0SJeremy Kerr * the current transfer only
177d52fc7f0SJeremy Kerr */
178b8564b19SJeremy Kerr netdev_dbg(netdev, "rx: invalid id %04x\n",
179b8564b19SJeremy Kerr be16_to_cpu(hdr->id));
180b8564b19SJeremy Kerr rc = -EPROTO;
181d52fc7f0SJeremy Kerr goto err_reset;
182b8564b19SJeremy Kerr }
183b8564b19SJeremy Kerr
184d52fc7f0SJeremy Kerr pkt_len = be16_to_cpu(hdr->len);
185d52fc7f0SJeremy Kerr /* v1.1, with span enabled, has a 13-bit length */
186d52fc7f0SJeremy Kerr pkt_len &= rx->span ?
187d52fc7f0SJeremy Kerr MCTP_USB_1_1_PKTLEN_MAX : MCTP_USB_1_0_PKTLEN_MAX;
188d52fc7f0SJeremy Kerr if (pkt_len < sizeof(*hdr) + sizeof(struct mctp_hdr)) {
189d52fc7f0SJeremy Kerr netdev_dbg(netdev, "rx: invalid len %d\n", pkt_len);
190b8564b19SJeremy Kerr rc = -EPROTO;
191d52fc7f0SJeremy Kerr goto err_reset;
192b8564b19SJeremy Kerr }
193b8564b19SJeremy Kerr
194d52fc7f0SJeremy Kerr /* span continues to the next transfer, resubmit */
195b8564b19SJeremy Kerr if (pkt_len > skb->len) {
196d52fc7f0SJeremy Kerr if (!rx->span) {
197b8564b19SJeremy Kerr netdev_dbg(netdev,
198d52fc7f0SJeremy Kerr "rx: short xfer (%d vs %d) in non-span mode",
199d52fc7f0SJeremy Kerr pkt_len, skb->len);
200d52fc7f0SJeremy Kerr rc = -EPROTO;
201d52fc7f0SJeremy Kerr goto err_reset;
202d52fc7f0SJeremy Kerr }
203b8564b19SJeremy Kerr break;
204b8564b19SJeremy Kerr }
205b8564b19SJeremy Kerr
206d52fc7f0SJeremy Kerr /* we have (exactly) a complete packet, RX it directly */
207d52fc7f0SJeremy Kerr if (pkt_len == skb->len) {
208d52fc7f0SJeremy Kerr mctp_usblib_rx(netdev, skb);
209d52fc7f0SJeremy Kerr rx->skb = NULL;
210d52fc7f0SJeremy Kerr break;
211d52fc7f0SJeremy Kerr }
212d52fc7f0SJeremy Kerr
213d52fc7f0SJeremy Kerr /* more packets follow - RX a clone so that we can continue
214d52fc7f0SJeremy Kerr * processing the current SKB, which may be the start of a
215d52fc7f0SJeremy Kerr * span.
216b8564b19SJeremy Kerr */
217b8564b19SJeremy Kerr skb2 = skb_clone(skb, GFP_ATOMIC);
218b8564b19SJeremy Kerr if (skb2) {
219d52fc7f0SJeremy Kerr skb_trim(skb2, pkt_len);
220d52fc7f0SJeremy Kerr mctp_usblib_rx(netdev, skb2);
221b8564b19SJeremy Kerr } else {
222b8564b19SJeremy Kerr mctp_usblib_rx_stats_single_drop(netdev);
223b8564b19SJeremy Kerr }
224d52fc7f0SJeremy Kerr skb_pull(skb, pkt_len);
225b8564b19SJeremy Kerr }
226b8564b19SJeremy Kerr
227d52fc7f0SJeremy Kerr return 0;
228b8564b19SJeremy Kerr
229d52fc7f0SJeremy Kerr err_reset:
230d52fc7f0SJeremy Kerr dev_kfree_skb_any(rx->skb);
231b8564b19SJeremy Kerr rx->skb = NULL;
232b8564b19SJeremy Kerr return rc;
233b8564b19SJeremy Kerr }
234b8564b19SJeremy Kerr EXPORT_SYMBOL_GPL(mctp_usblib_rx_complete);
235b8564b19SJeremy Kerr
236b8564b19SJeremy Kerr /*
237b8564b19SJeremy Kerr * Cancel a rx context; subsequent prepare/complete calls will not be a
238b8564b19SJeremy Kerr * continuation of any data already received.
239b8564b19SJeremy Kerr */
mctp_usblib_rx_cancel(struct mctp_usblib_rx * rx)240b8564b19SJeremy Kerr void mctp_usblib_rx_cancel(struct mctp_usblib_rx *rx)
241b8564b19SJeremy Kerr {
242b8564b19SJeremy Kerr dev_kfree_skb_any(rx->skb);
243b8564b19SJeremy Kerr rx->skb = NULL;
244b8564b19SJeremy Kerr }
245b8564b19SJeremy Kerr EXPORT_SYMBOL_GPL(mctp_usblib_rx_cancel);
246b8564b19SJeremy Kerr
247aadf5ed0SJeremy Kerr /* transmit context: encapsulates one transfer */
248aadf5ed0SJeremy Kerr struct mctp_usblib_tx_ctx {
249aadf5ed0SJeremy Kerr struct mctp_usblib_tx *tx;
25076a58ffdSJeremy Kerr struct sk_buff_head skbs;
251081ac93cSJeremy Kerr unsigned int buf_len, len;
252aadf5ed0SJeremy Kerr enum mctp_usblib_tx_buf_type {
253aadf5ed0SJeremy Kerr TX_SINGLE,
25476a58ffdSJeremy Kerr TX_FLAT,
255aadf5ed0SJeremy Kerr } buf_type;
25676a58ffdSJeremy Kerr u8 buf[] ____cacheline_aligned;
257aadf5ed0SJeremy Kerr };
258aadf5ed0SJeremy Kerr
mctp_usblib_tx_init(struct mctp_usblib_tx * tx,const struct mctp_usblib_tx_ops * ops,void * priv,bool span)259aadf5ed0SJeremy Kerr void mctp_usblib_tx_init(struct mctp_usblib_tx *tx,
260aadf5ed0SJeremy Kerr const struct mctp_usblib_tx_ops *ops,
261081ac93cSJeremy Kerr void *priv, bool span)
262aadf5ed0SJeremy Kerr {
263aadf5ed0SJeremy Kerr memset(tx, 0, sizeof(*tx));
264aadf5ed0SJeremy Kerr tx->ops = *ops;
265aadf5ed0SJeremy Kerr tx->priv = priv;
266081ac93cSJeremy Kerr tx->span = span;
26776a58ffdSJeremy Kerr spin_lock_init(&tx->lock);
268aadf5ed0SJeremy Kerr }
269aadf5ed0SJeremy Kerr EXPORT_SYMBOL_GPL(mctp_usblib_tx_init);
270aadf5ed0SJeremy Kerr
mctp_usblib_tx_avail(struct mctp_usblib_tx_ctx * ctx)27176a58ffdSJeremy Kerr static int mctp_usblib_tx_avail(struct mctp_usblib_tx_ctx *ctx)
272aadf5ed0SJeremy Kerr {
273081ac93cSJeremy Kerr return ctx->buf_type == TX_SINGLE ? 0 : ctx->buf_len - ctx->len;
274aadf5ed0SJeremy Kerr }
27576a58ffdSJeremy Kerr
mctp_usblib_tx_should_send(struct mctp_usblib_tx_ctx * ctx)27676a58ffdSJeremy Kerr static bool mctp_usblib_tx_should_send(struct mctp_usblib_tx_ctx *ctx)
27776a58ffdSJeremy Kerr {
27876a58ffdSJeremy Kerr /* Use the baseline length (ie, BTU) as an approximate
27976a58ffdSJeremy Kerr * "reasonably-sized" packet we could expect. If there is
28076a58ffdSJeremy Kerr * insufficient capacity for that, then send.
28176a58ffdSJeremy Kerr */
28276a58ffdSJeremy Kerr const size_t pkt_len = MCTP_USB_BTU + sizeof(struct mctp_usb_hdr);
28376a58ffdSJeremy Kerr
28476a58ffdSJeremy Kerr return mctp_usblib_tx_avail(ctx) < pkt_len;
28576a58ffdSJeremy Kerr }
28676a58ffdSJeremy Kerr
28776a58ffdSJeremy Kerr /*
28876a58ffdSJeremy Kerr * Returns zero on success, non-zero on failure - indicating that the new skb
28976a58ffdSJeremy Kerr * could not be appended. So, errors reported here to the TX path will result
29076a58ffdSJeremy Kerr * in the TX being transmitted.
29176a58ffdSJeremy Kerr */
mctp_usblib_tx_append(struct mctp_usblib_tx_ctx * ctx,struct sk_buff * skb)29276a58ffdSJeremy Kerr static int mctp_usblib_tx_append(struct mctp_usblib_tx_ctx *ctx,
29376a58ffdSJeremy Kerr struct sk_buff *skb)
29476a58ffdSJeremy Kerr {
29576a58ffdSJeremy Kerr if (ctx->buf_type == TX_SINGLE)
29676a58ffdSJeremy Kerr return -EINVAL;
29776a58ffdSJeremy Kerr
29876a58ffdSJeremy Kerr if (mctp_usblib_tx_avail(ctx) < skb->len)
29976a58ffdSJeremy Kerr return -ENOBUFS;
30076a58ffdSJeremy Kerr
30176a58ffdSJeremy Kerr __skb_queue_tail(&ctx->skbs, skb);
30276a58ffdSJeremy Kerr
30376a58ffdSJeremy Kerr ctx->len += skb->len;
30476a58ffdSJeremy Kerr
30576a58ffdSJeremy Kerr return 0;
30676a58ffdSJeremy Kerr }
30776a58ffdSJeremy Kerr
mctp_usblib_tx_send(struct mctp_usblib_tx_ctx * ctx)30876a58ffdSJeremy Kerr static int mctp_usblib_tx_send(struct mctp_usblib_tx_ctx *ctx)
30976a58ffdSJeremy Kerr {
31076a58ffdSJeremy Kerr void *buf;
31176a58ffdSJeremy Kerr
31276a58ffdSJeremy Kerr /* If we have a qlen of 1, we only ended up packing a single skb,
31376a58ffdSJeremy Kerr * despite allocating for multiple. Skip the copy and send directly
31476a58ffdSJeremy Kerr * from the skb data.
31576a58ffdSJeremy Kerr */
31676a58ffdSJeremy Kerr if (ctx->buf_type == TX_SINGLE || ctx->skbs.qlen == 1) {
31776a58ffdSJeremy Kerr buf = ctx->skbs.next->data;
31876a58ffdSJeremy Kerr
31976a58ffdSJeremy Kerr } else if (ctx->buf_type == TX_FLAT) {
32076a58ffdSJeremy Kerr struct sk_buff *skb;
32176a58ffdSJeremy Kerr size_t pos = 0;
32276a58ffdSJeremy Kerr
32376a58ffdSJeremy Kerr skb_queue_walk(&ctx->skbs, skb) {
32476a58ffdSJeremy Kerr skb_copy_bits(skb, 0, ctx->buf + pos, skb->len);
32576a58ffdSJeremy Kerr pos += skb->len;
32676a58ffdSJeremy Kerr }
32776a58ffdSJeremy Kerr
32876a58ffdSJeremy Kerr buf = ctx->buf;
32976a58ffdSJeremy Kerr } else {
33076a58ffdSJeremy Kerr return -EINVAL;
33176a58ffdSJeremy Kerr }
33276a58ffdSJeremy Kerr
33376a58ffdSJeremy Kerr return ctx->tx->ops.send(ctx, buf, ctx->len);
33476a58ffdSJeremy Kerr }
33576a58ffdSJeremy Kerr
mctp_usblib_tx_ctx_free(struct mctp_usblib_tx_ctx * ctx,enum skb_drop_reason reason)33676a58ffdSJeremy Kerr static void mctp_usblib_tx_ctx_free(struct mctp_usblib_tx_ctx *ctx,
33776a58ffdSJeremy Kerr enum skb_drop_reason reason)
33876a58ffdSJeremy Kerr {
33976a58ffdSJeremy Kerr struct sk_buff *skb;
34076a58ffdSJeremy Kerr
34176a58ffdSJeremy Kerr if (!ctx)
34276a58ffdSJeremy Kerr return;
34376a58ffdSJeremy Kerr
34476a58ffdSJeremy Kerr while ((skb = __skb_dequeue(&ctx->skbs)) != NULL)
34576a58ffdSJeremy Kerr dev_kfree_skb_any_reason(skb, reason);
34676a58ffdSJeremy Kerr kfree(ctx);
34776a58ffdSJeremy Kerr }
348aadf5ed0SJeremy Kerr
mctp_usblib_tx_ctx_priv(struct mctp_usblib_tx_ctx * tx_ctx)349aadf5ed0SJeremy Kerr void *mctp_usblib_tx_ctx_priv(struct mctp_usblib_tx_ctx *tx_ctx)
350aadf5ed0SJeremy Kerr {
351aadf5ed0SJeremy Kerr return tx_ctx->tx->priv;
352aadf5ed0SJeremy Kerr }
353aadf5ed0SJeremy Kerr EXPORT_SYMBOL_GPL(mctp_usblib_tx_ctx_priv);
354aadf5ed0SJeremy Kerr
35576a58ffdSJeremy Kerr /* caller must ensure the tx & completion path is quiesced */
mctp_usblib_tx_fini(struct mctp_usblib_tx * tx)35676a58ffdSJeremy Kerr void mctp_usblib_tx_fini(struct mctp_usblib_tx *tx)
357aadf5ed0SJeremy Kerr {
35876a58ffdSJeremy Kerr mctp_usblib_tx_ctx_free(tx->cur_ctx, SKB_DROP_REASON_NOT_SPECIFIED);
35976a58ffdSJeremy Kerr }
36076a58ffdSJeremy Kerr EXPORT_SYMBOL_GPL(mctp_usblib_tx_fini);
361aadf5ed0SJeremy Kerr
362081ac93cSJeremy Kerr /* Max size of a spanned TX. Since we allocate a separate span buffer, limit
363081ac93cSJeremy Kerr * the tx-time allocations to 4k. Larger packets will be sent as single
364081ac93cSJeremy Kerr * transfers.
365081ac93cSJeremy Kerr */
366081ac93cSJeremy Kerr static const unsigned int TX_SPAN_MAX = 4096 - sizeof(struct mctp_usblib_tx_ctx);
367081ac93cSJeremy Kerr
36876a58ffdSJeremy Kerr static struct mctp_usblib_tx_ctx *
mctp_usblib_tx_ctx_create(struct mctp_usblib_tx * tx,struct sk_buff * skb,bool single)36976a58ffdSJeremy Kerr mctp_usblib_tx_ctx_create(struct mctp_usblib_tx *tx, struct sk_buff *skb,
37076a58ffdSJeremy Kerr bool single)
37176a58ffdSJeremy Kerr {
37276a58ffdSJeremy Kerr enum mctp_usblib_tx_buf_type type;
37376a58ffdSJeremy Kerr struct mctp_usblib_tx_ctx *ctx;
37476a58ffdSJeremy Kerr size_t sz = 0;
37576a58ffdSJeremy Kerr
376081ac93cSJeremy Kerr if (single || skb->len > TX_SPAN_MAX) {
37776a58ffdSJeremy Kerr type = TX_SINGLE;
37876a58ffdSJeremy Kerr } else {
37976a58ffdSJeremy Kerr type = TX_FLAT;
380081ac93cSJeremy Kerr sz = tx->span ? TX_SPAN_MAX : MCTP_USB_1_0_XFER_SIZE;
38176a58ffdSJeremy Kerr }
38276a58ffdSJeremy Kerr
38376a58ffdSJeremy Kerr ctx = kzalloc_flex(*ctx, buf, sz, GFP_ATOMIC);
384aadf5ed0SJeremy Kerr if (!ctx)
385aadf5ed0SJeremy Kerr return NULL;
386aadf5ed0SJeremy Kerr
387aadf5ed0SJeremy Kerr ctx->tx = tx;
38876a58ffdSJeremy Kerr ctx->buf_type = type;
389081ac93cSJeremy Kerr ctx->buf_len = sz;
39076a58ffdSJeremy Kerr ctx->len = skb->len;
39176a58ffdSJeremy Kerr skb_queue_head_init(&ctx->skbs);
39276a58ffdSJeremy Kerr __skb_queue_tail(&ctx->skbs, skb);
393aadf5ed0SJeremy Kerr
394aadf5ed0SJeremy Kerr return ctx;
395aadf5ed0SJeremy Kerr }
396aadf5ed0SJeremy Kerr
mctp_usblib_tx_stats_update(struct mctp_usblib_tx_ctx * ctx,struct net_device * dev,bool ok)397aadf5ed0SJeremy Kerr static void mctp_usblib_tx_stats_update(struct mctp_usblib_tx_ctx *ctx,
398aadf5ed0SJeremy Kerr struct net_device *dev,
399aadf5ed0SJeremy Kerr bool ok)
400aadf5ed0SJeremy Kerr {
401aadf5ed0SJeremy Kerr struct pcpu_dstats *dstats = get_cpu_ptr(dev->dstats);
402aadf5ed0SJeremy Kerr unsigned long flags;
403aadf5ed0SJeremy Kerr
404aadf5ed0SJeremy Kerr flags = u64_stats_update_begin_irqsave(&dstats->syncp);
405aadf5ed0SJeremy Kerr if (ok) {
406aadf5ed0SJeremy Kerr /* Only include the network-layer data in tx stats; we know
407aadf5ed0SJeremy Kerr * that there is a 4-byte header pushed to all skbs in
408aadf5ed0SJeremy Kerr * tx_skb_prepare()
409aadf5ed0SJeremy Kerr */
41076a58ffdSJeremy Kerr u64 n = ctx->skbs.qlen;
41176a58ffdSJeremy Kerr s64 len = ctx->len - (n * sizeof(struct mctp_usb_hdr));
412aadf5ed0SJeremy Kerr
41376a58ffdSJeremy Kerr u64_stats_add(&dstats->tx_packets, n);
414aadf5ed0SJeremy Kerr u64_stats_add(&dstats->tx_bytes, len);
415aadf5ed0SJeremy Kerr } else {
41676a58ffdSJeremy Kerr u64_stats_add(&dstats->tx_drops, ctx->skbs.qlen);
417aadf5ed0SJeremy Kerr }
418aadf5ed0SJeremy Kerr u64_stats_update_end_irqrestore(&dstats->syncp, flags);
419aadf5ed0SJeremy Kerr put_cpu_ptr(dev->dstats);
420aadf5ed0SJeremy Kerr }
421aadf5ed0SJeremy Kerr
mctp_usblib_tx_stats_single_drop(struct net_device * dev)422aadf5ed0SJeremy Kerr static void mctp_usblib_tx_stats_single_drop(struct net_device *dev)
423aadf5ed0SJeremy Kerr {
424aadf5ed0SJeremy Kerr struct pcpu_dstats *dstats = get_cpu_ptr(dev->dstats);
425aadf5ed0SJeremy Kerr unsigned long flags;
426aadf5ed0SJeremy Kerr
427aadf5ed0SJeremy Kerr flags = u64_stats_update_begin_irqsave(&dstats->syncp);
428aadf5ed0SJeremy Kerr u64_stats_inc(&dstats->tx_drops);
429aadf5ed0SJeremy Kerr u64_stats_update_end_irqrestore(&dstats->syncp, flags);
430aadf5ed0SJeremy Kerr put_cpu_ptr(dev->dstats);
431aadf5ed0SJeremy Kerr }
432aadf5ed0SJeremy Kerr
433aadf5ed0SJeremy Kerr /*
434aadf5ed0SJeremy Kerr * Completion for the ->send() op. This will update netdev stats and
435aadf5ed0SJeremy Kerr * free the tx context.
436aadf5ed0SJeremy Kerr *
437aadf5ed0SJeremy Kerr * Likely called from (atomic) URB completion context.
438aadf5ed0SJeremy Kerr */
mctp_usblib_tx_send_complete(struct mctp_usblib_tx_ctx * tx_ctx,struct net_device * dev,bool ok)439aadf5ed0SJeremy Kerr void mctp_usblib_tx_send_complete(struct mctp_usblib_tx_ctx *tx_ctx,
440aadf5ed0SJeremy Kerr struct net_device *dev, bool ok)
441aadf5ed0SJeremy Kerr {
442aadf5ed0SJeremy Kerr enum skb_drop_reason reason =
443aadf5ed0SJeremy Kerr ok ? SKB_CONSUMED : SKB_DROP_REASON_NOT_SPECIFIED;
444aadf5ed0SJeremy Kerr
445aadf5ed0SJeremy Kerr mctp_usblib_tx_stats_update(tx_ctx, dev, ok);
446aadf5ed0SJeremy Kerr mctp_usblib_tx_ctx_free(tx_ctx, reason);
447aadf5ed0SJeremy Kerr }
448aadf5ed0SJeremy Kerr EXPORT_SYMBOL_GPL(mctp_usblib_tx_send_complete);
449aadf5ed0SJeremy Kerr
450aadf5ed0SJeremy Kerr /* Prepare a skb for push()
451aadf5ed0SJeremy Kerr *
452aadf5ed0SJeremy Kerr * On error, populates @reason.
453aadf5ed0SJeremy Kerr */
mctp_usblib_tx_skb_prepare(struct sk_buff * skb,bool span,enum skb_drop_reason * reason)454081ac93cSJeremy Kerr static int mctp_usblib_tx_skb_prepare(struct sk_buff *skb, bool span,
455aadf5ed0SJeremy Kerr enum skb_drop_reason *reason)
456aadf5ed0SJeremy Kerr {
457081ac93cSJeremy Kerr unsigned long plen, max_len;
458aadf5ed0SJeremy Kerr struct mctp_usb_hdr *hdr;
459aadf5ed0SJeremy Kerr int rc;
460aadf5ed0SJeremy Kerr
461081ac93cSJeremy Kerr max_len = span ? MCTP_USB_1_1_PKTLEN_MAX : MCTP_USB_1_0_PKTLEN_MAX;
462081ac93cSJeremy Kerr
463aadf5ed0SJeremy Kerr plen = skb->len;
464081ac93cSJeremy Kerr if (plen + sizeof(*hdr) > max_len) {
465aadf5ed0SJeremy Kerr *reason = SKB_DROP_REASON_PKT_TOO_BIG;
466aadf5ed0SJeremy Kerr return -EMSGSIZE;
467aadf5ed0SJeremy Kerr }
468aadf5ed0SJeremy Kerr
469aadf5ed0SJeremy Kerr rc = skb_cow_head(skb, sizeof(*hdr));
470aadf5ed0SJeremy Kerr if (rc) {
471aadf5ed0SJeremy Kerr *reason = SKB_DROP_REASON_NOMEM;
472aadf5ed0SJeremy Kerr return rc;
473aadf5ed0SJeremy Kerr }
474aadf5ed0SJeremy Kerr
475aadf5ed0SJeremy Kerr hdr = skb_push(skb, sizeof(*hdr));
476aadf5ed0SJeremy Kerr if (!hdr) {
477aadf5ed0SJeremy Kerr *reason = SKB_DROP_REASON_NOMEM;
478aadf5ed0SJeremy Kerr return -ENOMEM;
479aadf5ed0SJeremy Kerr }
480aadf5ed0SJeremy Kerr
481aadf5ed0SJeremy Kerr hdr->id = cpu_to_be16(MCTP_USB_DMTF_ID);
482f5bf226fSJeremy Kerr hdr->len = cpu_to_be16(plen + sizeof(*hdr));
483aadf5ed0SJeremy Kerr
484aadf5ed0SJeremy Kerr return 0;
485aadf5ed0SJeremy Kerr }
486aadf5ed0SJeremy Kerr
487aadf5ed0SJeremy Kerr /*
48876a58ffdSJeremy Kerr * Push a new skb to the transfer. May result in zero or more calls to
48976a58ffdSJeremy Kerr * ops->send().
490aadf5ed0SJeremy Kerr *
491aadf5ed0SJeremy Kerr * Takes ownership of @skb, including on error.
492aadf5ed0SJeremy Kerr */
mctp_usblib_tx_push(struct net_device * dev,struct mctp_usblib_tx * tx,struct sk_buff * skb,bool more)493aadf5ed0SJeremy Kerr int mctp_usblib_tx_push(struct net_device *dev,
494aadf5ed0SJeremy Kerr struct mctp_usblib_tx *tx,
495aadf5ed0SJeremy Kerr struct sk_buff *skb, bool more)
496aadf5ed0SJeremy Kerr {
49776a58ffdSJeremy Kerr struct mctp_usblib_tx_ctx *ctx, *send_ctx = NULL;
498aadf5ed0SJeremy Kerr enum skb_drop_reason reason;
49976a58ffdSJeremy Kerr const int max_tries = 3;
50076a58ffdSJeremy Kerr unsigned long flags;
50176a58ffdSJeremy Kerr int try = 1, rc;
502aadf5ed0SJeremy Kerr
503081ac93cSJeremy Kerr rc = mctp_usblib_tx_skb_prepare(skb, tx->span, &reason);
50476a58ffdSJeremy Kerr if (rc) {
50576a58ffdSJeremy Kerr mctp_usblib_tx_stats_single_drop(dev);
50676a58ffdSJeremy Kerr kfree_skb_reason(skb, reason);
50776a58ffdSJeremy Kerr /* we may still need to proceed, in case an existing ctx
50876a58ffdSJeremy Kerr * is now sendable (ie.: !more).
50976a58ffdSJeremy Kerr */
51076a58ffdSJeremy Kerr skb = NULL;
51176a58ffdSJeremy Kerr }
51276a58ffdSJeremy Kerr
51376a58ffdSJeremy Kerr reason = SKB_DROP_REASON_NOT_SPECIFIED;
51476a58ffdSJeremy Kerr retry:
51576a58ffdSJeremy Kerr /* Try and queue to the current context. We exit this critical section
51676a58ffdSJeremy Kerr * with a few bits of state:
51776a58ffdSJeremy Kerr * - send_ctx: indicating a prior context that needs to be sent
51876a58ffdSJeremy Kerr * - skb: indicating that a skb still needs to be queued/sent
51976a58ffdSJeremy Kerr */
52076a58ffdSJeremy Kerr spin_lock_irqsave(&tx->lock, flags);
52176a58ffdSJeremy Kerr ctx = tx->cur_ctx;
52276a58ffdSJeremy Kerr if (ctx) {
52376a58ffdSJeremy Kerr if (skb) {
52476a58ffdSJeremy Kerr rc = mctp_usblib_tx_append(ctx, skb);
52576a58ffdSJeremy Kerr if (rc) {
52676a58ffdSJeremy Kerr /* can't append to the pending tx - detach for
52776a58ffdSJeremy Kerr * sending, and we'll create a new tx below.
52876a58ffdSJeremy Kerr */
52976a58ffdSJeremy Kerr swap(tx->cur_ctx, send_ctx);
53076a58ffdSJeremy Kerr } else {
53176a58ffdSJeremy Kerr /* we have queued */
53276a58ffdSJeremy Kerr skb = NULL;
53376a58ffdSJeremy Kerr if (!more || mctp_usblib_tx_should_send(ctx))
53476a58ffdSJeremy Kerr swap(tx->cur_ctx, send_ctx);
53576a58ffdSJeremy Kerr }
53676a58ffdSJeremy Kerr } else if (!more) {
53776a58ffdSJeremy Kerr swap(tx->cur_ctx, send_ctx);
53876a58ffdSJeremy Kerr }
53976a58ffdSJeremy Kerr }
54076a58ffdSJeremy Kerr spin_unlock_irqrestore(&tx->lock, flags);
54176a58ffdSJeremy Kerr
54276a58ffdSJeremy Kerr if (send_ctx) {
54376a58ffdSJeremy Kerr rc = mctp_usblib_tx_send(send_ctx);
54476a58ffdSJeremy Kerr if (rc) {
54576a58ffdSJeremy Kerr mctp_usblib_tx_stats_update(send_ctx, dev, false);
54676a58ffdSJeremy Kerr mctp_usblib_tx_ctx_free(send_ctx, reason);
54776a58ffdSJeremy Kerr }
54876a58ffdSJeremy Kerr send_ctx = NULL;
54976a58ffdSJeremy Kerr }
55076a58ffdSJeremy Kerr
55176a58ffdSJeremy Kerr /* we have either queued, or the prepare failed; nothing more to do */
552aadf5ed0SJeremy Kerr if (!skb)
553aadf5ed0SJeremy Kerr return 0;
554aadf5ed0SJeremy Kerr
55576a58ffdSJeremy Kerr ctx = mctp_usblib_tx_ctx_create(tx, skb, !more);
556aadf5ed0SJeremy Kerr if (!ctx) {
55776a58ffdSJeremy Kerr netdev_dbg(dev, "TX context create failed\n");
55876a58ffdSJeremy Kerr mctp_usblib_tx_stats_single_drop(dev);
55976a58ffdSJeremy Kerr kfree_skb(skb);
56076a58ffdSJeremy Kerr return -ENOMEM;
561aadf5ed0SJeremy Kerr }
562aadf5ed0SJeremy Kerr
56376a58ffdSJeremy Kerr /* if we're ready to send now, no need to enqueue */
56476a58ffdSJeremy Kerr if (!more || mctp_usblib_tx_should_send(ctx)) {
565aadf5ed0SJeremy Kerr rc = mctp_usblib_tx_send(ctx);
566aadf5ed0SJeremy Kerr if (rc) {
567aadf5ed0SJeremy Kerr mctp_usblib_tx_stats_update(ctx, dev, false);
56876a58ffdSJeremy Kerr mctp_usblib_tx_ctx_free(ctx, reason);
56976a58ffdSJeremy Kerr }
57076a58ffdSJeremy Kerr return 0;
571aadf5ed0SJeremy Kerr }
572aadf5ed0SJeremy Kerr
57376a58ffdSJeremy Kerr spin_lock_irqsave(&tx->lock, flags);
57476a58ffdSJeremy Kerr if (!tx->cur_ctx) {
57576a58ffdSJeremy Kerr tx->cur_ctx = ctx;
57676a58ffdSJeremy Kerr ctx = NULL;
57776a58ffdSJeremy Kerr }
57876a58ffdSJeremy Kerr spin_unlock_irqrestore(&tx->lock, flags);
579aadf5ed0SJeremy Kerr
58076a58ffdSJeremy Kerr /* we may have lost the race with a concurrent tx; shouldn't happen, as
58176a58ffdSJeremy Kerr * ndo_start_xmit should be serialised over one queue, but try again
58276a58ffdSJeremy Kerr * from the top, as we may be able to queue the skb to that context.
58376a58ffdSJeremy Kerr */
58476a58ffdSJeremy Kerr if (ctx) {
58576a58ffdSJeremy Kerr /* unlink the new (sole) skb, we don't want it freed with ctx */
58676a58ffdSJeremy Kerr __skb_queue_head_init(&ctx->skbs);
58776a58ffdSJeremy Kerr mctp_usblib_tx_ctx_free(ctx, reason);
58876a58ffdSJeremy Kerr if (++try > max_tries) {
58976a58ffdSJeremy Kerr kfree_skb(skb);
590aadf5ed0SJeremy Kerr mctp_usblib_tx_stats_single_drop(dev);
59176a58ffdSJeremy Kerr return -EBUSY;
59276a58ffdSJeremy Kerr }
59376a58ffdSJeremy Kerr goto retry;
59476a58ffdSJeremy Kerr }
59576a58ffdSJeremy Kerr
59676a58ffdSJeremy Kerr return 0;
597aadf5ed0SJeremy Kerr }
598aadf5ed0SJeremy Kerr EXPORT_SYMBOL_GPL(mctp_usblib_tx_push);
599aadf5ed0SJeremy Kerr
600aadf5ed0SJeremy Kerr /* Cancel a tx: any un-sent context is released. */
mctp_usblib_tx_cancel(struct mctp_usblib_tx * tx,struct net_device * dev,enum skb_drop_reason reason)601aadf5ed0SJeremy Kerr void mctp_usblib_tx_cancel(struct mctp_usblib_tx *tx, struct net_device *dev,
602aadf5ed0SJeremy Kerr enum skb_drop_reason reason)
603aadf5ed0SJeremy Kerr {
60476a58ffdSJeremy Kerr struct mctp_usblib_tx_ctx *ctx = NULL;
60576a58ffdSJeremy Kerr unsigned long flags;
60676a58ffdSJeremy Kerr
60776a58ffdSJeremy Kerr spin_lock_irqsave(&tx->lock, flags);
60876a58ffdSJeremy Kerr swap(tx->cur_ctx, ctx);
60976a58ffdSJeremy Kerr spin_unlock_irqrestore(&tx->lock, flags);
61076a58ffdSJeremy Kerr
61176a58ffdSJeremy Kerr if (!ctx)
61276a58ffdSJeremy Kerr return;
61376a58ffdSJeremy Kerr
61476a58ffdSJeremy Kerr mctp_usblib_tx_stats_update(ctx, dev, false);
61576a58ffdSJeremy Kerr mctp_usblib_tx_ctx_free(ctx, reason);
616aadf5ed0SJeremy Kerr }
617aadf5ed0SJeremy Kerr EXPORT_SYMBOL_GPL(mctp_usblib_tx_cancel);
618aadf5ed0SJeremy Kerr
619b8564b19SJeremy Kerr MODULE_LICENSE("GPL");
620b8564b19SJeremy Kerr MODULE_AUTHOR("Jeremy Kerr <jk@codeconstruct.com.au>");
621b8564b19SJeremy Kerr MODULE_DESCRIPTION("MCTP USB transport library");
622*e84c0edbSJeremy Kerr
623*e84c0edbSJeremy Kerr #if IS_ENABLED(CONFIG_MCTP_TRANSPORT_USBLIB_TEST)
624*e84c0edbSJeremy Kerr #include "mctp-usblib-test.c"
625*e84c0edbSJeremy Kerr #endif
626