selftests: drivers: add scaffolding for Netlink tests in Python
Add drivers/net as a target for mixed-use tests. The setup is expected to work similarly to the forwarding tests. Since we only need one interface (unlike forwarding tests) read the target device name from NETIF. If not present we'll try to run the test against netdevsim. Reviewed-by: Petr Machata <petrm@nvidia.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
f216306bfb
commit
b4db9f8402
@ -17,6 +17,7 @@ TARGETS += devices
|
||||
TARGETS += dmabuf-heaps
|
||||
TARGETS += drivers/dma-buf
|
||||
TARGETS += drivers/s390x/uvdevice
|
||||
TARGETS += drivers/net
|
||||
TARGETS += drivers/net/bonding
|
||||
TARGETS += drivers/net/team
|
||||
TARGETS += dt
|
||||
@ -117,7 +118,7 @@ TARGETS_HOTPLUG = cpu-hotplug
|
||||
TARGETS_HOTPLUG += memory-hotplug
|
||||
|
||||
# Networking tests want the net/lib target, include it automatically
|
||||
ifneq ($(filter net,$(TARGETS)),)
|
||||
ifneq ($(filter net drivers/net,$(TARGETS)),)
|
||||
ifeq ($(filter net/lib,$(TARGETS)),)
|
||||
INSTALL_DEP_TARGETS := net/lib
|
||||
endif
|
||||
|
7
tools/testing/selftests/drivers/net/Makefile
Normal file
7
tools/testing/selftests/drivers/net/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
TEST_INCLUDES := $(wildcard lib/py/*.py)
|
||||
|
||||
TEST_PROGS := stats.py
|
||||
|
||||
include ../../lib.mk
|
30
tools/testing/selftests/drivers/net/README.rst
Normal file
30
tools/testing/selftests/drivers/net/README.rst
Normal file
@ -0,0 +1,30 @@
|
||||
Running tests
|
||||
=============
|
||||
|
||||
Tests are executed within kselftest framework like any other tests.
|
||||
By default tests execute against software drivers such as netdevsim.
|
||||
All tests must support running against a real device (SW-only tests
|
||||
should instead be placed in net/ or drivers/net/netdevsim, HW-only
|
||||
tests in drivers/net/hw).
|
||||
|
||||
Set appropriate variables to point the tests at a real device.
|
||||
|
||||
Variables
|
||||
=========
|
||||
|
||||
Variables can be set in the environment or by creating a net.config
|
||||
file in the same directory as this README file. Example::
|
||||
|
||||
$ NETIF=eth0 ./some_test.sh
|
||||
|
||||
or::
|
||||
|
||||
$ cat tools/testing/selftests/drivers/net/net.config
|
||||
# Variable set in a file
|
||||
NETIF=eth0
|
||||
|
||||
NETIF
|
||||
~~~~~
|
||||
|
||||
Name of the netdevice against which the test should be executed.
|
||||
When empty or not set software devices will be used.
|
17
tools/testing/selftests/drivers/net/lib/py/__init__.py
Normal file
17
tools/testing/selftests/drivers/net/lib/py/__init__.py
Normal file
@ -0,0 +1,17 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
KSFT_DIR = (Path(__file__).parent / "../../../..").resolve()
|
||||
|
||||
try:
|
||||
sys.path.append(KSFT_DIR.as_posix())
|
||||
from net.lib.py import *
|
||||
except ModuleNotFoundError as e:
|
||||
ksft_pr("Failed importing `net` library from kernel sources")
|
||||
ksft_pr(str(e))
|
||||
ktap_result(True, comment="SKIP")
|
||||
sys.exit(4)
|
||||
|
||||
from .env import *
|
52
tools/testing/selftests/drivers/net/lib/py/env.py
Normal file
52
tools/testing/selftests/drivers/net/lib/py/env.py
Normal file
@ -0,0 +1,52 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
import os
|
||||
import shlex
|
||||
from pathlib import Path
|
||||
from lib.py import ip
|
||||
from lib.py import NetdevSimDev
|
||||
|
||||
class NetDrvEnv:
|
||||
def __init__(self, src_path):
|
||||
self._ns = None
|
||||
|
||||
self.env = os.environ.copy()
|
||||
self._load_env_file(src_path)
|
||||
|
||||
if 'NETIF' in self.env:
|
||||
self.dev = ip("link show dev " + self.env['NETIF'], json=True)[0]
|
||||
else:
|
||||
self._ns = NetdevSimDev()
|
||||
self.dev = self._ns.nsims[0].dev
|
||||
self.ifindex = self.dev['ifindex']
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, ex_type, ex_value, ex_tb):
|
||||
"""
|
||||
__exit__ gets called at the end of a "with" block.
|
||||
"""
|
||||
self.__del__()
|
||||
|
||||
def __del__(self):
|
||||
if self._ns:
|
||||
self._ns.remove()
|
||||
self._ns = None
|
||||
|
||||
def _load_env_file(self, src_path):
|
||||
src_dir = Path(src_path).parent.resolve()
|
||||
if not (src_dir / "net.config").exists():
|
||||
return
|
||||
|
||||
lexer = shlex.shlex(open((src_dir / "net.config").as_posix(), 'r').read())
|
||||
k = None
|
||||
for token in lexer:
|
||||
if k is None:
|
||||
k = token
|
||||
self.env[k] = ""
|
||||
elif token == "=":
|
||||
pass
|
||||
else:
|
||||
self.env[k] = token
|
||||
k = None
|
@ -2,5 +2,6 @@
|
||||
|
||||
from .consts import KSRC
|
||||
from .ksft import *
|
||||
from .nsim import *
|
||||
from .utils import *
|
||||
from .ynl import NlError, YnlFamily, EthtoolFamily, NetdevFamily, RtnlFamily
|
||||
|
115
tools/testing/selftests/net/lib/py/nsim.py
Normal file
115
tools/testing/selftests/net/lib/py/nsim.py
Normal file
@ -0,0 +1,115 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import time
|
||||
from .utils import cmd, ip
|
||||
|
||||
|
||||
class NetdevSim:
|
||||
"""
|
||||
Class for netdevsim netdevice and its attributes.
|
||||
"""
|
||||
|
||||
def __init__(self, nsimdev, port_index, ifname, ns=None):
|
||||
# In case udev renamed the netdev to according to new schema,
|
||||
# check if the name matches the port_index.
|
||||
nsimnamere = re.compile(r"eni\d+np(\d+)")
|
||||
match = nsimnamere.match(ifname)
|
||||
if match and int(match.groups()[0]) != port_index + 1:
|
||||
raise Exception("netdevice name mismatches the expected one")
|
||||
|
||||
self.nsimdev = nsimdev
|
||||
self.port_index = port_index
|
||||
ret = ip("-j link show dev %s" % ifname, ns=ns)
|
||||
self.dev = json.loads(ret.stdout)[0]
|
||||
|
||||
def dfs_write(self, path, val):
|
||||
self.nsimdev.dfs_write(f'ports/{self.port_index}/' + path, val)
|
||||
|
||||
|
||||
class NetdevSimDev:
|
||||
"""
|
||||
Class for netdevsim bus device and its attributes.
|
||||
"""
|
||||
@staticmethod
|
||||
def ctrl_write(path, val):
|
||||
fullpath = os.path.join("/sys/bus/netdevsim/", path)
|
||||
with open(fullpath, "w") as f:
|
||||
f.write(val)
|
||||
|
||||
def dfs_write(self, path, val):
|
||||
fullpath = os.path.join(f"/sys/kernel/debug/netdevsim/netdevsim{self.addr}/", path)
|
||||
with open(fullpath, "w") as f:
|
||||
f.write(val)
|
||||
|
||||
def __init__(self, port_count=1, ns=None):
|
||||
# nsim will spawn in init_net, we'll set to actual ns once we switch it there
|
||||
self.ns = None
|
||||
|
||||
if not os.path.exists("/sys/bus/netdevsim"):
|
||||
cmd("modprobe netdevsim")
|
||||
|
||||
addr = random.randrange(1 << 15)
|
||||
while True:
|
||||
try:
|
||||
self.ctrl_write("new_device", "%u %u" % (addr, port_count))
|
||||
except OSError as e:
|
||||
if e.errno == errno.ENOSPC:
|
||||
addr = random.randrange(1 << 15)
|
||||
continue
|
||||
raise e
|
||||
break
|
||||
self.addr = addr
|
||||
|
||||
# As probe of netdevsim device might happen from a workqueue,
|
||||
# so wait here until all netdevs appear.
|
||||
self.wait_for_netdevs(port_count)
|
||||
|
||||
if ns:
|
||||
cmd(f"devlink dev reload netdevsim/netdevsim{addr} netns {ns.name}")
|
||||
self.ns = ns
|
||||
|
||||
cmd("udevadm settle", ns=self.ns)
|
||||
ifnames = self.get_ifnames()
|
||||
|
||||
self.dfs_dir = "/sys/kernel/debug/netdevsim/netdevsim%u/" % addr
|
||||
|
||||
self.nsims = []
|
||||
for port_index in range(port_count):
|
||||
self.nsims.append(NetdevSim(self, port_index, ifnames[port_index],
|
||||
ns=ns))
|
||||
|
||||
def get_ifnames(self):
|
||||
ifnames = []
|
||||
listdir = cmd(f"ls /sys/bus/netdevsim/devices/netdevsim{self.addr}/net/",
|
||||
ns=self.ns).stdout.split()
|
||||
for ifname in listdir:
|
||||
ifnames.append(ifname)
|
||||
ifnames.sort()
|
||||
return ifnames
|
||||
|
||||
def wait_for_netdevs(self, port_count):
|
||||
timeout = 5
|
||||
timeout_start = time.time()
|
||||
|
||||
while True:
|
||||
try:
|
||||
ifnames = self.get_ifnames()
|
||||
except FileNotFoundError as e:
|
||||
ifnames = []
|
||||
if len(ifnames) == port_count:
|
||||
break
|
||||
if time.time() < timeout_start + timeout:
|
||||
continue
|
||||
raise Exception("netdevices did not appear within timeout")
|
||||
|
||||
def remove(self):
|
||||
self.ctrl_write("del_device", "%u" % (self.addr, ))
|
||||
|
||||
def remove_nsim(self, nsim):
|
||||
self.nsims.remove(nsim)
|
||||
self.ctrl_write("devices/netdevsim%u/del_port" % (self.addr, ),
|
||||
"%u" % (nsim.port_index, ))
|
Loading…
x
Reference in New Issue
Block a user