xref: /linux/drivers/net/mctp/mctp-usblib-test.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * mctp-usblib-test.c - MCTP-over-USB (DMTF DSP0283) transport helper library,
4  * unit test definitions.
5  *
6  * Copyright (C) 2026 Code Construct Pty Ltd
7  */
8 
9 #include <uapi/linux/netdevice.h>
10 #include <linux/netdevice.h>
11 #include <kunit/test.h>
12 #include <linux/if_arp.h>
13 #include <net/mctp.h>
14 #include <net/mctpdevice.h>
15 #include <linux/usb/mctp-usb.h>
16 
17 struct mctp_usblib_test_dev {
18 	struct net_device *ndev;
19 	struct mctp_dev *mdev;
20 	struct sk_buff_head rx_pkts;
21 };
22 
23 struct mctp_usblib_test_ctx {
24 	struct mctp_usblib_test_dev *dev;
25 	struct mctp_route rt;
26 };
27 
mctp_usblib_dev_tx(struct sk_buff * skb,struct net_device * ndev)28 static netdev_tx_t mctp_usblib_dev_tx(struct sk_buff *skb,
29 				      struct net_device *ndev)
30 {
31 	/* we don't track any TXed packets at present */
32 	kfree_skb(skb);
33 	return NETDEV_TX_OK;
34 }
35 
36 static const struct net_device_ops mctp_test_netdev_ops = {
37 	.ndo_start_xmit = mctp_usblib_dev_tx,
38 };
39 
40 static const u16 ep_maxpacket = 512;
41 static const mctp_eid_t local_eid = 8;
42 
mctp_usblib_dev_setup(struct net_device * ndev)43 static void mctp_usblib_dev_setup(struct net_device *ndev)
44 {
45 	ndev->type = ARPHRD_MCTP;
46 	ndev->mtu = 8192;
47 	ndev->flags = IFF_NOARP;
48 	ndev->netdev_ops = &mctp_test_netdev_ops;
49 	ndev->needs_free_netdev = true;
50 	ndev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS;
51 }
52 
mctp_usblib_test_dev_action(void * data)53 static void mctp_usblib_test_dev_action(void *data)
54 {
55 	struct mctp_usblib_test_dev *dev = data;
56 
57 	skb_queue_purge(&dev->rx_pkts);
58 	if (dev->mdev)
59 		mctp_dev_put(dev->mdev);
60 	unregister_netdev(dev->ndev);
61 }
62 
63 static struct mctp_usblib_test_dev *
mctp_usblib_test_create_dev(struct kunit * test)64 mctp_usblib_test_create_dev(struct kunit *test)
65 {
66 	struct mctp_usblib_test_dev *dev;
67 	struct net_device *ndev;
68 	int rc;
69 
70 	ndev = alloc_netdev(sizeof(*dev), "mctptest%d", NET_NAME_ENUM,
71 			    mctp_usblib_dev_setup);
72 	if (!ndev)
73 		return NULL;
74 
75 	dev = netdev_priv(ndev);
76 	dev->ndev = ndev;
77 	skb_queue_head_init(&dev->rx_pkts);
78 
79 	rc = register_netdev(ndev);
80 	if (rc) {
81 		free_netdev(ndev);
82 		return NULL;
83 	}
84 
85 	rc = kunit_add_action_or_reset(test, mctp_usblib_test_dev_action, dev);
86 	if (rc)
87 		return NULL;
88 
89 	rcu_read_lock();
90 	dev->mdev = __mctp_dev_get(ndev);
91 	if (dev->mdev)
92 		dev->mdev->net = mctp_default_net(dev_net(ndev));
93 	rcu_read_unlock();
94 
95 	if (!dev->mdev)
96 		return NULL;
97 
98 	rtnl_lock();
99 	rc = dev_open(ndev, NULL);
100 	rtnl_unlock();
101 	if (rc)
102 		return NULL;
103 
104 	return dev;
105 }
106 
mctp_usblib_test_dst_output(struct mctp_dst * dst,struct sk_buff * skb)107 static int mctp_usblib_test_dst_output(struct mctp_dst *dst,
108 				       struct sk_buff *skb)
109 {
110 	struct mctp_usblib_test_dev *dev = netdev_priv(skb->dev);
111 
112 	skb_queue_tail(&dev->rx_pkts, skb);
113 
114 	return 0;
115 }
116 
mctp_usblib_test_fini_action(void * data)117 static void mctp_usblib_test_fini_action(void *data)
118 {
119 	struct mctp_usblib_test_ctx *ctx = data;
120 
121 	/* The device will have been destroyed, so ->rt will be unlinked.
122 	 * Just ensure that the refcount is as expected.
123 	 */
124 	KUNIT_EXPECT_TRUE(current->kunit_test,
125 			  refcount_dec_and_test(&ctx->rt.refs));
126 
127 	kfree(ctx);
128 }
129 
mctp_usblib_test_init(struct kunit * test)130 static struct mctp_usblib_test_ctx *mctp_usblib_test_init(struct kunit *test)
131 {
132 	struct mctp_usblib_test_ctx *ctx;
133 	struct mctp_route *rt;
134 	int rc;
135 
136 	ctx = kzalloc_obj(*ctx);
137 	KUNIT_ASSERT_NOT_NULL(test, ctx);
138 
139 	INIT_LIST_HEAD(&ctx->rt.list);
140 	rt = &ctx->rt;
141 	refcount_set(&rt->refs, 1);
142 
143 	rc = kunit_add_action_or_reset(test, mctp_usblib_test_fini_action, ctx);
144 	KUNIT_ASSERT_EQ(test, rc, 0);
145 
146 	ctx->dev = mctp_usblib_test_create_dev(test);
147 	KUNIT_ASSERT_NOT_NULL(test, ctx->dev);
148 
149 	rt->min = local_eid;
150 	rt->max = local_eid;
151 	rt->dst_type = MCTP_ROUTE_DIRECT;
152 	rt->type = RTN_LOCAL;
153 	rt->dev = ctx->dev->mdev;
154 	rt->output = mctp_usblib_test_dst_output;
155 
156 	rtnl_lock();
157 	list_add_rcu(&ctx->rt.list, &init_net.mctp.routes);
158 	refcount_inc(&rt->refs);
159 	rtnl_unlock();
160 
161 	return ctx;
162 }
163 
164 /* Init a MCTP-over-USB packet within a buffer. @len is the length of the
165  * buffer to write, @payload_len is the reported size of the MCTP-over-USB
166  * packet.
167  */
mctp_usblib_test_init_pkt(void * data,size_t len,size_t payload_len)168 static void mctp_usblib_test_init_pkt(void *data, size_t len,
169 				      size_t payload_len)
170 {
171 	struct {
172 		struct mctp_usb_hdr usb;
173 		struct mctp_hdr mctp;
174 	} hdr;
175 
176 	hdr.usb.id = cpu_to_be16(MCTP_USB_DMTF_ID);
177 	hdr.usb.len = cpu_to_be16(payload_len);
178 	hdr.mctp.ver = 1;
179 	hdr.mctp.dest = local_eid;
180 	hdr.mctp.src = 0;
181 	hdr.mctp.flags_seq_tag = 0;
182 
183 	memcpy(data, &hdr, min(len, sizeof(hdr)));
184 	if (len > sizeof(hdr))
185 		memset(data + sizeof(hdr), 0, len - sizeof(hdr));
186 }
187 
action_rx_fini(void * data)188 static void action_rx_fini(void *data)
189 {
190 	struct mctp_usblib_rx *rx = data;
191 
192 	mctp_usblib_rx_fini(rx);
193 	kfree(rx);
194 }
195 
196 static struct mctp_usblib_rx *
mctp_usblib_test_rx_init(struct kunit * test,bool span)197 mctp_usblib_test_rx_init(struct kunit *test, bool span)
198 {
199 	struct mctp_usblib_rx *rx;
200 	int rc;
201 
202 	rx = kzalloc_obj(*rx);
203 	if (rx) {
204 		rc = kunit_add_action_or_reset(test, action_rx_fini, rx);
205 		KUNIT_ASSERT_EQ(test, rc, 0);
206 	}
207 	KUNIT_ASSERT_NOT_NULL(test, rx);
208 
209 	rc = mctp_usblib_rx_init(rx, ep_maxpacket, span);
210 	KUNIT_ASSERT_EQ(test, rc, 0);
211 
212 	return rx;
213 }
214 
215 /* Wrappers for usblib's rx_complete callback, which is intended to be called
216  * from atomic context
217  */
mctp_usblib_test_rx_complete(struct net_device * netdev,struct mctp_usblib_rx * rx,size_t len)218 static int mctp_usblib_test_rx_complete(struct net_device *netdev,
219 					struct mctp_usblib_rx *rx, size_t len)
220 {
221 	int rc;
222 
223 	local_bh_disable();
224 	rc = mctp_usblib_rx_complete(netdev, rx, len);
225 	local_bh_enable();
226 
227 	return rc;
228 }
229 
230 /* Single packet, starting on a transfer boundary, contained entirely within
231  * the transfer
232  */
mctp_usblib_test_rx_single(struct kunit * test)233 static void mctp_usblib_test_rx_single(struct kunit *test)
234 {
235 	struct mctp_usblib_test_dev *dev;
236 	struct mctp_usblib_test_ctx *ctx;
237 	struct mctp_usblib_rx *rx;
238 	struct sk_buff *skb;
239 	size_t len;
240 	void *buf;
241 	int rc;
242 
243 	ctx = mctp_usblib_test_init(test);
244 	dev = ctx->dev;
245 
246 	rx = mctp_usblib_test_rx_init(test, true);
247 
248 	rc = mctp_usblib_rx_prepare(dev->ndev, rx,
249 				    &buf, &len, GFP_KERNEL);
250 	KUNIT_ASSERT_EQ(test, rc, 0);
251 
252 	/* we should always have a maxpacket of transfer available */
253 	KUNIT_ASSERT_GE(test, len, ep_maxpacket);
254 
255 	mctp_usblib_test_init_pkt(buf, 8, 8);
256 
257 	rc = mctp_usblib_test_rx_complete(dev->ndev, rx, 8);
258 	KUNIT_ASSERT_EQ(test, rc, 0);
259 
260 	skb = __skb_dequeue(&dev->rx_pkts);
261 	KUNIT_EXPECT_NOT_NULL(test, skb);
262 	if (skb)
263 		KUNIT_EXPECT_EQ(test, skb->len, 4);
264 	kfree_skb(skb);
265 }
266 
267 struct mctp_usblib_test_pkt_span {
268 	const char *name;
269 	size_t n_pkts;
270 	size_t pkts[6];
271 	size_t n_xfers;
272 	size_t xfers[6];
273 };
274 
275 static void
mctp_usblib_test_pkt_span_to_desc(const struct mctp_usblib_test_pkt_span * t,char * desc)276 mctp_usblib_test_pkt_span_to_desc(const struct mctp_usblib_test_pkt_span *t,
277 				  char *desc)
278 {
279 	strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE);
280 }
281 
282 static void
mctp_usblib_test_pkt_span_validate(struct kunit * test,const struct mctp_usblib_test_pkt_span * span,size_t * len)283 mctp_usblib_test_pkt_span_validate(struct kunit *test,
284 				   const struct mctp_usblib_test_pkt_span *span,
285 				   size_t *len)
286 {
287 	size_t pkt_len = 0, xfer_len = 0;
288 	unsigned int i;
289 
290 	for (i = 0; i < span->n_pkts; i++) {
291 		KUNIT_ASSERT_GE_MSG(test, span->pkts[i], 8,
292 				    "pkt[%u] len too small (%zu) for %s",
293 				    i, span->pkts[i], span->name);
294 		pkt_len += span->pkts[i];
295 	}
296 
297 	for (i = 0; i < span->n_xfers; i++)
298 		xfer_len += span->xfers[i];
299 
300 	KUNIT_ASSERT_EQ_MSG(test, pkt_len, xfer_len,
301 			    "invalid pkt_len (%zu) != xfer_len (%zu) for %s",
302 			    pkt_len, xfer_len, span->name);
303 
304 	*len = pkt_len;
305 }
306 
mctp_usblib_test_rx_pkt_span(struct kunit * test)307 static void mctp_usblib_test_rx_pkt_span(struct kunit *test)
308 {
309 	const struct mctp_usblib_test_pkt_span *pkt_span = test->param_value;
310 	size_t len, xfer_len, off, xfer_off;
311 	struct mctp_usblib_test_dev *dev;
312 	struct mctp_usblib_test_ctx *ctx;
313 	struct mctp_usblib_rx *rx;
314 	unsigned int i;
315 	u8 *pktbuf;
316 	void *buf;
317 	int rc;
318 
319 	mctp_usblib_test_pkt_span_validate(test, pkt_span, &len);
320 	pktbuf = kunit_kmalloc_array(test, 1, len, GFP_KERNEL);
321 	KUNIT_ASSERT_NOT_NULL(test, pktbuf);
322 
323 	/* lay out packets */
324 	for (off = 0, i = 0; i < pkt_span->n_pkts; i++) {
325 		len = pkt_span->pkts[i];
326 		mctp_usblib_test_init_pkt(pktbuf + off, len, len);
327 		off += len;
328 	}
329 
330 	ctx = mctp_usblib_test_init(test);
331 	dev = ctx->dev;
332 
333 	rx = mctp_usblib_test_rx_init(test, true);
334 
335 	/* feed transfers */
336 	for (off = 0, xfer_off = 0, i = 0; i < pkt_span->n_xfers;) {
337 		xfer_len = pkt_span->xfers[i] - xfer_off;
338 		rc = mctp_usblib_rx_prepare(dev->ndev, rx,
339 					    &buf, &len, GFP_KERNEL);
340 		KUNIT_ASSERT_EQ(test, rc, 0);
341 
342 		KUNIT_ASSERT_GE(test, len, ep_maxpacket);
343 
344 		len = min(len, xfer_len);
345 		memcpy(buf, pktbuf + off, len);
346 
347 		if (len == xfer_len) {
348 			/* whole/end xfer, proceed to next */
349 			xfer_off = 0;
350 			i++;
351 		} else {
352 			/* partial */
353 			xfer_off += len;
354 		}
355 
356 		rc = mctp_usblib_test_rx_complete(dev->ndev, rx, len);
357 		KUNIT_ASSERT_EQ(test, rc, 0);
358 		off += len;
359 	}
360 
361 	/* check received packets */
362 	KUNIT_EXPECT_EQ(test, dev->rx_pkts.qlen, pkt_span->n_pkts);
363 	for (i = 0; ; i++) {
364 		struct sk_buff *skb = __skb_dequeue(&dev->rx_pkts);
365 
366 		if (!skb)
367 			break;
368 
369 		if (i < pkt_span->n_pkts)
370 			KUNIT_EXPECT_EQ(test, skb->len, pkt_span->pkts[i] - 4);
371 
372 		kfree_skb(skb);
373 	}
374 }
375 
376 static const struct mctp_usblib_test_pkt_span mctp_usblib_test_pkt_spans[] = {
377 	/* One packet completely within a transfer */
378 	{ "1p1x-complete", 1, { 8 }, 1, { 8 } },
379 	/* Two small packets combined within one transfer */
380 	{ "2p1x-combined", 2, { 8, 8 }, 1, { 16 } },
381 	/* A packet split over two transfers, at the MCTP payload */
382 	{ "1p2x-split-payload", 1, { 16 }, 2, { 8, 8 } },
383 	/* A packet split over two transfers, at the USB transport header */
384 	{ "1p2x-split-usbhdr", 1, { 16 }, 2, { 2, 14 } },
385 	/* A packet split over two transfers, at the MCTP header */
386 	{ "1p2x-split-mctphdr", 1, { 16 }, 2, { 6, 10 } },
387 	/* Single packet split over 3 transfers, middle entirely continuation */
388 	{ "1p3x-split", 1, { 12 }, 3, { 4, 4, 4 } },
389 	/* Max-sized single transfer */
390 	{ "1p1x-large", 1, { 8191 }, 1, { 8191 } },
391 	/* Two large packets, split at the worst-case for allocation, with a
392 	 * single byte continuing the span
393 	 */
394 	{ "2p2x-large-split", 2, { 8190, 8190 }, 2, { 8191, 8189 } },
395 };
396 
397 KUNIT_ARRAY_PARAM(mctp_usblib_test_rx_pkt_span, mctp_usblib_test_pkt_spans,
398 		  mctp_usblib_test_pkt_span_to_desc);
399 
400 static struct kunit_case mctp_usblib_test_cases[] = {
401 	KUNIT_CASE(mctp_usblib_test_rx_single),
402 	KUNIT_CASE_PARAM(mctp_usblib_test_rx_pkt_span,
403 			 mctp_usblib_test_rx_pkt_span_gen_params),
404 	{}
405 };
406 
407 static struct kunit_suite mctp_usblib_test_suite = {
408 	.name = "mctp-usblib",
409 	.test_cases = mctp_usblib_test_cases,
410 };
411 
412 kunit_test_suite(mctp_usblib_test_suite);
413