2023-11-18 10:43:29

by huaweicloud

[permalink] [raw]
Subject: [PATCH -next 00/14] Implement a ligth weight device driver test framework

From: Zhang Xiaoxu <[email protected]>

Implement a ligth weight device driver test framework, KDDV(Kernel
Device Driver Verification). Which using eBPF based bus controllers
to mockup device chipsets.

The kddv already discover the following issues:
fc92d9e3de0b iio: health: afe4404: Fix oob read in afe4404_[read|write]_raw
58143c1ed588 iio: health: afe4403: Fix oob read in afe4403_read_raw
3f4033a811bc iio: filter: admv8818: close potential out-of-bounds read in __admv8818_read_[h|l]pf_freq()
8c9a59939deb Input: raydium_ts_i2c - fix memory leak in raydium_i2c_send()
7485edb2b6ca media: i2c: ov772x: Fix memleak in ov772x_probe()
bab76514aca3 regulator: da9121: Fix uninit-value in da9121_assign_chip_model()
9d47e01b9d80 power: supply: adp5061: fix out-of-bounds read in adp5061_get_chg_type()
...

Zhang Xiaoxu (14):
kddv/core: Implement a ligth weight device driver test framework
kddv/core: Allow test case config bpf program
kddv/core: Add io fault support to bpf program
kddv/core: Check kmsg before return from test case
kddv/core: Support kernel memory leak detector
kddv/core: Add page and slab fault inject support
kddv/cmd: Add command to create/remove mockup device
kddv/cmd: Add command to run testcases
kddv/core: Add test support for SPI driver
kddv/tests: Add support for testing hwmon driver
kddv/tests/hwmon: Add test cases for max31722 driver
kddv/tests: Add support for testing mtd driver
kddv/tests/mtd: Add test cases for mchp23k256 driver
kddv: Add document for kddv

Documentation/dev-tools/index.rst | 1 +
Documentation/dev-tools/kddv.rst | 183 ++++++++
tools/testing/kddv/.gitignore | 3 +
tools/testing/kddv/Makefile | 25 ++
tools/testing/kddv/kddv/Makefile | 28 ++
tools/testing/kddv/kddv/__init__.py | 0
tools/testing/kddv/kddv/cmds/__init__.py | 0
tools/testing/kddv/kddv/cmds/mock.py | 105 +++++
tools/testing/kddv/kddv/cmds/test.py | 75 ++++
tools/testing/kddv/kddv/cmds/utils.py | 28 ++
tools/testing/kddv/kddv/core/__init__.py | 13 +
.../testing/kddv/kddv/core/buses/__init__.py | 13 +
tools/testing/kddv/kddv/core/buses/spi.py | 74 +++
tools/testing/kddv/kddv/core/consts.py | 13 +
tools/testing/kddv/kddv/core/ddunit.py | 83 ++++
tools/testing/kddv/kddv/core/device.py | 78 ++++
tools/testing/kddv/kddv/core/dmesg.py | 41 ++
tools/testing/kddv/kddv/core/driver.py | 82 ++++
tools/testing/kddv/kddv/core/environ.py | 65 +++
tools/testing/kddv/kddv/core/failnth.py | 57 +++
tools/testing/kddv/kddv/core/faulter.py | 48 ++
.../testing/kddv/kddv/core/faults/__init__.py | 13 +
tools/testing/kddv/kddv/core/faults/fail.py | 86 ++++
tools/testing/kddv/kddv/core/faults/page.py | 40 ++
tools/testing/kddv/kddv/core/faults/slab.py | 36 ++
tools/testing/kddv/kddv/core/memleak.py | 39 ++
tools/testing/kddv/kddv/core/mockup.py | 193 ++++++++
tools/testing/kddv/kddv/core/model.py | 95 ++++
tools/testing/kddv/kddv/data/Makefile | 21 +
tools/testing/kddv/kddv/data/bpf/Makefile | 22 +
.../kddv/data/bpf/include/bpf-xfer-conf.h | 124 +++++
.../kddv/kddv/data/bpf/mtd/mtd-mchp23k256.c | 72 +++
.../kddv/kddv/data/bpf/spi/spi-xfer-base.h | 99 ++++
.../kddv/kddv/data/bpf/spi/spi-xfer-r1v1.c | 51 +++
.../kddv/kddv/data/bpf/spi/spi-xfer-r1v2.c | 51 +++
.../kddv/kddv/data/bpf/spi/spi-xfer-r1v3.c | 86 ++++
.../kddv/kddv/data/bpf/spi/spi-xfer-r2v1.c | 51 +++
.../kddv/kddv/data/bpf/spi/spi-xfer-r2v2.c | 51 +++
.../kddv/kddv/data/bpf/spi/spi-xfer-r3v1.c | 52 +++
.../kddv/kddv/data/bpf/spi/spi-xfer-r4v4.c | 89 ++++
tools/testing/kddv/kddv/tests/__init__.py | 0
.../testing/kddv/kddv/tests/hwmon/__init__.py | 425 ++++++++++++++++++
.../kddv/kddv/tests/hwmon/test_max31722.py | 40 ++
tools/testing/kddv/kddv/tests/mtd/__init__.py | 63 +++
.../kddv/kddv/tests/mtd/test_mchp23k256.py | 41 ++
45 files changed, 2855 insertions(+)
create mode 100755 Documentation/dev-tools/kddv.rst
create mode 100644 tools/testing/kddv/.gitignore
create mode 100644 tools/testing/kddv/Makefile
create mode 100644 tools/testing/kddv/kddv/Makefile
create mode 100755 tools/testing/kddv/kddv/__init__.py
create mode 100755 tools/testing/kddv/kddv/cmds/__init__.py
create mode 100755 tools/testing/kddv/kddv/cmds/mock.py
create mode 100755 tools/testing/kddv/kddv/cmds/test.py
create mode 100755 tools/testing/kddv/kddv/cmds/utils.py
create mode 100755 tools/testing/kddv/kddv/core/__init__.py
create mode 100755 tools/testing/kddv/kddv/core/buses/__init__.py
create mode 100755 tools/testing/kddv/kddv/core/buses/spi.py
create mode 100755 tools/testing/kddv/kddv/core/consts.py
create mode 100755 tools/testing/kddv/kddv/core/ddunit.py
create mode 100755 tools/testing/kddv/kddv/core/device.py
create mode 100755 tools/testing/kddv/kddv/core/dmesg.py
create mode 100755 tools/testing/kddv/kddv/core/driver.py
create mode 100755 tools/testing/kddv/kddv/core/environ.py
create mode 100755 tools/testing/kddv/kddv/core/failnth.py
create mode 100755 tools/testing/kddv/kddv/core/faulter.py
create mode 100755 tools/testing/kddv/kddv/core/faults/__init__.py
create mode 100755 tools/testing/kddv/kddv/core/faults/fail.py
create mode 100755 tools/testing/kddv/kddv/core/faults/page.py
create mode 100755 tools/testing/kddv/kddv/core/faults/slab.py
create mode 100755 tools/testing/kddv/kddv/core/memleak.py
create mode 100755 tools/testing/kddv/kddv/core/mockup.py
create mode 100755 tools/testing/kddv/kddv/core/model.py
create mode 100644 tools/testing/kddv/kddv/data/Makefile
create mode 100644 tools/testing/kddv/kddv/data/bpf/Makefile
create mode 100644 tools/testing/kddv/kddv/data/bpf/include/bpf-xfer-conf.h
create mode 100644 tools/testing/kddv/kddv/data/bpf/mtd/mtd-mchp23k256.c
create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-base.h
create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r1v1.c
create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r1v2.c
create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r1v3.c
create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r2v1.c
create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r2v2.c
create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r3v1.c
create mode 100644 tools/testing/kddv/kddv/data/bpf/spi/spi-xfer-r4v4.c
create mode 100755 tools/testing/kddv/kddv/tests/__init__.py
create mode 100755 tools/testing/kddv/kddv/tests/hwmon/__init__.py
create mode 100755 tools/testing/kddv/kddv/tests/hwmon/test_max31722.py
create mode 100644 tools/testing/kddv/kddv/tests/mtd/__init__.py
create mode 100755 tools/testing/kddv/kddv/tests/mtd/test_mchp23k256.py

