silly mistake, I should have been more cautious. Thank you for pickup up my slack On Mon, Aug 28, 2023 at 11:10 PM Marius Vlad <marius.vlad@xxxxxxxxxxxxx> wrote: > > Hi Brandon, > > See some minor missing rmdirs for connector_other and encoder_other. > > On Mon, Aug 28, 2023 at 08:17:06AM +0000, Brandon Pollack wrote: > > From: Jim Shargo <jshargo@xxxxxxxxxxxx> > > > > This change adds the basic scaffolding for ConfigFS, including setting > > up the default directories. It does not allow for the registration of > > configfs-backed devices, which is complex and provided in a follow-up > > commit. > > > > This CL includes docs about using ConfigFS with VKMS, but I'll summarize > > in brief here as well (assuming ConfigFS is mounted at /config/): > > > > To create a new device, you can do so via `mkdir > > /config/vkms/my-device`. > > > > This will create a number of directories and files automatically: > > > > /config > > `-- vkms > > `-- my-device > > |-- connectors > > |-- crtcs > > |-- encoders > > |-- planes > > `-- enabled > > > > You can then configure objects by mkdir'ing in each of the directories. > > > > When you're satisfied, you can `echo 1 > /config/vkms/my-device/enabled`. > > This will create a new device according to your configuration. > > > > For now, this will fail, but the next change will add support for it. > > > > Signed-off-by: Jim Shargo <jshargo@xxxxxxxxxxxx> > > Signed-off-by: Brandon Pollack <brpol@xxxxxxxxxxxx> > > --- > > Documentation/gpu/vkms.rst | 18 +- > > drivers/gpu/drm/Kconfig | 1 + > > drivers/gpu/drm/vkms/Makefile | 1 + > > drivers/gpu/drm/vkms/vkms_configfs.c | 648 +++++++++++++++++++++++++++ > > drivers/gpu/drm/vkms/vkms_drv.c | 56 ++- > > drivers/gpu/drm/vkms/vkms_drv.h | 92 +++- > > drivers/gpu/drm/vkms/vkms_output.c | 5 + > > 7 files changed, 804 insertions(+), 17 deletions(-) > > create mode 100644 drivers/gpu/drm/vkms/vkms_configfs.c > > > > diff --git a/Documentation/gpu/vkms.rst b/Documentation/gpu/vkms.rst > > index ba04ac7c2167..c3875bf66dba 100644 > > --- a/Documentation/gpu/vkms.rst > > +++ b/Documentation/gpu/vkms.rst > > @@ -51,6 +51,12 @@ To disable the driver, use :: > > > > sudo modprobe -r vkms > > > > +Configuration With ConfigFS > > +=========================== > > + > > +.. kernel-doc:: drivers/gpu/drm/vkms/vkms_configfs.c > > + :doc: ConfigFS Support for VKMS > > + > > Testing With IGT > > ================ > > > > @@ -135,22 +141,16 @@ project. > > Runtime Configuration > > --------------------- > > > > -We want to be able to reconfigure vkms instance without having to reload the > > -module. Use/Test-cases: > > +We want to be able to manipulate vkms instances without having to reload the > > +module. Such configuration can be added as extensions to vkms's ConfigFS > > +support. Use-cases: > > > > - Hotplug/hotremove connectors on the fly (to be able to test DP MST handling > > of compositors). > > > > -- Configure planes/crtcs/connectors (we'd need some code to have more than 1 of > > - them first). > > - > > - Change output configuration: Plug/unplug screens, change EDID, allow changing > > the refresh rate. > > > > -The currently proposed solution is to expose vkms configuration through > > -configfs. All existing module options should be supported through configfs > > -too. > > - > > Writeback support > > ----------------- > > > > diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig > > index ab9ef1c20349..e39ee0e8ca06 100644 > > --- a/drivers/gpu/drm/Kconfig > > +++ b/drivers/gpu/drm/Kconfig > > @@ -284,6 +284,7 @@ config DRM_VKMS > > depends on DRM && MMU > > select DRM_KMS_HELPER > > select DRM_GEM_SHMEM_HELPER > > + select CONFIGFS_FS > > select CRC32 > > default n > > help > > diff --git a/drivers/gpu/drm/vkms/Makefile b/drivers/gpu/drm/vkms/Makefile > > index 1b28a6a32948..6b83907ad554 100644 > > --- a/drivers/gpu/drm/vkms/Makefile > > +++ b/drivers/gpu/drm/vkms/Makefile > > @@ -1,5 +1,6 @@ > > # SPDX-License-Identifier: GPL-2.0-only > > vkms-y := \ > > + vkms_configfs.o \ > > vkms_drv.o \ > > vkms_plane.o \ > > vkms_output.o \ > > diff --git a/drivers/gpu/drm/vkms/vkms_configfs.c b/drivers/gpu/drm/vkms/vkms_configfs.c > > new file mode 100644 > > index 000000000000..f2439629b37b > > --- /dev/null > > +++ b/drivers/gpu/drm/vkms/vkms_configfs.c > > @@ -0,0 +1,648 @@ > > +// SPDX-License-Identifier: GPL-2.0+ > > + > > +#include <linux/configfs.h> > > +#include <linux/mutex.h> > > +#include <linux/platform_device.h> > > + > > +#include <drm/drm_plane.h> > > +#include <drm/drm_print.h> > > + > > +#include "vkms_drv.h" > > + > > +/** > > + * DOC: ConfigFS Support for VKMS > > + * > > + * VKMS is instrumented with support for configuration via :doc:`ConfigFS > > + * <../filesystems/configfs>`. > > + * > > + * With VKMS installed, you can mount ConfigFS at ``/config/`` like so:: > > + * > > + * mkdir -p /config/ > > + * sudo mount -t configfs none /config > > + * > > + * This allows you to configure multiple virtual devices. Note > > + * that the default device which can be enabled in the module params with:: > > + * > > + * modprobe vkms default_device=1 > > + * > > + * is immutable because we cannot pre-populate ConfigFS directories with normal > > + * files. > > + * > > + * To set up a new device, create a new directory under the VKMS configfs > > + * directory:: > > + * > > + * mkdir /config/vkms/test > > + * > > + * With your device created you'll find an new directory ready to be > > + * configured:: > > + * > > + * /config > > + * `-- vkms > > + * `-- test > > + * |-- connectors > > + * |-- crtcs > > + * |-- encoders > > + * |-- planes > > + * `-- enabled > > + * > > + * Each directory you add within the connectors, crtcs, encoders, and planes > > + * directories will let you configure a new object of that type. Adding new > > + * objects will automatically create a set of default files and folders you can > > + * use to configure that object. > > + * > > + * For instance, we can set up a two-output device like so:: > > + * > > + * DRM_PLANE_TYPE_PRIMARY=1 > > + * DRM_PLANE_TYPE_CURSOR=2 > > + * DRM_PLANE_TYPE_OVERLAY=0 > > + * > > + * mkdir /config/vkms/test/planes/primary > > + * echo $DRM_PLANE_TYPE_PRIMARY > /config/vkms/test/planes/primary/type > > + * > > + * mkdir /config/vkms/test/planes/other_primary > > + * echo $DRM_PLANE_TYPE_PRIMARY > /config/vkms/test/planes/other_primary/type > > + * > > + * mkdir /config/vkms/test/crtcs/crtc > > + * mkdir /config/vkms/test/crtcs/crtc_other > > + * > > + * mkdir /config/vkms/test/encoders/encoder > > + * mkdir /config/vkms/test/encoders/encoder_other > > + * > > + * mkdir /config/vkms/test/connectors/connector > > + * mkdir /config/vkms/test/connectors/connector_other > > + * > > + * You can see that specific attributes, such as ``.../<plane>/type``, can be > > + * configured by writing into them. Associating objects together can be done via > > + * symlinks:: > > + * > > + * ln -s /config/vkms/test/encoders/encoder /config/vkms/test/connectors/connector/possible_encoders > > + * ln -s /config/vkms/test/encoders/encoder_other /config/vkms/test/connectors/connector_other/possible_encoders > > + * > > + * ln -s /config/vkms/test/crtcs/crtc /config/vkms/test/planes/primary/possible_crtcs/ > > + * ln -s /config/vkms/test/crtcs/crtc_other /config/vkms/test/planes/other_primary/possible_crtcs/ > > + * > > + * ln -s /config/vkms/test/crtcs/crtc /config/vkms/test/encoders/encoder/possible_crtcs/ > > + * ln -s /config/vkms/test/crtcs/crtc_other /config/vkms/test/encoders/encoder_other/possible_crtcs/ > > + * > > + * Finally, to enable your configured device, just write 1 to the ``enabled`` > > + * file:: > > + * > > + * echo 1 > /config/vkms/test/enabled > > + * > > + * When you're done with the virtual device, you can clean up the device like > > + * so:: > > + * > > + * echo 0 > /config/vkms/test/enabled > > + * > > + * rm /config/vkms/test/connectors/connector/possible_encoders/encoder > > + * rm /config/vkms/test/encoders/encoder/possible_crtcs/crtc > > + * rm /config/vkms/test/planes/primary/possible_crtcs/crtc > > + * rm /config/vkms/test/planes/cursor/possible_crtcs/crtc > > + * rm /config/vkms/test/planes/overlay/possible_crtcs/crtc > > + * rm /config/vkms/test/planes/overlay/possible_crtcs/crtc_other > > + * rm /config/vkms/test/planes/other_primary/possible_crtcs/crtc_other > > + * > > + * rmdir /config/vkms/test/planes/primary > > + * rmdir /config/vkms/test/planes/other_primary > > + * rmdir /config/vkms/test/planes/cursor > > + * rmdir /config/vkms/test/planes/overlay > > + * rmdir /config/vkms/test/crtcs/crtc > > + * rmdir /config/vkms/test/crtcs/crtc_other > > + * rmdir /config/vkms/test/encoders/encoder > rmdir /config/vkms/test/encoders/encoder_other > > + * rmdir /config/vkms/test/connectors/connector > rmdir /config/vkms/test/connectors/connector_other > > + * > > + * rmdir /config/vkms/test > > + */ > > + > > +/* > > + * Common helpers (i.e. common sub-groups) > > + */ > > + > > +/* Possible CRTCs, e.g. /config/vkms/device/<object>/possible_crtcs/<symlink> */ > > + > > +static struct config_item_type crtc_type; > > + > > +static int possible_crtcs_allow_link(struct config_item *src, > > + struct config_item *target) > > +{ > > + struct vkms_config_links *links = item_to_config_links(src); > > + struct vkms_config_crtc *crtc; > > + > > + if (target->ci_type != &crtc_type) { > > + DRM_ERROR("Unable to link non-CRTCs.\n"); > > + return -EINVAL; > > + } > > + > > + crtc = item_to_config_crtc(target); > > + > > + if (links->linked_object_bitmap & BIT(crtc->crtc_config_idx)) { > > + DRM_ERROR( > > + "Tried to add two symlinks to the same CRTC from the same object\n"); > > + return -EINVAL; > > + } > > + > > + links->linked_object_bitmap |= BIT(crtc->crtc_config_idx); > > + > > + return 0; > > +} > > + > > +static void possible_crtcs_drop_link(struct config_item *src, > > + struct config_item *target) > > +{ > > + struct vkms_config_links *links = item_to_config_links(src); > > + struct vkms_config_crtc *crtc = item_to_config_crtc(target); > > + > > + links->linked_object_bitmap &= ~BIT(crtc->crtc_config_idx); > > +} > > + > > +static struct configfs_item_operations possible_crtcs_item_ops = { > > + .allow_link = &possible_crtcs_allow_link, > > + .drop_link = &possible_crtcs_drop_link, > > +}; > > + > > +static struct config_item_type possible_crtcs_group_type = { > > + .ct_item_ops = &possible_crtcs_item_ops, > > + .ct_owner = THIS_MODULE, > > +}; > > + > > +static void add_possible_crtcs(struct config_group *parent, > > + struct config_group *possible_crtcs) > > +{ > > + config_group_init_type_name(possible_crtcs, "possible_crtcs", > > + &possible_crtcs_group_type); > > + configfs_add_default_group(possible_crtcs, parent); > > +} > > + > > +/* Possible encoders, e.g. /config/vkms/device/connector/possible_encoders/<symlink> */ > > + > > +static struct config_item_type encoder_type; > > + > > +static int possible_encoders_allow_link(struct config_item *src, > > + struct config_item *target) > > +{ > > + struct vkms_config_links *links = item_to_config_links(src); > > + struct vkms_config_encoder *encoder; > > + > > + if (target->ci_type != &encoder_type) { > > + DRM_ERROR("Unable to link non-encoders.\n"); > > + return -EINVAL; > > + } > > + > > + encoder = item_to_config_encoder(target); > > + > > + if (links->linked_object_bitmap & BIT(encoder->encoder_config_idx)) { > > + DRM_ERROR( > > + "Tried to add two symlinks to the same encoder from the same object\n"); > > + return -EINVAL; > > + } > > + > > + links->linked_object_bitmap |= BIT(encoder->encoder_config_idx); > > + > > + return 0; > > +} > > + > > +static void possible_encoders_drop_link(struct config_item *src, > > + struct config_item *target) > > +{ > > + struct vkms_config_links *links = item_to_config_links(src); > > + struct vkms_config_encoder *encoder = item_to_config_encoder(target); > > + > > + links->linked_object_bitmap &= ~BIT(encoder->encoder_config_idx); > > +} > > + > > +static struct configfs_item_operations possible_encoders_item_ops = { > > + .allow_link = &possible_encoders_allow_link, > > + .drop_link = &possible_encoders_drop_link, > > +}; > > + > > +static struct config_item_type possible_encoders_group_type = { > > + .ct_item_ops = &possible_encoders_item_ops, > > + .ct_owner = THIS_MODULE, > > +}; > > + > > +static void add_possible_encoders(struct config_group *parent, > > + struct config_group *possible_encoders) > > +{ > > + config_group_init_type_name(possible_encoders, "possible_encoders", > > + &possible_encoders_group_type); > > + configfs_add_default_group(possible_encoders, parent); > > +} > > + > > +/* > > + * Individual objects (connectors, crtcs, encoders, planes): > > + */ > > + > > +/* Connector item, e.g. /config/vkms/device/connectors/ID */ > > + > > +static struct config_item_type connector_type = { > > + .ct_owner = THIS_MODULE, > > +}; > > + > > +/* Crtc item, e.g. /config/vkms/device/crtcs/ID */ > > + > > +static struct config_item_type crtc_type = { > > + .ct_owner = THIS_MODULE, > > +}; > > + > > +/* Encoder item, e.g. /config/vkms/device/encoder/ID */ > > + > > +static struct config_item_type encoder_type = { > > + .ct_owner = THIS_MODULE, > > +}; > > + > > +/* Plane item, e.g. /config/vkms/device/planes/ID */ > > + > > +static ssize_t plane_type_show(struct config_item *item, char *buf) > > +{ > > + struct vkms_config_plane *plane = item_to_config_plane(item); > > + struct vkms_configfs *configfs = plane_item_to_configfs(item); > > + enum drm_plane_type plane_type; > > + > > + mutex_lock(&configfs->lock); > > + plane_type = plane->type; > > + mutex_unlock(&configfs->lock); > > + > > + return sprintf(buf, "%u", plane_type); > > +} > > + > > +static ssize_t plane_type_store(struct config_item *item, const char *buf, > > + size_t len) > > +{ > > + struct vkms_config_plane *plane = item_to_config_plane(item); > > + struct vkms_configfs *configfs = plane_item_to_configfs(item); > > + int val, ret; > > + > > + ret = kstrtouint(buf, 10, &val); > > + if (ret) > > + return ret; > > + > > + if (val != DRM_PLANE_TYPE_PRIMARY && val != DRM_PLANE_TYPE_CURSOR && > > + val != DRM_PLANE_TYPE_OVERLAY) > > + return -EINVAL; > > + > > + mutex_lock(&configfs->lock); > > + plane->type = val; > > + mutex_unlock(&configfs->lock); > > + > > + return len; > > +} > > + > > +CONFIGFS_ATTR(plane_, type); > > + > > +static struct configfs_attribute *plane_attrs[] = { > > + &plane_attr_type, > > + NULL, > > +}; > > + > > +static struct config_item_type plane_type = { > > + .ct_attrs = plane_attrs, > > + .ct_owner = THIS_MODULE, > > +}; > > + > > +/* > > + * Directory groups, e.g. /config/vkms/device/{planes, crtcs, ...} > > + */ > > + > > +/* Connectors group: /config/vkms/device/connectors/ */ > > + > > +static struct config_group *connectors_group_make(struct config_group *group, > > + const char *name) > > +{ > > + struct vkms_config_connector *connector = > > + kzalloc(sizeof(*connector), GFP_KERNEL); > > + if (!connector) > > + return ERR_PTR(-ENOMEM); > > + > > + config_group_init_type_name(&connector->config_group, name, > > + &connector_type); > > + add_possible_encoders(&connector->config_group, > > + &connector->possible_encoders.group); > > + > > + return &connector->config_group; > > +} > > + > > +static void connectors_group_drop(struct config_group *group, > > + struct config_item *item) > > +{ > > + struct vkms_config_connector *connector = > > + item_to_config_connector(item); > > + kfree(connector); > > +} > > + > > +static struct configfs_group_operations connectors_group_ops = { > > + .make_group = &connectors_group_make, > > + .drop_item = &connectors_group_drop, > > +}; > > + > > +static struct config_item_type connectors_group_type = { > > + .ct_group_ops = &connectors_group_ops, > > + .ct_owner = THIS_MODULE, > > +}; > > + > > +/* CRTCs group: /config/vkms/device/crtcs/ */ > > + > > +static struct config_group *crtcs_group_make(struct config_group *group, > > + const char *name) > > +{ > > + struct vkms_configfs *configfs = > > + container_of(group, struct vkms_configfs, crtcs_group); > > + unsigned long next_idx; > > + struct vkms_config_crtc *crtc; > > + > > + mutex_lock(&configfs->lock); > > + > > + next_idx = find_first_zero_bit(&configfs->allocated_crtcs, > > + VKMS_MAX_OUTPUT_OBJECTS); > > + > > + if (next_idx == VKMS_MAX_OUTPUT_OBJECTS) { > > + DRM_ERROR("Unable to allocate another CRTC.\n"); > > + mutex_unlock(&configfs->lock); > > + return ERR_PTR(-ENOMEM); > > + } > > + > > + crtc = kzalloc(sizeof(*crtc), GFP_KERNEL); > > + if (!crtc) { > > + DRM_ERROR("Unable to allocate CRTC.\n"); > > + mutex_unlock(&configfs->lock); > > + return ERR_PTR(-ENOMEM); > > + } > > + > > + config_group_init_type_name(&crtc->config_group, name, &crtc_type); > > + crtc->crtc_config_idx = next_idx; > > + > > + set_bit(next_idx, &configfs->allocated_crtcs); > > + > > + mutex_unlock(&configfs->lock); > > + > > + return &crtc->config_group; > > +} > > + > > +static void crtcs_group_drop(struct config_group *group, > > + struct config_item *item) > > +{ > > + struct vkms_config_crtc *crtc = item_to_config_crtc(item); > > + > > + kfree(crtc); > > +} > > + > > +static struct configfs_group_operations crtcs_group_ops = { > > + .make_group = &crtcs_group_make, > > + .drop_item = &crtcs_group_drop, > > +}; > > + > > +static struct config_item_type crtcs_group_type = { > > + .ct_group_ops = &crtcs_group_ops, > > + .ct_owner = THIS_MODULE, > > +}; > > + > > +/* Encoders group: /config/vkms/device/encoders/ */ > > + > > +static struct config_group *encoders_group_make(struct config_group *group, > > + const char *name) > > +{ > > + struct vkms_configfs *configfs = > > + container_of(group, struct vkms_configfs, encoders_group); > > + unsigned long next_idx; > > + struct vkms_config_encoder *encoder; > > + > > + mutex_lock(&configfs->lock); > > + > > + next_idx = find_first_zero_bit(&configfs->allocated_encoders, > > + VKMS_MAX_OUTPUT_OBJECTS); > > + > > + if (next_idx == VKMS_MAX_OUTPUT_OBJECTS) { > > + DRM_ERROR("Unable to allocate another encoder.\n"); > > + mutex_unlock(&configfs->lock); > > + return ERR_PTR(-ENOMEM); > > + } > > + > > + encoder = kzalloc(sizeof(*encoder), GFP_KERNEL); > > + if (!encoder) { > > + DRM_ERROR("Unable to allocate encoder.\n"); > > + mutex_unlock(&configfs->lock); > > + return ERR_PTR(-ENOMEM); > > + } > > + > > + config_group_init_type_name(&encoder->config_group, name, > > + &encoder_type); > > + add_possible_crtcs(&encoder->config_group, > > + &encoder->possible_crtcs.group); > > + encoder->encoder_config_idx = next_idx; > > + set_bit(next_idx, &configfs->allocated_encoders); > > + > > + mutex_unlock(&configfs->lock); > > + > > + return &encoder->config_group; > > +} > > + > > +static void encoders_group_drop(struct config_group *group, > > + struct config_item *item) > > +{ > > + struct vkms_config_encoder *encoder = item_to_config_encoder(item); > > + > > + kfree(encoder); > > +} > > + > > +static struct configfs_group_operations encoders_group_ops = { > > + .make_group = &encoders_group_make, > > + .drop_item = &encoders_group_drop, > > +}; > > + > > +static struct config_item_type encoders_group_type = { > > + .ct_group_ops = &encoders_group_ops, > > + .ct_owner = THIS_MODULE, > > +}; > > + > > +/* Planes group: /config/vkms/device/planes/ */ > > + > > +static struct config_group *make_plane_group(struct config_group *group, > > + const char *name) > > +{ > > + struct vkms_config_plane *plane = kzalloc(sizeof(*plane), GFP_KERNEL); > > + > > + if (!plane) > > + return ERR_PTR(-ENOMEM); > > + > > + config_group_init_type_name(&plane->config_group, name, &plane_type); > > + add_possible_crtcs(&plane->config_group, &plane->possible_crtcs.group); > > + > > + return &plane->config_group; > > +} > > + > > +static void drop_plane_group(struct config_group *group, > > + struct config_item *item) > > +{ > > + struct vkms_config_plane *plane = item_to_config_plane(item); > > + > > + kfree(plane); > > +} > > + > > +static struct configfs_group_operations plane_group_ops = { > > + .make_group = &make_plane_group, > > + .drop_item = &drop_plane_group, > > +}; > > + > > +static struct config_item_type planes_group_type = { > > + .ct_group_ops = &plane_group_ops, > > + .ct_owner = THIS_MODULE, > > +}; > > + > > +/* Root directory group, e.g. /config/vkms/device */ > > + > > +static ssize_t device_enabled_show(struct config_item *item, char *buf) > > +{ > > + struct vkms_configfs *configfs = item_to_configfs(item); > > + bool is_enabled; > > + > > + mutex_lock(&configfs->lock); > > + is_enabled = configfs->vkms_device != NULL; > > + mutex_unlock(&configfs->lock); > > + > > + return sprintf(buf, "%d", is_enabled); > > +} > > + > > +static ssize_t device_enabled_store(struct config_item *item, const char *buf, > > + size_t len) > > +{ > > + struct vkms_configfs *configfs = item_to_configfs(item); > > + struct vkms_device *device; > > + int value, ret; > > + > > + ret = kstrtoint(buf, 0, &value); > > + if (ret) > > + return ret; > > + > > + if (value != 1) > > + return -EINVAL; > > + > > + mutex_lock(&configfs->lock); > > + > > + if (configfs->vkms_device) { > > + mutex_unlock(&configfs->lock); > > + return len; > > + } > > + > > + device = vkms_add_device(configfs); > > + mutex_unlock(&configfs->lock); > > + > > + if (IS_ERR(device)) > > + return -PTR_ERR(device); > > + > > + return len; > > +} > > + > > +CONFIGFS_ATTR(device_, enabled); > > + > > +static ssize_t device_id_show(struct config_item *item, char *buf) > > +{ > > + struct vkms_configfs *configfs = item_to_configfs(item); > > + int id = -1; > > + > > + mutex_lock(&configfs->lock); > > + if (configfs->vkms_device) > > + id = configfs->vkms_device->platform->id; > > + > > + mutex_unlock(&configfs->lock); > > + > > + return sprintf(buf, "%d", id); > > +} > > + > > +CONFIGFS_ATTR_RO(device_, id); > > + > > +static struct configfs_attribute *device_group_attrs[] = { > > + &device_attr_id, > > + &device_attr_enabled, > > + NULL, > > +}; > > + > > +static struct config_item_type device_group_type = { > > + .ct_attrs = device_group_attrs, > > + .ct_owner = THIS_MODULE, > > +}; > > + > > +static void vkms_configfs_setup_default_groups(struct vkms_configfs *configfs, > > + const char *name) > > +{ > > + config_group_init_type_name(&configfs->device_group, name, > > + &device_group_type); > > + > > + config_group_init_type_name(&configfs->connectors_group, "connectors", > > + &connectors_group_type); > > + configfs_add_default_group(&configfs->connectors_group, > > + &configfs->device_group); > > + > > + config_group_init_type_name(&configfs->crtcs_group, "crtcs", > > + &crtcs_group_type); > > + configfs_add_default_group(&configfs->crtcs_group, > > + &configfs->device_group); > > + > > + config_group_init_type_name(&configfs->encoders_group, "encoders", > > + &encoders_group_type); > > + configfs_add_default_group(&configfs->encoders_group, > > + &configfs->device_group); > > + > > + config_group_init_type_name(&configfs->planes_group, "planes", > > + &planes_group_type); > > + configfs_add_default_group(&configfs->planes_group, > > + &configfs->device_group); > > +} > > + > > +/* Root directory group and subsystem, e.g. /config/vkms/ */ > > + > > +static struct config_group *make_root_group(struct config_group *group, > > + const char *name) > > +{ > > + struct vkms_configfs *configfs = kzalloc(sizeof(*configfs), GFP_KERNEL); > > + > > + if (!configfs) > > + return ERR_PTR(-ENOMEM); > > + > > + vkms_configfs_setup_default_groups(configfs, name); > > + mutex_init(&configfs->lock); > > + > > + return &configfs->device_group; > > +} > > + > > +static void drop_root_group(struct config_group *group, > > + struct config_item *item) > > +{ > > + struct vkms_configfs *configfs = item_to_configfs(item); > > + > > + mutex_lock(&configfs->lock); > > + if (configfs->vkms_device) > > + vkms_remove_device(configfs->vkms_device); > > + mutex_unlock(&configfs->lock); > > + > > + kfree(configfs); > > +} > > + > > +static struct configfs_group_operations root_group_ops = { > > + .make_group = &make_root_group, > > + .drop_item = &drop_root_group, > > +}; > > + > > +static struct config_item_type vkms_type = { > > + .ct_group_ops = &root_group_ops, > > + .ct_owner = THIS_MODULE, > > +}; > > + > > +static struct configfs_subsystem vkms_subsys = { > > + .su_group = { > > + .cg_item = { > > + .ci_name = "vkms", > > + .ci_type = &vkms_type, > > + }, > > + }, > > + .su_mutex = __MUTEX_INITIALIZER(vkms_subsys.su_mutex), > > +}; > > + > > +int vkms_init_configfs(void) > > +{ > > + config_group_init(&vkms_subsys.su_group); > > + return configfs_register_subsystem(&vkms_subsys); > > +} > > + > > +void vkms_unregister_configfs(void) > > +{ > > + configfs_unregister_subsystem(&vkms_subsys); > > +} > > diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c > > index 6c94c2b5d529..819e880a8cf7 100644 > > --- a/drivers/gpu/drm/vkms/vkms_drv.c > > +++ b/drivers/gpu/drm/vkms/vkms_drv.c > > @@ -9,8 +9,10 @@ > > * the GPU in DRM API tests. > > */ > > > > -#include "asm-generic/errno-base.h" > > +#include <linux/configfs.h> > > #include <linux/device.h> > > +#include <linux/dma-mapping.h> > > +#include <linux/err.h> > > #include <linux/module.h> > > #include <linux/platform_device.h> > > #include <linux/dma-mapping.h> > > @@ -172,8 +174,8 @@ static int vkms_modeset_init(struct vkms_device *vkmsdev) > > dev->mode_config.preferred_depth = 0; > > dev->mode_config.helper_private = &vkms_mode_config_helpers; > > > > - return vkmsdev->is_default ? vkms_output_init_default(vkmsdev) : > > - -EINVAL; > > + return vkmsdev->configfs ? vkms_output_init(vkmsdev) : > > + vkms_output_init_default(vkmsdev); > > } > > > > static int vkms_platform_probe(struct platform_device *pdev) > > @@ -184,8 +186,10 @@ static int vkms_platform_probe(struct platform_device *pdev) > > void *grp; > > > > grp = devres_open_group(&pdev->dev, NULL, GFP_KERNEL); > > - if (!grp) > > + if (!grp) { > > + DRM_ERROR("Could not open devres group\n"); > > return -ENOMEM; > > + } > > > > vkms_device = devm_drm_dev_alloc(&pdev->dev, &vkms_driver, > > struct vkms_device, drm); > > @@ -198,7 +202,7 @@ static int vkms_platform_probe(struct platform_device *pdev) > > vkms_device->config.cursor = enable_cursor; > > vkms_device->config.writeback = enable_writeback; > > vkms_device->config.overlay = enable_overlay; > > - vkms_device->is_default = vkms_device_setup->is_default; > > + vkms_device->configfs = vkms_device_setup->configfs; > > > > ret = dma_coerce_mask_and_coherent(vkms_device->drm.dev, > > DMA_BIT_MASK(64)); > > @@ -258,12 +262,43 @@ static struct platform_driver vkms_platform_driver = { > > .driver.name = DRIVER_NAME, > > }; > > > > +struct vkms_device *vkms_add_device(struct vkms_configfs *configfs) > > +{ > > + struct device *dev = NULL; > > + struct platform_device *pdev; > > + int max_id = 1; > > + struct vkms_device_setup vkms_device_setup = { > > + .configfs = configfs, > > + }; > > + > > + while ((dev = platform_find_device_by_driver( > > + dev, &vkms_platform_driver.driver))) { > > + pdev = to_platform_device(dev); > > + max_id = max(max_id, pdev->id); > > + } > > + > > + pdev = platform_device_register_data(NULL, DRIVER_NAME, max_id + 1, > > + &vkms_device_setup, > > + sizeof(vkms_device_setup)); > > + if (IS_ERR(pdev)) { > > + DRM_ERROR("Unable to register vkms device'\n"); > > + return ERR_PTR(PTR_ERR(pdev)); > > + } > > + > > + return platform_get_drvdata(pdev); > > +} > > + > > +void vkms_remove_device(struct vkms_device *vkms_device) > > +{ > > + platform_device_unregister(vkms_device->platform); > > +} > > + > > static int __init vkms_init(void) > > { > > int ret; > > struct platform_device *pdev; > > struct vkms_device_setup vkms_device_setup = { > > - .is_default = true, > > + .configfs = NULL, > > }; > > > > ret = platform_driver_register(&vkms_platform_driver); > > @@ -281,6 +316,13 @@ static int __init vkms_init(void) > > return PTR_ERR(pdev); > > } > > > > + ret = vkms_init_configfs(); > > + if (ret) { > > + DRM_ERROR("Unable to initialize configfs\n"); > > + platform_device_unregister(pdev); > > + platform_driver_unregister(&vkms_platform_driver); > > + } > > + > > return 0; > > } > > > > @@ -288,6 +330,8 @@ static void __exit vkms_exit(void) > > { > > struct device *dev; > > > > + vkms_unregister_configfs(); > > + > > while ((dev = platform_find_device_by_driver( > > NULL, &vkms_platform_driver.driver))) { > > // platform_find_device_by_driver increments the refcount. Drop > > diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h > > index 4262dcffd7e1..8cdd7949f661 100644 > > --- a/drivers/gpu/drm/vkms/vkms_drv.h > > +++ b/drivers/gpu/drm/vkms/vkms_drv.h > > @@ -3,6 +3,7 @@ > > #ifndef _VKMS_DRV_H_ > > #define _VKMS_DRV_H_ > > > > +#include <linux/configfs.h> > > #include <linux/hrtimer.h> > > > > #include <drm/drm.h> > > @@ -10,6 +11,7 @@ > > #include <drm/drm_gem.h> > > #include <drm/drm_gem_atomic_helper.h> > > #include <drm/drm_encoder.h> > > +#include <drm/drm_plane.h> > > #include <drm/drm_writeback.h> > > > > #define XRES_MIN 10 > > @@ -138,14 +140,65 @@ struct vkms_config { > > bool overlay; > > }; > > > > +struct vkms_config_links { > > + struct config_group group; > > + unsigned long linked_object_bitmap; > > +}; > > + > > +struct vkms_config_connector { > > + struct config_group config_group; > > + struct vkms_config_links possible_encoders; > > +}; > > + > > +struct vkms_config_crtc { > > + struct config_group config_group; > > + unsigned long crtc_config_idx; > > +}; > > + > > +struct vkms_config_encoder { > > + struct config_group config_group; > > + struct vkms_config_links possible_crtcs; > > + unsigned long encoder_config_idx; > > +}; > > + > > +struct vkms_config_plane { > > + struct vkms_configfs *configfs; > > + struct config_group config_group; > > + struct vkms_config_links possible_crtcs; > > + enum drm_plane_type type; > > +}; > > + > > +struct vkms_configfs { > > + /* Directory group containing connector configs, e.g. /config/vkms/device/ */ > > + struct config_group device_group; > > + /* Directory group containing connector configs, e.g. /config/vkms/device/connectors/ */ > > + struct config_group connectors_group; > > + /* Directory group containing CRTC configs, e.g. /config/vkms/device/crtcs/ */ > > + struct config_group crtcs_group; > > + /* Directory group containing encoder configs, e.g. /config/vkms/device/encoders/ */ > > + struct config_group encoders_group; > > + /* Directory group containing plane configs, e.g. /config/vkms/device/planes/ */ > > + struct config_group planes_group; > > + > > + unsigned long allocated_crtcs; > > + unsigned long allocated_encoders; > > + > > + struct mutex lock; > > + > > + /* The platform device if this is registered, otherwise NULL */ > > + struct vkms_device *vkms_device; > > +}; > > + > > struct vkms_device_setup { > > - bool is_default; > > + // Is NULL in the case of the default card. > > + struct vkms_configfs *configfs; > > }; > > > > struct vkms_device { > > struct drm_device drm; > > struct platform_device *platform; > > - bool is_default; > > + // Is NULL in the case of the default card. > > + struct vkms_configfs *configfs; > > struct vkms_output output; > > struct vkms_config config; > > }; > > @@ -164,11 +217,42 @@ struct vkms_device { > > #define to_vkms_plane_state(target)\ > > container_of(target, struct vkms_plane_state, base.base) > > > > +#define item_to_configfs(item) \ > > + container_of(to_config_group(item), struct vkms_configfs, device_group) > > + > > +#define item_to_config_connector(item) \ > > + container_of(to_config_group(item), struct vkms_config_connector, \ > > + config_group) > > + > > +#define item_to_config_crtc(item) \ > > + container_of(to_config_group(item), struct vkms_config_crtc, \ > > + config_group) > > + > > +#define item_to_config_encoder(item) \ > > + container_of(to_config_group(item), struct vkms_config_encoder, \ > > + config_group) > > + > > +#define item_to_config_plane(item) \ > > + container_of(to_config_group(item), struct vkms_config_plane, \ > > + config_group) > > + > > +#define item_to_config_links(item) \ > > + container_of(to_config_group(item), struct vkms_config_links, group) > > + > > +#define plane_item_to_configfs(item) \ > > + container_of(to_config_group(item->ci_parent), struct vkms_configfs, \ > > + planes_group) > > + > > +/* Devices */ > > +struct vkms_device *vkms_add_device(struct vkms_configfs *configfs); > > +void vkms_remove_device(struct vkms_device *vkms_device); > > + > > /* CRTC */ > > struct vkms_crtc *vkms_crtc_init(struct vkms_device *vkmsdev, > > struct drm_plane *primary, > > struct drm_plane *cursor); > > > > +int vkms_output_init(struct vkms_device *vkmsdev); > > int vkms_output_init_default(struct vkms_device *vkmsdev); > > > > struct vkms_plane *vkms_plane_init(struct vkms_device *vkmsdev, > > @@ -191,4 +275,8 @@ void vkms_writeback_row(struct vkms_writeback_job *wb, const struct line_buffer > > int vkms_enable_writeback_connector(struct vkms_device *vkmsdev, > > struct vkms_crtc *vkms_crtc); > > > > +/* ConfigFS Support */ > > +int vkms_init_configfs(void); > > +void vkms_unregister_configfs(void); > > + > > #endif /* _VKMS_DRV_H_ */ > > diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c > > index bfc2e2362c6d..dc69959c5e1d 100644 > > --- a/drivers/gpu/drm/vkms/vkms_output.c > > +++ b/drivers/gpu/drm/vkms/vkms_output.c > > @@ -176,3 +176,8 @@ int vkms_output_init_default(struct vkms_device *vkmsdev) > > > > return ret; > > } > > + > > +int vkms_output_init(struct vkms_device *vkmsdev) > > +{ > > + return -EOPNOTSUPP; > > +} > > -- > > 2.42.0.rc1.204.g551eb34607-goog > >