[PATCH i-g-t v3 02/41] lib/vkms: Add minimal VKMS library and test device default files
Karthik B S
karthik.b.s at intel.com
Wed Jul 23 06:40:20 UTC 2025
Hi José,
On 7/21/2025 10:03 PM, Kamil Konieczny wrote:
> Hi José,
> On 2025-07-15 at 12:24:34 +0200, José Expósito wrote:
>> Create a library containing helpers for creating VKMS devices and
>> configuring them dynamically using configfs.
>> For the moment, add the minimal number of helpers to be able to start
>> testing VKMS's configfs support: Create device, destroy device and
>> destroy all devices.
>>
>> Also, include the simplest possible test using those helpers (checking
>> the device's default files) and the scaffolding required to generate
>> the documentation.
>>
>> Co-developed-by: Jim Shargo <jshargo at chromium.org>
>> Signed-off-by: Jim Shargo <jshargo at chromium.org>
>> Co-developed-by: Marius Vlad <marius.vlad at collabora.com>
>> Signed-off-by: Marius Vlad <marius.vlad at collabora.com>
>> Signed-off-by: José Expósito <jose.exposito89 at gmail.com>
>> ---
>> docs/testplan/meson.build | 7 +-
>> lib/igt_vkms.c | 207 +++++++++++++++++++++++++++++++
>> lib/igt_vkms.h | 27 ++++
>> lib/meson.build | 1 +
>> meson.build | 8 ++
>> tests/meson.build | 2 +
>> tests/vkms/meson.build | 13 ++
>> tests/vkms/vkms_configfs.c | 132 ++++++++++++++++++++
>> tests/vkms/vkms_test_config.json | 72 +++++++++++
>> 9 files changed, 467 insertions(+), 2 deletions(-)
>> create mode 100644 lib/igt_vkms.c
>> create mode 100644 lib/igt_vkms.h
>> create mode 100644 tests/vkms/meson.build
>> create mode 100644 tests/vkms/vkms_configfs.c
>> create mode 100644 tests/vkms/vkms_test_config.json
>>
>> diff --git a/docs/testplan/meson.build b/docs/testplan/meson.build
>> index 5560347f1..9e22f4c7d 100644
>> --- a/docs/testplan/meson.build
>> +++ b/docs/testplan/meson.build
>> @@ -11,6 +11,7 @@ stylesheet = join_paths(meson.current_source_dir(), 'testplan.css')
>> xe_test_config = join_paths(source_root, 'tests', 'intel', 'xe_test_config.json')
>> kms_test_config = join_paths(source_root, 'tests', 'intel', 'kms_test_config.json')
>> i915_test_config = join_paths(source_root, 'tests', 'intel', 'i915_test_config.json')
>> +vkms_test_config = join_paths(source_root, 'tests', 'vkms', 'vkms_test_config.json')
> I am not sure if we need them in a testplan here and below?
> +cc Karthik and Swati
Agree. This is not required as these are skipping on intel CI in any case.
Regards,
Karthik.B.S
>
>>
>> check_testlist = []
>> kms_check_testlist = []
>> @@ -37,12 +38,14 @@ if build_xe
>> test_dict = {
>> 'i915_tests': { 'input': i915_test_config, 'extra_args': check_testlist },
>> 'kms_tests': { 'input': kms_test_config, 'extra_args': kms_check_testlist },
>> - 'xe_tests': { 'input': xe_test_config, 'extra_args': check_testlist }
>> + 'xe_tests': { 'input': xe_test_config, 'extra_args': check_testlist },
>> + 'vkms_tests': { 'input': vkms_test_config, 'extra_args': check_testlist }
>> }
>> else
>> test_dict = {
>> 'i915_tests': { 'input': i915_test_config, 'extra_args': check_testlist },
>> - 'kms_tests': { 'input': kms_test_config, 'extra_args': kms_check_testlist }
>> + 'kms_tests': { 'input': kms_test_config, 'extra_args': kms_check_testlist },
>> + 'vkms_tests': { 'input': vkms_test_config, 'extra_args': check_testlist }
>> }
>> endif
>>
>> diff --git a/lib/igt_vkms.c b/lib/igt_vkms.c
>> new file mode 100644
>> index 000000000..fa41f741e
>> --- /dev/null
>> +++ b/lib/igt_vkms.c
>> @@ -0,0 +1,207 @@
>> +// SPDX-License-Identifier: MIT
>> +/*
>> + * Copyright © 2023 Google LLC.
>> + * Copyright © 2023 Collabora, Ltd.
>> + * Copyright © 2024 Red Hat, Inc.
> Should it also be 2025 here?
>
>> + */
>> +
>> +#include <dirent.h>
>> +#include <errno.h>
>> +#include <string.h>
>> +
>> +#include <ftw.h>
>> +#include <linux/limits.h>
> Why not <limits.h> ?
>
>> +#include <sys/stat.h>
> Why not <stat.h> ?
>
> Btw please keep all system libs sorted alphabetically.
> The same goes for other code below.
>
> Regards,
> Kamil
>
>> +
>> +#include "igt.h"
>> +#include "igt_vkms.h"
>> +
>> +#define VKMS_ROOT_DIR_NAME "vkms"
>> +
>> +/**
>> + * SECTION:igt_vkms
>> + * @short_description: Helpers to create and configure VKMS devices
>> + * @title: VKMS
>> + * @include: igt_vkms.h
>> + *
>> + * Helpers for creating VKMS devices and configuring them dynamically.
>> + *
>> + * First, create a VKMS device, next, add pipeline items (planes, CRTCs,
>> + * encoders, CRTCs and connectors) compose the pipeline by attaching each item
>> + * using the _attach_ functions and finally, enable the VKMS device.
>> + */
>> +
>> +static const char *mount_vkms_configfs(void)
>> +{
>> + static char vkms_root_path[PATH_MAX];
>> + const char *configfs_path;
>> + int ret;
>> +
>> + configfs_path = igt_configfs_mount();
>> + igt_assert_f(configfs_path, "Error mounting configfs");
>> +
>> + ret = snprintf(vkms_root_path, sizeof(vkms_root_path), "%s/%s",
>> + configfs_path, VKMS_ROOT_DIR_NAME);
>> + igt_assert(ret >= 0 && ret < sizeof(vkms_root_path));
>> +
>> + return vkms_root_path;
>> +}
>> +
>> +/**
>> + * igt_require_vkms_configfs:
>> + *
>> + * Require that VKMS supports configfs configuration.
>> + */
>> +void igt_require_vkms_configfs(void)
>> +{
>> + const char *vkms_root_path;
>> + DIR *dir;
>> +
>> + vkms_root_path = mount_vkms_configfs();
>> +
>> + dir = opendir(vkms_root_path);
>> + igt_require(dir);
>> + if (dir)
>> + closedir(dir);
>> +}
>> +
>> +/**
>> + * igt_vkms_device_create:
>> + * @name: VKMS device name
>> + *
>> + * Create a directory in the ConfigFS VKMS root directory, where the entire
>> + * pipeline will be configured.
>> + */
>> +igt_vkms_t *igt_vkms_device_create(const char *name)
>> +{
>> + igt_vkms_t *dev;
>> + const char *vkms_root_path;
>> + size_t path_len;
>> + DIR *dir;
>> + int ret;
>> +
>> + dev = calloc(1, sizeof(*dev));
>> +
>> + vkms_root_path = mount_vkms_configfs();
>> +
>> + path_len = strlen(vkms_root_path) + strlen(name) + 2;
>> + dev->path = malloc(path_len);
>> + ret = snprintf(dev->path, path_len, "%s/%s", vkms_root_path, name);
>> + igt_assert(ret >= 0 && ret < path_len);
>> +
>> + dir = opendir(dev->path);
>> + if (dir) {
>> + igt_debug("Device at path %s already exists\n", dev->path);
>> + closedir(dir);
>> + } else {
>> + ret = mkdir(dev->path, 0777);
>> + if (ret != 0) {
>> + free(dev->path);
>> + free(dev);
>> + dev = NULL;
>> + }
>> + }
>> +
>> + return dev;
>> +}
>> +
>> +static int detach_pipeline_items(const char *path, const struct stat *info,
>> + const int typeflag, struct FTW *pathinfo)
>> +{
>> + /*
>> + * Level 4 are the links in the possible_* directories:
>> + * vkms/<dev>/<pipeline items>/<pipeline item>/<possible_*>/<links>
>> + */
>> + if (pathinfo->level == 4 && typeflag == FTW_SL) {
>> + igt_debug("Detaching pipeline item %s\n", path);
>> + return unlink(path);
>> + }
>> +
>> + /* Ignore the other files, they are removed by remove_pipeline_items */
>> + return 0;
>> +}
>> +
>> +static int remove_pipeline_items(const char *path, const struct stat *info,
>> + const int typeflag, struct FTW *pathinfo)
>> +{
>> + /* Level 0 is the device root directory: vkms/<dev> */
>> + if (pathinfo->level == 0) {
>> + igt_debug("Removing pipeline item %s\n", path);
>> + return rmdir(path);
>> + }
>> +
>> + /*
>> + * Level 2 directories are the pipeline items:
>> + * vkms/<dev>/<pipeline items>/<pipeline item>
>> + */
>> + if (pathinfo->level == 2 && typeflag == FTW_DP) {
>> + igt_debug("Removing pipeline item %s\n", path);
>> + return rmdir(path);
>> + }
>> +
>> + /* Ignore the other files, they are removed by VKMS */
>> + return 0;
>> +}
>> +
>> +static int remove_device_dir(igt_vkms_t *dev)
>> +{
>> + int ret;
>> +
>> + ret = nftw(dev->path, detach_pipeline_items, 64, FTW_DEPTH | FTW_PHYS);
>> + if (ret)
>> + return ret;
>> +
>> + ret = nftw(dev->path, remove_pipeline_items, 64, FTW_DEPTH | FTW_PHYS);
>> + return ret;
>> +}
>> +
>> +/**
>> + * igt_vkms_device_destroy:
>> + * @dev: Device to destroy
>> + *
>> + * Remove and free the VKMS device.
>> + */
>> +void igt_vkms_device_destroy(igt_vkms_t *dev)
>> +{
>> + int ret;
>> +
>> + igt_assert(dev);
>> +
>> + ret = remove_device_dir(dev);
>> + igt_assert_f(ret == 0,
>> + "Unable to rmdir device directory '%s'. Got errno=%d (%s)\n",
>> + dev->path, errno, strerror(errno));
>> +
>> + free(dev->path);
>> + free(dev);
>> +}
>> +
>> +/**
>> + * igt_vkms_destroy_all_devices:
>> + *
>> + * Remove all VKMS devices created via configfs.
>> + */
>> +void igt_vkms_destroy_all_devices(void)
>> +{
>> + igt_vkms_t *dev;
>> + const char *vkms_root_path;
>> + DIR *dir;
>> + struct dirent *ent;
>> +
>> + vkms_root_path = mount_vkms_configfs();
>> + dir = opendir(vkms_root_path);
>> + igt_assert_f(dir, "VKMS configfs directory not available at '%s'. "
>> + "Got errno=%d (%s)\n", vkms_root_path, errno,
>> + strerror(errno));
>> +
>> + while ((ent = readdir(dir)) != NULL) {
>> + if (strcmp(ent->d_name, ".") == 0 ||
>> + strcmp(ent->d_name, "..") == 0)
>> + continue;
>> +
>> + dev = igt_vkms_device_create(ent->d_name);
>> + igt_vkms_device_destroy(dev);
>> + }
>> +
>> + closedir(dir);
>> +}
>> diff --git a/lib/igt_vkms.h b/lib/igt_vkms.h
>> new file mode 100644
>> index 000000000..48c48f296
>> --- /dev/null
>> +++ b/lib/igt_vkms.h
>> @@ -0,0 +1,27 @@
>> +/* SPDX-License-Identifier: MIT */
>> +/*
>> + * Copyright © 2023 Google LLC.
>> + * Copyright © 2023 Collabora, Ltd.
>> + * Copyright © 2024 Red Hat, Inc.
>> + */
>> +
>> +#ifndef __IGT_VKMS_H__
>> +#define __IGT_VKMS_H__
>> +
>> +/**
>> + * igt_vkms_t:
>> + * @path: VKMS root directory inside configfs mounted directory
>> + *
>> + * A struct representing a VKMS device.
>> + */
>> +typedef struct igt_vkms {
>> + char *path;
>> +} igt_vkms_t;
>> +
>> +void igt_require_vkms_configfs(void);
>> +
>> +igt_vkms_t *igt_vkms_device_create(const char *name);
>> +void igt_vkms_device_destroy(igt_vkms_t *dev);
>> +void igt_vkms_destroy_all_devices(void);
>> +
>> +#endif /* __IGT_VKMS_H__ */
>> diff --git a/lib/meson.build b/lib/meson.build
>> index 35ca0e0e9..c47ab29b1 100644
>> --- a/lib/meson.build
>> +++ b/lib/meson.build
>> @@ -50,6 +50,7 @@ lib_sources = [
>> 'igt_types.c',
>> 'igt_vec.c',
>> 'igt_vgem.c',
>> + 'igt_vkms.c',
>> 'igt_x86.c',
>> 'instdone.c',
>> 'intel_allocator.c',
>> diff --git a/meson.build b/meson.build
>> index 4efad72cf..dce46e0b2 100644
>> --- a/meson.build
>> +++ b/meson.build
>> @@ -290,6 +290,7 @@ msmdir = join_paths(libexecdir, 'msm')
>> panfrostdir = join_paths(libexecdir, 'panfrost')
>> v3ddir = join_paths(libexecdir, 'v3d')
>> vc4dir = join_paths(libexecdir, 'vc4')
>> +vkmsdir = join_paths(libexecdir, 'vkms')
>> vmwgfxdir = join_paths(libexecdir, 'vmwgfx')
>> mandir = get_option('mandir')
>> pkgconfigdir = join_paths(libdir, 'pkgconfig')
>> @@ -352,6 +353,12 @@ if get_option('use_rpath')
>> endforeach
>> vc4_rpathdir = join_paths(vc4_rpathdir, libdir)
>>
>> + vkms_rpathdir = '$ORIGIN'
>> + foreach p : vkmsdir.split('/')
>> + vkms_rpathdir = join_paths(vkms_rpathdir, '..')
>> + endforeach
>> + vkms_rpathdir = join_paths(vkms_rpathdir, libdir)
>> +
>> vmwgfx_rpathdir = '$ORIGIN'
>> foreach p : vmwgfxdir.split('/')
>> vmwgfx_rpathdir = join_paths(vmwgfx_rpathdir, '..')
>> @@ -365,6 +372,7 @@ else
>> panfrost_rpathdir = ''
>> v3d_rpathdir = ''
>> vc4_rpathdir = ''
>> + vkms_rpathdir = ''
>> vmwgfx_rpathdir = ''
>> endif
>>
>> diff --git a/tests/meson.build b/tests/meson.build
>> index 881b204a5..8886c1cb8 100644
>> --- a/tests/meson.build
>> +++ b/tests/meson.build
>> @@ -487,6 +487,8 @@ subdir('v3d')
>>
>> subdir('vc4')
>>
>> +subdir('vkms')
>> +
>> subdir('vmwgfx')
>>
>> gen_testlist = find_program('generate_testlist.sh')
>> diff --git a/tests/vkms/meson.build b/tests/vkms/meson.build
>> new file mode 100644
>> index 000000000..e55ba32ba
>> --- /dev/null
>> +++ b/tests/vkms/meson.build
>> @@ -0,0 +1,13 @@
>> +vkms_progs = [
>> + 'vkms_configfs',
>> +]
>> +vkms_deps = test_deps
>> +
>> +foreach prog : vkms_progs
>> + test_executables += executable(prog, prog + '.c',
>> + dependencies : vkms_deps,
>> + install_dir : vkmsdir,
>> + install_rpath : vkms_rpathdir,
>> + install : true)
>> + test_list += join_paths('vkms', prog)
>> +endforeach
>> diff --git a/tests/vkms/vkms_configfs.c b/tests/vkms/vkms_configfs.c
>> new file mode 100644
>> index 000000000..b942cec64
>> --- /dev/null
>> +++ b/tests/vkms/vkms_configfs.c
>> @@ -0,0 +1,132 @@
>> +// SPDX-License-Identifier: MIT
>> +/*
>> + * Copyright © 2023 Google LLC.
>> + * Copyright © 2023 Collabora, Ltd.
>> + * Copyright © 2024 Red Hat, Inc.
>> + */
>> +
>> +/**
>> + * TEST: Tests for VKMS configfs support.
>> + * Category: Display
>> + * Mega feature: General Display Features
>> + * Sub-category: uapi
>> + * Functionality: vkms,configfs
>> + * Test category: functionality test
>> + */
>> +
>> +#include <dirent.h>
>> +#include <string.h>
>> +
>> +#include <linux/limits.h>
>> +#include <sys/stat.h>
>> +
>> +#include "drmtest.h"
>> +#include "igt.h"
>> +#include "igt_vkms.h"
>> +
>> +static void assert_default_files(const char *path,
>> + const char **files, size_t n_files,
>> + const char **dirs, size_t n_dirs)
>> +{
>> + DIR *dir;
>> + struct dirent *ent;
>> + int total = 0;
>> + int ret;
>> +
>> + /* Check that the number of files/directories matches the expected */
>> + dir = opendir(path);
>> + igt_assert(dir);
>> + while ((ent = readdir(dir)) != NULL) {
>> + if (strcmp(ent->d_name, ".") == 0 ||
>> + strcmp(ent->d_name, "..") == 0)
>> + continue;
>> +
>> + total++;
>> + }
>> + igt_assert_eq(total, n_dirs + n_files);
>> + closedir(dir);
>> +
>> + /* Check that the files/directories are present */
>> + for (int i = 0; i < n_files; i++) {
>> + char file_path[PATH_MAX];
>> + struct stat buf;
>> +
>> + ret = snprintf(file_path, sizeof(file_path), "%s/%s", path,
>> + files[i]);
>> + igt_assert(ret >= 0 && ret < sizeof(file_path));
>> +
>> + igt_assert_f(stat(file_path, &buf) == 0,
>> + "File %s does not exists\n", file_path);
>> + }
>> +
>> + for (int i = 0; i < n_dirs; i++) {
>> + char dir_path[PATH_MAX];
>> +
>> + ret = snprintf(dir_path, sizeof(dir_path), "%s/%s", path,
>> + dirs[i]);
>> + igt_assert(ret >= 0 && ret < sizeof(dir_path));
>> +
>> + dir = opendir(dir_path);
>> + igt_assert_f(dir, "Directory %s does not exists\n", dir_path);
>> + closedir(dir);
>> + }
>> +}
>> +
>> +/**
>> + * SUBTEST: device-default-files
>> + * Description: Test that creating a VKMS device creates the default files and
>> + * directories.
>> + */
>> +
>> +static void test_device_default_files(void)
>> +{
>> + igt_vkms_t *dev;
>> +
>> + const char *files[] = {
>> + "enabled",
>> + };
>> +
>> + const char *dirs[] = {
>> + "planes",
>> + "crtcs",
>> + "encoders",
>> + "connectors",
>> + };
>> +
>> + dev = igt_vkms_device_create(__func__);
>> + igt_assert(dev);
>> +
>> + assert_default_files(dev->path,
>> + files, ARRAY_SIZE(files),
>> + dirs, ARRAY_SIZE(dirs));
>> +
>> + igt_vkms_device_destroy(dev);
>> +}
>> +
>> +igt_main
>> +{
>> + struct {
>> + const char *name;
>> + void (*fn)(void);
>> + } tests[] = {
>> + { "device-default-files", test_device_default_files },
>> + };
>> +
>> + igt_fixture {
>> + drm_load_module(DRIVER_VKMS);
>> + igt_require_vkms();
>> + igt_require_vkms_configfs();
>> + igt_vkms_destroy_all_devices();
>> + }
>> +
>> + for (int i = 0; i < ARRAY_SIZE(tests); i++) {
>> + igt_subtest(tests[i].name)
>> + tests[i].fn();
>> + }
>> +
>> + igt_fixture {
>> + igt_require_vkms();
>> + igt_require_vkms_configfs();
>> + igt_vkms_destroy_all_devices();
>> + }
>> +}
>> diff --git a/tests/vkms/vkms_test_config.json b/tests/vkms/vkms_test_config.json
>> new file mode 100644
>> index 000000000..4e84e184a
>> --- /dev/null
>> +++ b/tests/vkms/vkms_test_config.json
>> @@ -0,0 +1,72 @@
>> +{
>> + "description": "JSON file to be used to parse VKMS documentation",
>> + "name": "Tests for VKMS Driver",
>> + "drivers": [ "vkms" ],
>> + "files": [ "*.c" ],
>> + "fields": {
>> + "Run type": {
>> + "_properties_": {
>> + "mandatory": true
>> + },
>> + "Category": {
>> + "_properties_": {
>> + "description": "Contains the major group for the tested functionality 'Display'"
>> + },
>> + "Mega feature": {
>> + "_properties_": {
>> + "mandatory": true,
>> + "description": "Contains the mega feature for end to end use case"
>> + },
>> + "Sub-category": {
>> + "_properties_": {
>> + "mandatory": true,
>> + "description": "Contains the technical feature/functionality"
>> + },
>> + "Functionality": {
>> + "_properties_": {
>> + "mandatory": true,
>> + "description": "Groups page table tests on buckets containing more detailed functionality"
>> + },
>> + "Feature": {
>> + "_properties_": {
>> + "description": "Describes the lowest level feature bucket"
>> + }
>> + }
>> + }
>> + }
>> + }
>> + }
>> + },
>> + "Test category": {
>> + "_properties_": {
>> + "description": "Defines the test category. Usually used at subtest level."
>> + }
>> + },
>> + "Test requirement": {
>> + "_properties_": {
>> + "description": "Defines Kernel parameters required for the test to run"
>> + }
>> + },
>> + "Issue": {
>> + "_properties_": {
>> + "description": "If the test is used to solve an issue, point to the URL containing the issue."
>> + }
>> + },
>> + "Depends on": {
>> + "_properties_": {
>> + "description": "List other subtests that are required to not be skipped before calling this one."
>> + }
>> + },
>> + "TODO": {
>> + "_properties_": {
>> + "description": "Point to known missing features at the test or subtest."
>> + }
>> + },
>> + "Description": {
>> + "_properties_": {
>> + "mandatory": true,
>> + "description": "Provides a description for the test/subtest."
>> + }
>> + }
>> + }
>> +}
>> --
>> 2.50.0
>>
More information about the igt-dev
mailing list