Add support for ethtool selftest for MAC loopback. This includes the sanity check and helps in finding the misconfiguration of HW. Signed-off-by: Raju Rangoju --- Changes since v1: - fix build warnings for s390 arch reported by kernel test robot drivers/net/ethernet/amd/xgbe/Makefile | 2 +- drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c | 7 + drivers/net/ethernet/amd/xgbe/xgbe-selftest.c | 397 ++++++++++++++++++ drivers/net/ethernet/amd/xgbe/xgbe.h | 5 + 4 files changed, 410 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/amd/xgbe/xgbe-selftest.c diff --git a/drivers/net/ethernet/amd/xgbe/Makefile b/drivers/net/ethernet/amd/xgbe/Makefile index 980e27652237..5992f7fd4d9b 100644 --- a/drivers/net/ethernet/amd/xgbe/Makefile +++ b/drivers/net/ethernet/amd/xgbe/Makefile @@ -5,7 +5,7 @@ amd-xgbe-objs := xgbe-main.o xgbe-drv.o xgbe-dev.o \ xgbe-desc.o xgbe-ethtool.o xgbe-mdio.o \ xgbe-hwtstamp.o xgbe-ptp.o xgbe-pps.o \ xgbe-i2c.o xgbe-phy-v1.o xgbe-phy-v2.o \ - xgbe-platform.o + xgbe-platform.o xgbe-selftest.o amd-xgbe-$(CONFIG_PCI) += xgbe-pci.o amd-xgbe-$(CONFIG_AMD_XGBE_DCB) += xgbe-dcb.o diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c index b6e1b67a2d0e..0d19b09497a0 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c @@ -85,6 +85,9 @@ static void xgbe_get_strings(struct net_device *netdev, u32 stringset, u8 *data) int i; switch (stringset) { + case ETH_SS_TEST: + xgbe_selftest_get_strings(pdata, data); + break; case ETH_SS_STATS: for (i = 0; i < XGBE_STATS_COUNT; i++) ethtool_puts(&data, xgbe_gstring_stats[i].stat_string); @@ -131,6 +134,9 @@ static int xgbe_get_sset_count(struct net_device *netdev, int stringset) int ret; switch (stringset) { + case ETH_SS_TEST: + ret = xgbe_selftest_get_count(pdata); + break; case ETH_SS_STATS: ret = XGBE_STATS_COUNT + (pdata->tx_ring_count * 2) + @@ -760,6 +766,7 @@ static const struct ethtool_ops xgbe_ethtool_ops = { .set_ringparam = xgbe_set_ringparam, .get_channels = xgbe_get_channels, .set_channels = xgbe_set_channels, + .self_test = xgbe_selftest_run, }; const struct ethtool_ops *xgbe_get_ethtool_ops(void) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-selftest.c b/drivers/net/ethernet/amd/xgbe/xgbe-selftest.c new file mode 100644 index 000000000000..5209222fb4de --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-selftest.c @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-3-Clause) +/* + * Copyright (c) 2014-2025, Advanced Micro Devices, Inc. + * Copyright (c) 2014, Synopsys, Inc. + * All rights reserved + * + * Author: Raju Rangoju + */ +#include +#include +#include +#include +#include +#include "xgbe.h" +#include "xgbe-common.h" + +#define XGBE_TEST_PKT_SIZE (sizeof(struct ethhdr) + \ + sizeof(struct iphdr) + \ + sizeof(struct xgbe_hdr)) + +#define XGBE_TEST_PKT_MAGIC 0xdeadbeefdeadfeedULL +#define XGBE_LB_TIMEOUT msecs_to_jiffies(200) + +#define XGBE_LOOPBACK_NONE 0 +#define XGBE_LOOPBACK_MAC 1 + +struct xgbe_hdr { + __be32 version; + __be64 magic; + u8 id; +} __packed; + +struct xgbe_pkt_attrs { + unsigned char *src; + const unsigned char *dst; + u32 ip_src; + u32 ip_dst; + int tcp; + int sport; + int dport; + int timeout; + int size; + int max_size; + u8 id; + u16 queue_mapping; + u64 timestamp; +}; + +struct xgbe_test_data { + struct xgbe_pkt_attrs *packet; + struct packet_type pt; + struct completion comp; + int ok; +}; + +struct xgbe_test { + char name[ETH_GSTRING_LEN]; + int lb; + int (*fn)(struct xgbe_prv_data *pdata); +}; + +static u8 xgbe_test_id; + +static int xgbe_config_mac_loopback(struct xgbe_prv_data *pdata, bool enable) +{ + XGMAC_IOWRITE_BITS(pdata, MAC_RCR, LM, enable ? 1 : 0); + return 0; +} + +static struct sk_buff *xgbe_test_get_skb(struct xgbe_prv_data *pdata, + struct xgbe_pkt_attrs *attr) +{ + struct sk_buff *skb = NULL; + struct udphdr *uh = NULL; + struct tcphdr *th = NULL; + struct xgbe_hdr *hdr; + struct ethhdr *eh; + struct iphdr *ih; + int iplen, size; + + size = attr->size + XGBE_TEST_PKT_SIZE; + + if (attr->tcp) + size += sizeof(struct tcphdr); + else + size += sizeof(struct udphdr); + + if (attr->max_size && attr->max_size > size) + size = attr->max_size; + + skb = netdev_alloc_skb(pdata->netdev, size); + if (!skb) + return NULL; + + prefetchw(skb->data); + + eh = skb_push(skb, ETH_HLEN); + skb_reset_mac_header(skb); + + skb_set_network_header(skb, skb->len); + ih = skb_put(skb, sizeof(*ih)); + + skb_set_transport_header(skb, skb->len); + if (attr->tcp) + th = skb_put(skb, sizeof(*th)); + else + uh = skb_put(skb, sizeof(*uh)); + + eth_zero_addr(eh->h_source); + eth_zero_addr(eh->h_dest); + if (attr->src) + ether_addr_copy(eh->h_source, attr->src); + if (attr->dst) + ether_addr_copy(eh->h_dest, attr->dst); + + eh->h_proto = htons(ETH_P_IP); + + if (attr->tcp) { + th->source = htons(attr->sport); + th->dest = htons(attr->dport); + th->doff = sizeof(struct tcphdr) / 4; + th->check = 0; + } else { + uh->source = htons(attr->sport); + uh->dest = htons(attr->dport); + uh->len = htons(sizeof(*hdr) + sizeof(*uh) + attr->size); + if (attr->max_size) + uh->len = htons(attr->max_size - + (sizeof(*ih) + sizeof(*eh))); + uh->check = 0; + } + + ih->ihl = 5; + ih->ttl = 32; + ih->version = IPVERSION; + if (attr->tcp) + ih->protocol = IPPROTO_TCP; + else + ih->protocol = IPPROTO_UDP; + iplen = sizeof(*ih) + sizeof(*hdr) + attr->size; + if (attr->tcp) + iplen += sizeof(*th); + else + iplen += sizeof(*uh); + + if (attr->max_size) + iplen = attr->max_size - sizeof(*eh); + + ih->tot_len = htons(iplen); + ih->frag_off = 0; + ih->saddr = htonl(attr->ip_src); + ih->daddr = htonl(attr->ip_dst); + ih->tos = 0; + ih->id = 0; + ip_send_check(ih); + + hdr = skb_put(skb, sizeof(*hdr)); + hdr->version = 0; + hdr->magic = cpu_to_be64(XGBE_TEST_PKT_MAGIC); + attr->id = xgbe_test_id; + hdr->id = xgbe_test_id++; + + if (attr->size) + skb_put(skb, attr->size); + if (attr->max_size && attr->max_size > skb->len) + skb_put(skb, attr->max_size - skb->len); + + skb->csum = 0; + skb->ip_summed = CHECKSUM_PARTIAL; + if (attr->tcp) { + th->check = ~tcp_v4_check(skb->len, ih->saddr, ih->daddr, 0); + skb->csum_start = skb_transport_header(skb) - skb->head; + skb->csum_offset = offsetof(struct tcphdr, check); + } else { + udp4_hwcsum(skb, ih->saddr, ih->daddr); + } + + skb->protocol = htons(ETH_P_IP); + skb->pkt_type = PACKET_HOST; + skb->dev = pdata->netdev; + + if (attr->timestamp) + skb->tstamp = ns_to_ktime(attr->timestamp); + + return skb; +} + +static int xgbe_test_loopback_validate(struct sk_buff *skb, + struct net_device *ndev, + struct packet_type *pt, + struct net_device *orig_ndev) +{ + struct xgbe_test_data *tdata = pt->af_packet_priv; + const unsigned char *dst = tdata->packet->dst; + unsigned char *src = tdata->packet->src; + struct xgbe_hdr *hdr; + struct ethhdr *eh; + struct iphdr *ih; + struct tcphdr *th; + struct udphdr *uh; + + skb = skb_unshare(skb, GFP_ATOMIC); + if (!skb) + goto out; + + if (skb_linearize(skb)) + goto out; + + if (skb_headlen(skb) < (XGBE_TEST_PKT_SIZE - ETH_HLEN)) + goto out; + + eh = (struct ethhdr *)skb_mac_header(skb); + if (dst) { + if (!ether_addr_equal_unaligned(eh->h_dest, dst)) + goto out; + } + if (src) { + if (!ether_addr_equal_unaligned(eh->h_source, src)) + goto out; + } + + ih = ip_hdr(skb); + + if (tdata->packet->tcp) { + if (ih->protocol != IPPROTO_TCP) + goto out; + + th = (struct tcphdr *)((u8 *)ih + 4 * ih->ihl); + if (th->dest != htons(tdata->packet->dport)) + goto out; + + hdr = (struct xgbe_hdr *)((u8 *)th + sizeof(*th)); + } else { + if (ih->protocol != IPPROTO_UDP) + goto out; + + uh = (struct udphdr *)((u8 *)ih + 4 * ih->ihl); + if (uh->dest != htons(tdata->packet->dport)) + goto out; + + hdr = (struct xgbe_hdr *)((u8 *)uh + sizeof(*uh)); + } + + if (hdr->magic != cpu_to_be64(XGBE_TEST_PKT_MAGIC)) + goto out; + if (tdata->packet->id != hdr->id) + goto out; + + tdata->ok = true; + complete(&tdata->comp); +out: + kfree_skb(skb); + return 0; +} + +static int __xgbe_test_loopback(struct xgbe_prv_data *pdata, + struct xgbe_pkt_attrs *attr) +{ + struct xgbe_test_data *tdata; + struct sk_buff *skb = NULL; + int ret = 0; + + tdata = kzalloc(sizeof(*tdata), GFP_KERNEL); + if (!tdata) + return -ENOMEM; + + tdata->ok = false; + init_completion(&tdata->comp); + + tdata->pt.type = htons(ETH_P_IP); + tdata->pt.func = xgbe_test_loopback_validate; + tdata->pt.dev = pdata->netdev; + tdata->pt.af_packet_priv = tdata; + tdata->packet = attr; + + dev_add_pack(&tdata->pt); + + skb = xgbe_test_get_skb(pdata, attr); + if (!skb) { + ret = -ENOMEM; + goto cleanup; + } + + ret = dev_direct_xmit(skb, attr->queue_mapping); + if (ret) + goto cleanup; + + if (!attr->timeout) + attr->timeout = XGBE_LB_TIMEOUT; + + wait_for_completion_timeout(&tdata->comp, attr->timeout); + ret = tdata->ok ? 0 : -ETIMEDOUT; + + if (ret) + netdev_err(pdata->netdev, "Response timedout: ret %d\n", ret); +cleanup: + dev_remove_pack(&tdata->pt); + kfree(tdata); + return ret; +} + +static int xgbe_test_mac_loopback(struct xgbe_prv_data *pdata) +{ + struct xgbe_pkt_attrs attr = {}; + + attr.dst = pdata->netdev->dev_addr; + return __xgbe_test_loopback(pdata, &attr); +} + +static const struct xgbe_test xgbe_selftests[] = { + { + .name = "MAC Loopback ", + .lb = XGBE_LOOPBACK_MAC, + .fn = xgbe_test_mac_loopback, + }, +}; + +void xgbe_selftest_run(struct net_device *dev, + struct ethtool_test *etest, u64 *buf) +{ + struct xgbe_prv_data *pdata = netdev_priv(dev); + int count = xgbe_selftest_get_count(pdata); + int i, ret; + + memset(buf, 0, sizeof(*buf) * count); + xgbe_test_id = 0; + + if (etest->flags != ETH_TEST_FL_OFFLINE) { + netdev_err(pdata->netdev, "Only offline tests are supported\n"); + etest->flags |= ETH_TEST_FL_FAILED; + return; + } else if (!netif_carrier_ok(dev)) { + netdev_err(pdata->netdev, + "Invalid link, cannot execute tests\n"); + etest->flags |= ETH_TEST_FL_FAILED; + return; + } + + /* Wait for queues drain */ + msleep(200); + + for (i = 0; i < count; i++) { + ret = 0; + + switch (xgbe_selftests[i].lb) { + case XGBE_LOOPBACK_MAC: + ret = xgbe_config_mac_loopback(pdata, true); + break; + case XGBE_LOOPBACK_NONE: + break; + default: + ret = -EOPNOTSUPP; + break; + } + + /* + * First tests will always be MAC / PHY loopback. + * If any of them is not supported we abort earlier. + */ + if (ret) { + netdev_err(pdata->netdev, "Loopback not supported\n"); + etest->flags |= ETH_TEST_FL_FAILED; + break; + } + + ret = xgbe_selftests[i].fn(pdata); + if (ret && (ret != -EOPNOTSUPP)) + etest->flags |= ETH_TEST_FL_FAILED; + buf[i] = ret; + + switch (xgbe_selftests[i].lb) { + case XGBE_LOOPBACK_MAC: + xgbe_config_mac_loopback(pdata, false); + break; + default: + break; + } + } +} + +void xgbe_selftest_get_strings(struct xgbe_prv_data *pdata, u8 *data) +{ + u8 *p = data; + int i; + + for (i = 0; i < xgbe_selftest_get_count(pdata); i++) { + snprintf(p, ETH_GSTRING_LEN, "%2d. %s", i + 1, + xgbe_selftests[i].name); + p += ETH_GSTRING_LEN; + } +} + +int xgbe_selftest_get_count(struct xgbe_prv_data *pdata) +{ + return ARRAY_SIZE(xgbe_selftests); +} + diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index e8bbb6805901..f4da4d834e0d 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -1321,6 +1321,11 @@ void xgbe_update_tstamp_time(struct xgbe_prv_data *pdata, unsigned int sec, int xgbe_pps_config(struct xgbe_prv_data *pdata, struct xgbe_pps_config *cfg, int index, bool on); +void xgbe_selftest_run(struct net_device *dev, + struct ethtool_test *etest, u64 *buf); +void xgbe_selftest_get_strings(struct xgbe_prv_data *pdata, u8 *data); +int xgbe_selftest_get_count(struct xgbe_prv_data *pdata); + #ifdef CONFIG_DEBUG_FS void xgbe_debugfs_init(struct xgbe_prv_data *); void xgbe_debugfs_exit(struct xgbe_prv_data *); -- 2.34.1 Adds support for ethtool PHY loopback selftest. It uses genphy_loopback function, which use BMCR loopback bit to enable or disable loopback. Signed-off-by: Raju Rangoju --- drivers/net/ethernet/amd/xgbe/xgbe-selftest.c | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-selftest.c b/drivers/net/ethernet/amd/xgbe/xgbe-selftest.c index 5209222fb4de..0c7770aab979 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-selftest.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-selftest.c @@ -23,6 +23,7 @@ #define XGBE_LOOPBACK_NONE 0 #define XGBE_LOOPBACK_MAC 1 +#define XGBE_LOOPBACK_PHY 2 struct xgbe_hdr { __be32 version; @@ -307,11 +308,36 @@ static int xgbe_test_mac_loopback(struct xgbe_prv_data *pdata) return __xgbe_test_loopback(pdata, &attr); } +static int xgbe_test_phy_loopback(struct xgbe_prv_data *pdata) +{ + struct xgbe_pkt_attrs attr = {}; + int ret; + + if (!pdata->netdev->phydev) { + netdev_err(pdata->netdev, "phydev not found: cannot start PHY loopback test\n"); + return -EOPNOTSUPP; + } + + ret = phy_loopback(pdata->netdev->phydev, true, 0); + if (ret) + return ret; + + attr.dst = pdata->netdev->dev_addr; + ret = __xgbe_test_loopback(pdata, &attr); + + phy_loopback(pdata->netdev->phydev, false, 0); + return ret; +} + static const struct xgbe_test xgbe_selftests[] = { { .name = "MAC Loopback ", .lb = XGBE_LOOPBACK_MAC, .fn = xgbe_test_mac_loopback, + }, { + .name = "PHY Loopback ", + .lb = XGBE_LOOPBACK_NONE, + .fn = xgbe_test_phy_loopback, }, }; @@ -343,6 +369,13 @@ void xgbe_selftest_run(struct net_device *dev, ret = 0; switch (xgbe_selftests[i].lb) { + case XGBE_LOOPBACK_PHY: + ret = -EOPNOTSUPP; + if (dev->phydev) + ret = phy_loopback(dev->phydev, true, 0); + if (!ret) + break; + fallthrough; case XGBE_LOOPBACK_MAC: ret = xgbe_config_mac_loopback(pdata, true); break; @@ -369,6 +402,13 @@ void xgbe_selftest_run(struct net_device *dev, buf[i] = ret; switch (xgbe_selftests[i].lb) { + case XGBE_LOOPBACK_PHY: + ret = -EOPNOTSUPP; + if (dev->phydev) + ret = phy_loopback(dev->phydev, false, 0); + if (!ret) + break; + fallthrough; case XGBE_LOOPBACK_MAC: xgbe_config_mac_loopback(pdata, false); break; -- 2.34.1 Adds support for ethtool split header selftest. Performs UDP and TCP check to ensure split header selft test works for both packet types. Signed-off-by: Raju Rangoju --- drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 2 + drivers/net/ethernet/amd/xgbe/xgbe-selftest.c | 47 +++++++++++++++++++ drivers/net/ethernet/amd/xgbe/xgbe.h | 1 + 3 files changed, 50 insertions(+) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index e5391a2eca51..71d67bdeae92 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -211,6 +211,7 @@ static void xgbe_config_sph_mode(struct xgbe_prv_data *pdata) } XGMAC_IOWRITE_BITS(pdata, MAC_RCR, HDSMS, XGBE_SPH_HDSMS_SIZE); + pdata->sph = true; } static void xgbe_disable_sph_mode(struct xgbe_prv_data *pdata) @@ -223,6 +224,7 @@ static void xgbe_disable_sph_mode(struct xgbe_prv_data *pdata) XGMAC_DMA_IOWRITE_BITS(pdata->channel[i], DMA_CH_CR, SPH, 0); } + pdata->sph = false; } static int xgbe_write_rss_reg(struct xgbe_prv_data *pdata, unsigned int type, diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-selftest.c b/drivers/net/ethernet/amd/xgbe/xgbe-selftest.c index 0c7770aab979..ae7c8d6aca61 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-selftest.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-selftest.c @@ -329,6 +329,48 @@ static int xgbe_test_phy_loopback(struct xgbe_prv_data *pdata) return ret; } +static int xgbe_test_sph(struct xgbe_prv_data *pdata) +{ + unsigned long cnt_end, cnt_start; + struct xgbe_pkt_attrs attr = {}; + int ret; + + cnt_start = pdata->ext_stats.rx_split_header_packets; + + if (!pdata->sph) { + netdev_err(pdata->netdev, "Split Header not enabled\n"); + return -EOPNOTSUPP; + } + + /* UDP test */ + attr.dst = pdata->netdev->dev_addr; + attr.tcp = false; + + ret = __xgbe_test_loopback(pdata, &attr); + if (ret) + return ret; + + cnt_end = pdata->ext_stats.rx_split_header_packets; + if (cnt_end <= cnt_start) + return -EINVAL; + + /* TCP test */ + cnt_start = cnt_end; + + attr.dst = pdata->netdev->dev_addr; + attr.tcp = true; + + ret = __xgbe_test_loopback(pdata, &attr); + if (ret) + return ret; + + cnt_end = pdata->ext_stats.rx_split_header_packets; + if (cnt_end <= cnt_start) + return -EINVAL; + + return 0; +} + static const struct xgbe_test xgbe_selftests[] = { { .name = "MAC Loopback ", @@ -338,7 +380,12 @@ static const struct xgbe_test xgbe_selftests[] = { .name = "PHY Loopback ", .lb = XGBE_LOOPBACK_NONE, .fn = xgbe_test_phy_loopback, + }, { + .name = "Split Header ", + .lb = XGBE_LOOPBACK_PHY, + .fn = xgbe_test_sph, }, + }; void xgbe_selftest_run(struct net_device *dev, diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index f4da4d834e0d..a51498af4aac 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -1246,6 +1246,7 @@ struct xgbe_prv_data { int rx_adapt_retries; bool rx_adapt_done; bool mode_set; + bool sph; }; /* Function prototypes*/ -- 2.34.1 Adds support for jumbo frame selftest. Works only for mtu size greater than 1500. Signed-off-by: Raju Rangoju --- drivers/net/ethernet/amd/xgbe/xgbe-selftest.c | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-selftest.c b/drivers/net/ethernet/amd/xgbe/xgbe-selftest.c index ae7c8d6aca61..717e03040420 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-selftest.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-selftest.c @@ -199,10 +199,18 @@ static int xgbe_test_loopback_validate(struct sk_buff *skb, struct iphdr *ih; struct tcphdr *th; struct udphdr *uh; + int eat; skb = skb_unshare(skb, GFP_ATOMIC); if (!skb) - goto out; + return -1; + + eat = (skb->tail + skb->data_len) - skb->end; + if (eat > 0 && skb_shared(skb)) { + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + return -1; + } if (skb_linearize(skb)) goto out; @@ -371,6 +379,17 @@ static int xgbe_test_sph(struct xgbe_prv_data *pdata) return 0; } +static int xgbe_test_jumbo(struct xgbe_prv_data *pdata) +{ + struct xgbe_pkt_attrs attr = {}; + int size = pdata->rx_buf_size; + + attr.dst = pdata->netdev->dev_addr; + attr.max_size = size - ETH_FCS_LEN; + + return __xgbe_test_loopback(pdata, &attr); +} + static const struct xgbe_test xgbe_selftests[] = { { .name = "MAC Loopback ", @@ -384,8 +403,11 @@ static const struct xgbe_test xgbe_selftests[] = { .name = "Split Header ", .lb = XGBE_LOOPBACK_PHY, .fn = xgbe_test_sph, + }, { + .name = "Jumbo Frame ", + .lb = XGBE_LOOPBACK_PHY, + .fn = xgbe_test_jumbo, }, - }; void xgbe_selftest_run(struct net_device *dev, -- 2.34.1