Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/vkms: Add and remove VKMS instances via configfs

Allow to create, enable, disable and destroy VKMS instances using
configfs.

For the moment, it is not possible to add pipeline items, so trying to
enable the device will fail printing an informative error to the log.

Tested-by: Mark Yacoub <markyacoub@google.com>
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
Reviewed-by: Harry Wentland <harry.wentland@amd.com>
Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Co-developed-by: José Expósito <jose.exposito89@gmail.com>
Signed-off-by: José Expósito <jose.exposito89@gmail.com>
Link: https://lore.kernel.org/r/20251016175618.10051-3-jose.exposito89@gmail.com
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>

authored by

Louis Chauvet and committed by
Luca Ceresoli
13fc9b97 7965d1c5

+222 -1
+32
Documentation/gpu/vkms.rst
··· 51 51 52 52 sudo modprobe -r vkms 53 53 54 + Configuring With Configfs 55 + ========================= 56 + 57 + It is possible to create and configure multiple VKMS instances via configfs. 58 + 59 + Start by mounting configfs and loading VKMS:: 60 + 61 + sudo mount -t configfs none /config 62 + sudo modprobe vkms 63 + 64 + Once VKMS is loaded, ``/config/vkms`` is created automatically. Each directory 65 + under ``/config/vkms`` represents a VKMS instance, create a new one:: 66 + 67 + sudo mkdir /config/vkms/my-vkms 68 + 69 + By default, the instance is disabled:: 70 + 71 + cat /config/vkms/my-vkms/enabled 72 + 0 73 + 74 + Once you are done configuring the VKMS instance, enable it:: 75 + 76 + echo "1" | sudo tee /config/vkms/my-vkms/enabled 77 + 78 + Finally, you can remove the VKMS instance disabling it:: 79 + 80 + echo "0" | sudo tee /config/vkms/my-vkms/enabled 81 + 82 + And removing the top level directory:: 83 + 84 + sudo rmdir /config/vkms/my-vkms 85 + 54 86 Testing With IGT 55 87 ================ 56 88
+1
drivers/gpu/drm/vkms/Kconfig
··· 7 7 select DRM_KMS_HELPER 8 8 select DRM_GEM_SHMEM_HELPER 9 9 select CRC32 10 + select CONFIGFS_FS 10 11 default n 11 12 help 12 13 Virtual Kernel Mode-Setting (VKMS) is used for testing or for
+2 -1
drivers/gpu/drm/vkms/Makefile
··· 8 8 vkms_composer.o \ 9 9 vkms_writeback.o \ 10 10 vkms_connector.o \ 11 - vkms_config.o 11 + vkms_config.o \ 12 + vkms_configfs.o 12 13 13 14 obj-$(CONFIG_DRM_VKMS) += vkms.o 14 15 obj-$(CONFIG_DRM_VKMS_KUNIT_TEST) += tests/
+172
drivers/gpu/drm/vkms/vkms_configfs.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + #include <linux/cleanup.h> 3 + #include <linux/configfs.h> 4 + #include <linux/mutex.h> 5 + #include <linux/slab.h> 6 + 7 + #include "vkms_drv.h" 8 + #include "vkms_config.h" 9 + #include "vkms_configfs.h" 10 + 11 + /* To avoid registering configfs more than once or unregistering on error */ 12 + static bool is_configfs_registered; 13 + 14 + /** 15 + * struct vkms_configfs_device - Configfs representation of a VKMS device 16 + * 17 + * @group: Top level configuration group that represents a VKMS device. 18 + * Initialized when a new directory is created under "/config/vkms/" 19 + * @lock: Lock used to project concurrent access to the configuration attributes 20 + * @config: Protected by @lock. Configuration of the VKMS device 21 + * @enabled: Protected by @lock. The device is created or destroyed when this 22 + * option changes 23 + */ 24 + struct vkms_configfs_device { 25 + struct config_group group; 26 + 27 + struct mutex lock; 28 + struct vkms_config *config; 29 + bool enabled; 30 + }; 31 + 32 + #define device_item_to_vkms_configfs_device(item) \ 33 + container_of(to_config_group((item)), struct vkms_configfs_device, \ 34 + group) 35 + 36 + static ssize_t device_enabled_show(struct config_item *item, char *page) 37 + { 38 + struct vkms_configfs_device *dev; 39 + bool enabled; 40 + 41 + dev = device_item_to_vkms_configfs_device(item); 42 + 43 + scoped_guard(mutex, &dev->lock) 44 + enabled = dev->enabled; 45 + 46 + return sprintf(page, "%d\n", enabled); 47 + } 48 + 49 + static ssize_t device_enabled_store(struct config_item *item, const char *page, 50 + size_t count) 51 + { 52 + struct vkms_configfs_device *dev; 53 + bool enabled; 54 + int ret = 0; 55 + 56 + dev = device_item_to_vkms_configfs_device(item); 57 + 58 + if (kstrtobool(page, &enabled)) 59 + return -EINVAL; 60 + 61 + scoped_guard(mutex, &dev->lock) { 62 + if (!dev->enabled && enabled) { 63 + if (!vkms_config_is_valid(dev->config)) 64 + return -EINVAL; 65 + 66 + ret = vkms_create(dev->config); 67 + if (ret) 68 + return ret; 69 + } else if (dev->enabled && !enabled) { 70 + vkms_destroy(dev->config); 71 + } 72 + 73 + dev->enabled = enabled; 74 + } 75 + 76 + return (ssize_t)count; 77 + } 78 + 79 + CONFIGFS_ATTR(device_, enabled); 80 + 81 + static struct configfs_attribute *device_item_attrs[] = { 82 + &device_attr_enabled, 83 + NULL, 84 + }; 85 + 86 + static void device_release(struct config_item *item) 87 + { 88 + struct vkms_configfs_device *dev; 89 + 90 + dev = device_item_to_vkms_configfs_device(item); 91 + 92 + if (dev->enabled) 93 + vkms_destroy(dev->config); 94 + 95 + mutex_destroy(&dev->lock); 96 + vkms_config_destroy(dev->config); 97 + kfree(dev); 98 + } 99 + 100 + static struct configfs_item_operations device_item_operations = { 101 + .release = &device_release, 102 + }; 103 + 104 + static const struct config_item_type device_item_type = { 105 + .ct_attrs = device_item_attrs, 106 + .ct_item_ops = &device_item_operations, 107 + .ct_owner = THIS_MODULE, 108 + }; 109 + 110 + static struct config_group *make_device_group(struct config_group *group, 111 + const char *name) 112 + { 113 + struct vkms_configfs_device *dev; 114 + 115 + if (strcmp(name, DEFAULT_DEVICE_NAME) == 0) 116 + return ERR_PTR(-EINVAL); 117 + 118 + dev = kzalloc(sizeof(*dev), GFP_KERNEL); 119 + if (!dev) 120 + return ERR_PTR(-ENOMEM); 121 + 122 + dev->config = vkms_config_create(name); 123 + if (IS_ERR(dev->config)) { 124 + kfree(dev); 125 + return ERR_CAST(dev->config); 126 + } 127 + 128 + config_group_init_type_name(&dev->group, name, &device_item_type); 129 + mutex_init(&dev->lock); 130 + 131 + return &dev->group; 132 + } 133 + 134 + static struct configfs_group_operations device_group_ops = { 135 + .make_group = &make_device_group, 136 + }; 137 + 138 + static const struct config_item_type device_group_type = { 139 + .ct_group_ops = &device_group_ops, 140 + .ct_owner = THIS_MODULE, 141 + }; 142 + 143 + static struct configfs_subsystem vkms_subsys = { 144 + .su_group = { 145 + .cg_item = { 146 + .ci_name = "vkms", 147 + .ci_type = &device_group_type, 148 + }, 149 + }, 150 + .su_mutex = __MUTEX_INITIALIZER(vkms_subsys.su_mutex), 151 + }; 152 + 153 + int vkms_configfs_register(void) 154 + { 155 + int ret; 156 + 157 + if (is_configfs_registered) 158 + return 0; 159 + 160 + config_group_init(&vkms_subsys.su_group); 161 + ret = configfs_register_subsystem(&vkms_subsys); 162 + 163 + is_configfs_registered = ret == 0; 164 + 165 + return ret; 166 + } 167 + 168 + void vkms_configfs_unregister(void) 169 + { 170 + if (is_configfs_registered) 171 + configfs_unregister_subsystem(&vkms_subsys); 172 + }
+8
drivers/gpu/drm/vkms/vkms_configfs.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0+ */ 2 + #ifndef _VKMS_CONFIGFS_H_ 3 + #define _VKMS_CONFIGFS_H_ 4 + 5 + int vkms_configfs_register(void); 6 + void vkms_configfs_unregister(void); 7 + 8 + #endif /* _VKMS_CONFIGFS_H_ */
+7
drivers/gpu/drm/vkms/vkms_drv.c
··· 28 28 #include <drm/drm_vblank.h> 29 29 30 30 #include "vkms_config.h" 31 + #include "vkms_configfs.h" 31 32 #include "vkms_drv.h" 32 33 33 34 #define DRIVER_NAME "vkms" ··· 215 214 int ret; 216 215 struct vkms_config *config; 217 216 217 + ret = vkms_configfs_register(); 218 + if (ret) 219 + return ret; 220 + 218 221 config = vkms_config_default_create(enable_cursor, enable_writeback, enable_overlay); 219 222 if (IS_ERR(config)) 220 223 return PTR_ERR(config); ··· 255 250 256 251 static void __exit vkms_exit(void) 257 252 { 253 + vkms_configfs_unregister(); 254 + 258 255 if (!default_config) 259 256 return; 260 257