[RFC PATCH 06/22] vhost: Introduce configfs entry for configuring VHOST

Kishon Vijay Abraham I
2020-07-02T08:22:39Z

Create a configfs entry for each entry populated in
"struct vhost_device_id" in the VHOST driver and create a configfs entry
for each VHOST device. This is used to link VHOST driver to VHOST device
(by assigning deviceID and vendorID to the VHOST device) and register
VHOST device, thereby letting VHOST client driver to be selected in the
userspace.

Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
drivers/vhost/Makefile | 2 +-
drivers/vhost/vhost.c | 63 +++++++
drivers/vhost/vhost_cfs.c | 354 ++++++++++++++++++++++++++++++++++++++
include/linux/vhost.h | 11 ++
4 files changed, 429 insertions(+), 1 deletion(-)
create mode 100644 drivers/vhost/vhost_cfs.c

diff --git a/drivers/vhost/Makefile b/drivers/vhost/Makefile
index f3e1897cce85..6520c820c896 100644
--- a/drivers/vhost/Makefile
+++ b/drivers/vhost/Makefile
@@ -13,7 +13,7 @@ obj-$(CONFIG_VHOST_RING) += vringh.o
obj-$(CONFIG_VHOST_VDPA) += vhost_vdpa.o
vhost_vdpa-y := vdpa.o

-obj-$(CONFIG_VHOST) += vhost.o
+obj-$(CONFIG_VHOST) += vhost.o vhost_cfs.o

obj-$(CONFIG_VHOST_IOTLB) += vhost_iotlb.o
vhost_iotlb-y := iotlb.o
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index 8a3ad4698393..539619208783 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -3091,6 +3091,64 @@ static struct bus_type vhost_bus_type = {
.remove = vhost_dev_remove,
};