--
2.34.1


2023-11-18 10:43:51

by huaweicloud

[permalink] [raw]
Subject: [PATCH -next 03/14] kddv/core: Add io fault support to bpf program

From: Zhang Xiaoxu <[email protected]>

Add common io fault interface, then the bpf program can use
it to mock the hardware io error.

Signed-off-by: Wei Yongjun <[email protected]>
Signed-off-by: Zhang Xiaoxu <[email protected]>
---
tools/testing/kddv/kddv/core/consts.py | 1 +
tools/testing/kddv/kddv/core/model.py | 4 ++++
.../kddv/kddv/data/bpf/include/bpf-xfer-conf.h | 13 +++++++++++++
3 files changed, 18 insertions(+)

diff --git a/tools/testing/kddv/kddv/core/consts.py b/tools/testing/kddv/kddv/core/consts.py
index 22abd7fc655c..b761407f5e88 100755
--- a/tools/testing/kddv/kddv/core/consts.py
+++ b/tools/testing/kddv/kddv/core/consts.py
@@ -10,3 +10,4 @@ CFG_REG_MASK = 0x10
CFG_REG_RSH = 0x11
CFG_REG_LSH = 0x12
CFG_REG_ORD = 0x13
+CFG_IO_FAULT = 0x20
diff --git a/tools/testing/kddv/kddv/core/model.py b/tools/testing/kddv/kddv/core/model.py
index 494b69566536..ff782c20313d 100755
--- a/tools/testing/kddv/kddv/core/model.py
+++ b/tools/testing/kddv/kddv/core/model.py
@@ -8,6 +8,7 @@

from .driver import Driver
from .mockup import Mockup
+from .consts import CFG_IO_FAULT

class DriverModel(object):
bus = None
@@ -89,3 +90,6 @@ class DriverModel(object):

