* [PATCH v6 0/3] cgroup: dmem: add selftest helper and coverage
@ 2026-08-31 13:25 Albert Esteve
2026-08-31 13:25 ` [PATCH v6 1/3] selftests: cgroup: Add dmem_selftest helper module Albert Esteve
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Albert Esteve @ 2026-08-31 13:25 UTC (permalink / raw)
To: Tejun Heo, Johannes Weiner, Michal Koutný, Shuah Khan
Cc: linux-kernel, cgroups, linux-kselftest, Albert Esteve
Hi all,
This small series adds practical test coverage for the dmem
cgroup controller.
The motivation came from following the recent dmem API discussion in
thread [1]. That discussion considered changing the dmem API and
adding a new knob. Currently there are no dedicated tests covering
dmem behaviour, which makes such changes riskier.
Adding selftests has an additional challenge: dmem charging paths
are driver-driven today, so regression testing is harder unless a
suitable driver is present in the test environment.
This series addresses that by adding:
- an out-of-tree dmem_selftest helper with alloc/free module parameters,
- KB() definition next to existing MB()/GB() helpers
- test_dmem for dmem.max enforcement and byte-accurate dmem.current
The goal is to make dmem behavior easier to validate when evolving the API
and implementation, while keeping tests deterministic and driver-independent.
Thanks.
[1] - https://lore.kernel.org/all/aZoHfloupKvF2oSu@fedora/
Signed-off-by: Albert Esteve <aesteve@redhat.com>
---
Changes in v6:
- Drop virtme-ng wrappers
- Build the helper out of tree, drop CONFIG_DMEM_SELFTEST
- Rename charge/uncharge to alloc/free
- Add KB() in cgroup_util.h
- Use cg_read_key_long(), ARRAY_SIZE(), and KB()/MB() size literals
- Drop test_dmem_min/low
- Check dmem.current for exact equality
- stat() the helper once in main() instead of per-subtest
- Link to v5: https://lore.kernel.org/r/20260706-kunit_cgroups-v5-0-6c42c8753468@redhat.com
Changes in v5:
- Change parse_first_region() by find_selftest_region to scan
dmem.capacity lines
- Use exact equality for limit readback, not values_close()
- Write VM tempfile to $SCRIPT_DIR
- Fix KTAP by dropping outer framing in test_dmem
- Use array for optional vng args to handle paths with spaces
- Respect O=/KBUILD_OUTPUT= when building, per bpf/vmtest.sh convention
- Link to v4: https://lore.kernel.org/r/20260519-kunit_cgroups-v4-0-f6c2f498fae4@redhat.com
Changes in v4:
- Fix charged_pool leak in dmem_selftest_init()
- Replace ssh-based VM approach in vmtest-dmem.sh with vng --exec
- Add main() wrapper to vmtest-dmem.sh
- Other small fixes suggested by Sashiko
- Link to v3: https://lore.kernel.org/r/20260504-kunit_cgroups-v3-0-4eac90b76f91@redhat.com
Changes in v3:
- Set charged flag on unexpected over-limit charge success.
- Add CONFIG_DMEM_SELFTEST=m to selftest config.
- Simplify -v to a boolean; single -v was a no-op.
- Unquote kernel_opt to avoid empty-string arg to vng.
- Document -b in usage() output.
- Link to v2: https://lore.kernel.org/r/20260421-kunit_cgroups-v2-0-bb6675d8249c@redhat.com
Changes in v2:
- Fix debugfs_create_dir() error check
- Fix module teardown race: call dmem_selftest_remove() before
uncharging so debugfs files are torn down
- Use IS_ERR_OR_NULL() in selftest() sanity check
- Add CONFIG_CGROUP_DMEM=y to the cgroup selftest config
- Replace config-file parsing in check_guest_requirements() with
a direct check of /sys/fs/cgroup/cgroup.controllers
- Add new patch 4 (from Eric Chanudet): vmtest-dmem.sh -b flag
to configure and build a local kernel tree
- Link to v1: https://lore.kernel.org/r/20260327-kunit_cgroups-v1-0-971b3c739a00@redhat.com
---
Albert Esteve (3):
selftests: cgroup: Add dmem_selftest helper module
selftests: cgroup: Add KB() helper
selftests: cgroup: Add dmem selftest coverage
tools/testing/selftests/cgroup/.gitignore | 1 +
tools/testing/selftests/cgroup/Makefile | 3 +
tools/testing/selftests/cgroup/config | 1 +
.../selftests/cgroup/lib/include/cgroup_util.h | 1 +
tools/testing/selftests/cgroup/test_dmem.c | 314 +++++++++++++++++++++
.../testing/selftests/cgroup/test_modules/Makefile | 17 ++
.../selftests/cgroup/test_modules/dmem_selftest.c | 146 ++++++++++
7 files changed, 483 insertions(+)
---
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
change-id: 20260318-kunit_cgroups-7fb0b9e64017
Best regards,
--
Albert Esteve <aesteve@redhat.com>
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v6 1/3] selftests: cgroup: Add dmem_selftest helper module 2026-08-31 13:25 [PATCH v6 0/3] cgroup: dmem: add selftest helper and coverage Albert Esteve @ 2026-08-31 13:25 ` Albert Esteve 2026-09-10 6:49 ` R Nageswara Sastry 2026-08-31 13:25 ` [PATCH v6 2/3] selftests: cgroup: Add KB() helper Albert Esteve 2026-08-31 13:25 ` [PATCH v6 3/3] selftests: cgroup: Add dmem selftest coverage Albert Esteve 2 siblings, 1 reply; 7+ messages in thread From: Albert Esteve @ 2026-08-31 13:25 UTC (permalink / raw) To: Tejun Heo, Johannes Weiner, Michal Koutný, Shuah Khan Cc: linux-kernel, cgroups, linux-kselftest, Albert Esteve Currently, dmem charging is driver-driven through direct calls to dmem_cgroup_try_charge() (for example TTM). That does not give cgroup selftests a generic way to trigger alloc/free paths from userspace in a chosen cgroup, so coverage of nested dmem.max still needs a synthetic region. Add tools/testing/selftests/cgroup/test_modules/dmem_selftest.c as an OOT helper, built like other test_modules (no extra Kconfig needed). Tests can insmod the .ko module to register a "dmem_selftest" region. Module parameters drive a single outstanding allocation against the calling task's cgroup: /sys/module/dmem_selftest/parameters/alloc /sys/module/dmem_selftest/parameters/free Writing a size to alloc triggers dmem_cgroup_try_charge() for the calling task's cgroup. Writing to free with no argument releases the outstanding charge via dmem_cgroup_uncharge(). Only a single outstanding charge is supported. This provides a deterministic, driver-independent mechanism for exercising dmem accounting paths in selftests. Signed-off-by: Albert Esteve <aesteve@redhat.com> --- tools/testing/selftests/cgroup/Makefile | 1 + tools/testing/selftests/cgroup/config | 1 + .../testing/selftests/cgroup/test_modules/Makefile | 17 +++ .../selftests/cgroup/test_modules/dmem_selftest.c | 146 +++++++++++++++++++++ 4 files changed, 165 insertions(+) diff --git a/tools/testing/selftests/cgroup/Makefile b/tools/testing/selftests/cgroup/Makefile index e01584c2189a..a08661320a07 100644 --- a/tools/testing/selftests/cgroup/Makefile +++ b/tools/testing/selftests/cgroup/Makefile @@ -6,6 +6,7 @@ all: ${HELPER_PROGS} TEST_FILES := with_stress.sh TEST_PROGS := test_stress.sh test_cpuset_prs.sh test_cpuset_v1_hp.sh TEST_GEN_FILES := wait_inotify +TEST_GEN_MODS_DIR := test_modules # Keep the lists lexicographically sorted TEST_GEN_PROGS = test_core TEST_GEN_PROGS += test_cpu diff --git a/tools/testing/selftests/cgroup/config b/tools/testing/selftests/cgroup/config index 39f979690dd3..2ee0488c3d65 100644 --- a/tools/testing/selftests/cgroup/config +++ b/tools/testing/selftests/cgroup/config @@ -1,5 +1,6 @@ CONFIG_CGROUPS=y CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_DMEM=y CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_SCHED=y CONFIG_MEMCG=y diff --git a/tools/testing/selftests/cgroup/test_modules/Makefile b/tools/testing/selftests/cgroup/test_modules/Makefile new file mode 100644 index 000000000000..dd8a17313cec --- /dev/null +++ b/tools/testing/selftests/cgroup/test_modules/Makefile @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0 +TESTMODS_DIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST))))) +KDIR ?= /lib/modules/$(shell uname -r)/build + +obj-m += dmem_selftest.o + +# Ensure that KDIR exists, otherwise skip the compilation +modules: +ifneq ("$(wildcard $(KDIR))", "") + $(Q)$(MAKE) -C $(KDIR) modules KBUILD_EXTMOD=$(TESTMODS_DIR) +endif + +# Ensure that KDIR exists, otherwise skip the clean target +clean: +ifneq ("$(wildcard $(KDIR))", "") + $(Q)$(MAKE) -C $(KDIR) clean KBUILD_EXTMOD=$(TESTMODS_DIR) +endif diff --git a/tools/testing/selftests/cgroup/test_modules/dmem_selftest.c b/tools/testing/selftests/cgroup/test_modules/dmem_selftest.c new file mode 100644 index 000000000000..a763a156f13f --- /dev/null +++ b/tools/testing/selftests/cgroup/test_modules/dmem_selftest.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Kselftest helper for the dmem cgroup controller. + * + * Registers a synthetic dmem region so tests can trigger allocations + * from the calling task's cgroup via module parameters: + * /sys/module/dmem_selftest/parameters/alloc + * /sys/module/dmem_selftest/parameters/free + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/cgroup_dmem.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/mutex.h> +#include <linux/panic.h> +#include <linux/sysfs.h> + +#define DM_SELFTEST_REGION_NAME "dmem_selftest" +#define DM_SELFTEST_REGION_SIZE (256ULL * 1024 * 1024) + +static struct dmem_cgroup_region *selftest_region; +static struct dmem_cgroup_pool_state *alloc_pool; +static u64 alloc_size; +static DEFINE_MUTEX(alloc_lock); + +static int param_set_alloc(const char *val, const struct kernel_param *kp) +{ + struct dmem_cgroup_pool_state *pool = NULL, *limit = NULL; + u64 size; + int ret; + + if (!selftest_region) + return -ENODEV; + + ret = kstrtou64(val, 0, &size); + if (ret) + return ret; + if (!size || size > DM_SELFTEST_REGION_SIZE) + return -EINVAL; + + mutex_lock(&alloc_lock); + if (alloc_pool) { + mutex_unlock(&alloc_lock); + return -EBUSY; + } + + ret = dmem_cgroup_try_charge(selftest_region, size, &pool, &limit); + if (ret == -EAGAIN && limit) + dmem_cgroup_pool_state_put(limit); + if (ret) { + mutex_unlock(&alloc_lock); + return ret; + } + + alloc_pool = pool; + alloc_size = size; + mutex_unlock(&alloc_lock); + return 0; +} + +static int param_get_alloc(char *buffer, const struct kernel_param *kp) +{ + u64 size; + + mutex_lock(&alloc_lock); + size = alloc_size; + mutex_unlock(&alloc_lock); + return sysfs_emit(buffer, "%llu\n", size); +} + +static const struct kernel_param_ops alloc_ops = { + .set = param_set_alloc, + .get = param_get_alloc, +}; + +module_param_cb(alloc, &alloc_ops, NULL, 0644); +MODULE_PARM_DESC(alloc, "Allocate (charge) SIZE bytes against the calling task's dmem cgroup"); + +static int param_set_free(const char *val, const struct kernel_param *kp) +{ + mutex_lock(&alloc_lock); + if (!alloc_pool) { + mutex_unlock(&alloc_lock); + return -EINVAL; + } + + dmem_cgroup_uncharge(alloc_pool, alloc_size); + alloc_pool = NULL; + alloc_size = 0; + mutex_unlock(&alloc_lock); + return 0; +} + +static const struct kernel_param_ops free_ops = { + .flags = KERNEL_PARAM_OPS_FL_NOARG, + .set = param_set_free, +}; + +module_param_cb(free, &free_ops, NULL, 0200); +MODULE_PARM_DESC(free, "Free the outstanding dmem selftest allocation"); + +static int __init dmem_selftest_init(void) +{ + static const struct dmem_cgroup_init init = { + .size = DM_SELFTEST_REGION_SIZE, + }; + + selftest_region = dmem_cgroup_register_region(&init, DM_SELFTEST_REGION_NAME); + if (IS_ERR(selftest_region)) + return PTR_ERR(selftest_region); + if (!selftest_region) + return -EINVAL; + + add_taint(TAINT_TEST, LOCKDEP_STILL_OK); + pr_info("region '%s' registered; parameters alloc/free\n", + DM_SELFTEST_REGION_NAME); + return 0; +} + +static void __exit dmem_selftest_exit(void) +{ + mutex_lock(&alloc_lock); + if (alloc_pool) { + dmem_cgroup_uncharge(alloc_pool, alloc_size); + alloc_pool = NULL; + alloc_size = 0; + } + mutex_unlock(&alloc_lock); + + if (selftest_region) { + dmem_cgroup_unregister_region(selftest_region); + selftest_region = NULL; + } + pr_info("unloaded.\n"); +} + +module_init(dmem_selftest_init); +module_exit(dmem_selftest_exit); + +MODULE_AUTHOR("Albert Esteve <aesteve@redhat.com>"); +MODULE_DESCRIPTION("Kselftest helper for cgroup dmem controller"); +MODULE_LICENSE("GPL"); +MODULE_INFO(test, "Y"); -- 2.55.0 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v6 1/3] selftests: cgroup: Add dmem_selftest helper module 2026-08-31 13:25 ` [PATCH v6 1/3] selftests: cgroup: Add dmem_selftest helper module Albert Esteve @ 2026-09-10 6:49 ` R Nageswara Sastry 0 siblings, 0 replies; 7+ messages in thread From: R Nageswara Sastry @ 2026-09-10 6:49 UTC (permalink / raw) To: Albert Esteve, Tejun Heo, Johannes Weiner, Michal Koutný, Shuah Khan Cc: linux-kernel, cgroups, linux-kselftest On 31.08.2026 6:55 PM, Albert Esteve wrote: > Currently, dmem charging is driver-driven through direct > calls to dmem_cgroup_try_charge() (for example TTM). > That does not give cgroup selftests a generic way to > trigger alloc/free paths from userspace in a chosen > cgroup, so coverage of nested dmem.max still > needs a synthetic region. > > Add tools/testing/selftests/cgroup/test_modules/dmem_selftest.c > as an OOT helper, built like other test_modules (no extra > Kconfig needed). Tests can insmod the .ko module to register a > "dmem_selftest" region. > > Module parameters drive a single outstanding allocation against > the calling task's cgroup: > > /sys/module/dmem_selftest/parameters/alloc > /sys/module/dmem_selftest/parameters/free > > Writing a size to alloc triggers dmem_cgroup_try_charge() for > the calling task's cgroup. Writing to free with no argument > releases the outstanding charge via dmem_cgroup_uncharge(). > Only a single outstanding charge is supported. > > This provides a deterministic, driver-independent mechanism > for exercising dmem accounting paths in selftests. > > Signed-off-by: Albert Esteve <aesteve@redhat.com> Tested-by: R Nageswara Sastry <rnsastry@linux.ibm.com> System: ppc64le LPAR (IBM POWER), Linux 7.3-rc2 # insmod test_modules/dmem_selftest.ko # ./test_dmem TAP version 13 1..2 ok 1 test_dmem_max ok 2 test_dmem_alloc_byte_granularity # Totals: pass:2 fail:0 xfail:0 xpass:0 skip:0 error:0 > --- > tools/testing/selftests/cgroup/Makefile | 1 + > tools/testing/selftests/cgroup/config | 1 + > .../testing/selftests/cgroup/test_modules/Makefile | 17 +++ > .../selftests/cgroup/test_modules/dmem_selftest.c | 146 +++++++++++++++++++++ > 4 files changed, 165 insertions(+) > > diff --git a/tools/testing/selftests/cgroup/Makefile b/tools/testing/selftests/cgroup/Makefile > index e01584c2189a..a08661320a07 100644 > --- a/tools/testing/selftests/cgroup/Makefile > +++ b/tools/testing/selftests/cgroup/Makefile > @@ -6,6 +6,7 @@ all: ${HELPER_PROGS} > TEST_FILES := with_stress.sh > TEST_PROGS := test_stress.sh test_cpuset_prs.sh test_cpuset_v1_hp.sh > TEST_GEN_FILES := wait_inotify > +TEST_GEN_MODS_DIR := test_modules > # Keep the lists lexicographically sorted > TEST_GEN_PROGS = test_core > TEST_GEN_PROGS += test_cpu > diff --git a/tools/testing/selftests/cgroup/config b/tools/testing/selftests/cgroup/config > index 39f979690dd3..2ee0488c3d65 100644 > --- a/tools/testing/selftests/cgroup/config > +++ b/tools/testing/selftests/cgroup/config > @@ -1,5 +1,6 @@ > CONFIG_CGROUPS=y > CONFIG_CGROUP_CPUACCT=y > +CONFIG_CGROUP_DMEM=y > CONFIG_CGROUP_FREEZER=y > CONFIG_CGROUP_SCHED=y > CONFIG_MEMCG=y > diff --git a/tools/testing/selftests/cgroup/test_modules/Makefile b/tools/testing/selftests/cgroup/test_modules/Makefile > new file mode 100644 > index 000000000000..dd8a17313cec > --- /dev/null > +++ b/tools/testing/selftests/cgroup/test_modules/Makefile > @@ -0,0 +1,17 @@ > +# SPDX-License-Identifier: GPL-2.0 > +TESTMODS_DIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST))))) > +KDIR ?= /lib/modules/$(shell uname -r)/build > + > +obj-m += dmem_selftest.o > + > +# Ensure that KDIR exists, otherwise skip the compilation > +modules: > +ifneq ("$(wildcard $(KDIR))", "") > + $(Q)$(MAKE) -C $(KDIR) modules KBUILD_EXTMOD=$(TESTMODS_DIR) > +endif > + > +# Ensure that KDIR exists, otherwise skip the clean target > +clean: > +ifneq ("$(wildcard $(KDIR))", "") > + $(Q)$(MAKE) -C $(KDIR) clean KBUILD_EXTMOD=$(TESTMODS_DIR) > +endif > diff --git a/tools/testing/selftests/cgroup/test_modules/dmem_selftest.c b/tools/testing/selftests/cgroup/test_modules/dmem_selftest.c > new file mode 100644 > index 000000000000..a763a156f13f > --- /dev/null > +++ b/tools/testing/selftests/cgroup/test_modules/dmem_selftest.c > @@ -0,0 +1,146 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Kselftest helper for the dmem cgroup controller. > + * > + * Registers a synthetic dmem region so tests can trigger allocations > + * from the calling task's cgroup via module parameters: > + * /sys/module/dmem_selftest/parameters/alloc > + * /sys/module/dmem_selftest/parameters/free > + */ > + > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > + > +#include <linux/cgroup_dmem.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/moduleparam.h> > +#include <linux/mutex.h> > +#include <linux/panic.h> > +#include <linux/sysfs.h> > + > +#define DM_SELFTEST_REGION_NAME "dmem_selftest" > +#define DM_SELFTEST_REGION_SIZE (256ULL * 1024 * 1024) > + > +static struct dmem_cgroup_region *selftest_region; > +static struct dmem_cgroup_pool_state *alloc_pool; > +static u64 alloc_size; > +static DEFINE_MUTEX(alloc_lock); > + > +static int param_set_alloc(const char *val, const struct kernel_param *kp) > +{ > + struct dmem_cgroup_pool_state *pool = NULL, *limit = NULL; > + u64 size; > + int ret; > + > + if (!selftest_region) > + return -ENODEV; > + > + ret = kstrtou64(val, 0, &size); > + if (ret) > + return ret; > + if (!size || size > DM_SELFTEST_REGION_SIZE) > + return -EINVAL; > + > + mutex_lock(&alloc_lock); > + if (alloc_pool) { > + mutex_unlock(&alloc_lock); > + return -EBUSY; > + } > + > + ret = dmem_cgroup_try_charge(selftest_region, size, &pool, &limit); > + if (ret == -EAGAIN && limit) > + dmem_cgroup_pool_state_put(limit); > + if (ret) { > + mutex_unlock(&alloc_lock); > + return ret; > + } > + > + alloc_pool = pool; > + alloc_size = size; > + mutex_unlock(&alloc_lock); > + return 0; > +} > + > +static int param_get_alloc(char *buffer, const struct kernel_param *kp) > +{ > + u64 size; > + > + mutex_lock(&alloc_lock); > + size = alloc_size; > + mutex_unlock(&alloc_lock); > + return sysfs_emit(buffer, "%llu\n", size); > +} > + > +static const struct kernel_param_ops alloc_ops = { > + .set = param_set_alloc, > + .get = param_get_alloc, > +}; > + > +module_param_cb(alloc, &alloc_ops, NULL, 0644); > +MODULE_PARM_DESC(alloc, "Allocate (charge) SIZE bytes against the calling task's dmem cgroup"); > + > +static int param_set_free(const char *val, const struct kernel_param *kp) > +{ > + mutex_lock(&alloc_lock); > + if (!alloc_pool) { > + mutex_unlock(&alloc_lock); > + return -EINVAL; > + } > + > + dmem_cgroup_uncharge(alloc_pool, alloc_size); > + alloc_pool = NULL; > + alloc_size = 0; > + mutex_unlock(&alloc_lock); > + return 0; > +} > + > +static const struct kernel_param_ops free_ops = { > + .flags = KERNEL_PARAM_OPS_FL_NOARG, > + .set = param_set_free, > +}; > + > +module_param_cb(free, &free_ops, NULL, 0200); > +MODULE_PARM_DESC(free, "Free the outstanding dmem selftest allocation"); > + > +static int __init dmem_selftest_init(void) > +{ > + static const struct dmem_cgroup_init init = { > + .size = DM_SELFTEST_REGION_SIZE, > + }; > + > + selftest_region = dmem_cgroup_register_region(&init, DM_SELFTEST_REGION_NAME); > + if (IS_ERR(selftest_region)) > + return PTR_ERR(selftest_region); > + if (!selftest_region) > + return -EINVAL; > + > + add_taint(TAINT_TEST, LOCKDEP_STILL_OK); > + pr_info("region '%s' registered; parameters alloc/free\n", > + DM_SELFTEST_REGION_NAME); > + return 0; > +} > + > +static void __exit dmem_selftest_exit(void) > +{ > + mutex_lock(&alloc_lock); > + if (alloc_pool) { > + dmem_cgroup_uncharge(alloc_pool, alloc_size); > + alloc_pool = NULL; > + alloc_size = 0; > + } > + mutex_unlock(&alloc_lock); > + > + if (selftest_region) { > + dmem_cgroup_unregister_region(selftest_region); > + selftest_region = NULL; > + } > + pr_info("unloaded.\n"); > +} > + > +module_init(dmem_selftest_init); > +module_exit(dmem_selftest_exit); > + > +MODULE_AUTHOR("Albert Esteve <aesteve@redhat.com>"); > +MODULE_DESCRIPTION("Kselftest helper for cgroup dmem controller"); > +MODULE_LICENSE("GPL"); > +MODULE_INFO(test, "Y"); > -- Thanks and Regards R.Nageswara Sastry ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v6 2/3] selftests: cgroup: Add KB() helper 2026-08-31 13:25 [PATCH v6 0/3] cgroup: dmem: add selftest helper and coverage Albert Esteve 2026-08-31 13:25 ` [PATCH v6 1/3] selftests: cgroup: Add dmem_selftest helper module Albert Esteve @ 2026-08-31 13:25 ` Albert Esteve 2026-09-10 6:50 ` R Nageswara Sastry 2026-08-31 13:25 ` [PATCH v6 3/3] selftests: cgroup: Add dmem selftest coverage Albert Esteve 2 siblings, 1 reply; 7+ messages in thread From: Albert Esteve @ 2026-08-31 13:25 UTC (permalink / raw) To: Tejun Heo, Johannes Weiner, Michal Koutný, Shuah Khan Cc: linux-kernel, cgroups, linux-kselftest, Albert Esteve cgroup_util.h already has MB() and GB() for size literals. Add KB() in the same style so tests can spell kilobyte values without in-code shifts. Signed-off-by: Albert Esteve <aesteve@redhat.com> --- tools/testing/selftests/cgroup/lib/include/cgroup_util.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/cgroup/lib/include/cgroup_util.h b/tools/testing/selftests/cgroup/lib/include/cgroup_util.h index 5d39c709ac7a..0e5d7b87ad19 100644 --- a/tools/testing/selftests/cgroup/lib/include/cgroup_util.h +++ b/tools/testing/selftests/cgroup/lib/include/cgroup_util.h @@ -6,6 +6,7 @@ #define BUF_SIZE 4096 #endif +#define KB(x) (x << 10) #define MB(x) (x << 20) #define GB(x) ((unsigned long long)(x) << 30) -- 2.55.0 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v6 2/3] selftests: cgroup: Add KB() helper 2026-08-31 13:25 ` [PATCH v6 2/3] selftests: cgroup: Add KB() helper Albert Esteve @ 2026-09-10 6:50 ` R Nageswara Sastry 0 siblings, 0 replies; 7+ messages in thread From: R Nageswara Sastry @ 2026-09-10 6:50 UTC (permalink / raw) To: Albert Esteve, Tejun Heo, Johannes Weiner, Michal Koutný, Shuah Khan Cc: linux-kernel, cgroups, linux-kselftest On 31.08.2026 6:55 PM, Albert Esteve wrote: > cgroup_util.h already has MB() and GB() for size > literals. Add KB() in the same style so tests can > spell kilobyte values without in-code shifts. > > Signed-off-by: Albert Esteve <aesteve@redhat.com> Tested-by: R Nageswara Sastry <rnsastry@linux.ibm.com> System: ppc64le LPAR (IBM POWER), Linux 7.3-rc2 # insmod test_modules/dmem_selftest.ko # ./test_dmem TAP version 13 1..2 ok 1 test_dmem_max ok 2 test_dmem_alloc_byte_granularity # Totals: pass:2 fail:0 xfail:0 xpass:0 skip:0 error:0 > --- > tools/testing/selftests/cgroup/lib/include/cgroup_util.h | 1 + > 1 file changed, 1 insertion(+) > > diff --git a/tools/testing/selftests/cgroup/lib/include/cgroup_util.h b/tools/testing/selftests/cgroup/lib/include/cgroup_util.h > index 5d39c709ac7a..0e5d7b87ad19 100644 > --- a/tools/testing/selftests/cgroup/lib/include/cgroup_util.h > +++ b/tools/testing/selftests/cgroup/lib/include/cgroup_util.h > @@ -6,6 +6,7 @@ > #define BUF_SIZE 4096 > #endif > > +#define KB(x) (x << 10) > #define MB(x) (x << 20) > #define GB(x) ((unsigned long long)(x) << 30) > > -- Thanks and Regards R.Nageswara Sastry ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v6 3/3] selftests: cgroup: Add dmem selftest coverage 2026-08-31 13:25 [PATCH v6 0/3] cgroup: dmem: add selftest helper and coverage Albert Esteve 2026-08-31 13:25 ` [PATCH v6 1/3] selftests: cgroup: Add dmem_selftest helper module Albert Esteve 2026-08-31 13:25 ` [PATCH v6 2/3] selftests: cgroup: Add KB() helper Albert Esteve @ 2026-08-31 13:25 ` Albert Esteve 2026-09-10 6:50 ` R Nageswara Sastry 2 siblings, 1 reply; 7+ messages in thread From: Albert Esteve @ 2026-08-31 13:25 UTC (permalink / raw) To: Tejun Heo, Johannes Weiner, Michal Koutný, Shuah Khan Cc: linux-kernel, cgroups, linux-kselftest, Albert Esteve Currently, tools/testing/selftests/cgroup/ has no dmem-specific test. This leaves dmem accounting and dmem.max enforcement largely unvalidated without a production driver. Add test_dmem, driven by the dmem_selftest helper module parameters (alloc/free) on the dmem_selftest region. insmod the helper before running the tests. The tests cover: - test_dmem_max: nested hierarchy with per-leaf dmem.max values and verifies that over-limit charges fail while in-limit charges succeed in dmem.current. - test_dmem_alloc_byte_granularity: non-page-aligned alloc sizes, dmem.current equals the requested byte count, and free-to-zero behavior. Limits are read with cg_read_key_long(). The helper is stat'd in main() so a missing module skips the whole test instead of each subtest. Signed-off-by: Albert Esteve <aesteve@redhat.com> --- tools/testing/selftests/cgroup/.gitignore | 1 + tools/testing/selftests/cgroup/Makefile | 2 + tools/testing/selftests/cgroup/test_dmem.c | 314 +++++++++++++++++++++++++++++ 3 files changed, 317 insertions(+) diff --git a/tools/testing/selftests/cgroup/.gitignore b/tools/testing/selftests/cgroup/.gitignore index 952e4448bf07..ea2322598217 100644 --- a/tools/testing/selftests/cgroup/.gitignore +++ b/tools/testing/selftests/cgroup/.gitignore @@ -2,6 +2,7 @@ test_core test_cpu test_cpuset +test_dmem test_freezer test_hugetlb_memcg test_kill diff --git a/tools/testing/selftests/cgroup/Makefile b/tools/testing/selftests/cgroup/Makefile index a08661320a07..d01e8eb1ead2 100644 --- a/tools/testing/selftests/cgroup/Makefile +++ b/tools/testing/selftests/cgroup/Makefile @@ -11,6 +11,7 @@ TEST_GEN_MODS_DIR := test_modules TEST_GEN_PROGS = test_core TEST_GEN_PROGS += test_cpu TEST_GEN_PROGS += test_cpuset +TEST_GEN_PROGS += test_dmem TEST_GEN_PROGS += test_freezer TEST_GEN_PROGS += test_hugetlb_memcg TEST_GEN_PROGS += test_kill @@ -27,6 +28,7 @@ include lib/libcgroup.mk $(OUTPUT)/test_core: $(LIBCGROUP_O) $(OUTPUT)/test_cpu: $(LIBCGROUP_O) $(OUTPUT)/test_cpuset: $(LIBCGROUP_O) +$(OUTPUT)/test_dmem: $(LIBCGROUP_O) $(OUTPUT)/test_freezer: $(LIBCGROUP_O) $(OUTPUT)/test_hugetlb_memcg: $(LIBCGROUP_O) $(OUTPUT)/test_kill: $(LIBCGROUP_O) diff --git a/tools/testing/selftests/cgroup/test_dmem.c b/tools/testing/selftests/cgroup/test_dmem.c new file mode 100644 index 000000000000..3793664678b9 --- /dev/null +++ b/tools/testing/selftests/cgroup/test_dmem.c @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test the dmem (device memory) cgroup controller. + * + * Depends on the dmem_selftest helper module + * (tools/testing/selftests/cgroup/test_modules/dmem_selftest.c). + */ + +#define _GNU_SOURCE + +#include <linux/limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "kselftest.h" +#include "cgroup_util.h" + +#define DM_SELFTEST_REGION "dmem_selftest" +#define DM_SELFTEST_ALLOC "/sys/module/dmem_selftest/parameters/alloc" +#define DM_SELFTEST_FREE "/sys/module/dmem_selftest/parameters/free" + +static long dmem_read_limit(const char *cgroup, const char *ctrl) +{ + return cg_read_key_long(cgroup, ctrl, DM_SELFTEST_REGION " "); +} + +static int dmem_write_limit(const char *cgroup, const char *ctrl, long val) +{ + char wr[64]; + + snprintf(wr, sizeof(wr), "%s %ld", DM_SELFTEST_REGION, val); + return cg_write(cgroup, ctrl, wr); +} + +static int dmem_selftest_alloc(long bytes) +{ + char wr[32]; + + snprintf(wr, sizeof(wr), "%ld", bytes); + return write_text(DM_SELFTEST_ALLOC, wr, strlen(wr)); +} + +static int dmem_selftest_free(void) +{ + char wr[] = "1"; + + return write_text(DM_SELFTEST_FREE, wr, strlen(wr)); +} + +/* + * First, this test creates the following hierarchy: + * A + * A/B dmem.max=8M + * A/B/C dmem.max=2M + * A/B/D dmem.max=1M + * A/B/E dmem.max=75K + * A/B/F dmem.max=25K + * A/B/G dmem.max=8K + * A/B/H dmem.max=0 + * + * Then for each leaf cgroup it tries to alloc above dmem.max + * and expects the request to fail and dmem.current to remain + * unchanged. + * + * For leaves with non-zero dmem.max, it additionally allocs a + * smaller amount and verifies dmem.current matches the requested + * size exactly (the controller accounts in bytes, with no page + * rounding), then frees and verifies dmem.current returns + * to the previous value. + */ +static int test_dmem_max(const char *root) +{ + static const long leaf_max[] = { + MB(2), MB(1), KB(75), KB(25), KB(8), 0 + }; + static const long pass_sz[] = { + MB(1), MB(1), KB(4), KB(4), KB(4), 0 + }; + char *parent[2] = {NULL}; + char *children[ARRAY_SIZE(leaf_max)] = {NULL}; + + _Static_assert(ARRAY_SIZE(pass_sz) == ARRAY_SIZE(leaf_max), + "pass_sz doesn't match leaf_max length"); + long cur_before, cur_after; + int ret = KSFT_FAIL; + int charged = 0; + int in_child = 0; + long v; + int i; + + parent[0] = cg_name(root, "dmem_prot_0"); + if (!parent[0]) + goto cleanup; + + parent[1] = cg_name(parent[0], "dmem_prot_1"); + if (!parent[1]) + goto cleanup; + + if (cg_create(parent[0])) + goto cleanup; + + if (cg_write(parent[0], "cgroup.subtree_control", "+dmem")) + goto cleanup; + + if (cg_create(parent[1])) + goto cleanup; + + if (cg_write(parent[1], "cgroup.subtree_control", "+dmem")) + goto cleanup; + + for (i = 0; i < ARRAY_SIZE(children); i++) { + children[i] = cg_name_indexed(parent[1], "dmem_child", i); + if (!children[i]) + goto cleanup; + if (cg_create(children[i])) + goto cleanup; + } + + if (dmem_write_limit(parent[1], "dmem.max", MB(8))) + goto cleanup; + for (i = 0; i < ARRAY_SIZE(children); i++) + if (dmem_write_limit(children[i], "dmem.max", leaf_max[i])) + goto cleanup; + + v = dmem_read_limit(parent[1], "dmem.max"); + if (v != MB(8)) + goto cleanup; + for (i = 0; i < ARRAY_SIZE(children); i++) { + v = dmem_read_limit(children[i], "dmem.max"); + if (v != leaf_max[i]) + goto cleanup; + } + + for (i = 0; i < ARRAY_SIZE(children); i++) { + if (cg_enter_current(children[i])) + goto cleanup; + in_child = 1; + + cur_before = dmem_read_limit(children[i], "dmem.current"); + if (cur_before < 0) + goto cleanup; + + if (dmem_selftest_alloc(leaf_max[i] + 1) >= 0) { + charged = 1; + goto cleanup; + } + + cur_after = dmem_read_limit(children[i], "dmem.current"); + if (cur_after != cur_before) + goto cleanup; + + if (pass_sz[i] > 0) { + if (dmem_selftest_alloc(pass_sz[i]) < 0) + goto cleanup; + charged = 1; + + cur_after = dmem_read_limit(children[i], "dmem.current"); + if (cur_after != cur_before + pass_sz[i]) + goto cleanup; + + if (dmem_selftest_free() < 0) + goto cleanup; + charged = 0; + + cur_after = dmem_read_limit(children[i], "dmem.current"); + if (cur_after != cur_before) + goto cleanup; + } + + if (cg_enter_current(root)) + goto cleanup; + in_child = 0; + } + + ret = KSFT_PASS; + +cleanup: + if (charged) + dmem_selftest_free(); + if (in_child) + cg_enter_current(root); + for (i = ARRAY_SIZE(children) - 1; i >= 0; i--) { + if (!children[i]) + continue; + cg_destroy(children[i]); + free(children[i]); + } + for (i = ARRAY_SIZE(parent) - 1; i >= 0; i--) { + if (!parent[i]) + continue; + cg_destroy(parent[i]); + free(parent[i]); + } + return ret; +} + +/* + * Alloc non-page-aligned byte sizes and verify dmem.current matches + * the requested size exactly. The controller charges the byte count + * passed to dmem_cgroup_try_charge() with no page rounding, for this + * helper and for production drivers. Then free must return usage to 0. + */ +static int test_dmem_alloc_byte_granularity(const char *root) +{ + static const long sizes[] = { + 1, 4095, 4097, KB(75) + 1, MB(1), MB(1) + 1 + }; + char *cg = NULL; + long cur; + int ret = KSFT_FAIL; + int charged = 0; + int in_child = 0; + size_t i; + + cg = cg_name(root, "dmem_dbg_byte_gran"); + if (!cg) + goto cleanup; + + if (cg_create(cg)) + goto cleanup; + + if (dmem_write_limit(cg, "dmem.max", MB(16))) + goto cleanup; + + if (cg_enter_current(cg)) + goto cleanup; + in_child = 1; + + for (i = 0; i < ARRAY_SIZE(sizes); i++) { + if (dmem_selftest_alloc(sizes[i]) < 0) + goto cleanup; + charged = 1; + + cur = dmem_read_limit(cg, "dmem.current"); + if (cur != sizes[i]) + goto cleanup; + + if (dmem_selftest_free() < 0) + goto cleanup; + charged = 0; + + cur = dmem_read_limit(cg, "dmem.current"); + if (cur != 0) + goto cleanup; + } + + ret = KSFT_PASS; + +cleanup: + if (charged) + dmem_selftest_free(); + if (in_child) + cg_enter_current(root); + if (cg) { + cg_destroy(cg); + free(cg); + } + return ret; +} + +#define T(x) { x, #x } +struct dmem_test { + int (*fn)(const char *root); + const char *name; +} tests[] = { + T(test_dmem_max), + T(test_dmem_alloc_byte_granularity), +}; +#undef T + +int main(int argc, char **argv) +{ + char root[PATH_MAX]; + struct stat st; + int i; + + ksft_print_header(); + + if (cg_find_unified_root(root, sizeof(root), NULL)) + ksft_exit_skip("cgroup v2 isn't mounted\n"); + + if (cg_read_strstr(root, "cgroup.controllers", "dmem")) + ksft_exit_skip("dmem controller isn't available (CONFIG_CGROUP_DMEM?)\n"); + + if (cg_read_strstr(root, "cgroup.subtree_control", "dmem")) + if (cg_write(root, "cgroup.subtree_control", "+dmem")) + ksft_exit_skip("Failed to enable dmem controller\n"); + + if (stat(DM_SELFTEST_ALLOC, &st) < 0) + ksft_exit_skip( + "dmem_selftest helper not loaded (insmod test_modules/dmem_selftest.ko)\n"); + + if (dmem_read_limit(root, "dmem.capacity") < 0) + ksft_exit_skip("dmem_selftest region not registered\n"); + + ksft_set_plan(ARRAY_SIZE(tests)); + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + switch (tests[i].fn(root)) { + case KSFT_PASS: + ksft_test_result_pass("%s\n", tests[i].name); + break; + default: + ksft_test_result_fail("%s\n", tests[i].name); + break; + } + } + + ksft_finished(); +} -- 2.55.0 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v6 3/3] selftests: cgroup: Add dmem selftest coverage 2026-08-31 13:25 ` [PATCH v6 3/3] selftests: cgroup: Add dmem selftest coverage Albert Esteve @ 2026-09-10 6:50 ` R Nageswara Sastry 0 siblings, 0 replies; 7+ messages in thread From: R Nageswara Sastry @ 2026-09-10 6:50 UTC (permalink / raw) To: Albert Esteve, Tejun Heo, Johannes Weiner, Michal Koutný, Shuah Khan Cc: linux-kernel, cgroups, linux-kselftest On 31.08.2026 6:55 PM, Albert Esteve wrote: > Currently, tools/testing/selftests/cgroup/ has no > dmem-specific test. This leaves dmem accounting and > dmem.max enforcement largely unvalidated without a > production driver. > > Add test_dmem, driven by the dmem_selftest helper module > parameters (alloc/free) on the dmem_selftest region. > insmod the helper before running the tests. > > The tests cover: > - test_dmem_max: nested hierarchy with per-leaf dmem.max > values and verifies that over-limit charges fail while > in-limit charges succeed in dmem.current. > - test_dmem_alloc_byte_granularity: non-page-aligned alloc > sizes, dmem.current equals the requested byte count, and > free-to-zero behavior. > > Limits are read with cg_read_key_long(). The helper is stat'd > in main() so a missing module skips the whole test instead > of each subtest. > > Signed-off-by: Albert Esteve <aesteve@redhat.com> Tested-by: R Nageswara Sastry <rnsastry@linux.ibm.com> System: ppc64le LPAR (IBM POWER), Linux 7.3-rc2 # insmod test_modules/dmem_selftest.ko # ./test_dmem TAP version 13 1..2 ok 1 test_dmem_max ok 2 test_dmem_alloc_byte_granularity # Totals: pass:2 fail:0 xfail:0 xpass:0 skip:0 error:0 > --- > tools/testing/selftests/cgroup/.gitignore | 1 + > tools/testing/selftests/cgroup/Makefile | 2 + > tools/testing/selftests/cgroup/test_dmem.c | 314 +++++++++++++++++++++++++++++ > 3 files changed, 317 insertions(+) > > diff --git a/tools/testing/selftests/cgroup/.gitignore b/tools/testing/selftests/cgroup/.gitignore > index 952e4448bf07..ea2322598217 100644 > --- a/tools/testing/selftests/cgroup/.gitignore > +++ b/tools/testing/selftests/cgroup/.gitignore > @@ -2,6 +2,7 @@ > test_core > test_cpu > test_cpuset > +test_dmem > test_freezer > test_hugetlb_memcg > test_kill > diff --git a/tools/testing/selftests/cgroup/Makefile b/tools/testing/selftests/cgroup/Makefile > index a08661320a07..d01e8eb1ead2 100644 > --- a/tools/testing/selftests/cgroup/Makefile > +++ b/tools/testing/selftests/cgroup/Makefile > @@ -11,6 +11,7 @@ TEST_GEN_MODS_DIR := test_modules > TEST_GEN_PROGS = test_core > TEST_GEN_PROGS += test_cpu > TEST_GEN_PROGS += test_cpuset > +TEST_GEN_PROGS += test_dmem > TEST_GEN_PROGS += test_freezer > TEST_GEN_PROGS += test_hugetlb_memcg > TEST_GEN_PROGS += test_kill > @@ -27,6 +28,7 @@ include lib/libcgroup.mk > $(OUTPUT)/test_core: $(LIBCGROUP_O) > $(OUTPUT)/test_cpu: $(LIBCGROUP_O) > $(OUTPUT)/test_cpuset: $(LIBCGROUP_O) > +$(OUTPUT)/test_dmem: $(LIBCGROUP_O) > $(OUTPUT)/test_freezer: $(LIBCGROUP_O) > $(OUTPUT)/test_hugetlb_memcg: $(LIBCGROUP_O) > $(OUTPUT)/test_kill: $(LIBCGROUP_O) > diff --git a/tools/testing/selftests/cgroup/test_dmem.c b/tools/testing/selftests/cgroup/test_dmem.c > new file mode 100644 > index 000000000000..3793664678b9 > --- /dev/null > +++ b/tools/testing/selftests/cgroup/test_dmem.c > @@ -0,0 +1,314 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Test the dmem (device memory) cgroup controller. > + * > + * Depends on the dmem_selftest helper module > + * (tools/testing/selftests/cgroup/test_modules/dmem_selftest.c). > + */ > + > +#define _GNU_SOURCE > + > +#include <linux/limits.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <sys/stat.h> > +#include <sys/types.h> > +#include <unistd.h> > + > +#include "kselftest.h" > +#include "cgroup_util.h" > + > +#define DM_SELFTEST_REGION "dmem_selftest" > +#define DM_SELFTEST_ALLOC "/sys/module/dmem_selftest/parameters/alloc" > +#define DM_SELFTEST_FREE "/sys/module/dmem_selftest/parameters/free" > + > +static long dmem_read_limit(const char *cgroup, const char *ctrl) > +{ > + return cg_read_key_long(cgroup, ctrl, DM_SELFTEST_REGION " "); > +} > + > +static int dmem_write_limit(const char *cgroup, const char *ctrl, long val) > +{ > + char wr[64]; > + > + snprintf(wr, sizeof(wr), "%s %ld", DM_SELFTEST_REGION, val); > + return cg_write(cgroup, ctrl, wr); > +} > + > +static int dmem_selftest_alloc(long bytes) > +{ > + char wr[32]; > + > + snprintf(wr, sizeof(wr), "%ld", bytes); > + return write_text(DM_SELFTEST_ALLOC, wr, strlen(wr)); > +} > + > +static int dmem_selftest_free(void) > +{ > + char wr[] = "1"; > + > + return write_text(DM_SELFTEST_FREE, wr, strlen(wr)); > +} > + > +/* > + * First, this test creates the following hierarchy: > + * A > + * A/B dmem.max=8M > + * A/B/C dmem.max=2M > + * A/B/D dmem.max=1M > + * A/B/E dmem.max=75K > + * A/B/F dmem.max=25K > + * A/B/G dmem.max=8K > + * A/B/H dmem.max=0 > + * > + * Then for each leaf cgroup it tries to alloc above dmem.max > + * and expects the request to fail and dmem.current to remain > + * unchanged. > + * > + * For leaves with non-zero dmem.max, it additionally allocs a > + * smaller amount and verifies dmem.current matches the requested > + * size exactly (the controller accounts in bytes, with no page > + * rounding), then frees and verifies dmem.current returns > + * to the previous value. > + */ > +static int test_dmem_max(const char *root) > +{ > + static const long leaf_max[] = { > + MB(2), MB(1), KB(75), KB(25), KB(8), 0 > + }; > + static const long pass_sz[] = { > + MB(1), MB(1), KB(4), KB(4), KB(4), 0 > + }; > + char *parent[2] = {NULL}; > + char *children[ARRAY_SIZE(leaf_max)] = {NULL}; > + > + _Static_assert(ARRAY_SIZE(pass_sz) == ARRAY_SIZE(leaf_max), > + "pass_sz doesn't match leaf_max length"); > + long cur_before, cur_after; > + int ret = KSFT_FAIL; > + int charged = 0; > + int in_child = 0; > + long v; > + int i; > + > + parent[0] = cg_name(root, "dmem_prot_0"); > + if (!parent[0]) > + goto cleanup; > + > + parent[1] = cg_name(parent[0], "dmem_prot_1"); > + if (!parent[1]) > + goto cleanup; > + > + if (cg_create(parent[0])) > + goto cleanup; > + > + if (cg_write(parent[0], "cgroup.subtree_control", "+dmem")) > + goto cleanup; > + > + if (cg_create(parent[1])) > + goto cleanup; > + > + if (cg_write(parent[1], "cgroup.subtree_control", "+dmem")) > + goto cleanup; > + > + for (i = 0; i < ARRAY_SIZE(children); i++) { > + children[i] = cg_name_indexed(parent[1], "dmem_child", i); > + if (!children[i]) > + goto cleanup; > + if (cg_create(children[i])) > + goto cleanup; > + } > + > + if (dmem_write_limit(parent[1], "dmem.max", MB(8))) > + goto cleanup; > + for (i = 0; i < ARRAY_SIZE(children); i++) > + if (dmem_write_limit(children[i], "dmem.max", leaf_max[i])) > + goto cleanup; > + > + v = dmem_read_limit(parent[1], "dmem.max"); > + if (v != MB(8)) > + goto cleanup; > + for (i = 0; i < ARRAY_SIZE(children); i++) { > + v = dmem_read_limit(children[i], "dmem.max"); > + if (v != leaf_max[i]) > + goto cleanup; > + } > + > + for (i = 0; i < ARRAY_SIZE(children); i++) { > + if (cg_enter_current(children[i])) > + goto cleanup; > + in_child = 1; > + > + cur_before = dmem_read_limit(children[i], "dmem.current"); > + if (cur_before < 0) > + goto cleanup; > + > + if (dmem_selftest_alloc(leaf_max[i] + 1) >= 0) { > + charged = 1; > + goto cleanup; > + } > + > + cur_after = dmem_read_limit(children[i], "dmem.current"); > + if (cur_after != cur_before) > + goto cleanup; > + > + if (pass_sz[i] > 0) { > + if (dmem_selftest_alloc(pass_sz[i]) < 0) > + goto cleanup; > + charged = 1; > + > + cur_after = dmem_read_limit(children[i], "dmem.current"); > + if (cur_after != cur_before + pass_sz[i]) > + goto cleanup; > + > + if (dmem_selftest_free() < 0) > + goto cleanup; > + charged = 0; > + > + cur_after = dmem_read_limit(children[i], "dmem.current"); > + if (cur_after != cur_before) > + goto cleanup; > + } > + > + if (cg_enter_current(root)) > + goto cleanup; > + in_child = 0; > + } > + > + ret = KSFT_PASS; > + > +cleanup: > + if (charged) > + dmem_selftest_free(); > + if (in_child) > + cg_enter_current(root); > + for (i = ARRAY_SIZE(children) - 1; i >= 0; i--) { > + if (!children[i]) > + continue; > + cg_destroy(children[i]); > + free(children[i]); > + } > + for (i = ARRAY_SIZE(parent) - 1; i >= 0; i--) { > + if (!parent[i]) > + continue; > + cg_destroy(parent[i]); > + free(parent[i]); > + } > + return ret; > +} > + > +/* > + * Alloc non-page-aligned byte sizes and verify dmem.current matches > + * the requested size exactly. The controller charges the byte count > + * passed to dmem_cgroup_try_charge() with no page rounding, for this > + * helper and for production drivers. Then free must return usage to 0. > + */ > +static int test_dmem_alloc_byte_granularity(const char *root) > +{ > + static const long sizes[] = { > + 1, 4095, 4097, KB(75) + 1, MB(1), MB(1) + 1 > + }; > + char *cg = NULL; > + long cur; > + int ret = KSFT_FAIL; > + int charged = 0; > + int in_child = 0; > + size_t i; > + > + cg = cg_name(root, "dmem_dbg_byte_gran"); > + if (!cg) > + goto cleanup; > + > + if (cg_create(cg)) > + goto cleanup; > + > + if (dmem_write_limit(cg, "dmem.max", MB(16))) > + goto cleanup; > + > + if (cg_enter_current(cg)) > + goto cleanup; > + in_child = 1; > + > + for (i = 0; i < ARRAY_SIZE(sizes); i++) { > + if (dmem_selftest_alloc(sizes[i]) < 0) > + goto cleanup; > + charged = 1; > + > + cur = dmem_read_limit(cg, "dmem.current"); > + if (cur != sizes[i]) > + goto cleanup; > + > + if (dmem_selftest_free() < 0) > + goto cleanup; > + charged = 0; > + > + cur = dmem_read_limit(cg, "dmem.current"); > + if (cur != 0) > + goto cleanup; > + } > + > + ret = KSFT_PASS; > + > +cleanup: > + if (charged) > + dmem_selftest_free(); > + if (in_child) > + cg_enter_current(root); > + if (cg) { > + cg_destroy(cg); > + free(cg); > + } > + return ret; > +} > + > +#define T(x) { x, #x } > +struct dmem_test { > + int (*fn)(const char *root); > + const char *name; > +} tests[] = { > + T(test_dmem_max), > + T(test_dmem_alloc_byte_granularity), > +}; > +#undef T > + > +int main(int argc, char **argv) > +{ > + char root[PATH_MAX]; > + struct stat st; > + int i; > + > + ksft_print_header(); > + > + if (cg_find_unified_root(root, sizeof(root), NULL)) > + ksft_exit_skip("cgroup v2 isn't mounted\n"); > + > + if (cg_read_strstr(root, "cgroup.controllers", "dmem")) > + ksft_exit_skip("dmem controller isn't available (CONFIG_CGROUP_DMEM?)\n"); > + > + if (cg_read_strstr(root, "cgroup.subtree_control", "dmem")) > + if (cg_write(root, "cgroup.subtree_control", "+dmem")) > + ksft_exit_skip("Failed to enable dmem controller\n"); > + > + if (stat(DM_SELFTEST_ALLOC, &st) < 0) > + ksft_exit_skip( > + "dmem_selftest helper not loaded (insmod test_modules/dmem_selftest.ko)\n"); > + > + if (dmem_read_limit(root, "dmem.capacity") < 0) > + ksft_exit_skip("dmem_selftest region not registered\n"); > + > + ksft_set_plan(ARRAY_SIZE(tests)); > + > + for (i = 0; i < ARRAY_SIZE(tests); i++) { > + switch (tests[i].fn(root)) { > + case KSFT_PASS: > + ksft_test_result_pass("%s\n", tests[i].name); > + break; > + default: > + ksft_test_result_fail("%s\n", tests[i].name); > + break; > + } > + } > + > + ksft_finished(); > +} > -- Thanks and Regards R.Nageswara Sastry ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-09-10 6:50 UTC | newest] Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-08-31 13:25 [PATCH v6 0/3] cgroup: dmem: add selftest helper and coverage Albert Esteve 2026-08-31 13:25 ` [PATCH v6 1/3] selftests: cgroup: Add dmem_selftest helper module Albert Esteve 2026-09-10 6:49 ` R Nageswara Sastry 2026-08-31 13:25 ` [PATCH v6 2/3] selftests: cgroup: Add KB() helper Albert Esteve 2026-09-10 6:50 ` R Nageswara Sastry 2026-08-31 13:25 ` [PATCH v6 3/3] selftests: cgroup: Add dmem selftest coverage Albert Esteve 2026-09-10 6:50 ` R Nageswara Sastry
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®