File Index
Files and directories
Sign in
FILE

xgbe-selftest.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/amd/xgbe/xgbe-selftest.c C
  1// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-3-Clause)
  2/*
  3 * Copyright (c) 2014-2025, Advanced Micro Devices, Inc.
  4 * Copyright (c) 2014, Synopsys, Inc.
  5 * All rights reserved
  6 *
  7 * Author: Raju Rangoju <Raju.Rangoju@amd.com>
  8 */
  9#include <linux/crc32.h>
 10#include <linux/ip.h>
 11#include <linux/udp.h>
 12#include <net/tcp.h>
 13#include <net/udp.h>
 14#include <net/checksum.h>
 15#include <net/selftests.h>
 16
 17#include "xgbe.h"
 18#include "xgbe-common.h"
 19
 20#define XGBE_LOOPBACK_NONE	0
 21#define XGBE_LOOPBACK_MAC	1
 22#define XGBE_LOOPBACK_PHY	2
 23
 24struct xgbe_test {
 25	char name[ETH_GSTRING_LEN];
 26	int lb;
 27	int (*fn)(struct xgbe_prv_data *pdata);
 28};
 29
 30static u8 xgbe_test_id;
 31
 32static int xgbe_test_loopback_validate(struct sk_buff *skb,
 33				       struct net_device *ndev,
 34				       struct packet_type *pt,
 35				       struct net_device *orig_ndev)
 36{
 37	struct net_test_priv *tdata = pt->af_packet_priv;
 38	const unsigned char *dst = tdata->packet->dst;
 39	const unsigned char *src = tdata->packet->src;
 40	struct netsfhdr *hdr;
 41	struct ethhdr *eh;
 42	struct tcphdr *th;
 43	struct udphdr *uh;
 44	struct iphdr *ih;
 45	int eat;
 46
 47	skb = skb_unshare(skb, GFP_ATOMIC);
 48	if (!skb)
 49		goto out;
 50
 51	eat = (skb->tail + skb->data_len) - skb->end;
 52	if (eat > 0 && skb_shared(skb)) {
 53		skb = skb_share_check(skb, GFP_ATOMIC);
 54		if (!skb)
 55			goto out;
 56	}
 57
 58	if (skb_linearize(skb))
 59		goto out;
 60
 61	if (skb_headlen(skb) < (NET_TEST_PKT_SIZE - ETH_HLEN))
 62		goto out;
 63
 64	eh = (struct ethhdr *)skb_mac_header(skb);
 65	if (dst) {
 66		if (!ether_addr_equal_unaligned(eh->h_dest, dst))
 67			goto out;
 68	}
 69	if (src) {
 70		if (!ether_addr_equal_unaligned(eh->h_source, src))
 71			goto out;
 72	}
 73
 74	ih = ip_hdr(skb);
 75
 76	if (tdata->packet->tcp) {
 77		if (ih->protocol != IPPROTO_TCP)
 78			goto out;
 79
 80		th = (struct tcphdr *)((u8 *)ih + 4 * ih->ihl);
 81		if (th->dest != htons(tdata->packet->dport))
 82			goto out;
 83
 84		hdr = (struct netsfhdr *)((u8 *)th + sizeof(*th));
 85	} else {
 86		if (ih->protocol != IPPROTO_UDP)
 87			goto out;
 88
 89		uh = (struct udphdr *)((u8 *)ih + 4 * ih->ihl);
 90		if (uh->dest != htons(tdata->packet->dport))
 91			goto out;
 92
 93		hdr = (struct netsfhdr *)((u8 *)uh + sizeof(*uh));
 94	}
 95
 96	if (hdr->magic != cpu_to_be64(NET_TEST_PKT_MAGIC))
 97		goto out;
 98	if (tdata->packet->id != hdr->id)
 99		goto out;
100
101	tdata->ok = true;
102	complete(&tdata->comp);
103out:
104	kfree_skb(skb);
105	return 0;
106}
107
108static int __xgbe_test_loopback(struct xgbe_prv_data *pdata,
109				struct net_packet_attrs *attr)
110{
111	struct net_test_priv *tdata;
112	struct sk_buff *skb = NULL;
113	int ret = 0;
114
115	tdata = kzalloc(sizeof(*tdata), GFP_KERNEL);
116	if (!tdata)
117		return -ENOMEM;
118
119	tdata->ok = false;
120	init_completion(&tdata->comp);
121
122	tdata->pt.type = htons(ETH_P_IP);
123	tdata->pt.func = xgbe_test_loopback_validate;
124	tdata->pt.dev = pdata->netdev;
125	tdata->pt.af_packet_priv = tdata;
126	tdata->packet = attr;
127
128	dev_add_pack(&tdata->pt);
129
130	skb = net_test_get_skb(pdata->netdev, xgbe_test_id, attr);
131	if (!skb) {
132		ret = -ENOMEM;
133		goto cleanup;
134	}
135
136	xgbe_test_id++;
137	ret = dev_direct_xmit(skb, attr->queue_mapping);
138	if (ret)
139		goto cleanup;
140
141	if (!attr->timeout)
142		attr->timeout = NET_LB_TIMEOUT;
143
144	wait_for_completion_timeout(&tdata->comp, attr->timeout);
145	ret = tdata->ok ? 0 : -ETIMEDOUT;
146
147	if (ret)
148		netdev_err(pdata->netdev, "Response timedout: ret %d\n", ret);
149cleanup:
150	dev_remove_pack(&tdata->pt);
151	kfree(tdata);
152	return ret;
153}
154
155static int xgbe_test_mac_loopback(struct xgbe_prv_data *pdata)
156{
157	struct net_packet_attrs attr = {};
158
159	attr.dst = pdata->netdev->dev_addr;
160	return __xgbe_test_loopback(pdata, &attr);
161}
162
163static int xgbe_test_phy_loopback(struct xgbe_prv_data *pdata)
164{
165	struct net_packet_attrs attr = {};
166	int ret;
167
168	if (!pdata->netdev->phydev) {
169		netdev_err(pdata->netdev, "phydev not found: cannot start PHY loopback test\n");
170		return -EOPNOTSUPP;
171	}
172
173	ret = phy_loopback(pdata->netdev->phydev, true, 0);
174	if (ret)
175		return ret;
176
177	attr.dst = pdata->netdev->dev_addr;
178	ret = __xgbe_test_loopback(pdata, &attr);
179
180	phy_loopback(pdata->netdev->phydev, false, 0);
181	return ret;
182}
183
184static int xgbe_test_sph(struct xgbe_prv_data *pdata)
185{
186	struct net_packet_attrs attr = {};
187	unsigned long cnt_end, cnt_start;
188	int ret;
189
190	cnt_start = pdata->ext_stats.rx_split_header_packets;
191
192	if (!pdata->sph) {
193		netdev_err(pdata->netdev, "Split Header not enabled\n");
194		return -EOPNOTSUPP;
195	}
196
197	/* UDP test */
198	attr.dst = pdata->netdev->dev_addr;
199	attr.tcp = false;
200
201	ret = __xgbe_test_loopback(pdata, &attr);
202	if (ret)
203		return ret;
204
205	cnt_end = pdata->ext_stats.rx_split_header_packets;
206	if (cnt_end <= cnt_start)
207		return -EINVAL;
208
209	/* TCP test */
210	cnt_start = cnt_end;
211
212	attr.dst = pdata->netdev->dev_addr;
213	attr.tcp = true;
214
215	ret = __xgbe_test_loopback(pdata, &attr);
216	if (ret)
217		return ret;
218
219	cnt_end = pdata->ext_stats.rx_split_header_packets;
220	if (cnt_end <= cnt_start)
221		return -EINVAL;
222
223	return 0;
224}
225
226static int xgbe_test_jumbo(struct xgbe_prv_data *pdata)
227{
228	struct net_packet_attrs attr = {};
229	int size = pdata->rx_buf_size;
230
231	attr.dst = pdata->netdev->dev_addr;
232	attr.max_size = size - ETH_FCS_LEN;
233
234	return __xgbe_test_loopback(pdata, &attr);
235}
236
237static const struct xgbe_test xgbe_selftests[] = {
238	{
239		.name = "MAC Loopback   ",
240		.lb = XGBE_LOOPBACK_MAC,
241		.fn = xgbe_test_mac_loopback,
242	}, {
243		.name = "PHY Loopback   ",
244		.lb = XGBE_LOOPBACK_NONE,
245		.fn = xgbe_test_phy_loopback,
246	}, {
247		.name = "Split Header   ",
248		.lb = XGBE_LOOPBACK_PHY,
249		.fn = xgbe_test_sph,
250	}, {
251		.name = "Jumbo Frame    ",
252		.lb = XGBE_LOOPBACK_PHY,
253		.fn = xgbe_test_jumbo,
254	},
255};
256
257void xgbe_selftest_run(struct net_device *dev,
258		       struct ethtool_test *etest, u64 *buf)
259{
260	struct xgbe_prv_data *pdata = netdev_priv(dev);
261	int count = xgbe_selftest_get_count(pdata);
262	int i, ret;
263
264	memset(buf, 0, sizeof(*buf) * count);
265	xgbe_test_id = 0;
266
267	if (etest->flags != ETH_TEST_FL_OFFLINE) {
268		netdev_err(pdata->netdev, "Only offline tests are supported\n");
269		etest->flags |= ETH_TEST_FL_FAILED;
270		return;
271	} else if (!netif_carrier_ok(dev)) {
272		netdev_err(pdata->netdev,
273			   "Invalid link, cannot execute tests\n");
274		etest->flags |= ETH_TEST_FL_FAILED;
275		return;
276	}
277
278	/* Wait for queues drain */
279	msleep(200);
280
281	for (i = 0; i < count; i++) {
282		ret = 0;
283
284		switch (xgbe_selftests[i].lb) {
285		case XGBE_LOOPBACK_PHY:
286			ret = -EOPNOTSUPP;
287			if (dev->phydev)
288				ret = phy_loopback(dev->phydev, true, 0);
289			if (!ret)
290				break;
291			fallthrough;
292		case XGBE_LOOPBACK_MAC:
293			ret = xgbe_enable_mac_loopback(pdata);
294			break;
295		case XGBE_LOOPBACK_NONE:
296			break;
297		default:
298			ret = -EOPNOTSUPP;
299			break;
300		}
301
302		/*
303		 * First tests will always be MAC / PHY loopback.
304		 * If any of them is not supported we abort earlier.
305		 */
306		if (ret) {
307			netdev_err(pdata->netdev, "Loopback not supported\n");
308			etest->flags |= ETH_TEST_FL_FAILED;
309			break;
310		}
311
312		ret = xgbe_selftests[i].fn(pdata);
313		if (ret && (ret != -EOPNOTSUPP))
314			etest->flags |= ETH_TEST_FL_FAILED;
315		buf[i] = ret;
316
317		switch (xgbe_selftests[i].lb) {
318		case XGBE_LOOPBACK_PHY:
319			ret = -EOPNOTSUPP;
320			if (dev->phydev)
321				ret = phy_loopback(dev->phydev, false, 0);
322			if (!ret)
323				break;
324			fallthrough;
325		case XGBE_LOOPBACK_MAC:
326			xgbe_disable_mac_loopback(pdata);
327			break;
328		default:
329			break;
330		}
331	}
332}
333
334void xgbe_selftest_get_strings(struct xgbe_prv_data *pdata, u8 *data)
335{
336	u8 *p = data;
337	int i;
338
339	for (i = 0; i < xgbe_selftest_get_count(pdata); i++)
340		ethtool_puts(&p, xgbe_selftests[i].name);
341}
342
343int xgbe_selftest_get_count(struct xgbe_prv_data *pdata)
344{
345	return ARRAY_SIZE(xgbe_selftests);
346}
~/ Read-only file preview.