[PATCH RFC v2 8/8] selftests/dmabuf-heaps: Add dma-buf memcg accounting tests
sashiko-bot at kernel.org
sashiko-bot at kernel.org
Thu Oct 22 15:44:25 UTC 2026
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Medium] selftests/dmabuf-heaps: missing skip check in test_xfer_charge_cgroupfd_self()
- [Medium] selftests/dmabuf-heaps: missing cgroup2 and memcg checks in open_self_cgroupfd()
- [Medium] selftests/dmabuf-heaps: vmtest.sh executes runner from unshared /tmp in guest
- [Medium] selftests/dmabuf-heaps: vmtest.sh runs vng/make before check_deps()
--
commit 0809a03b2fbc81e6f2b637ef5c0744b2c8ae99f1
Author: Albert Esteve <aesteve at redhat.com>
selftests/dmabuf-heaps: Add dma-buf memcg accounting tests
This commit adds selftests for dma-buf memcg accounting via charge_fd in
tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c and
tools/testing/selftests/cgroup/test_memcontrol.c, along with a virtme-ng
runner script in tools/testing/selftests/dmabuf-heaps/vmtest.sh.
> diff --git a/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c b/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c
> index 1d49df6719198..b1c58e5883f64 100644
> --- a/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c
> +++ b/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c
[ ... ]
> @@ -496,6 +505,213 @@ static void test_alloc_no_fd_leak_on_efault(char *heap_name)
[ ... ]
> +static int open_self_cgroupfd(void)
> +{
> + char cgroup_path[PATH_MAX];
> + char buf[PATH_MAX];
> + FILE *f;
> +
> + f = fopen("/proc/self/cgroup", "r");
> + if (!f)
> + return -1;
> +
> + cgroup_path[0] = '\0';
> + while (fgets(buf, sizeof(buf), f)) {
> + char *p = buf;
> +
> + if (strncmp(p, "0::", 3) == 0) {
> + p += 3;
> + p[strcspn(p, "\n")] = '\0';
> + snprintf(cgroup_path, sizeof(cgroup_path),
> + "/sys/fs/cgroup%s", p);
> + break;
> + }
> + }
> + fclose(f);
> +
> + if (!cgroup_path[0])
> + return -1;
> +
> + return open(cgroup_path, O_RDONLY | O_DIRECTORY);
> +}
> +
> +/*
> + * Test: allocate charging the calling process's own cgroup via a cgroupfd.
> + */
> +static void test_alloc_cgroupfd_self(char *heap_name)
> +{
> + int heap_fd = -1, cgroup_fd = -1, dmabuf_fd = -1, ret;
> +
> + heap_fd = dmabuf_heap_open(heap_name);
> +
> + if (!pidfd_alloc_supported(heap_fd)) {
> + ksft_test_result_skip("charge_fd not supported by this kernel\n");
> + goto out;
> + }
> +
> + cgroup_fd = open_self_cgroupfd();
> + if (cgroup_fd < 0) {
> + ksft_test_result_skip("cannot open self cgroupfd\n");
> + goto out;
> + }
> +
> + ret = dmabuf_heap_alloc_pidfd(heap_fd, ONE_MEG, 0, cgroup_fd, &dmabuf_fd);
> + ksft_test_result(!ret, "Allocation with cgroupfd %d\n", ret);
> + if (dmabuf_fd >= 0)
> + close(dmabuf_fd);
> + close(cgroup_fd);
> +out:
> + close(heap_fd);
> +}
[Severity: Medium]
Can open_self_cgroupfd() return a directory fd that causes
test_alloc_cgroupfd_self() and test_xfer_charge_cgroupfd_self() to report a
hard failure instead of skipping when cgroup v2 or the memory controller is
not active on that path?
In open_self_cgroupfd(), the 0:: entry from /proc/self/cgroup is appended to a
hardcoded /sys/fs/cgroup prefix without checking that /sys/fs/cgroup is a
cgroup2 mount. On systems using a hybrid hierarchy where /sys/fs/cgroup is a
tmpfs and unified cgroup v2 is mounted at /sys/fs/cgroup/unified, opening
/sys/fs/cgroup/ succeeds on tmpfs, and the kernel's cgroup_get_from_fd() then
rejects charge_fd with EBADF.
Furthermore, unlike the pidfd path in dma_heap_ioctl_allocate() and
dma_buf_xfer_charge() which resolves the effective memcg via
get_mem_cgroup_from_mm(task->mm), the cgroupfd path calls
cgroup_css(cgrp, &memory_cgrp_subsys) directly on the target cgroup and
returns ENOENT if the memory controller is not enabled for that specific
cgroup (for example, when the test runs in a non-root cgroup whose parent has
not enabled +memory in cgroup.subtree_control).
Should open_self_cgroupfd() verify that the resolved directory is on a
cgroup2 filesystem with the memory controller enabled, or should the callers
skip when the ioctl fails with ENOENT or EBADF?
> +
> +/*
> + * Test: transfer a dma-buf's charge to self cgroup via DMA_BUF_IOCTL_XFER_CHARGE.
> + */
> +static void test_xfer_charge_cgroupfd_self(char *heap_name)
> +{
> + int heap_fd = -1, cgroup_fd = -1, dmabuf_fd = -1, ret;
> + struct dma_buf_transfer_charge xfer = { 0 };
> +
> + heap_fd = dmabuf_heap_open(heap_name);
> +
> + ret = dmabuf_heap_alloc(heap_fd, ONE_MEG, 0, &dmabuf_fd);
> + if (ret) {
> + ksft_test_result_fail("Allocation failed %d\n", ret);
> + goto out;
> + }
> +
> + cgroup_fd = open_self_cgroupfd();
> + if (cgroup_fd < 0) {
> + ksft_test_result_skip("cannot open self cgroupfd\n");
> + close(dmabuf_fd);
> + goto out;
> + }
> +
> + xfer.charge_fd = cgroup_fd;
> + ret = ioctl(dmabuf_fd, DMA_BUF_IOCTL_XFER_CHARGE, &xfer);
> + ksft_test_result(!ret, "XFER_CHARGE to self cgroupfd %d\n", ret);
> +
> + close(cgroup_fd);
> + close(dmabuf_fd);
> out:
> close(heap_fd);
> }
[Severity: Medium]
Unlike test_alloc_pidfd_self(), test_alloc_pidfd_child(), and
test_alloc_cgroupfd_self(), test_xfer_charge_cgroupfd_self() does not call
pidfd_alloc_supported(heap_fd) or check for errno == ENOTTY when
ioctl(dmabuf_fd, DMA_BUF_IOCTL_XFER_CHARGE, &xfer) fails.
On an unpatched kernel where dmabuf_heap_alloc() and open_self_cgroupfd()
both succeed, won't ioctl(dmabuf_fd, DMA_BUF_IOCTL_XFER_CHARGE, &xfer) return
-1 with errno == ENOTTY and cause ksft_test_result(!ret, ...) to report a
failure instead of skipping?
[ ... ]
> diff --git a/tools/testing/selftests/dmabuf-heaps/vmtest.sh b/tools/testing/selftests/dmabuf-heaps/vmtest.sh
> new file mode 100755
> index 0000000000000..434857b765b84
> --- /dev/null
> +++ b/tools/testing/selftests/dmabuf-heaps/vmtest.sh
> @@ -0,0 +1,205 @@
[ ... ]
> +readonly DMABUF_HEAP_TEST="${SCRIPT_DIR}"/dmabuf-heap
> +readonly MEMCONTROL_TEST="${CGROUP_DIR}"/test_memcontrol
> +readonly TMP_DIR=$(mktemp -d /tmp/dmabuf-vmtest.XXXXXXXX)
[ ... ]
> +build_selftests() {
> + make -C "${KERNEL_CHECKOUT}" headers_install \
> + INSTALL_HDR_PATH="${TMP_DIR}/usr" -j"$(nproc)"
> +
> + local khdr="-isystem ${TMP_DIR}/usr/include"
> +
> + if ! make -C "${SCRIPT_DIR}" KHDR_INCLUDES="${khdr}" -j"$(nproc)"; then
> + die "failed to build dmabuf-heaps selftests"
> + fi
> +
> + if ! make -C "${CGROUP_DIR}" KHDR_INCLUDES="${khdr}" \
> + "${MEMCONTROL_TEST}" -j"$(nproc)"; then
> + die "failed to build cgroup/test_memcontrol selftest"
> + fi
> +}
[ ... ]
> +make_runner() {
> + # virtme-ng shares the host filesystem, so TMP_DIR is accessible
> + # inside the VM at the same absolute path.
> + cat > "${TMP_DIR}/run_tests.sh" <<-EOF
[ ... ]
> + EOF
> + chmod +x "${TMP_DIR}/run_tests.sh"
> +}
> +
> +run_vm() {
> + local verbose_opt=""
> + local kernel_opt=""
> +
> + ${VERBOSE} && verbose_opt="--verbose"
> +
> + # If we are running from within the kernel source tree, use the kernel
> + # source tree as the kernel to boot, otherwise use the running kernel.
> + if [[ "$(realpath "$(pwd)")" == "${KERNEL_CHECKOUT}"* ]]; then
> + kernel_opt="${KERNEL_CHECKOUT}"
> + fi
> +
> + vng --run ${kernel_opt} ${verbose_opt} --user root --memory 512M \
> + --exec "${TMP_DIR}/run_tests.sh"
> +}
[Severity: Medium]
Will "${TMP_DIR}/run_tests.sh" be visible inside the guest VM when TMP_DIR is
created under /tmp?
When virtme-ng boots the guest, virtme-init mounts a fresh tmpfs over /tmp
inside the VM, which masks the host's /tmp directory unless TMP_DIR is
explicitly shared via --rodir / --rwdir or created under a shared directory
such as SCRIPT_DIR. Won't vng --run --exec "${TMP_DIR}/run_tests.sh" fail in
the guest because /tmp/dmabuf-vmtest.XXXXXXXX/run_tests.sh does not exist
there?
> +
> +while getopts :hvbH:p: o
> +do
> + case $o in
> + v) VERBOSE=true;;
> + b) BUILD=true;;
> + H) BUILD_HOST=$OPTARG;;
> + p) BUILD_HOST_PODMAN_CONTAINER_NAME=$OPTARG;;
> + h|*) usage;;
> + esac
> +done
> +shift $((OPTIND-1))
> +
> +trap cleanup EXIT
> +
> +check_vng
> +handle_build
> +check_deps
> +make_runner
> +
> +echo "Booting VM and running tests..."
> +run_vm
[Severity: Medium]
Should check_deps() verify vng and make before check_vng() and handle_build()
are called?
Right now, if vng or make is not installed, check_vng() runs vng --version
first and prints a command-not-found error plus an untested-version warning,
and if -b is passed, handle_build() fails via die() with KSFT_FAIL instead of
exiting with KSFT_SKIP in check_deps().
Also, in handle_build(), the script uses pushd "${KERNEL_CHECKOUT}" and popd
to build the kernel, whereas run_vm() only sets kernel_opt="${KERNEL_CHECKOUT}"
when $(pwd) is inside KERNEL_CHECKOUT. If vmtest.sh -b is invoked from
outside KERNEL_CHECKOUT, won't run_vm() ignore the freshly built kernel and
boot the running host kernel instead? And in build_selftests(), should the
make headers_install command also be checked for failure before building the
tests?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261021-v2_20230123_tjmercier_google_com-v2-0-ef6ee2dd8c5c@redhat.com?part=8
More information about the Linux-security-module-archive
mailing list