resource, kunit: add test case for region_intersects()

Patch series "resource: Fix region_intersects() vs
add_memory_driver_managed()", v3.

The patchset fixes a bug of region_intersects() for systems with CXL
memory.  The details of the bug can be found in [1/3].  To avoid similar
bugs in the future.  A kunit test case for region_intersects() is added in
[3/3].  [2/3] is a preparation patch for [3/3].


This patch (of 3):

region_intersects() is important because it's used for /dev/mem permission
checking.  To avoid possible bug of region_intersects() in the future, a
kunit test case for region_intersects() is added.

Link: https://lkml.kernel.org/r/20240906030713.204292-1-ying.huang@intel.com
Link: https://lkml.kernel.org/r/20240906030713.204292-4-ying.huang@intel.com
Signed-off-by: "Huang, Ying" <ying.huang@intel.com>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
Cc: Dave Jiang <dave.jiang@intel.com>
Cc: Alison Schofield <alison.schofield@intel.com>
Cc: Vishal Verma <vishal.l.verma@intel.com>
Cc: Ira Weiny <ira.weiny@intel.com>
Cc: Alistair Popple <apopple@nvidia.com>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Baoquan He <bhe@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
Huang Ying 2024-09-06 11:07:13 +08:00 committed by Andrew Morton
parent bacf9c3cbb
commit 99185c10d5
3 changed files with 158 additions and 6 deletions

View File

