Merge branch 'dev_addr-const'
Jakub Kicinski says: ==================== net: constify netdev->dev_addr Take care of a few stragglers and make netdev->dev_addr const. netdev->dev_addr can be held on the address tree like any other address now. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
979594c5ff
@ -586,7 +586,7 @@ static inline int bnx2x_vfpf_release(struct bnx2x *bp) {return 0; }
|
||||
static inline int bnx2x_vfpf_init(struct bnx2x *bp) {return 0; }
|
||||
static inline void bnx2x_vfpf_close_vf(struct bnx2x *bp) {}
|
||||
static inline int bnx2x_vfpf_setup_q(struct bnx2x *bp, struct bnx2x_fastpath *fp, bool is_leading) {return 0; }
|
||||
static inline int bnx2x_vfpf_config_mac(struct bnx2x *bp, u8 *addr,
|
||||
static inline int bnx2x_vfpf_config_mac(struct bnx2x *bp, const u8 *addr,
|
||||
u8 vf_qid, bool set) {return 0; }
|
||||
static inline int bnx2x_vfpf_config_rss(struct bnx2x *bp,
|
||||
struct bnx2x_config_rss_params *params) {return 0; }
|
||||
|
@ -1178,7 +1178,8 @@ found:
|
||||
DEB(DEB_PROBE,printk(KERN_INFO "%s: 82596 at %#3lx,", dev->name, dev->base_addr));
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
DEB(DEB_PROBE,printk(" %2.2X", dev->dev_addr[i] = eth_addr[i]));
|
||||
DEB(DEB_PROBE,printk(" %2.2X", eth_addr[i]));
|
||||
eth_hw_addr_set(dev, eth_addr);
|
||||
|
||||
DEB(DEB_PROBE,printk(" IRQ %d.\n", dev->irq));
|
||||
|
||||
|
@ -1942,6 +1942,8 @@ enum netdev_ml_priv_type {
|
||||
* @unlink_list: As netif_addr_lock() can be called recursively,
|
||||
* keep a list of interfaces to be deleted.
|
||||
*
|
||||
* @dev_addr_shadow: Copy of @dev_addr to catch direct writes.
|
||||
*
|
||||
* FIXME: cleanup struct net_device such that network protocol info
|
||||
* moves out.
|
||||
*/
|
||||
@ -2117,7 +2119,7 @@ struct net_device {
|
||||
* Cache lines mostly used on receive path (including eth_type_trans())
|
||||
*/
|
||||
/* Interface address info used in eth_type_trans() */
|
||||
unsigned char *dev_addr;
|
||||
const unsigned char *dev_addr;
|
||||
|
||||
struct netdev_rx_queue *_rx;
|
||||
unsigned int num_rx_queues;
|
||||
@ -2268,6 +2270,8 @@ struct net_device {
|
||||
|
||||
/* protected by rtnl_lock */
|
||||
struct bpf_xdp_entity xdp_state[__MAX_XDP_MODE];
|
||||
|
||||
u8 dev_addr_shadow[MAX_ADDR_LEN];
|
||||
};
|
||||
#define to_net_dev(d) container_of(d, struct net_device, dev)
|
||||
|
||||
@ -4268,10 +4272,13 @@ void __hw_addr_unsync_dev(struct netdev_hw_addr_list *list,
|
||||
void __hw_addr_init(struct netdev_hw_addr_list *list);
|
||||
|
||||
/* Functions used for device addresses handling */
|
||||
void dev_addr_mod(struct net_device *dev, unsigned int offset,
|
||||
const void *addr, size_t len);
|
||||
|
||||
static inline void
|
||||
__dev_addr_set(struct net_device *dev, const void *addr, size_t len)
|
||||
{
|
||||
memcpy(dev->dev_addr, addr, len);
|
||||
dev_addr_mod(dev, 0, addr, len);
|
||||
}
|
||||
|
||||
static inline void dev_addr_set(struct net_device *dev, const u8 *addr)
|
||||
@ -4279,19 +4286,13 @@ static inline void dev_addr_set(struct net_device *dev, const u8 *addr)
|
||||
__dev_addr_set(dev, addr, dev->addr_len);
|
||||
}
|
||||
|
||||
static inline void
|
||||
dev_addr_mod(struct net_device *dev, unsigned int offset,
|
||||
const void *addr, size_t len)
|
||||
{
|
||||
memcpy(&dev->dev_addr[offset], addr, len);
|
||||
}
|
||||
|
||||
int dev_addr_add(struct net_device *dev, const unsigned char *addr,
|
||||
unsigned char addr_type);
|
||||
int dev_addr_del(struct net_device *dev, const unsigned char *addr,
|
||||
unsigned char addr_type);
|
||||
void dev_addr_flush(struct net_device *dev);
|
||||
int dev_addr_init(struct net_device *dev);
|
||||
void dev_addr_check(struct net_device *dev);
|
||||
|
||||
/* Functions used for unicast addresses handling */
|
||||
int dev_uc_add(struct net_device *dev, const unsigned char *addr);
|
||||
|
@ -455,4 +455,9 @@ config ETHTOOL_NETLINK
|
||||
netlink. It provides better extensibility and some new features,
|
||||
e.g. notification messages.
|
||||
|
||||
config NETDEV_ADDR_LIST_TEST
|
||||
tristate "Unit tests for device address list"
|
||||
default KUNIT_ALL_TESTS
|
||||
depends on KUNIT
|
||||
|
||||
endif # if NET
|
||||
|
@ -13,6 +13,8 @@ obj-y += dev.o dev_addr_lists.o dst.o netevent.o \
|
||||
sock_diag.o dev_ioctl.o tso.o sock_reuseport.o \
|
||||
fib_notifier.o xdp.o flow_offload.o gro.o
|
||||
|
||||
obj-$(CONFIG_NETDEV_ADDR_LIST_TEST) += dev_addr_lists_test.o
|
||||
|
||||
obj-y += net-sysfs.o
|
||||
obj-$(CONFIG_PAGE_POOL) += page_pool.o
|
||||
obj-$(CONFIG_PROC_FS) += net-procfs.o
|
||||
|
@ -1377,6 +1377,7 @@ static int __dev_open(struct net_device *dev, struct netlink_ext_ack *extack)
|
||||
int ret;
|
||||
|
||||
ASSERT_RTNL();
|
||||
dev_addr_check(dev);
|
||||
|
||||
if (!netif_device_present(dev)) {
|
||||
/* may be detached because parent is runtime-suspended */
|
||||
|
@ -16,6 +16,35 @@
|
||||
* General list handling functions
|
||||
*/
|
||||
|
||||
static int __hw_addr_insert(struct netdev_hw_addr_list *list,
|
||||
struct netdev_hw_addr *new, int addr_len)
|
||||
{
|
||||
struct rb_node **ins_point = &list->tree.rb_node, *parent = NULL;
|
||||
struct netdev_hw_addr *ha;
|
||||
|
||||
while (*ins_point) {
|
||||
int diff;
|
||||
|
||||
ha = rb_entry(*ins_point, struct netdev_hw_addr, node);
|
||||
diff = memcmp(new->addr, ha->addr, addr_len);
|
||||
if (diff == 0)
|
||||
diff = memcmp(&new->type, &ha->type, sizeof(new->type));
|
||||
|
||||
parent = *ins_point;
|
||||
if (diff < 0)
|
||||
ins_point = &parent->rb_left;
|
||||
else if (diff > 0)
|
||||
ins_point = &parent->rb_right;
|
||||
else
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
rb_link_node_rcu(&new->node, parent, ins_point);
|
||||
rb_insert_color(&new->node, &list->tree);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct netdev_hw_addr*
|
||||
__hw_addr_create(const unsigned char *addr, int addr_len,
|
||||
unsigned char addr_type, bool global, bool sync)
|
||||
@ -50,11 +79,6 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
|
||||
if (addr_len > MAX_ADDR_LEN)
|
||||
return -EINVAL;
|
||||
|
||||
ha = list_first_entry(&list->list, struct netdev_hw_addr, list);
|
||||
if (ha && !memcmp(addr, ha->addr, addr_len) &&
|
||||
(!addr_type || addr_type == ha->type))
|
||||
goto found_it;
|
||||
|
||||
while (*ins_point) {
|
||||
int diff;
|
||||
|
||||
@ -69,7 +93,6 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
|
||||
} else if (diff > 0) {
|
||||
ins_point = &parent->rb_right;
|
||||
} else {
|
||||
found_it:
|
||||
if (exclusive)
|
||||
return -EEXIST;
|
||||
if (global) {
|
||||
@ -94,16 +117,8 @@ found_it:
|
||||
if (!ha)
|
||||
return -ENOMEM;
|
||||
|
||||
/* The first address in dev->dev_addrs is pointed to by dev->dev_addr
|
||||
* and mutated freely by device drivers and netdev ops, so if we insert
|
||||
* it into the tree we'll end up with an invalid rbtree.
|
||||
*/
|
||||
if (list->count > 0) {
|
||||
rb_link_node(&ha->node, parent, ins_point);
|
||||
rb_insert_color(&ha->node, &list->tree);
|
||||
} else {
|
||||
RB_CLEAR_NODE(&ha->node);
|
||||
}
|
||||
rb_link_node(&ha->node, parent, ins_point);
|
||||
rb_insert_color(&ha->node, &list->tree);
|
||||
|
||||
list_add_tail_rcu(&ha->list, &list->list);
|
||||
list->count++;
|
||||
@ -138,8 +153,7 @@ static int __hw_addr_del_entry(struct netdev_hw_addr_list *list,
|
||||
if (--ha->refcount)
|
||||
return 0;
|
||||
|
||||
if (!RB_EMPTY_NODE(&ha->node))
|
||||
rb_erase(&ha->node, &list->tree);
|
||||
rb_erase(&ha->node, &list->tree);
|
||||
|
||||
list_del_rcu(&ha->list);
|
||||
kfree_rcu(ha, rcu_head);
|
||||
@ -151,18 +165,8 @@ static struct netdev_hw_addr *__hw_addr_lookup(struct netdev_hw_addr_list *list,
|
||||
const unsigned char *addr, int addr_len,
|
||||
unsigned char addr_type)
|
||||
{
|
||||
struct netdev_hw_addr *ha;
|
||||
struct rb_node *node;
|
||||
|
||||
/* The first address isn't inserted into the tree because in the dev->dev_addrs
|
||||
* list it's the address pointed to by dev->dev_addr which is freely mutated
|
||||
* in place, so we need to check it separately.
|
||||
*/
|
||||
ha = list_first_entry(&list->list, struct netdev_hw_addr, list);
|
||||
if (ha && !memcmp(addr, ha->addr, addr_len) &&
|
||||
(!addr_type || addr_type == ha->type))
|
||||
return ha;
|
||||
|
||||
node = list->tree.rb_node;
|
||||
|
||||
while (node) {
|
||||
@ -498,6 +502,21 @@ EXPORT_SYMBOL(__hw_addr_init);
|
||||
* Device addresses handling functions
|
||||
*/
|
||||
|
||||
/* Check that netdev->dev_addr is not written to directly as this would
|
||||
* break the rbtree layout. All changes should go thru dev_addr_set() and co.
|
||||
* Remove this check in mid-2024.
|
||||
*/
|
||||
void dev_addr_check(struct net_device *dev)
|
||||
{
|
||||
if (!memcmp(dev->dev_addr, dev->dev_addr_shadow, MAX_ADDR_LEN))
|
||||
return;
|
||||
|
||||
netdev_warn(dev, "Current addr: %*ph\n", MAX_ADDR_LEN, dev->dev_addr);
|
||||
netdev_warn(dev, "Expected addr: %*ph\n",
|
||||
MAX_ADDR_LEN, dev->dev_addr_shadow);
|
||||
netdev_WARN(dev, "Incorrect netdev->dev_addr\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* dev_addr_flush - Flush device address list
|
||||
* @dev: device
|
||||
@ -509,11 +528,11 @@ EXPORT_SYMBOL(__hw_addr_init);
|
||||
void dev_addr_flush(struct net_device *dev)
|
||||
{
|
||||
/* rtnl_mutex must be held here */
|
||||
dev_addr_check(dev);
|
||||
|
||||
__hw_addr_flush(&dev->dev_addrs);
|
||||
dev->dev_addr = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(dev_addr_flush);
|
||||
|
||||
/**
|
||||
* dev_addr_init - Init device address list
|
||||
@ -547,7 +566,21 @@ int dev_addr_init(struct net_device *dev)
|
||||
}
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(dev_addr_init);
|
||||
|
||||
void dev_addr_mod(struct net_device *dev, unsigned int offset,
|
||||
const void *addr, size_t len)
|
||||
{
|
||||
struct netdev_hw_addr *ha;
|
||||
|
||||
dev_addr_check(dev);
|
||||
|
||||
ha = container_of(dev->dev_addr, struct netdev_hw_addr, addr[0]);
|
||||
rb_erase(&ha->node, &dev->dev_addrs.tree);
|
||||
memcpy(&ha->addr[offset], addr, len);
|
||||
memcpy(&dev->dev_addr_shadow[offset], addr, len);
|
||||
WARN_ON(__hw_addr_insert(&dev->dev_addrs, ha, dev->addr_len));
|
||||
}
|
||||
EXPORT_SYMBOL(dev_addr_mod);
|
||||
|
||||
/**
|
||||
* dev_addr_add - Add a device address
|
||||
|
236
net/core/dev_addr_lists_test.c
Normal file
236
net/core/dev_addr_lists_test.c
Normal file
@ -0,0 +1,236 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <kunit/test.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
static const struct net_device_ops dummy_netdev_ops = {
|
||||
};
|
||||
|
||||
struct dev_addr_test_priv {
|
||||
u32 addr_seen;
|
||||
};
|
||||
|
||||
static int dev_addr_test_sync(struct net_device *netdev, const unsigned char *a)
|
||||
{
|
||||
struct dev_addr_test_priv *datp = netdev_priv(netdev);
|
||||
|
||||
if (a[0] < 31 && !memchr_inv(a, a[0], ETH_ALEN))
|
||||
datp->addr_seen |= 1 << a[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dev_addr_test_unsync(struct net_device *netdev,
|
||||
const unsigned char *a)
|
||||
{
|
||||
struct dev_addr_test_priv *datp = netdev_priv(netdev);
|
||||
|
||||
if (a[0] < 31 && !memchr_inv(a, a[0], ETH_ALEN))
|
||||
datp->addr_seen &= ~(1 << a[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dev_addr_test_init(struct kunit *test)
|
||||
{
|
||||
struct dev_addr_test_priv *datp;
|
||||
struct net_device *netdev;
|
||||
int err;
|
||||
|
||||
netdev = alloc_etherdev(sizeof(*datp));
|
||||
KUNIT_ASSERT_TRUE(test, !!netdev);
|
||||
|
||||
test->priv = netdev;
|
||||
netdev->netdev_ops = &dummy_netdev_ops;
|
||||
|
||||
err = register_netdev(netdev);
|
||||
if (err) {
|
||||
free_netdev(netdev);
|
||||
KUNIT_FAIL(test, "Can't register netdev %d", err);
|
||||
}
|
||||
|
||||
rtnl_lock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dev_addr_test_exit(struct kunit *test)
|
||||
{
|
||||
struct net_device *netdev = test->priv;
|
||||
|
||||
rtnl_unlock();
|
||||
unregister_netdev(netdev);
|
||||
free_netdev(netdev);
|
||||
}
|
||||
|
||||
static void dev_addr_test_basic(struct kunit *test)
|
||||
{
|
||||
struct net_device *netdev = test->priv;
|
||||
u8 addr[ETH_ALEN];
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, !!netdev->dev_addr);
|
||||
|
||||
memset(addr, 2, sizeof(addr));
|
||||
eth_hw_addr_set(netdev, addr);
|
||||
KUNIT_EXPECT_EQ(test, 0, memcmp(netdev->dev_addr, addr, sizeof(addr)));
|
||||
|
||||
memset(addr, 3, sizeof(addr));
|
||||
dev_addr_set(netdev, addr);
|
||||
KUNIT_EXPECT_EQ(test, 0, memcmp(netdev->dev_addr, addr, sizeof(addr)));
|
||||
}
|
||||
|
||||
static void dev_addr_test_sync_one(struct kunit *test)
|
||||
{
|
||||
struct net_device *netdev = test->priv;
|
||||
struct dev_addr_test_priv *datp;
|
||||
u8 addr[ETH_ALEN];
|
||||
|
||||
datp = netdev_priv(netdev);
|
||||
|
||||
memset(addr, 1, sizeof(addr));
|
||||
eth_hw_addr_set(netdev, addr);
|
||||
|
||||
__hw_addr_sync_dev(&netdev->dev_addrs, netdev, dev_addr_test_sync,
|
||||
dev_addr_test_unsync);
|
||||
KUNIT_EXPECT_EQ(test, 2, datp->addr_seen);
|
||||
|
||||
memset(addr, 2, sizeof(addr));
|
||||
eth_hw_addr_set(netdev, addr);
|
||||
|
||||
datp->addr_seen = 0;
|
||||
__hw_addr_sync_dev(&netdev->dev_addrs, netdev, dev_addr_test_sync,
|
||||
dev_addr_test_unsync);
|
||||
/* It's not going to sync anything because the main address is
|
||||
* considered synced and we overwrite in place.
|
||||
*/
|
||||
KUNIT_EXPECT_EQ(test, 0, datp->addr_seen);
|
||||
}
|
||||
|
||||
static void dev_addr_test_add_del(struct kunit *test)
|
||||
{
|
||||
struct net_device *netdev = test->priv;
|
||||
struct dev_addr_test_priv *datp;
|
||||
u8 addr[ETH_ALEN];
|
||||
int i;
|
||||
|
||||
datp = netdev_priv(netdev);
|
||||
|
||||
for (i = 1; i < 4; i++) {
|
||||
memset(addr, i, sizeof(addr));
|
||||
KUNIT_EXPECT_EQ(test, 0, dev_addr_add(netdev, addr,
|
||||
NETDEV_HW_ADDR_T_LAN));
|
||||
}
|
||||
/* Add 3 again */
|
||||
KUNIT_EXPECT_EQ(test, 0, dev_addr_add(netdev, addr,
|
||||
NETDEV_HW_ADDR_T_LAN));
|
||||
|
||||
__hw_addr_sync_dev(&netdev->dev_addrs, netdev, dev_addr_test_sync,
|
||||
dev_addr_test_unsync);
|
||||
KUNIT_EXPECT_EQ(test, 0xf, datp->addr_seen);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, 0, dev_addr_del(netdev, addr,
|
||||
NETDEV_HW_ADDR_T_LAN));
|
||||
|
||||
__hw_addr_sync_dev(&netdev->dev_addrs, netdev, dev_addr_test_sync,
|
||||
dev_addr_test_unsync);
|
||||
KUNIT_EXPECT_EQ(test, 0xf, datp->addr_seen);
|
||||
|
||||
for (i = 1; i < 4; i++) {
|
||||
memset(addr, i, sizeof(addr));
|
||||
KUNIT_EXPECT_EQ(test, 0, dev_addr_del(netdev, addr,
|
||||
NETDEV_HW_ADDR_T_LAN));
|
||||
}
|
||||
|
||||
__hw_addr_sync_dev(&netdev->dev_addrs, netdev, dev_addr_test_sync,
|
||||
dev_addr_test_unsync);
|
||||
KUNIT_EXPECT_EQ(test, 1, datp->addr_seen);
|
||||
}
|
||||
|
||||
static void dev_addr_test_del_main(struct kunit *test)
|
||||
{
|
||||
struct net_device *netdev = test->priv;
|
||||
u8 addr[ETH_ALEN];
|
||||
|
||||
memset(addr, 1, sizeof(addr));
|
||||
eth_hw_addr_set(netdev, addr);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, -ENOENT, dev_addr_del(netdev, addr,
|
||||
NETDEV_HW_ADDR_T_LAN));
|
||||
KUNIT_EXPECT_EQ(test, 0, dev_addr_add(netdev, addr,
|
||||
NETDEV_HW_ADDR_T_LAN));
|
||||
KUNIT_EXPECT_EQ(test, 0, dev_addr_del(netdev, addr,
|
||||
NETDEV_HW_ADDR_T_LAN));
|
||||
KUNIT_EXPECT_EQ(test, -ENOENT, dev_addr_del(netdev, addr,
|
||||
NETDEV_HW_ADDR_T_LAN));
|
||||
}
|
||||
|
||||
static void dev_addr_test_add_set(struct kunit *test)
|
||||
{
|
||||
struct net_device *netdev = test->priv;
|
||||
struct dev_addr_test_priv *datp;
|
||||
u8 addr[ETH_ALEN];
|
||||
int i;
|
||||
|
||||
datp = netdev_priv(netdev);
|
||||
|
||||
/* There is no external API like dev_addr_add_excl(),
|
||||
* so shuffle the tree a little bit and exploit aliasing.
|
||||
*/
|
||||
for (i = 1; i < 16; i++) {
|
||||
memset(addr, i, sizeof(addr));
|
||||
KUNIT_EXPECT_EQ(test, 0, dev_addr_add(netdev, addr,
|
||||
NETDEV_HW_ADDR_T_LAN));
|
||||
}
|
||||
|
||||
memset(addr, i, sizeof(addr));
|
||||
eth_hw_addr_set(netdev, addr);
|
||||
KUNIT_EXPECT_EQ(test, 0, dev_addr_add(netdev, addr,
|
||||
NETDEV_HW_ADDR_T_LAN));
|
||||
memset(addr, 0, sizeof(addr));
|
||||
eth_hw_addr_set(netdev, addr);
|
||||
|
||||
__hw_addr_sync_dev(&netdev->dev_addrs, netdev, dev_addr_test_sync,
|
||||
dev_addr_test_unsync);
|
||||
KUNIT_EXPECT_EQ(test, 0xffff, datp->addr_seen);
|
||||
}
|
||||
|
||||
static void dev_addr_test_add_excl(struct kunit *test)
|
||||
{
|
||||
struct net_device *netdev = test->priv;
|
||||
u8 addr[ETH_ALEN];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
memset(addr, i, sizeof(addr));
|
||||
KUNIT_EXPECT_EQ(test, 0, dev_uc_add_excl(netdev, addr));
|
||||
}
|
||||
KUNIT_EXPECT_EQ(test, -EEXIST, dev_uc_add_excl(netdev, addr));
|
||||
|
||||
for (i = 0; i < 10; i += 2) {
|
||||
memset(addr, i, sizeof(addr));
|
||||
KUNIT_EXPECT_EQ(test, 0, dev_uc_del(netdev, addr));
|
||||
}
|
||||
for (i = 1; i < 10; i += 2) {
|
||||
memset(addr, i, sizeof(addr));
|
||||
KUNIT_EXPECT_EQ(test, -EEXIST, dev_uc_add_excl(netdev, addr));
|
||||
}
|
||||
}
|
||||
|
||||
static struct kunit_case dev_addr_test_cases[] = {
|
||||
KUNIT_CASE(dev_addr_test_basic),
|
||||
KUNIT_CASE(dev_addr_test_sync_one),
|
||||
KUNIT_CASE(dev_addr_test_add_del),
|
||||
KUNIT_CASE(dev_addr_test_del_main),
|
||||
KUNIT_CASE(dev_addr_test_add_set),
|
||||
KUNIT_CASE(dev_addr_test_add_excl),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct kunit_suite dev_addr_test_suite = {
|
||||
.name = "dev-addr-list-test",
|
||||
.test_cases = dev_addr_test_cases,
|
||||
.init = dev_addr_test_init,
|
||||
.exit = dev_addr_test_exit,
|
||||
};
|
||||
kunit_test_suite(dev_addr_test_suite);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
Loading…
Reference in New Issue
Block a user