def write_regs(self, addr, data):
self.mockup.write_regs(addr, data)
+
+ def trigger_io_fault(self, count = 1):
+ self.mockup.write_config(CFG_IO_FAULT, count)
diff --git a/tools/testing/kddv/kddv/data/bpf/include/bpf-xfer-conf.h b/tools/testing/kddv/kddv/data/bpf/include/bpf-xfer-conf.h
index 49adbcc6a1af..6a09bd391641 100644
--- a/tools/testing/kddv/kddv/data/bpf/include/bpf-xfer-conf.h
+++ b/tools/testing/kddv/kddv/data/bpf/include/bpf-xfer-conf.h
@@ -18,6 +18,7 @@
#define BPF_CONF_REG_RSHIFT 0x11
#define BPF_CONF_REG_LSHIFT 0x12
#define BPF_CONF_REG_XBSWAP 0x13
+#define BPF_CONF_IO_FAULT 0x20

struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
@@ -28,6 +29,7 @@ struct {

static u32 bpf_reg_mask, bpf_reg_xbswap;
static u32 bpf_reg_rshift, bpf_reg_lshift;
+static u32 bpf_io_fault;

static u32 bpf_xfer_read_conf(u32 key)
{
@@ -59,6 +61,7 @@ static int bpf_xfer_update_config(void)
bpf_reg_rshift = bpf_xfer_read_conf(BPF_CONF_REG_RSHIFT);
bpf_reg_lshift = bpf_xfer_read_conf(BPF_CONF_REG_LSHIFT);
bpf_reg_xbswap = bpf_xfer_read_conf(BPF_CONF_REG_XBSWAP);
+ bpf_io_fault = bpf_xfer_read_conf(BPF_CONF_IO_FAULT);

return 0;
}
@@ -108,4 +111,14 @@ u32 bpf_xfer_reg_u32(u32 reg)
reg = reg << bpf_reg_lshift;
return reg;
}
+
+bool bpf_xfer_should_fault(void)
+{
+ bpf_xfer_update_config();
+
+ if (bpf_io_fault)
+ bpf_xfer_write_conf(BPF_CONF_IO_FAULT, bpf_io_fault - 1);
+
+ return !!bpf_io_fault;
+}
#endif
--
2.34.1

2023-11-18 10:44:03

by huaweicloud

[permalink] [raw]
Subject: [PATCH -next 14/14] kddv: Add document for kddv

From: Zhang Xiaoxu <[email protected]>

This add documentation for the kddv, include the design principles,
how to run the testcases, and how to contribute new testcases

Signed-off-by: Zhang Xiaoxu <[email protected]>
---
Documentation/dev-tools/index.rst | 1 +
Documentation/dev-tools/kddv.rst | 183 ++++++++++++++++++++++++++++++
2 files changed, 184 insertions(+)
create mode 100755 Documentation/dev-tools/kddv.rst

diff --git a/Documentation/dev-tools/index.rst b/Documentation/dev-tools/index.rst
index 6b0663075dc0..ef8203db8fd7 100644
--- a/Documentation/dev-tools/index.rst
+++ b/Documentation/dev-tools/index.rst
@@ -34,6 +34,7 @@ Documentation/dev-tools/testing-overview.rst
kselftest
kunit/index
ktap
+ kddv