@ -1817,7 +1817,17 @@ EXPORT_SYMBOL(resource_list_free);
#ifdef CONFIG_GET_FREE_REGION #ifdef CONFIG_GET_FREE_REGION
#define GFR_DESCENDING (1UL << 0) #define GFR_DESCENDING (1UL << 0)
#define GFR_REQUEST_REGION (1UL << 1) #define GFR_REQUEST_REGION (1UL << 1)
#define GFR_DEFAULT_ALIGN (1UL << PA_SECTION_SHIFT) #ifdef PA_SECTION_SHIFT
#define GFR_DEFAULT_ALIGN (1UL << PA_SECTION_SHIFT)
#else
#define GFR_DEFAULT_ALIGN PAGE_SIZE
#endif
#ifdef MAX_PHYSMEM_BITS
#define MAX_PHYS_ADDR ((1ULL << MAX_PHYSMEM_BITS) - 1)
#else
#define MAX_PHYS_ADDR (-1ULL)
#endif
static resource_size_t gfr_start(struct resource *base, resource_size_t size, static resource_size_t gfr_start(struct resource *base, resource_size_t size,
resource_size_t align, unsigned long flags) resource_size_t align, unsigned long flags)
@ -1825,8 +1835,7 @@ static resource_size_t gfr_start(struct resource *base, resource_size_t size,
if (flags & GFR_DESCENDING) { if (flags & GFR_DESCENDING) {
resource_size_t end; resource_size_t end;
end = min_t(resource_size_t, base->end, end = min_t(resource_size_t, base->end, MAX_PHYS_ADDR);
(1ULL << MAX_PHYSMEM_BITS) - 1);
return end - size + 1; return end - size + 1;
} }
@ -1843,8 +1852,7 @@ static bool gfr_continue(struct resource *base, resource_size_t addr,
* @size did not wrap 0. * @size did not wrap 0.
*/ */
return addr > addr - size && return addr > addr - size &&
addr <= min_t(resource_size_t, base->end, addr <= min_t(resource_size_t, base->end, MAX_PHYS_ADDR);
(1ULL << MAX_PHYSMEM_BITS) - 1);
} }
static resource_size_t gfr_next(resource_size_t addr, resource_size_t size, static resource_size_t gfr_next(resource_size_t addr, resource_size_t size,
@ -2005,7 +2013,7 @@ struct resource *alloc_free_mem_region(struct resource *base,
return get_free_mem_region(NULL, base, size, align, name, return get_free_mem_region(NULL, base, size, align, name,
IORES_DESC_NONE, flags); IORES_DESC_NONE, flags);
} }
EXPORT_SYMBOL_NS_GPL(alloc_free_mem_region, CXL); EXPORT_SYMBOL_GPL(alloc_free_mem_region);
#endif /* CONFIG_GET_FREE_REGION */ #endif /* CONFIG_GET_FREE_REGION */
static int __init strict_iomem(char *str) static int __init strict_iomem(char *str)

View File

@ -7,6 +7,8 @@
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/sizes.h>
#include <linux/mm.h>
#define R0_START 0x0000 #define R0_START 0x0000
#define R0_END 0xffff #define R0_END 0xffff
@ -137,9 +139,150 @@ static void resource_test_intersection(struct kunit *test)
} while (++i < ARRAY_SIZE(results_for_intersection)); } while (++i < ARRAY_SIZE(results_for_intersection));
} }
/*
* The test resource tree for region_intersects() test:
*
* BASE-BASE+1M-1 : Test System RAM 0
* # hole 0 (BASE+1M-BASE+2M)
* BASE+2M-BASE+3M-1 : Test CXL Window 0
* BASE+3M-BASE+4M-1 : Test System RAM 1
* BASE+4M-BASE+7M-1 : Test CXL Window 1
* BASE+4M-BASE+5M-1 : Test System RAM 2
* BASE+4M+128K-BASE+4M+256K-1: Test Code
* BASE+5M-BASE+6M-1 : Test System RAM 3
*/
#define RES_TEST_RAM0_OFFSET 0
#define RES_TEST_RAM0_SIZE SZ_1M
#define RES_TEST_HOLE0_OFFSET (RES_TEST_RAM0_OFFSET + RES_TEST_RAM0_SIZE)
#define RES_TEST_HOLE0_SIZE SZ_1M
#define RES_TEST_WIN0_OFFSET (RES_TEST_HOLE0_OFFSET + RES_TEST_HOLE0_SIZE)
#define RES_TEST_WIN0_SIZE SZ_1M
#define RES_TEST_RAM1_OFFSET (RES_TEST_WIN0_OFFSET + RES_TEST_WIN0_SIZE)
#define RES_TEST_RAM1_SIZE SZ_1M
#define RES_TEST_WIN1_OFFSET (RES_TEST_RAM1_OFFSET + RES_TEST_RAM1_SIZE)
#define RES_TEST_WIN1_SIZE (SZ_1M * 3)
#define RES_TEST_RAM2_OFFSET RES_TEST_WIN1_OFFSET
#define RES_TEST_RAM2_SIZE SZ_1M
#define RES_TEST_CODE_OFFSET (RES_TEST_RAM2_OFFSET + SZ_128K)
#define RES_TEST_CODE_SIZE SZ_128K
#define RES_TEST_RAM3_OFFSET (RES_TEST_RAM2_OFFSET + RES_TEST_RAM2_SIZE)
#define RES_TEST_RAM3_SIZE SZ_1M
#define RES_TEST_TOTAL_SIZE ((RES_TEST_WIN1_OFFSET + RES_TEST_WIN1_SIZE))
static void remove_free_resource(void *ctx)
{
struct resource *res = (struct resource *)ctx;
remove_resource(res);
kfree(res);
}
static void resource_test_request_region(struct kunit *test, struct resource *parent,
resource_size_t start, resource_size_t size,
const char *name, unsigned long flags)
{
struct resource *res;
res = __request_region(parent, start, size, name, flags);
KUNIT_ASSERT_NOT_NULL(test, res);
kunit_add_action_or_reset(test, remove_free_resource, res);
}
static void resource_test_insert_resource(struct kunit *test, struct resource *parent,
resource_size_t start, resource_size_t size,
const char *name, unsigned long flags)
{
struct resource *res;
res = kzalloc(sizeof(*res), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, res);
res->name = name;
res->start = start;
res->end = start + size - 1;
res->flags = flags;
if (insert_resource(parent, res)) {
kfree(res);
KUNIT_FAIL_AND_ABORT(test, "Fail to insert resource %pR\n", res);
}
kunit_add_action_or_reset(test, remove_free_resource, res);
}
static void resource_test_region_intersects(struct kunit *test)
{
unsigned long flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY;
struct resource *parent;
resource_size_t start;
/* Find an iomem_resource hole to hold test resources */
parent = alloc_free_mem_region(&iomem_resource, RES_TEST_TOTAL_SIZE, SZ_1M,
"test resources");
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
start = parent->start;
kunit_add_action_or_reset(test, remove_free_resource, parent);
resource_test_request_region(test, parent, start + RES_TEST_RAM0_OFFSET,
RES_TEST_RAM0_SIZE, "Test System RAM 0", flags);
resource_test_insert_resource(test, parent, start + RES_TEST_WIN0_OFFSET,
RES_TEST_WIN0_SIZE, "Test CXL Window 0",
IORESOURCE_MEM);
resource_test_request_region(test, parent, start + RES_TEST_RAM1_OFFSET,
RES_TEST_RAM1_SIZE, "Test System RAM 1", flags);
resource_test_insert_resource(test, parent, start + RES_TEST_WIN1_OFFSET,
RES_TEST_WIN1_SIZE, "Test CXL Window 1",
IORESOURCE_MEM);
resource_test_request_region(test, parent, start + RES_TEST_RAM2_OFFSET,
RES_TEST_RAM2_SIZE, "Test System RAM 2", flags);
resource_test_insert_resource(test, parent, start + RES_TEST_CODE_OFFSET,
RES_TEST_CODE_SIZE, "Test Code", flags);
resource_test_request_region(test, parent, start + RES_TEST_RAM3_OFFSET,
RES_TEST_RAM3_SIZE, "Test System RAM 3", flags);
kunit_release_action(test, remove_free_resource, parent);
KUNIT_EXPECT_EQ(test, REGION_INTERSECTS,
region_intersects(start + RES_TEST_RAM0_OFFSET, PAGE_SIZE,
IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE));
KUNIT_EXPECT_EQ(test, REGION_INTERSECTS,
region_intersects(start + RES_TEST_RAM0_OFFSET +
RES_TEST_RAM0_SIZE - PAGE_SIZE, 2 * PAGE_SIZE,
IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE));
KUNIT_EXPECT_EQ(test, REGION_DISJOINT,
region_intersects(start + RES_TEST_HOLE0_OFFSET, PAGE_SIZE,
IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE));
KUNIT_EXPECT_EQ(test, REGION_DISJOINT,
region_intersects(start + RES_TEST_HOLE0_OFFSET +
RES_TEST_HOLE0_SIZE - PAGE_SIZE, 2 * PAGE_SIZE,
IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE));
KUNIT_EXPECT_EQ(test, REGION_MIXED,
region_intersects(start + RES_TEST_WIN0_OFFSET +
RES_TEST_WIN0_SIZE - PAGE_SIZE, 2 * PAGE_SIZE,
IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE));
KUNIT_EXPECT_EQ(test, REGION_INTERSECTS,
region_intersects(start + RES_TEST_RAM1_OFFSET +
RES_TEST_RAM1_SIZE - PAGE_SIZE, 2 * PAGE_SIZE,
IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE));
KUNIT_EXPECT_EQ(test, REGION_INTERSECTS,
region_intersects(start + RES_TEST_RAM2_OFFSET +
RES_TEST_RAM2_SIZE - PAGE_SIZE, 2 * PAGE_SIZE,
IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE));
KUNIT_EXPECT_EQ(test, REGION_INTERSECTS,
region_intersects(start + RES_TEST_CODE_OFFSET, PAGE_SIZE,
IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE));
KUNIT_EXPECT_EQ(test, REGION_INTERSECTS,
region_intersects(start + RES_TEST_RAM2_OFFSET,
RES_TEST_RAM2_SIZE + PAGE_SIZE,
IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE));
KUNIT_EXPECT_EQ(test, REGION_MIXED,
region_intersects(start + RES_TEST_RAM3_OFFSET,
RES_TEST_RAM3_SIZE + PAGE_SIZE,
IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE));
}
static struct kunit_case resource_test_cases[] = { static struct kunit_case resource_test_cases[] = {
KUNIT_CASE(resource_test_union), KUNIT_CASE(resource_test_union),
KUNIT_CASE(resource_test_intersection), KUNIT_CASE(resource_test_intersection),
KUNIT_CASE(resource_test_region_intersects),
{} {}
}; };

View File

@ -2616,6 +2616,7 @@ config RESOURCE_KUNIT_TEST
tristate "KUnit test for resource API" if !KUNIT_ALL_TESTS tristate "KUnit test for resource API" if !KUNIT_ALL_TESTS
depends on KUNIT depends on KUNIT
default KUNIT_ALL_TESTS default KUNIT_ALL_TESTS
select GET_FREE_REGION
help help
This builds the resource API unit test. This builds the resource API unit test.
Tests the logic of API provided by resource.c and ioport.h. Tests the logic of API provided by resource.c and ioport.h.