[PATCH v5 4/7] drm/vkms: Add ConfigFS scaffolding to VKMS

Brandon Ross Pollack brpol at chromium.org
Tue Aug 29 04:49:02 UTC 2023


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 at collabora.com> 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 at chromium.org>
> >
> > 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 at chromium.org>
> > Signed-off-by: Brandon Pollack <brpol at chromium.org>
> > ---
> >  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
> >


More information about the dri-devel mailing list