.. only:: subproject and html
diff --git a/Documentation/dev-tools/kddv.rst b/Documentation/dev-tools/kddv.rst
new file mode 100755
index 000000000000..9a031448312a
--- /dev/null
+++ b/Documentation/dev-tools/kddv.rst
@@ -0,0 +1,183 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. Copyright(c) 2022 Huawei Technologies Co., Ltd.
+
+Kernel Device Driver Verification (KDDV)
+========================================
+
+The kernel code contains a large amount of driver code which
+depends on specific hardware. As a result, the test of this part
+of code is difficult. Due to resource limitations, common detection
+methods such as KASAN are difficult to deploy. Therefore, a test
+method independent of physical hardware is required.
+
+Generally, the driver reads related information from the physical
+hardware by calling a specific function. Then the driver code can
+be tested by mocking the specific function to simulate physical
+hardware behavior by returning virtual values.
+
+With the development of eBPF, it is possible to mock a specific
+function based on the eBPF TracePoint mechanism, and then use
+eBPF to simulate physical hardware.
+
+Based on the python unittests framework, KDDV mounts devices to
+virtual buses and simulates hardware behaviors through the ebpf
+program to test specific driver functions. In addition, KDDV supports
+fault injection to simulate exceptions, verify that the driver
+behaves properly.
+
+
+Design principles
+-----------------
+
+The driver can register these device to the mock bus, it can be interacts
+with bpf program, then the physical hardware can be mocked by bpf.
+
+::
+
+ __________________________________ _______________________________
+ |User Program | | Linux kernel |
+ | | | |
+ | | | |
+ | _________________ | load | _____________ _____ |
+ |llvm -----> | eBPF bytecode | -|-------|--> | Verifier | | M | |
+ | ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ | | ¯¯¯¯¯¯¯¯¯¯¯¯¯ | O | |
+ | | | | C | |
+ | | | _____________ | K | |
+ | | | | eBPF | <--> | | |
+ | | | ¯¯¯¯¯¯¯¯¯¯¯¯¯ | B | |
+ | _______________ | read | _____________ | U | |
+ |bpftool --> | Access Regs | ----|-------|--> | eBPF Maps | | S | |
+ | ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ | write | ¯¯¯¯¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯ |
+ ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
+
+
+
+Running the KDDV
+----------------
+
+The kddv depends on the mock bus, should configure kernel as below::
+
+ CONFIG_SPI_MOCK=y
+ ...
+
+To build the tests::
+
+ $ make -C tools/testing/kddv/
+
+To install kddv in a user specified location::
+
+ $ make -C tools/testing/selftests install INSTALL_PATH=/some/other/path
+
+To run the tests::
+
+ $ cd /some/other/path
+ $ python3 -m kddv.cmds.test
+
+More useful info can be got from the help::
+
+ $ python3 -m kddv.cmds.test --help
+
+
+Contributing new tests
+----------------------
+
+Test file placement
+~~~~~~~~~~~~~~~~~~~
+
+The directory structure is as below::
+
+ tools/testing/kddv/kddv
+ ├── cmds # Python unittests command line
+ ├── core # General data structure of the kddv
+ ├── data # Extra data, eg: bpf program
+ │   ├── bpf
+ │   │   ├── include
+ │   │   │   └── bpf-xfer-conf.h
+ │   │   ├── Makefile
+ │   │   ├── mtd
+ │   │   │   └── mtd-mchp23k256.c
+ │   │   └── spi
+ │   └── Makefile
+ ├── tests # KDDV testcases
+ │ ├── __init__.py
+ │ ├── hwmon
+ │ │   ├── __init__.py
+ │ │   └── test_max31722.py
+ │ └── mtd
+ │ ├── __init__.py
+ │ └── test_mchp23k256.py
+ ├── __init__.py
+ └── Makefile
+
+If you want to add a new testcase to kddv, you should:
+ 1. add the python unittests to the **tests** directory.
+ 2. add the bpf program to the **data/bpf** directory.
+
+Basic example
+~~~~~~~~~~~~~
+
+The kddv based on python unittests, it provides a set of tools for write
+the testcase, here is a basic example for test some feature of mchp23k256
+mtd device::
+
+ from kddv.core import SPIDriverTest
+ from . import MTDDriver
+
+ MCHP23K256_TEST_DATA = [0x78] * 16
+
+ class TestMCHP23K256(SPIDriverTest, MTDDriver):
+ name = "mchp23k256"
+
+ @property
+ def bpf(self):
+ return f"mtd-{self.name}"
+
+ def test_device_probe(self):
+ with self.assertRaisesFault():
+ with self.device() as _:
+ pass
+
+ def test_device_size(self):
+ with self.device() as dev:
+ size = self.mtd_read_attr(dev, 'size')
+ self.assertEqual(size, '32768')
+
+ def test_read_data(self):
+ with self.device() as dev:
+ self.write_regs(0x00, MCHP23K256_TEST_DATA)
+ data = self.mtd_read_bytes(dev, 16)
+ self.assertEqual(data, bytes(MCHP23K256_TEST_DATA))
+
+ def test_write_data(self):
+ with self.device() as dev:
+ self.write_regs(0x00, [0] * 16)
+ self.mtd_write_bytes(dev, bytes(MCHP23K256_TEST_DATA))
+ self.assertRegsEqual(0x00, MCHP23K256_TEST_DATA)
+
+**SPIDriverTest** provides the basic functions for SPI driver, such as
+loading the driver, read/write SPI registers.
+
+**MTDDriver** provides some basic function for MTD driver, such as get the
+mtd device info, read/write data from mtd device.
+
+Since the mchp23k256 is a mtd driver based on SPI bus, so **TestMCHP23K256**
+inherited from **SPIDriverTest** and **MTDDriver**.
+
+There are 4 testcases for mchp23k256 basic function:
+ 1. driver and device can be loading successfully;
+ 2. the device size should be 32k as the default;
+ 3. read data from device, the data should be equal with the regs;
+ 4. write data to device, the regs should be equal with the data;
+
+The above script produces an output that looks like this::
+
+ $ python3 -m kddv.cmds.test kddv.tests.mtd.test_mchp23k256
+ test_device_probe (kddv.tests.mtd.test_mchp23k256.TestMCHP23K256) ... ok
+ test_device_size (kddv.tests.mtd.test_mchp23k256.TestMCHP23K256) ... ok
+ test_read_data (kddv.tests.mtd.test_mchp23k256.TestMCHP23K256) ... ok
+ test_write_data (kddv.tests.mtd.test_mchp23k256.TestMCHP23K256) ... ok
+
+ ----------------------------------------------------------------------
+ Ran 4 tests in 36.100s
+
+ OK
--
2.34.1

2023-11-18 10:44:08

by huaweicloud

[permalink] [raw]
Subject: [PATCH -next 07/14] kddv/cmd: Add command to create/remove mockup device

From: Zhang Xiaoxu <[email protected]>

Add command kddv.cmds.mock to create/remove mockup device which support
by the test framework. Usage:

