xref: /linux/drivers/net/ethernet/amd/xgbe/xgbe-selftest.c (revision 9c11b6b1abcd328136fc0cbc381734d6815d1c16)
1862a64c8SRaju Rangoju // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-3-Clause)
2862a64c8SRaju Rangoju /*
3862a64c8SRaju Rangoju  * Copyright (c) 2014-2025, Advanced Micro Devices, Inc.
4862a64c8SRaju Rangoju  * Copyright (c) 2014, Synopsys, Inc.
5862a64c8SRaju Rangoju  * All rights reserved
6862a64c8SRaju Rangoju  *
7862a64c8SRaju Rangoju  * Author: Raju Rangoju <Raju.Rangoju@amd.com>
8862a64c8SRaju Rangoju  */
9862a64c8SRaju Rangoju #include <linux/crc32.h>
10862a64c8SRaju Rangoju #include <linux/ip.h>
11862a64c8SRaju Rangoju #include <linux/udp.h>
12862a64c8SRaju Rangoju #include <net/tcp.h>
13862a64c8SRaju Rangoju #include <net/udp.h>
14862a64c8SRaju Rangoju #include <net/checksum.h>
15862a64c8SRaju Rangoju #include <net/selftests.h>
16862a64c8SRaju Rangoju 
17862a64c8SRaju Rangoju #include "xgbe.h"
18862a64c8SRaju Rangoju #include "xgbe-common.h"
19862a64c8SRaju Rangoju 
20862a64c8SRaju Rangoju #define XGBE_LOOPBACK_NONE	0
21862a64c8SRaju Rangoju #define XGBE_LOOPBACK_MAC	1
2242b06fccSRaju Rangoju #define XGBE_LOOPBACK_PHY	2
23862a64c8SRaju Rangoju 
24862a64c8SRaju Rangoju struct xgbe_test {
25862a64c8SRaju Rangoju 	char name[ETH_GSTRING_LEN];
26862a64c8SRaju Rangoju 	int lb;
27862a64c8SRaju Rangoju 	int (*fn)(struct xgbe_prv_data *pdata);
28862a64c8SRaju Rangoju };
29862a64c8SRaju Rangoju 
30862a64c8SRaju Rangoju static u8 xgbe_test_id;
31862a64c8SRaju Rangoju 
32862a64c8SRaju Rangoju static int xgbe_test_loopback_validate(struct sk_buff *skb,
33862a64c8SRaju Rangoju 				       struct net_device *ndev,
34862a64c8SRaju Rangoju 				       struct packet_type *pt,
35862a64c8SRaju Rangoju 				       struct net_device *orig_ndev)
36862a64c8SRaju Rangoju {
37862a64c8SRaju Rangoju 	struct net_test_priv *tdata = pt->af_packet_priv;
38862a64c8SRaju Rangoju 	const unsigned char *dst = tdata->packet->dst;
39862a64c8SRaju Rangoju 	const unsigned char *src = tdata->packet->src;
40862a64c8SRaju Rangoju 	struct netsfhdr *hdr;
41862a64c8SRaju Rangoju 	struct ethhdr *eh;
42862a64c8SRaju Rangoju 	struct tcphdr *th;
43862a64c8SRaju Rangoju 	struct udphdr *uh;
44862a64c8SRaju Rangoju 	struct iphdr *ih;
45*9c11b6b1SRaju Rangoju 	int eat;
46862a64c8SRaju Rangoju 
47862a64c8SRaju Rangoju 	skb = skb_unshare(skb, GFP_ATOMIC);
48862a64c8SRaju Rangoju 	if (!skb)
49862a64c8SRaju Rangoju 		goto out;
50862a64c8SRaju Rangoju 
51*9c11b6b1SRaju Rangoju 	eat = (skb->tail + skb->data_len) - skb->end;
52*9c11b6b1SRaju Rangoju 	if (eat > 0 && skb_shared(skb)) {
53*9c11b6b1SRaju Rangoju 		skb = skb_share_check(skb, GFP_ATOMIC);
54*9c11b6b1SRaju Rangoju 		if (!skb)
55*9c11b6b1SRaju Rangoju 			goto out;
56*9c11b6b1SRaju Rangoju 	}
57*9c11b6b1SRaju Rangoju 
58862a64c8SRaju Rangoju 	if (skb_linearize(skb))
59862a64c8SRaju Rangoju 		goto out;
60862a64c8SRaju Rangoju 
61862a64c8SRaju Rangoju 	if (skb_headlen(skb) < (NET_TEST_PKT_SIZE - ETH_HLEN))
62862a64c8SRaju Rangoju 		goto out;
63862a64c8SRaju Rangoju 
64862a64c8SRaju Rangoju 	eh = (struct ethhdr *)skb_mac_header(skb);
65862a64c8SRaju Rangoju 	if (dst) {
66862a64c8SRaju Rangoju 		if (!ether_addr_equal_unaligned(eh->h_dest, dst))
67862a64c8SRaju Rangoju 			goto out;
68862a64c8SRaju Rangoju 	}
69862a64c8SRaju Rangoju 	if (src) {
70862a64c8SRaju Rangoju 		if (!ether_addr_equal_unaligned(eh->h_source, src))
71862a64c8SRaju Rangoju 			goto out;
72862a64c8SRaju Rangoju 	}
73862a64c8SRaju Rangoju 
74862a64c8SRaju Rangoju 	ih = ip_hdr(skb);
75862a64c8SRaju Rangoju 
76862a64c8SRaju Rangoju 	if (tdata->packet->tcp) {
77862a64c8SRaju Rangoju 		if (ih->protocol != IPPROTO_TCP)
78862a64c8SRaju Rangoju 			goto out;
79862a64c8SRaju Rangoju 
80862a64c8SRaju Rangoju 		th = (struct tcphdr *)((u8 *)ih + 4 * ih->ihl);
81862a64c8SRaju Rangoju 		if (th->dest != htons(tdata->packet->dport))
82862a64c8SRaju Rangoju 			goto out;
83862a64c8SRaju Rangoju 
84862a64c8SRaju Rangoju 		hdr = (struct netsfhdr *)((u8 *)th + sizeof(*th));
85862a64c8SRaju Rangoju 	} else {
86862a64c8SRaju Rangoju 		if (ih->protocol != IPPROTO_UDP)
87862a64c8SRaju Rangoju 			goto out;
88862a64c8SRaju Rangoju 
89862a64c8SRaju Rangoju 		uh = (struct udphdr *)((u8 *)ih + 4 * ih->ihl);
90862a64c8SRaju Rangoju 		if (uh->dest != htons(tdata->packet->dport))
91862a64c8SRaju Rangoju 			goto out;
92862a64c8SRaju Rangoju 
93862a64c8SRaju Rangoju 		hdr = (struct netsfhdr *)((u8 *)uh + sizeof(*uh));
94862a64c8SRaju Rangoju 	}
95862a64c8SRaju Rangoju 
96862a64c8SRaju Rangoju 	if (hdr->magic != cpu_to_be64(NET_TEST_PKT_MAGIC))
97862a64c8SRaju Rangoju 		goto out;
98862a64c8SRaju Rangoju 	if (tdata->packet->id != hdr->id)
99862a64c8SRaju Rangoju 		goto out;
100862a64c8SRaju Rangoju 
101862a64c8SRaju Rangoju 	tdata->ok = true;
102862a64c8SRaju Rangoju 	complete(&tdata->comp);
103862a64c8SRaju Rangoju out:
104862a64c8SRaju Rangoju 	kfree_skb(skb);
105862a64c8SRaju Rangoju 	return 0;
106862a64c8SRaju Rangoju }
107862a64c8SRaju Rangoju 
108862a64c8SRaju Rangoju static int __xgbe_test_loopback(struct xgbe_prv_data *pdata,
109862a64c8SRaju Rangoju 				struct net_packet_attrs *attr)
110862a64c8SRaju Rangoju {
111862a64c8SRaju Rangoju 	struct net_test_priv *tdata;
112862a64c8SRaju Rangoju 	struct sk_buff *skb = NULL;
113862a64c8SRaju Rangoju 	int ret = 0;
114862a64c8SRaju Rangoju 
115862a64c8SRaju Rangoju 	tdata = kzalloc(sizeof(*tdata), GFP_KERNEL);
116862a64c8SRaju Rangoju 	if (!tdata)
117862a64c8SRaju Rangoju 		return -ENOMEM;
118862a64c8SRaju Rangoju 
119862a64c8SRaju Rangoju 	tdata->ok = false;
120862a64c8SRaju Rangoju 	init_completion(&tdata->comp);
121862a64c8SRaju Rangoju 
122862a64c8SRaju Rangoju 	tdata->pt.type = htons(ETH_P_IP);
123862a64c8SRaju Rangoju 	tdata->pt.func = xgbe_test_loopback_validate;
124862a64c8SRaju Rangoju 	tdata->pt.dev = pdata->netdev;
125862a64c8SRaju Rangoju 	tdata->pt.af_packet_priv = tdata;
126862a64c8SRaju Rangoju 	tdata->packet = attr;
127862a64c8SRaju Rangoju 
128862a64c8SRaju Rangoju 	dev_add_pack(&tdata->pt);
129862a64c8SRaju Rangoju 
130862a64c8SRaju Rangoju 	skb = net_test_get_skb(pdata->netdev, xgbe_test_id, attr);
131862a64c8SRaju Rangoju 	if (!skb) {
132862a64c8SRaju Rangoju 		ret = -ENOMEM;
133862a64c8SRaju Rangoju 		goto cleanup;
134862a64c8SRaju Rangoju 	}
135862a64c8SRaju Rangoju 
136862a64c8SRaju Rangoju 	xgbe_test_id++;
137862a64c8SRaju Rangoju 	ret = dev_direct_xmit(skb, attr->queue_mapping);
138862a64c8SRaju Rangoju 	if (ret)
139862a64c8SRaju Rangoju 		goto cleanup;
140862a64c8SRaju Rangoju 
141862a64c8SRaju Rangoju 	if (!attr->timeout)
142862a64c8SRaju Rangoju 		attr->timeout = NET_LB_TIMEOUT;
143862a64c8SRaju Rangoju 
144862a64c8SRaju Rangoju 	wait_for_completion_timeout(&tdata->comp, attr->timeout);
145862a64c8SRaju Rangoju 	ret = tdata->ok ? 0 : -ETIMEDOUT;
146862a64c8SRaju Rangoju 
147862a64c8SRaju Rangoju 	if (ret)
148862a64c8SRaju Rangoju 		netdev_err(pdata->netdev, "Response timedout: ret %d\n", ret);
149862a64c8SRaju Rangoju cleanup:
150862a64c8SRaju Rangoju 	dev_remove_pack(&tdata->pt);
151862a64c8SRaju Rangoju 	kfree(tdata);
152862a64c8SRaju Rangoju 	return ret;
153862a64c8SRaju Rangoju }
154862a64c8SRaju Rangoju 
155862a64c8SRaju Rangoju static int xgbe_test_mac_loopback(struct xgbe_prv_data *pdata)
156862a64c8SRaju Rangoju {
157862a64c8SRaju Rangoju 	struct net_packet_attrs attr = {};
158862a64c8SRaju Rangoju 
159862a64c8SRaju Rangoju 	attr.dst = pdata->netdev->dev_addr;
160862a64c8SRaju Rangoju 	return __xgbe_test_loopback(pdata, &attr);
161862a64c8SRaju Rangoju }
162862a64c8SRaju Rangoju 
16342b06fccSRaju Rangoju static int xgbe_test_phy_loopback(struct xgbe_prv_data *pdata)
16442b06fccSRaju Rangoju {
16542b06fccSRaju Rangoju 	struct net_packet_attrs attr = {};
16642b06fccSRaju Rangoju 	int ret;
16742b06fccSRaju Rangoju 
16842b06fccSRaju Rangoju 	if (!pdata->netdev->phydev) {
16942b06fccSRaju Rangoju 		netdev_err(pdata->netdev, "phydev not found: cannot start PHY loopback test\n");
17042b06fccSRaju Rangoju 		return -EOPNOTSUPP;
17142b06fccSRaju Rangoju 	}
17242b06fccSRaju Rangoju 
17342b06fccSRaju Rangoju 	ret = phy_loopback(pdata->netdev->phydev, true, 0);
17442b06fccSRaju Rangoju 	if (ret)
17542b06fccSRaju Rangoju 		return ret;
17642b06fccSRaju Rangoju 
17742b06fccSRaju Rangoju 	attr.dst = pdata->netdev->dev_addr;
17842b06fccSRaju Rangoju 	ret = __xgbe_test_loopback(pdata, &attr);
17942b06fccSRaju Rangoju 
18042b06fccSRaju Rangoju 	phy_loopback(pdata->netdev->phydev, false, 0);
18142b06fccSRaju Rangoju 	return ret;
18242b06fccSRaju Rangoju }
18342b06fccSRaju Rangoju 
184d7735c6bSRaju Rangoju static int xgbe_test_sph(struct xgbe_prv_data *pdata)
185d7735c6bSRaju Rangoju {
186d7735c6bSRaju Rangoju 	struct net_packet_attrs attr = {};
187d7735c6bSRaju Rangoju 	unsigned long cnt_end, cnt_start;
188d7735c6bSRaju Rangoju 	int ret;
189d7735c6bSRaju Rangoju 
190d7735c6bSRaju Rangoju 	cnt_start = pdata->ext_stats.rx_split_header_packets;
191d7735c6bSRaju Rangoju 
192d7735c6bSRaju Rangoju 	if (!pdata->sph) {
193d7735c6bSRaju Rangoju 		netdev_err(pdata->netdev, "Split Header not enabled\n");
194d7735c6bSRaju Rangoju 		return -EOPNOTSUPP;
195d7735c6bSRaju Rangoju 	}
196d7735c6bSRaju Rangoju 
197d7735c6bSRaju Rangoju 	/* UDP test */
198d7735c6bSRaju Rangoju 	attr.dst = pdata->netdev->dev_addr;
199d7735c6bSRaju Rangoju 	attr.tcp = false;
200d7735c6bSRaju Rangoju 
201d7735c6bSRaju Rangoju 	ret = __xgbe_test_loopback(pdata, &attr);
202d7735c6bSRaju Rangoju 	if (ret)
203d7735c6bSRaju Rangoju 		return ret;
204d7735c6bSRaju Rangoju 
205d7735c6bSRaju Rangoju 	cnt_end = pdata->ext_stats.rx_split_header_packets;
206d7735c6bSRaju Rangoju 	if (cnt_end <= cnt_start)
207d7735c6bSRaju Rangoju 		return -EINVAL;
208d7735c6bSRaju Rangoju 
209d7735c6bSRaju Rangoju 	/* TCP test */
210d7735c6bSRaju Rangoju 	cnt_start = cnt_end;
211d7735c6bSRaju Rangoju 
212d7735c6bSRaju Rangoju 	attr.dst = pdata->netdev->dev_addr;
213d7735c6bSRaju Rangoju 	attr.tcp = true;
214d7735c6bSRaju Rangoju 
215d7735c6bSRaju Rangoju 	ret = __xgbe_test_loopback(pdata, &attr);
216d7735c6bSRaju Rangoju 	if (ret)
217d7735c6bSRaju Rangoju 		return ret;
218d7735c6bSRaju Rangoju 
219d7735c6bSRaju Rangoju 	cnt_end = pdata->ext_stats.rx_split_header_packets;
220d7735c6bSRaju Rangoju 	if (cnt_end <= cnt_start)
221d7735c6bSRaju Rangoju 		return -EINVAL;
222d7735c6bSRaju Rangoju 
223d7735c6bSRaju Rangoju 	return 0;
224d7735c6bSRaju Rangoju }
225d7735c6bSRaju Rangoju 
226*9c11b6b1SRaju Rangoju static int xgbe_test_jumbo(struct xgbe_prv_data *pdata)
227*9c11b6b1SRaju Rangoju {
228*9c11b6b1SRaju Rangoju 	struct net_packet_attrs attr = {};
229*9c11b6b1SRaju Rangoju 	int size = pdata->rx_buf_size;
230*9c11b6b1SRaju Rangoju 
231*9c11b6b1SRaju Rangoju 	attr.dst = pdata->netdev->dev_addr;
232*9c11b6b1SRaju Rangoju 	attr.max_size = size - ETH_FCS_LEN;
233*9c11b6b1SRaju Rangoju 
234*9c11b6b1SRaju Rangoju 	return __xgbe_test_loopback(pdata, &attr);
235*9c11b6b1SRaju Rangoju }
236*9c11b6b1SRaju Rangoju 
237862a64c8SRaju Rangoju static const struct xgbe_test xgbe_selftests[] = {
238862a64c8SRaju Rangoju 	{
239862a64c8SRaju Rangoju 		.name = "MAC Loopback   ",
240862a64c8SRaju Rangoju 		.lb = XGBE_LOOPBACK_MAC,
241862a64c8SRaju Rangoju 		.fn = xgbe_test_mac_loopback,
24242b06fccSRaju Rangoju 	}, {
24342b06fccSRaju Rangoju 		.name = "PHY Loopback   ",
24442b06fccSRaju Rangoju 		.lb = XGBE_LOOPBACK_NONE,
24542b06fccSRaju Rangoju 		.fn = xgbe_test_phy_loopback,
246d7735c6bSRaju Rangoju 	}, {
247d7735c6bSRaju Rangoju 		.name = "Split Header   ",
248d7735c6bSRaju Rangoju 		.lb = XGBE_LOOPBACK_PHY,
249d7735c6bSRaju Rangoju 		.fn = xgbe_test_sph,
250*9c11b6b1SRaju Rangoju 	}, {
251*9c11b6b1SRaju Rangoju 		.name = "Jumbo Frame    ",
252*9c11b6b1SRaju Rangoju 		.lb = XGBE_LOOPBACK_PHY,
253*9c11b6b1SRaju Rangoju 		.fn = xgbe_test_jumbo,
254862a64c8SRaju Rangoju 	},
255862a64c8SRaju Rangoju };
256862a64c8SRaju Rangoju 
257862a64c8SRaju Rangoju void xgbe_selftest_run(struct net_device *dev,
258862a64c8SRaju Rangoju 		       struct ethtool_test *etest, u64 *buf)
259862a64c8SRaju Rangoju {
260862a64c8SRaju Rangoju 	struct xgbe_prv_data *pdata = netdev_priv(dev);
261862a64c8SRaju Rangoju 	int count = xgbe_selftest_get_count(pdata);
262862a64c8SRaju Rangoju 	int i, ret;
263862a64c8SRaju Rangoju 
264862a64c8SRaju Rangoju 	memset(buf, 0, sizeof(*buf) * count);
265862a64c8SRaju Rangoju 	xgbe_test_id = 0;
266862a64c8SRaju Rangoju 
267862a64c8SRaju Rangoju 	if (etest->flags != ETH_TEST_FL_OFFLINE) {
268862a64c8SRaju Rangoju 		netdev_err(pdata->netdev, "Only offline tests are supported\n");
269862a64c8SRaju Rangoju 		etest->flags |= ETH_TEST_FL_FAILED;
270862a64c8SRaju Rangoju 		return;
271862a64c8SRaju Rangoju 	} else if (!netif_carrier_ok(dev)) {
272862a64c8SRaju Rangoju 		netdev_err(pdata->netdev,
273862a64c8SRaju Rangoju 			   "Invalid link, cannot execute tests\n");
274862a64c8SRaju Rangoju 		etest->flags |= ETH_TEST_FL_FAILED;
275862a64c8SRaju Rangoju 		return;
276862a64c8SRaju Rangoju 	}
277862a64c8SRaju Rangoju 
278862a64c8SRaju Rangoju 	/* Wait for queues drain */
279862a64c8SRaju Rangoju 	msleep(200);
280862a64c8SRaju Rangoju 
281862a64c8SRaju Rangoju 	for (i = 0; i < count; i++) {
282862a64c8SRaju Rangoju 		ret = 0;
283862a64c8SRaju Rangoju 
284862a64c8SRaju Rangoju 		switch (xgbe_selftests[i].lb) {
28542b06fccSRaju Rangoju 		case XGBE_LOOPBACK_PHY:
28642b06fccSRaju Rangoju 			ret = -EOPNOTSUPP;
28742b06fccSRaju Rangoju 			if (dev->phydev)
28842b06fccSRaju Rangoju 				ret = phy_loopback(dev->phydev, true, 0);
28942b06fccSRaju Rangoju 			if (!ret)
29042b06fccSRaju Rangoju 				break;
29142b06fccSRaju Rangoju 			fallthrough;
292862a64c8SRaju Rangoju 		case XGBE_LOOPBACK_MAC:
293862a64c8SRaju Rangoju 			ret = xgbe_enable_mac_loopback(pdata);
294862a64c8SRaju Rangoju 			break;
295862a64c8SRaju Rangoju 		case XGBE_LOOPBACK_NONE:
296862a64c8SRaju Rangoju 			break;
297862a64c8SRaju Rangoju 		default:
298862a64c8SRaju Rangoju 			ret = -EOPNOTSUPP;
299862a64c8SRaju Rangoju 			break;
300862a64c8SRaju Rangoju 		}
301862a64c8SRaju Rangoju 
302862a64c8SRaju Rangoju 		/*
303862a64c8SRaju Rangoju 		 * First tests will always be MAC / PHY loopback.
304862a64c8SRaju Rangoju 		 * If any of them is not supported we abort earlier.
305862a64c8SRaju Rangoju 		 */
306862a64c8SRaju Rangoju 		if (ret) {
307862a64c8SRaju Rangoju 			netdev_err(pdata->netdev, "Loopback not supported\n");
308862a64c8SRaju Rangoju 			etest->flags |= ETH_TEST_FL_FAILED;
309862a64c8SRaju Rangoju 			break;
310862a64c8SRaju Rangoju 		}
311862a64c8SRaju Rangoju 
312862a64c8SRaju Rangoju 		ret = xgbe_selftests[i].fn(pdata);
313862a64c8SRaju Rangoju 		if (ret && (ret != -EOPNOTSUPP))
314862a64c8SRaju Rangoju 			etest->flags |= ETH_TEST_FL_FAILED;
315862a64c8SRaju Rangoju 		buf[i] = ret;
316862a64c8SRaju Rangoju 
317862a64c8SRaju Rangoju 		switch (xgbe_selftests[i].lb) {
31842b06fccSRaju Rangoju 		case XGBE_LOOPBACK_PHY:
31942b06fccSRaju Rangoju 			ret = -EOPNOTSUPP;
32042b06fccSRaju Rangoju 			if (dev->phydev)
32142b06fccSRaju Rangoju 				ret = phy_loopback(dev->phydev, false, 0);
32242b06fccSRaju Rangoju 			if (!ret)
32342b06fccSRaju Rangoju 				break;
32442b06fccSRaju Rangoju 			fallthrough;
325862a64c8SRaju Rangoju 		case XGBE_LOOPBACK_MAC:
326862a64c8SRaju Rangoju 			xgbe_disable_mac_loopback(pdata);
327862a64c8SRaju Rangoju 			break;
328862a64c8SRaju Rangoju 		default:
329862a64c8SRaju Rangoju 			break;
330862a64c8SRaju Rangoju 		}
331862a64c8SRaju Rangoju 	}
332862a64c8SRaju Rangoju }
333862a64c8SRaju Rangoju 
334862a64c8SRaju Rangoju void xgbe_selftest_get_strings(struct xgbe_prv_data *pdata, u8 *data)
335862a64c8SRaju Rangoju {
336862a64c8SRaju Rangoju 	u8 *p = data;
337862a64c8SRaju Rangoju 	int i;
338862a64c8SRaju Rangoju 
339862a64c8SRaju Rangoju 	for (i = 0; i < xgbe_selftest_get_count(pdata); i++)
340862a64c8SRaju Rangoju 		ethtool_puts(&p, xgbe_selftests[i].name);
341862a64c8SRaju Rangoju }
342862a64c8SRaju Rangoju 
343862a64c8SRaju Rangoju int xgbe_selftest_get_count(struct xgbe_prv_data *pdata)
344862a64c8SRaju Rangoju {
345862a64c8SRaju Rangoju 	return ARRAY_SIZE(xgbe_selftests);
346862a64c8SRaju Rangoju }
347