+/**
+ * vhost_remove_cfs() - Remove configfs directory for vhost driver
+ * @driver: Vhost driver for which configfs directory has to be removed
+ *
+ * Remove configfs directory for vhost driver.
+ */
+static void vhost_remove_cfs(struct vhost_driver *driver)
+{
+ struct config_group *driver_group, *group;
+ struct config_item *item, *tmp;
+
+ driver_group = driver->group;
+
+ list_for_each_entry_safe(item, tmp, &driver_group->cg_children,
+ ci_entry) {
+ group = to_config_group(item);
+ vhost_cfs_remove_driver_item(group);
+ }
+
+ vhost_cfs_remove_driver_group(driver_group);
+}
+
+/**
+ * vhost_add_cfs() - Add configfs directory for vhost driver
+ * @driver: Vhost driver for which configfs directory has to be added
+ *
+ * Add configfs directory for vhost driver.
+ */
+static int vhost_add_cfs(struct vhost_driver *driver)
+{
+ struct config_group *driver_group, *group;
+ const struct vhost_device_id *ids;
+ int ret, i;
+
+ driver_group = vhost_cfs_add_driver_group(driver->driver.name);
+ if (IS_ERR(driver_group))
+ return PTR_ERR(driver_group);
+
+ driver->group = driver_group;
+
+ ids = driver->id_table;
+ for (i = 0; ids[i].device; i++) {
+ group = vhost_cfs_add_driver_item(driver_group, ids[i].vendor,
+ ids[i].device);
+ if (IS_ERR(group)) {
+ ret = PTR_ERR(driver_group);
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ vhost_remove_cfs(driver);
+
+ return ret;
+}
+
/**
* vhost_register_driver() - Register a vhost driver
* @driver: Vhost driver that has to be registered
@@ -3107,6 +3165,10 @@ int vhost_register_driver(struct vhost_driver *driver)
if (ret)
return ret;

+ ret = vhost_add_cfs(driver);
+ if (ret)
+ return ret;
+
return 0;
}
EXPORT_SYMBOL_GPL(vhost_register_driver);
@@ -3119,6 +3181,7 @@ EXPORT_SYMBOL_GPL(vhost_register_driver);
*/
void vhost_unregister_driver(struct vhost_driver *driver)
{
+ vhost_remove_cfs(driver);
driver_unregister(&driver->driver);
}
EXPORT_SYMBOL_GPL(vhost_unregister_driver);
diff --git a/drivers/vhost/vhost_cfs.c b/drivers/vhost/vhost_cfs.c
new file mode 100644
index 000000000000..ae46e71968f1
--- /dev/null
+++ b/drivers/vhost/vhost_cfs.c
@@ -0,0 +1,354 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * configfs to configure VHOST
+ *
+ * Copyright (C) 2020 Texas Instruments
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ */
+
+#include <linux/configfs.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/vhost.h>
+#include <linux/slab.h>
+
+/* VHOST driver like net, scsi etc., */
+static struct config_group *vhost_driver_group;
+
+/* VHOST device like PCIe EP, NTB etc., */
+static struct config_group *vhost_device_group;
+
+struct vhost_driver_item {
+ struct config_group group;
+ u32 vendor;
+ u32 device;
+};
+
+struct vhost_driver_group {
+ struct config_group group;
+};
+
+struct vhost_device_item {
+ struct config_group group;
+ struct vhost_dev *vdev;
+};
+
+static inline
+struct vhost_driver_item *to_vhost_driver_item(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct vhost_driver_item,
+ group);
+}
+
+static inline
+struct vhost_device_item *to_vhost_device_item(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct vhost_device_item,
+ group);
+}
+
+/**
+ * vhost_cfs_device_link() - Create softlink of driver directory to device
+ * directory
+ * @device_item: Represents configfs entry of vhost_dev
+ * @driver_item: Represents configfs of a particular entry of
+ * vhost_device_id table in vhost driver
+ *
+ * Bind a vhost driver to vhost device in order to assign a particular
+ * device ID and vendor ID
+ */
+static int vhost_cfs_device_link(struct config_item *device_item,
+ struct config_item *driver_item)
+{
+ struct vhost_driver_item *vdriver_item;
+ struct vhost_device_item *vdevice_item;
+ struct vhost_dev *vdev;
+ int ret;
+
+ vdriver_item = to_vhost_driver_item(driver_item);
+ vdevice_item = to_vhost_device_item(device_item);
+
+ vdev = vdevice_item->vdev;
+ vdev->id.device = vdriver_item->device;
+ vdev->id.vendor = vdriver_item->vendor;
+
+ ret = vhost_register_device(vdev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/**
+ * vhost_cfs_device_unlink() - Delete softlink of driver directory from device
+ * directory
+ * @device_item: Represents configfs entry of vhost_dev
+ * @driver_item: Represents configfs of a particular entry of
+ * vhost_device_id table in vhost driver
+ *
+ * Un-bind vhost driver from vhost device.
+ */
+static void vhost_cfs_device_unlink(struct config_item *device_item,
+ struct config_item *driver_item)
+{
+ struct vhost_driver_item *vdriver_item;
+ struct vhost_device_item *vdevice_item;
+ struct vhost_dev *vdev;
+
+ vdriver_item = to_vhost_driver_item(driver_item);
+ vdevice_item = to_vhost_device_item(device_item);
+
+ vdev = vdevice_item->vdev;
+ vhost_unregister_device(vdev);
+}
+
+static struct configfs_item_operations vhost_cfs_device_item_ops = {
+ .allow_link = vhost_cfs_device_link,
+ .drop_link = vhost_cfs_device_unlink,
+};
+
+static const struct config_item_type vhost_cfs_device_item_type = {
+ .ct_item_ops = &vhost_cfs_device_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+/**
+ * vhost_cfs_add_device_item() - Create configfs directory for new vhost_dev
+ * @vdev: vhost device for which configfs directory has to be created
+ *
+ * Create configfs directory for new vhost device. Drivers that create
+ * vhost device can invoke this API if they require the vhost device to
+ * be assigned a device ID and vendorID by the user.
+ */
+struct config_group *vhost_cfs_add_device_item(struct vhost_dev *vdev)
+{
+ struct device *dev = &vdev->dev;
+ struct vhost_device_item *vdevice_item;
+ struct config_group *group;
+ const char *name;
+ int ret;
+
+ vdevice_item = kzalloc(sizeof(*vdevice_item), GFP_KERNEL);
+ if (!vdevice_item)
+ return ERR_PTR(-ENOMEM);
+
+ name = dev_name(dev->parent);
+ group = &vdevice_item->group;
+ config_group_init_type_name(group, name, &vhost_cfs_device_item_type);
+
+ ret = configfs_register_group(vhost_device_group, group);
+ if (ret)
+ return ERR_PTR(ret);
+
+ vdevice_item->vdev = vdev;
+
+ return group;
+}
+EXPORT_SYMBOL(vhost_cfs_add_device_item);
+
+/**
+ * vhost_cfs_remove_device_item() - Remove configfs directory for the vhost_dev
+ * @vdev: vhost device for which configfs directory has to be removed
+ *
+ * Remove configfs directory for the vhost device.
+ */
+void vhost_cfs_remove_device_item(struct config_group *group)
+{
+ struct vhost_device_item *vdevice_item;
+
+ if (!group)
+ return;
+
+ vdevice_item = container_of(group, struct vhost_device_item, group);
+ configfs_unregister_group(&vdevice_item->group);
+ kfree(vdevice_item);
+}
+EXPORT_SYMBOL(vhost_cfs_remove_device_item);
+
+static const struct config_item_type vhost_driver_item_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+/**
+ * vhost_cfs_add_driver_item() - Add configfs directory for an entry in
+ * vhost_device_id
+ * @driver_group: configfs directory corresponding to the vhost driver
+ * @vendor: vendor ID populated in vhost_device_id table by vhost driver
+ * @device: device ID populated in vhost_device_id table by vhost driver
+ *
+ * Add configfs directory for each entry in vhost_device_id populated by
+ * vhost driver. Store the device ID and vendor ID in a local data structure
+ * and use it when user links this directory with a vhost device configfs
+ * directory.
+ */
+struct config_group *
+vhost_cfs_add_driver_item(struct config_group *driver_group, u32 vendor,
+ u32 device)
+{
+ struct vhost_driver_item *vdriver_item;
+ struct config_group *group;
+ char name[20];
+ int ret;
+
+ vdriver_item = kzalloc(sizeof(*vdriver_item), GFP_KERNEL);
+ if (!vdriver_item)
+ return ERR_PTR(-ENOMEM);
+
+ vdriver_item->vendor = vendor;
+ vdriver_item->device = device;
+
+ snprintf(name, sizeof(name), "%08x:%08x", vendor, device);
+ group = &vdriver_item->group;
+
+ config_group_init_type_name(group, name, &vhost_driver_item_type);
+ ret = configfs_register_group(driver_group, group);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return group;
+}
+EXPORT_SYMBOL(vhost_cfs_add_driver_item);
+
+/**
+ * vhost_cfs_remove_driver_item() - Remove configfs directory corresponding
+ * to an entry in vhost_device_id
+ * @group: Configfs group corresponding to an entry in vhost_device_id
+ *
+ * Remove configfs directory corresponding to an entry in vhost_device_id
+ */
+void vhost_cfs_remove_driver_item(struct config_group *group)
+{
+ struct vhost_driver_item *vdriver_item;
+
+ if (!group)
+ return;
+
+ vdriver_item = container_of(group, struct vhost_driver_item, group);
+ configfs_unregister_group(&vdriver_item->group);
+ kfree(vdriver_item);
+}
+EXPORT_SYMBOL(vhost_cfs_remove_driver_item);
+
+static const struct config_item_type vhost_driver_group_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+/**
+ * vhost_cfs_add_driver_group() - Add configfs directory for vhost driver
+ * @name: Name of the vhost driver as populated in driver structure
+ *
+ * Add configfs directory for vhost driver.
+ */
+struct config_group *vhost_cfs_add_driver_group(const char *name)
+{
+ struct vhost_driver_group *vdriver_group;
+ struct config_group *group;
+
+ vdriver_group = kzalloc(sizeof(*vdriver_group), GFP_KERNEL);
+ if (!vdriver_group)
+ return ERR_PTR(-ENOMEM);
+
+ group = &vdriver_group->group;
+
+ config_group_init_type_name(group, name, &vhost_driver_group_type);
+ configfs_register_group(vhost_driver_group, group);
+
+ return group;
+}
+EXPORT_SYMBOL(vhost_cfs_add_driver_group);
+
+/**
+ * vhost_cfs_remove_driver_group() - Remove configfs directory for vhost driver
+ * @group: Configfs group corresponding to the vhost driver
+ *
+ * Remove configfs directory for vhost driver.
+ */
+void vhost_cfs_remove_driver_group(struct config_group *group)
+{
+ if (IS_ERR_OR_NULL(group))
+ return;
+
+ configfs_unregister_default_group(group);
+}
+EXPORT_SYMBOL(vhost_cfs_remove_driver_group);
+
+static const struct config_item_type vhost_driver_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static const struct config_item_type vhost_device_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static const struct config_item_type vhost_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static struct configfs_subsystem vhost_cfs_subsys = {
+ .su_group = {
+ .cg_item = {
+ .ci_namebuf = "vhost",
+ .ci_type = &vhost_type,
+ },
+ },
+ .su_mutex = __MUTEX_INITIALIZER(vhost_cfs_subsys.su_mutex),
+};
+
+static int __init vhost_cfs_init(void)
+{
+ int ret;
+ struct config_group *root = &vhost_cfs_subsys.su_group;
+
+ config_group_init(root);
+
+ ret = configfs_register_subsystem(&vhost_cfs_subsys);
+ if (ret) {
+ pr_err("Error %d while registering subsystem %s\n",
+ ret, root->cg_item.ci_namebuf);
+ goto err;
+ }
+
+ vhost_driver_group =
+ configfs_register_default_group(root, "vhost-client",
+ &vhost_driver_type);
+ if (IS_ERR(vhost_driver_group)) {
+ ret = PTR_ERR(vhost_driver_group);
+ pr_err("Error %d while registering channel group\n",
+ ret);
+ goto err_vhost_driver_group;
+ }
+
+ vhost_device_group =
+ configfs_register_default_group(root, "vhost-transport",
+ &vhost_device_type);
+ if (IS_ERR(vhost_device_group)) {
+ ret = PTR_ERR(vhost_device_group);
+ pr_err("Error %d while registering virtproc group\n",
+ ret);
+ goto err_vhost_device_group;
+ }
+
+ return 0;
+
+err_vhost_device_group:
+ configfs_unregister_default_group(vhost_driver_group);
+
+err_vhost_driver_group:
+ configfs_unregister_subsystem(&vhost_cfs_subsys);
+
+err:
+ return ret;
+}
+module_init(vhost_cfs_init);
+
+static void __exit vhost_cfs_exit(void)
+{
+ configfs_unregister_default_group(vhost_device_group);
+ configfs_unregister_default_group(vhost_driver_group);
+ configfs_unregister_subsystem(&vhost_cfs_subsys);
+}
+module_exit(vhost_cfs_exit);
+
+MODULE_DESCRIPTION("PCI VHOST CONFIGFS");
+MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/vhost.h b/include/linux/vhost.h
index 8efb9829c1b1..be9341ffd266 100644
--- a/include/linux/vhost.h
+++ b/include/linux/vhost.h
@@ -2,6 +2,7 @@
#ifndef _VHOST_H
#define _VHOST_H

+#include <linux/configfs.h>
#include <linux/eventfd.h>
#include <linux/mm.h>
#include <linux/mutex.h>
@@ -181,6 +182,7 @@ struct vhost_config_ops {
struct vhost_driver {
struct device_driver driver;
struct vhost_device_id *id_table;
+ struct config_group *group;
int (*probe)(struct vhost_dev *dev);
int (*remove)(struct vhost_dev *dev);
};
@@ -233,6 +235,15 @@ void vhost_unregister_driver(struct vhost_driver *driver);
int vhost_register_device(struct vhost_dev *vdev);
void vhost_unregister_device(struct vhost_dev *vdev);

+struct config_group *vhost_cfs_add_driver_group(const char *name);
+void vhost_cfs_remove_driver_group(struct config_group *group);
+struct config_group *
+vhost_cfs_add_driver_item(struct config_group *driver_group, u32 vendor,
+ u32 device);
+void vhost_cfs_remove_driver_item(struct config_group *group);
+struct config_group *vhost_cfs_add_device_item(struct vhost_dev *vdev);
+void vhost_cfs_remove_device_item(struct config_group *group);
+
int vhost_create_vqs(struct vhost_dev *vdev, unsigned int nvqs,
unsigned int num_bufs, struct vhost_virtqueue *vqs[],
vhost_vq_callback_t *callbacks[],
--
2.17.1