Create device:
$ python3 -m kddv.cmds.mock --bus spi --devid mchp23k256
create spi device mchp23k256 success!

Then the mockup device can be accessed by exists user space tools.
$ ls /dev/mtd0
mtd0 mtd0ro
$ hexdump /dev/mtd0
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
0008000

Remove the mockup device:
$ python3 -m kddv.cmds.mock --bus spi --devid mchp23k256 -r
[ 198.718172] Deleting MTD partitions on "spi0.0":
remove spi device mchp23k256 success!

Signed-off-by: Wei Yongjun <[email protected]>
Signed-off-by: Zhang Xiaoxu <[email protected]>
---
tools/testing/kddv/kddv/Makefile | 1 +
tools/testing/kddv/kddv/cmds/__init__.py | 0
tools/testing/kddv/kddv/cmds/mock.py | 105 +++++++++++++++++++++++
tools/testing/kddv/kddv/cmds/utils.py | 28 ++++++
4 files changed, 134 insertions(+)
create mode 100755 tools/testing/kddv/kddv/cmds/__init__.py
create mode 100755 tools/testing/kddv/kddv/cmds/mock.py
create mode 100755 tools/testing/kddv/kddv/cmds/utils.py

diff --git a/tools/testing/kddv/kddv/Makefile b/tools/testing/kddv/kddv/Makefile
index a68112154669..a5c91fcb0e9a 100644
--- a/tools/testing/kddv/kddv/Makefile
+++ b/tools/testing/kddv/kddv/Makefile
@@ -12,6 +12,7 @@ install:
$(INSTALL) -m 0755 -d $(INSTALL_PATH)
$(INSTALL) __init__.py $(INSTALL_PATH)
cp -rf core/ $(INSTALL_PATH)
+ cp -rf cmds/ $(INSTALL_PATH)
cp -rf tests/ $(INSTALL_PATH)

clean:
diff --git a/tools/testing/kddv/kddv/cmds/__init__.py b/tools/testing/kddv/kddv/cmds/__init__.py
new file mode 100755
index 000000000000..e69de29bb2d1
diff --git a/tools/testing/kddv/kddv/cmds/mock.py b/tools/testing/kddv/kddv/cmds/mock.py
new file mode 100755
index 000000000000..2ec5e45219a0
--- /dev/null
+++ b/tools/testing/kddv/kddv/cmds/mock.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <[email protected]>
+
+import os
+import sys
+import logging
+import argparse
+import unittest
+import subprocess
+
+from kddv.core.mockup import Mockup
+from kddv.core.ddunit import DriverTest
+from kddv.core.buses import *
+from . import utils
+
+ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+logger = logging.getLogger()
+
+def search(suite, bus: str, devid: str):
+ mdrv = None
+ for t in suite:
+ if isinstance(t, unittest.TestSuite):
+ driver = search(t, bus, devid)
+ if driver:
+ if driver.device_id == devid:
+ return driver
+ mdrv = driver
+ elif isinstance(t, DriverTest):
+ if not hasattr(t, 'bus') or not hasattr(t, 'device_id'):
+ logger.debug(f"not a driver test case: {t}")
+ continue
+ if t.bus != bus:
+ continue
+ if t.device_id == devid:
+ return t
+ if t.driver_name == devid:
+ mdrv = t
+ else:
+ return mdrv
+
+def do_mockup_device(t):
+ mock = Mockup.create(t.bus, t)
+ try:
+ subprocess.check_output(
+ ["/sbin/modprobe", t.module_name], stderr=subprocess.STDOUT
+ )
+ except:
+ logger.warning(f"Module {t.module_name} not found")
+ sys.exit(1)
+
+ mock.load()
+ logger.warning(f"create {t.bus} device {t.device_id} success!")
+
+def do_remove_device(t):
+ mock = Mockup.create(t.bus, t)
+ try:
+ mock.unload()
+ logger.warning(f"remove {t.bus} device {t.device_id} success!")
+ except:
+ logger.warning(f"{t.bus} device {t.device_id} not found")
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--bus", "-b", type=str, required=True,
+ choices=["i2c", "spi", "pci", "platform"], help="Bus Types"
+ )
+ parser.add_argument(
+ "--devid", "-d", type=str, required=True, help="Device ID"
+ )
+ parser.add_argument(
+ "--log-level", "-l", type=str, default=None,
+ choices=utils.LOG_LEVELS, help="Log level"
+ )
+ parser.add_argument(
+ "--remove", "-r", action='store_true', default=False,
+ help="Remove device",
+ )
+ args = parser.parse_args()
+
+ if args.log_level:
+ utils.setup_logger(args.log_level)
+
+ loader = unittest.defaultTestLoader
+ suites = loader.discover(os.path.join(ROOT_DIR, 'tests'))
+ driver = search(suites, args.bus, args.devid)
+ if driver is None:
+ logger.error(f"{args.bus} device {args.devid} not support")
+ sys.exit(1)
+
+ if not args.remove:
+ do_mockup_device(driver)
+ else:
+ do_remove_device(driver)
+
+ sys.exit(0)
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/testing/kddv/kddv/cmds/utils.py b/tools/testing/kddv/kddv/cmds/utils.py
new file mode 100755
index 000000000000..8130d7a57a36
--- /dev/null
+++ b/tools/testing/kddv/kddv/cmds/utils.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <[email protected]>
+
+import sys
+import logging
+
+logger = logging.getLogger()
+
+LOG_FORMAT = "%(asctime)-15s [%(levelname)-7s] %(message)s"
+LOG_LEVELS = {
+ 'ERROR': logging.ERROR,
+ 'WARN': logging.WARN,
+ 'INFO': logging.INFO,
+ 'DEBUG': logging.DEBUG
+}
+
+def setup_logger(level):
+ logger.setLevel(LOG_LEVELS.get(level))
+ handler = logging.StreamHandler(sys.stdout)
+ handler.setFormatter(logging.Formatter(
+ fmt=LOG_FORMAT, datefmt="%Y-%m-%d %H:%M:%S"
+ ))
+ logger.addHandler(handler)
--
2.34.1

