[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250421134358.1241851-2-shaojijie@huawei.com>
Date: Mon, 21 Apr 2025 21:43:57 +0800
From: Jijie Shao <shaojijie@...wei.com>
To: <davem@...emloft.net>, <edumazet@...gle.com>, <kuba@...nel.org>,
<pabeni@...hat.com>, <andrew+netdev@...n.ch>, <horms@...nel.org>
CC: <shenjian15@...wei.com>, <wangpeiyang1@...wei.com>,
<liuyonglong@...wei.com>, <chenhao418@...wei.com>,
<jonathan.cameron@...wei.com>, <shameerali.kolothum.thodi@...wei.com>,
<salil.mehta@...wei.com>, <netdev@...r.kernel.org>,
<linux-kernel@...r.kernel.org>, <gerhard@...leder-embedded.com>,
<shaojijie@...wei.com>
Subject: [PATCH RFC net-next 1/2] net: selftest: add net_selftest_custom() interface
In net/core/selftests.c,
net_selftest() supports loopback tests.
However, the loopback content of this interface is a fixed common test
and cannot be expanded to add the driver's own test.
In this patch, the net_selftest_custom() interface is added
to support driver customized loopback tests and
extra common loopback tests.
Signed-off-by: Jijie Shao <shaojijie@...wei.com>
---
include/net/selftests.h | 61 +++++++++++++
net/core/selftests.c | 188 +++++++++++++++++++++++++++++++++++++++-
2 files changed, 245 insertions(+), 4 deletions(-)
diff --git a/include/net/selftests.h b/include/net/selftests.h
index e65e8d230d33..a36e6ee0a41f 100644
--- a/include/net/selftests.h
+++ b/include/net/selftests.h
@@ -4,6 +4,48 @@
#include <linux/ethtool.h>
+#define NET_TEST_NETIF_CARRIER BIT(0)
+#define NET_TEST_FULL_DUPLEX BIT(1)
+#define NET_TEST_TCP BIT(2)
+#define NET_TEST_UDP BIT(3)
+#define NET_TEST_UDP_MAX_MTU BIT(4)
+
+#define NET_EXTRA_CARRIER_TEST BIT(0)
+#define NET_EXTRA_FULL_DUPLEX_TEST BIT(1)
+#define NET_EXTRA_PHY_TEST BIT(2)
+
+struct net_test_entry {
+ char name[ETH_GSTRING_LEN];
+
+ /* can set to NULL */
+ int (*enable)(struct net_device *ndev, bool enable);
+
+ /* can set to NULL */
+ int (*fn)(struct net_device *ndev);
+
+ /* if flag is set, fn() will be ignored,
+ * and will do test according to the flag,
+ * such as NET_TEST_UDP...
+ */
+ unsigned long flags;
+};
+
+#define NET_TEST_E(_name, _enable, _flags) { \
+ .name = _name, \
+ .enable = _enable, \
+ .fn = NULL, \
+ .flags = _flags }
+
+#define NET_TEST_ENTRY_MAX_COUNT 10
+struct net_test {
+ /* extra tests will be added based on this flag */
+ unsigned long extra_flags;
+
+ struct net_test_entry entries[NET_TEST_ENTRY_MAX_COUNT];
+ /* the count of entries, must <= NET_TEST_ENTRY_MAX_COUNT */
+ u32 count;
+};
+
#if IS_ENABLED(CONFIG_NET_SELFTESTS)
void net_selftest(struct net_device *ndev, struct ethtool_test *etest,
@@ -11,6 +53,11 @@ void net_selftest(struct net_device *ndev, struct ethtool_test *etest,
int net_selftest_get_count(void);
void net_selftest_get_strings(u8 *data);
+void net_selftest_custom(struct net_device *ndev, const struct net_test *test,
+ struct ethtool_test *etest, u64 *buf);
+int net_selftest_get_count_custom(const struct net_test *test);
+void net_selftest_get_strings_custom(const struct net_test *test, u8 *data);
+
#else
static inline void net_selftest(struct net_device *ndev, struct ethtool_test *etest,
@@ -27,5 +74,19 @@ static inline void net_selftest_get_strings(u8 *data)
{
}
+void net_selftest_custom(struct net_device *ndev, struct net_test *test,
+ struct ethtool_test *etest, u64 *buf)
+{
+}
+
+int net_selftest_get_count_custom(struct net_test *test)
+{
+ return 0;
+}
+
+void net_selftest_get_strings_custom(struct net_test *test, u8 *data)
+{
+}
+
#endif
#endif /* _NET_SELFTESTS */
diff --git a/net/core/selftests.c b/net/core/selftests.c
index e99ae983fca9..e6abae17f324 100644
--- a/net/core/selftests.c
+++ b/net/core/selftests.c
@@ -289,6 +289,11 @@ static int net_test_netif_carrier(struct net_device *ndev)
return netif_carrier_ok(ndev) ? 0 : -ENOLINK;
}
+static int net_test_full_duplex(struct net_device *ndev)
+{
+ return ndev->phydev->duplex == DUPLEX_FULL ? 0 : -EINVAL;
+}
+
static int net_test_phy_phydev(struct net_device *ndev)
{
return ndev->phydev ? 0 : -EOPNOTSUPP;
@@ -336,10 +341,7 @@ static int net_test_phy_loopback_tcp(struct net_device *ndev)
return __net_test_loopback(ndev, &attr);
}
-static const struct net_test {
- char name[ETH_GSTRING_LEN];
- int (*fn)(struct net_device *ndev);
-} net_selftests[] = {
+static const struct net_test_entry net_selftests[] = {
{
.name = "Carrier ",
.fn = net_test_netif_carrier,
@@ -405,6 +407,184 @@ void net_selftest_get_strings(u8 *data)
}
EXPORT_SYMBOL_GPL(net_selftest_get_strings);
+static const struct net_do_test_func {
+ int flag;
+ int (*fn)(struct net_device *ndev);
+} net_do_test_funcs[] = {
+ { NET_TEST_NETIF_CARRIER, net_test_netif_carrier },
+ { NET_TEST_FULL_DUPLEX, net_test_full_duplex },
+ { NET_TEST_UDP, net_test_phy_loopback_udp },
+ { NET_TEST_TCP, net_test_phy_loopback_tcp },
+ { NET_TEST_UDP_MAX_MTU, net_test_phy_loopback_udp_mtu },
+};
+
+static int net_do_test(struct net_device *ndev,
+ const struct net_test_entry *entry)
+{
+ int ret = -EOPNOTSUPP;
+ u32 i;
+
+ if (!entry->flags && entry->fn)
+ return entry->fn(ndev);
+
+ for (i = 0; i < ARRAY_SIZE(net_do_test_funcs); i++) {
+ if (!(entry->flags & net_do_test_funcs[i].flag))
+ continue;
+
+ ret = net_do_test_funcs[i].fn(ndev);
+ if (ret) {
+ netdev_err(ndev, "failed to do test, bit: %#x\n",
+ net_do_test_funcs[i].flag);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int net_selftest_entry(struct net_device *ndev,
+ const struct net_test_entry *entry)
+{
+ int ret;
+
+ if (entry->enable) {
+ ret = entry->enable(ndev, true);
+ if (ret) {
+ netdev_err(ndev,
+ "failed to enable test, ret = %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = net_do_test(ndev, entry);
+ if (entry->enable)
+ entry->enable(ndev, false);
+ return ret;
+}
+
+static void net_selftest_check_result(struct net_device *ndev,
+ const struct net_test_entry *entry,
+ struct ethtool_test *etest, u64 *result)
+{
+ *result = net_selftest_entry(ndev, entry);
+ if (*result)
+ etest->flags |= ETH_TEST_FL_FAILED;
+}
+
+static int net_test_prepare(struct net_device *ndev,
+ const struct net_test *test,
+ struct ethtool_test *etest, u64 *buf)
+{
+ u32 i;
+
+ /* first set all results to -ENOEXEC,
+ * test->count is also checked in .net_selftest_get_count_custom()
+ */
+ for (i = 0; i < net_selftest_get_count_custom(test); i++)
+ buf[i] = -ENOEXEC;
+
+ if (etest->flags != ETH_TEST_FL_OFFLINE) {
+ netdev_err(ndev, "Only offline tests are supported\n");
+ etest->flags |= ETH_TEST_FL_FAILED;
+ return -EOPNOTSUPP;
+ }
+
+ if (test->count > ARRAY_SIZE(test->entries)) {
+ netdev_err(ndev, "The count of entries exceeds the maximum\n");
+ etest->flags |= ETH_TEST_FL_FAILED;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int net_test_phy_enable(struct net_device *ndev, bool enable)
+{
+ if (!ndev->phydev)
+ return -EOPNOTSUPP;
+
+ return phy_loopback(ndev->phydev, enable, 0);
+}
+
+static const struct net_extra_test {
+ int flag;
+ struct net_test_entry entry;
+} net_extra_tests[] = {
+ {
+ .flag = NET_EXTRA_CARRIER_TEST,
+ .entry = NET_TEST_E("Carrier", NULL, NET_TEST_NETIF_CARRIER),
+ }, {
+ .flag = NET_EXTRA_FULL_DUPLEX_TEST,
+ .entry = NET_TEST_E("Full Duplex", NULL, NET_TEST_FULL_DUPLEX),
+ }, {
+ /* this test must be the last one */
+ .flag = NET_EXTRA_PHY_TEST,
+ .entry = NET_TEST_E("PHY internal loopback",
+ net_test_phy_enable,
+ NET_TEST_UDP_MAX_MTU | NET_TEST_TCP),
+ }
+};
+
+void net_selftest_custom(struct net_device *ndev, const struct net_test *test,
+ struct ethtool_test *etest, u64 *buf)
+{
+ u32 i, j = 0;
+ int ret;
+
+ ret = net_test_prepare(ndev, test, etest, buf);
+ if (ret)
+ return;
+
+ for (i = 0; i < test->count; i++)
+ net_selftest_check_result(ndev, &test->entries[i],
+ etest, &buf[j++]);
+
+ for (i = 0; i < ARRAY_SIZE(net_extra_tests); i++)
+ if (test->extra_flags & net_extra_tests[i].flag)
+ net_selftest_check_result(ndev,
+ &net_extra_tests[i].entry,
+ etest, &buf[j++]);
+}
+EXPORT_SYMBOL_GPL(net_selftest_custom);
+
+int net_selftest_get_count_custom(const struct net_test *test)
+{
+ u32 i, exter_count = 0;
+
+ if (test->count > ARRAY_SIZE(test->entries))
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(net_extra_tests); i++)
+ if (test->extra_flags & net_extra_tests[i].flag)
+ exter_count++;
+
+ return test->count + exter_count;
+}
+EXPORT_SYMBOL_GPL(net_selftest_get_count_custom);
+
+static void net_selftest_get_entry_string(const struct net_test_entry *entry,
+ u32 index, u8 **data)
+{
+ ethtool_sprintf(data, "%2d. %-30s", index, entry->name);
+}
+
+void net_selftest_get_strings_custom(const struct net_test *test, u8 *data)
+{
+ u32 i, j = 1;
+
+ if (test->count > ARRAY_SIZE(test->entries))
+ return;
+
+ for (i = 0; i < test->count; i++)
+ net_selftest_get_entry_string(&test->entries[i], j++, &data);
+
+ for (i = 0; i < ARRAY_SIZE(net_extra_tests); i++)
+ if (test->extra_flags & net_extra_tests[i].flag)
+ net_selftest_get_entry_string(&net_extra_tests[i].entry,
+ j++, &data);
+}
+EXPORT_SYMBOL_GPL(net_selftest_get_strings_custom);
+
MODULE_DESCRIPTION("Common library for generic PHY ethtool selftests");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Oleksij Rempel <o.rempel@...gutronix.de>");
--
2.33.0
Powered by blists - more mailing lists