2023-11-18 10:44:14

by huaweicloud

[permalink] [raw]
Subject: [PATCH -next 06/14] kddv/core: Add page and slab fault inject support

From: Zhang Xiaoxu <[email protected]>

Implement page and slab fault inject, to test whether the drivers
can work properly when out of memory.

Signed-off-by: Wei Yongjun <[email protected]>
Signed-off-by: Zhang Xiaoxu <[email protected]>
---
tools/testing/kddv/kddv/core/ddunit.py | 29 ++++++-
tools/testing/kddv/kddv/core/driver.py | 3 +
tools/testing/kddv/kddv/core/environ.py | 26 ++++++
tools/testing/kddv/kddv/core/failnth.py | 57 ++++++++++++
tools/testing/kddv/kddv/core/faulter.py | 48 +++++++++++
.../testing/kddv/kddv/core/faults/__init__.py | 13 +++
tools/testing/kddv/kddv/core/faults/fail.py | 86 +++++++++++++++++++
tools/testing/kddv/kddv/core/faults/page.py | 40 +++++++++
tools/testing/kddv/kddv/core/faults/slab.py | 36 ++++++++
9 files changed, 337 insertions(+), 1 deletion(-)
create mode 100755 tools/testing/kddv/kddv/core/failnth.py
create mode 100755 tools/testing/kddv/kddv/core/faulter.py
create mode 100755 tools/testing/kddv/kddv/core/faults/__init__.py
create mode 100755 tools/testing/kddv/kddv/core/faults/fail.py
create mode 100755 tools/testing/kddv/kddv/core/faults/page.py
create mode 100755 tools/testing/kddv/kddv/core/faults/slab.py

diff --git a/tools/testing/kddv/kddv/core/ddunit.py b/tools/testing/kddv/kddv/core/ddunit.py
index 197300f8a6d4..716e0e1505d1 100755
--- a/tools/testing/kddv/kddv/core/ddunit.py
+++ b/tools/testing/kddv/kddv/core/ddunit.py
@@ -11,9 +11,26 @@ import unittest

from .model import DriverModel
from .environ import environ
+from .failnth import FaultIterator

logger = logging.getLogger(__name__)

+class _AssertRaisesFaultContext(unittest.case._AssertRaisesContext):
+ def __enter__(self):
+ environ.enter_fault_inject()
+ return self
+
+ def __exit__(self, exc_type, exc_value, tb):
+ if not environ.exit_fault_inject():
+ return False
+ if exc_type is None:
+ return True
+ if issubclass(exc_type, self.expected):
+ return True
+ if issubclass(exc_type, AssertionError):
+ return True
+ return super().__exit__(exc_type, exc_value, tb)
+
class DriverTest(unittest.TestCase, DriverModel):
def __init__(self, methodName=None):
super().__init__(methodName)
@@ -35,7 +52,10 @@ class DriverTest(unittest.TestCase, DriverModel):
super().tearDown()

def _callTestMethod(self, method):
- method()
+ fault = FaultIterator()
+ for nth in iter(fault):
+ logger.debug(f"fault inject: nth={nth}")
+ method()
self.assertFault()

def assertRegEqual(self, reg, data, msg=None):
@@ -54,3 +74,10 @@ class DriverTest(unittest.TestCase, DriverModel):
msg = environ.check_failure()
if msg:
raise self.failureException(msg)
+
+ def assertRaisesFault(self, *args, **kwargs):
+ context = _AssertRaisesFaultContext(OSError, self)
+ try:
+ return context.handle('assertRaises', args, kwargs)
+ finally:
+ context = None
diff --git a/tools/testing/kddv/kddv/core/driver.py b/tools/testing/kddv/kddv/core/driver.py
index 55ad804068b5..a6fcf3dcdd7d 100755
--- a/tools/testing/kddv/kddv/core/driver.py
+++ b/tools/testing/kddv/kddv/core/driver.py
@@ -12,6 +12,7 @@ import subprocess
from pathlib import Path

from .device import Device
+from .environ import environ

logger = logging.getLogger(__name__)

@@ -54,9 +55,11 @@ class Driver(object):
subprocess.check_output(
["/sbin/modprobe", self.module], stderr=subprocess.STDOUT
)
+ environ.notify_insmod(self.module)

def remove_mdule(self):
logger.debug(f'rmmod {self.module}')
+ environ.notify_rmmod()
subprocess.check_output(["/sbin/rmmod", self.module])

def setup(self):
diff --git a/tools/testing/kddv/kddv/core/environ.py b/tools/testing/kddv/kddv/core/environ.py
index 68c98e8c44da..8bd7d6a60cb7 100755
--- a/tools/testing/kddv/kddv/core/environ.py
+++ b/tools/testing/kddv/kddv/core/environ.py
@@ -9,6 +9,7 @@
import logging

from .dmesg import KernelMessage
+from .faulter import FaultInject
from .memleak import Kmemleak

logger = logging.getLogger(__name__)
@@ -17,12 +18,15 @@ class Environ(object):
def __init__(self):
self.kmsg = KernelMessage()
self.leak = Kmemleak()
+ self.fault = FaultInject()

def setup(self):
self.kmsg.setup()
self.leak.setup()
+ self.fault.setup()

def teardown(self):
+ self.fault.teardown()
self.leak.teardown()
self.kmsg.teardown()

@@ -36,4 +40,26 @@ class Environ(object):
return msg
return self.leak.check_failure()

+ def enable_fault_inject(self, feature):
+ """Enable fault injection feature"""
+ self.fault.enable_feature(feature)
+
+ def fault_running(self):
+ """Fault injection has been enabled"""
+ return self.fault.running
+
+ def enter_fault_inject(self):
+ """Enter fault injection"""
+ self.fault.start_features()
+
+ def exit_fault_inject(self):
+ """Exit fault injection"""
+ return self.fault.stop_features()
+
+ def notify_insmod(self, name):
+ self.fault.filter_module(name)
+
+ def notify_rmmod(self):
+ self.fault.filter_module(None)
+
environ = Environ()
diff --git a/tools/testing/kddv/kddv/core/failnth.py b/tools/testing/kddv/kddv/core/failnth.py
new file mode 100755
index 000000000000..6d547aabb0a1
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/failnth.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <[email protected]>
+
+import os
+import re
+import logging
+
+from pathlib import Path
+from .environ import environ
+
+logger = logging.getLogger(__name__)
+
+class FaultIterator(object):
+ def __init__(self, max_loop = 0):
+ self._max_loop = max_loop
+ self._cur_fail = 0
+ self._max_fail = 3
+ self.path = Path(f"/proc/self/fail-nth")
+
+ def __iter__(self):
+ self.nth = -1
+ return self
+
+ def __next__(self):
+ self.nth += 1
+ if not self.nth:
+ return self.nth
+ if not environ.fault_running():
+ logger.debug('fault inject not running')
+ raise StopIteration
+ if not os.path.exists(self.path):
+ logger.debug('fault inject not exists')
+ raise StopIteration
+ if self._max_loop and self._max_loop < self.nth:
+ raise StopIteration
+ if self.read_nth() > 0:
+ self.write_nth(0)
+ self._cur_fail += 1
+ if self._cur_fail >= self._max_fail:
+ logger.debug('end fault inject')
+ raise StopIteration
+ else:
+ self._cur_fail = 0
+ self.write_nth(self.nth)
+ return self.nth
+
+ def read_nth(self):
+ return int(self.path.read_text().rstrip())
+
+ def write_nth(self, val):
+ logger.debug(f"write {val} to fail-nth")
+ self.path.write_text(str(val))
diff --git a/tools/testing/kddv/kddv/core/faulter.py b/tools/testing/kddv/kddv/core/faulter.py
new file mode 100755
index 000000000000..32f4d5c3a57c
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/faulter.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <[email protected]>
+
+from .faults import FailModule
+
+class FaultInject(object):
+ def __init__(self):
+ self.enabled = False
+ self.running = False
+ self.features = []
+ for subclass in FailModule.__subclasses__():
+ self.features.append(subclass())
+
+ def setup(self):
+ pass
+
+ def teardown(self):
+ self.running = False
+
+ def start_features(self):
+ if not self.enabled:
+ return
+ for feature in self.features:
+ feature.start()
+ self.running = True
+
+ def stop_features(self):
+ if not self.enabled:
+ return False
+ for feature in self.features:
+ feature.stop()
+ return True
+
+ def filter_module(self, module):
+ for feature in self.features:
+ feature.filter_module(module)
+
+ def enable_feature(self, name):
+ for feature in self.features:
+ if name in [feature.key, 'all']:
+ if feature.has_support:
+ feature.enabled = True
+ self.enabled = True
diff --git a/tools/testing/kddv/kddv/core/faults/__init__.py b/tools/testing/kddv/kddv/core/faults/__init__.py
new file mode 100755
index 000000000000..2e1280ca20da
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/faults/__init__.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <[email protected]>
+
+__all__ = ['FailModule']
+
+from .fail import FailModule
+from .slab import FailSlab
+from .page import FailPage
diff --git a/tools/testing/kddv/kddv/core/faults/fail.py b/tools/testing/kddv/kddv/core/faults/fail.py
new file mode 100755
index 000000000000..4d4344e00e9a
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/faults/fail.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <[email protected]>
+
+import re
+import os
+import logging
+from pathlib import Path
+
+logger = logging.getLogger(__name__)
+
+class FailModule(object):
+ name = None
+ key = None
+
+ def __init__(self):
+ self.has_support = os.path.exists(f"/sys/kernel/debug/{self.name}")
+ self.ftext = Path(f"/sys/kernel/debug/{self.name}/require-start")
+ self.fdata = Path(f"/sys/kernel/debug/{self.name}/require-end")
+ self.fdepth = Path(f"/sys/kernel/debug/{self.name}/stacktrace-depth")
+ self.nowarn = Path(f"/sys/kernel/debug/{self.name}/verbose")
+ self.enabled = False
+ self.module = None
+
+ def feature_enabled(self):
+ if not self.has_support:
+ return False
+ return self.enabled
+
+ def filter_module(self, name):
+ if name is None:
+ self.module = None
+ else:
+ self.module = re.sub('-', '_', name)
+
+ def enable_verbose(self):
+ if not self.feature_enabled():
+ return
+ self.nowarn.write_text('1')
+
+ def disable_verbose(self):
+ if not self.feature_enabled():
+ return
+ self.nowarn.write_text('0')
+
+ def enable_module_filter(self):
+ if not self.feature_enabled():
+ return
+ if self.module is None:
+ return
+ logger.debug(f"enter module filter for fail {self.name}")
+ mtext = Path(f"/sys/module/{self.module}/sections/.text")
+ mdata = Path(f"/sys/module/{self.module}/sections/.data")
+ self.ftext.write_text(mtext.read_text().rstrip())
+ self.fdata.write_text(mdata.read_text().rstrip())
+ self.fdepth.write_text('32')
+
+ def disable_module_filter(self):
+ if not self.feature_enabled():
+ return
+ if self.module is None:
+ return
+ logger.debug(f"exit module filter for fail {self.name}")
+ self.ftext.write_text('0')
+ self.fdata.write_text('0')
+ self.fdepth.write_text('32')
+
+ def enable_feature(self):
+ pass
+
+ def disable_feature(self):
+ pass
+
+ def start(self):
+ self.enable_module_filter()
+ self.enable_verbose()
+ self.enable_feature()
+
+ def stop(self):
+ self.disable_feature()
+ self.disable_verbose()
+ self.disable_module_filter()
diff --git a/tools/testing/kddv/kddv/core/faults/page.py b/tools/testing/kddv/kddv/core/faults/page.py
new file mode 100755
index 000000000000..e78f6e1a8c88
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/faults/page.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <[email protected]>
+
+import logging
+from pathlib import Path
+
+from .fail import FailModule
+
+FAILPAGE_IGNORE_HMEM = '/sys/kernel/debug/fail_page_alloc/ignore-gfp-highmem'
+FAILPAGE_IGNORE_WAIT = '/sys/kernel/debug/fail_page_alloc/ignore-gfp-wait'
+
+logger = logging.getLogger(__name__)
+
+class FailPage(FailModule):
+ name = 'fail_page_alloc'
+ key = 'page'
+
+ def __init__(self):
+ super().__init__()
+ self.ignore_hmem = Path(FAILPAGE_IGNORE_HMEM)
+ self.ignore_wait = Path(FAILPAGE_IGNORE_WAIT)
+
+ def enable_feature(self):
+ if not self.feature_enabled():
+ return
+ logger.debug("enter fail page injection")
+ self.ignore_hmem.write_text('N')
+ self.ignore_wait.write_text('N')
+
+ def disable_feature(self):
+ if not self.feature_enabled():
+ return
+ logger.debug("exit fail page injection")
+ self.ignore_hmem.write_text('Y')
+ self.ignore_wait.write_text('Y')
diff --git a/tools/testing/kddv/kddv/core/faults/slab.py b/tools/testing/kddv/kddv/core/faults/slab.py
new file mode 100755
index 000000000000..5472f1ec4795
--- /dev/null
+++ b/tools/testing/kddv/kddv/core/faults/slab.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel device driver verification
+#
+# Copyright (C) 2022-2023 Huawei Technologies Co., Ltd
+# Author: Wei Yongjun <[email protected]>
+
+import logging
+from pathlib import Path
+
+from .fail import FailModule
+
+FAILSLAB_IGNORE = '/sys/kernel/debug/failslab/ignore-gfp-wait'
+
+logger = logging.getLogger(__name__)
+
+class FailSlab(FailModule):
+ name = 'failslab'
+ key = 'slab'
+
+ def __init__(self):
+ super().__init__()
+ self.ignore = Path(FAILSLAB_IGNORE)
+
+ def enable_feature(self):
+ if not self.feature_enabled():
+ return
+ logger.debug("enter fail slab injection")
+ self.ignore.write_text('N')
+
+ def disable_feature(self):
+ if not self.feature_enabled():
+ return
+ logger.debug("exit fail slab injection")
+ self.ignore.write_text('Y')
--
2.34.1