All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kishon Vijay Abraham I <kishon@ti.com>
To: Ohad Ben-Cohen <ohad@wizery.com>,
	Bjorn Andersson <bjorn.andersson@linaro.org>,
	Jon Mason <jdmason@kudzu.us>, Dave Jiang <dave.jiang@intel.com>,
	Allen Hubbe <allenbh@gmail.com>,
	Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>,
	Bjorn Helgaas <bhelgaas@google.com>,
	"Michael S. Tsirkin" <mst@redhat.com>,
	Jason Wang <jasowang@redhat.com>,
	Paolo Bonzini <pbonzini@redhat.com>,
	Stefan Hajnoczi <stefanha@redhat.com>,
	Stefano Garzarella <sgarzare@redhat.com>
Cc: <linux-doc@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
	<linux-remoteproc@vger.kernel.org>, <linux-ntb@googlegroups.com>,
	<linux-pci@vger.kernel.org>, <kvm@vger.kernel.org>,
	<virtualization@lists.linux-foundation.org>,
	<netdev@vger.kernel.org>
Subject: [RFC PATCH 09/22] rpmsg: Introduce configfs entry for configuring rpmsg
Date: Thu, 2 Jul 2020 13:51:30 +0530	[thread overview]
Message-ID: <20200702082143.25259-10-kishon@ti.com> (raw)
In-Reply-To: <20200702082143.25259-1-kishon@ti.com>

Create a configfs entry for each "struct rpmsg_device_id" populated
in rpmsg client driver and create a configfs entry for each rpmsg
device. This will be used to bind a rpmsg client driver to a rpmsg
device in order to create a new rpmsg channel.

This is used for creating channel for VHOST based rpmsg bus (channels
are created in VIRTIO based bus during namespace announcement).

Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
 drivers/rpmsg/Makefile         |   2 +-
 drivers/rpmsg/rpmsg_cfs.c      | 394 +++++++++++++++++++++++++++++++++
 drivers/rpmsg/rpmsg_core.c     |   7 +
 drivers/rpmsg/rpmsg_internal.h |  16 ++
 include/linux/rpmsg.h          |   5 +
 5 files changed, 423 insertions(+), 1 deletion(-)
 create mode 100644 drivers/rpmsg/rpmsg_cfs.c

diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile
index ae92a7fb08f6..047acfda518a 100644
--- a/drivers/rpmsg/Makefile
+++ b/drivers/rpmsg/Makefile
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_RPMSG)		+= rpmsg_core.o
+obj-$(CONFIG_RPMSG)		+= rpmsg_core.o rpmsg_cfs.o
 obj-$(CONFIG_RPMSG_CHAR)	+= rpmsg_char.o
 obj-$(CONFIG_RPMSG_MTK_SCP)	+= mtk_rpmsg.o
 obj-$(CONFIG_RPMSG_QCOM_GLINK_RPM) += qcom_glink_rpm.o
diff --git a/drivers/rpmsg/rpmsg_cfs.c b/drivers/rpmsg/rpmsg_cfs.c
new file mode 100644
index 000000000000..a5c77aba00ee
--- /dev/null
+++ b/drivers/rpmsg/rpmsg_cfs.c
@@ -0,0 +1,394 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * configfs to configure RPMSG
+ *
+ * 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/idr.h>
+#include <linux/slab.h>
+
+#include "rpmsg_internal.h"
+
+static struct config_group *channel_group;
+static struct config_group *virtproc_group;
+
+enum rpmsg_channel_status {
+	STATUS_FREE,
+	STATUS_BUSY,
+};
+
+struct rpmsg_channel {
+	struct config_item item;
+	struct device *dev;
+	enum rpmsg_channel_status status;
+};
+
+struct rpmsg_channel_group {
+	struct config_group group;
+};
+
+struct rpmsg_virtproc_group {
+	struct config_group group;
+	struct device *dev;
+	const struct rpmsg_virtproc_ops *ops;
+};
+
+static inline
+struct rpmsg_channel *to_rpmsg_channel(struct config_item *channel_item)
+{
+	return container_of(channel_item, struct rpmsg_channel, item);
+}
+
+static inline struct rpmsg_channel_group
+*to_rpmsg_channel_group(struct config_group *channel_group)
+{
+	return container_of(channel_group, struct rpmsg_channel_group, group);
+}
+
+static inline
+struct rpmsg_virtproc_group *to_rpmsg_virtproc_group(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct rpmsg_virtproc_group,
+			    group);
+}
+
+/**
+ * rpmsg_virtproc_channel_link() - Create softlink of rpmsg client device
+ *   directory to virtproc configfs directory
+ * @virtproc_item: Config item representing configfs entry of virtual remote
+ *   processor
+ * @channel_item: Config item representing configfs entry of rpmsg client
+ *   driver
+ *
+ * Bind rpmsg client device to virtual remote processor by creating softlink
+ * between rpmsg client device directory to virtproc configfs directory
+ * in order to create a new rpmsg channel.
+ */
+static int rpmsg_virtproc_channel_link(struct config_item *virtproc_item,
+				       struct config_item *channel_item)
+{
+	struct rpmsg_virtproc_group *vgroup;
+	struct rpmsg_channel *channel;
+	struct config_group *cgroup;
+	struct device *dev;
+
+	vgroup = to_rpmsg_virtproc_group(virtproc_item);
+	channel = to_rpmsg_channel(channel_item);
+
+	if (channel->status == STATUS_BUSY)
+		return -EBUSY;
+
+	cgroup = channel_item->ci_group;
+
+	if (vgroup->ops && vgroup->ops->create_channel) {
+		dev = vgroup->ops->create_channel(vgroup->dev,
+						  cgroup->cg_item.ci_name);
+		if (IS_ERR_OR_NULL(dev))
+			return PTR_ERR(dev);
+	}
+
+	channel->dev = dev;
+	channel->status = STATUS_BUSY;
+
+	return 0;
+}
+
+/**
+ * rpmsg_virtproc_channel_unlink() - Remove softlink of rpmsg client device
+ *   directory from virtproc configfs directory
+ * @virtproc_item: Config item representing configfs entry of virtual remote
+ *   processor
+ * @channel_item: Config item representing configfs entry of rpmsg client
+ *   driver
+ *
+ * Unbind rpmsg client device from virtual remote processor by removing softlink
+ * of rpmsg client device directory from virtproc configfs directory which
+ * deletes the rpmsg channel.
+ */
+static void rpmsg_virtproc_channel_unlink(struct config_item *virtproc_item,
+					  struct config_item *channel_item)
+{
+	struct rpmsg_virtproc_group *vgroup;
+	struct rpmsg_channel *channel;
+
+	channel = to_rpmsg_channel(channel_item);
+	vgroup = to_rpmsg_virtproc_group(virtproc_item);
+
+	if (vgroup->ops && vgroup->ops->delete_channel)
+		vgroup->ops->delete_channel(channel->dev);
+
+	channel->status = STATUS_FREE;
+}
+
+static struct configfs_item_operations rpmsg_virtproc_item_ops = {
+	.allow_link	= rpmsg_virtproc_channel_link,
+	.drop_link	= rpmsg_virtproc_channel_unlink,
+};
+
+static const struct config_item_type rpmsg_virtproc_item_type = {
+	.ct_item_ops	= &rpmsg_virtproc_item_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+/**
+ * rpmsg_cfs_add_virtproc_group() - Add new configfs directory for virtproc
+ *   device
+ * @dev: Device representing the virtual remote processor
+ * @ops: rpmsg_virtproc_ops to create or delete rpmsg channel
+ *
+ * Add new configfs directory for virtproc device. The rpmsg client driver's
+ * configfs entry can be linked with this directory for creating a new
+ * rpmsg channel and the link can be removed for deleting the rpmsg channel.
+ */
+struct config_group *
+rpmsg_cfs_add_virtproc_group(struct device *dev,
+			     const struct rpmsg_virtproc_ops *ops)
+{
+	struct rpmsg_virtproc_group *vgroup;
+	struct config_group *group;
+	struct device *vdev;
+	int ret;
+
+	vgroup = kzalloc(sizeof(*vgroup), GFP_KERNEL);
+	if (!vgroup)
+		return ERR_PTR(-ENOMEM);
+
+	group = &vgroup->group;
+	config_group_init_type_name(group, dev_name(dev),
+				    &rpmsg_virtproc_item_type);
+	ret = configfs_register_group(virtproc_group, group);
+	if (ret)
+		goto err_register_group;
+
+	if (!try_module_get(ops->owner)) {
+		ret = -EPROBE_DEFER;
+		goto err_module_get;
+	}
+
+	vdev = get_device(dev);
+	vgroup->dev = vdev;
+	vgroup->ops = ops;
+
+	return group;
+
+err_module_get:
+	configfs_unregister_group(group);
+
+err_register_group:
+	kfree(vgroup);
+
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(rpmsg_cfs_add_virtproc_group);
+
+/**
+ * rpmsg_cfs_remove_virtproc_group() - Remove the configfs directory for
+ *   virtproc device
+ * @group: config_group of the virtproc device
+ *
+ * Remove the configfs directory for virtproc device.
+ */
+void rpmsg_cfs_remove_virtproc_group(struct config_group *group)
+{
+	struct rpmsg_virtproc_group *vgroup;
+
+	if (!group)
+		return;
+
+	vgroup = container_of(group, struct rpmsg_virtproc_group, group);
+	put_device(vgroup->dev);
+	module_put(vgroup->ops->owner);
+	configfs_unregister_group(&vgroup->group);
+	kfree(vgroup);
+}
+EXPORT_SYMBOL(rpmsg_cfs_remove_virtproc_group);
+
+static const struct config_item_type rpmsg_channel_item_type = {
+	.ct_owner	= THIS_MODULE,
+};
+
+/**
+ * rpmsg_channel_make() - Allow user to create sub-directory of rpmsg client
+ *   driver
+ * @name: Name of the sub-directory created by the user.
+ *
+ * Invoked when user creates a sub-directory to the configfs directory
+ * representing the rpmsg client driver. This can be linked with the virtproc
+ * directory for creating a new rpmsg channel.
+ */
+static struct config_item *
+rpmsg_channel_make(struct config_group *group, const char *name)
+{
+	struct rpmsg_channel *channel;
+
+	channel = kzalloc(sizeof(*channel), GFP_KERNEL);
+	if (!channel)
+		return ERR_PTR(-ENOMEM);
+
+	channel->status = STATUS_FREE;
+
+	config_item_init_type_name(&channel->item, name, &rpmsg_channel_item_type);
+	return &channel->item;
+}
+
+/**
+ * rpmsg_channel_drop() - Allow user to delete sub-directory of rpmsg client
+ *   driver
+ * @item: Config item representing the sub-directory the user created returned
+ *   by rpmsg_channel_make()
+ *
+ * Invoked when user creates a sub-directory to the configfs directory
+ * representing the rpmsg client driver. This can be linked with the virtproc
+ * directory for creating a new rpmsg channel.
+ */
+static void rpmsg_channel_drop(struct config_group *group, struct config_item *item)
+{
+	struct rpmsg_channel *channel;
+
+	channel = to_rpmsg_channel(item);
+	kfree(channel);
+}
+
+static struct configfs_group_operations rpmsg_channel_group_ops = {
+	.make_item     = &rpmsg_channel_make,
+	.drop_item      = &rpmsg_channel_drop,
+};
+
+static const struct config_item_type rpmsg_channel_group_type = {
+	.ct_group_ops	= &rpmsg_channel_group_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+/**
+ * rpmsg_cfs_add_channel_group() - Create a configfs directory for each
+ *   registered rpmsg client driver
+ * @name: The name of the rpmsg client driver
+ *
+ * Create a configfs directory for each registered rpmsg client driver. The
+ * user can create sub-directory within this directory for creating
+ * rpmsg channels to be used by the rpmsg client driver.
+ */
+struct config_group *rpmsg_cfs_add_channel_group(const char *name)
+{
+	struct rpmsg_channel_group *cgroup;
+	struct config_group *group;
+	int ret;
+
+	cgroup = kzalloc(sizeof(*cgroup), GFP_KERNEL);
+	if (!cgroup)
+		return ERR_PTR(-ENOMEM);
+
+	group = &cgroup->group;
+	config_group_init_type_name(group, name, &rpmsg_channel_group_type);
+	ret = configfs_register_group(channel_group, group);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return group;
+}
+EXPORT_SYMBOL(rpmsg_cfs_add_channel_group);
+
+/**
+ * rpmsg_cfs_remove_channel_group() - Remove the configfs directory associated
+ *   with the rpmsg client driver
+ * @group: Config group representing the rpmsg client driver
+ *
+ * Remove the configfs directory associated with the rpmsg client driver.
+ */
+void rpmsg_cfs_remove_channel_group(struct config_group *group)
+{
+	struct rpmsg_channel_group *cgroup;
+
+	if (IS_ERR_OR_NULL(group))
+		return;
+
+	cgroup = to_rpmsg_channel_group(group);
+	configfs_unregister_default_group(group);
+	kfree(cgroup);
+}
+EXPORT_SYMBOL(rpmsg_cfs_remove_channel_group);
+
+static const struct config_item_type rpmsg_channel_type = {
+	.ct_owner	= THIS_MODULE,
+};
+
+static const struct config_item_type rpmsg_virtproc_type = {
+	.ct_owner	= THIS_MODULE,
+};
+
+static const struct config_item_type rpmsg_type = {
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct configfs_subsystem rpmsg_cfs_subsys = {
+	.su_group = {
+		.cg_item = {
+			.ci_namebuf = "rpmsg",
+			.ci_type = &rpmsg_type,
+		},
+	},
+	.su_mutex = __MUTEX_INITIALIZER(rpmsg_cfs_subsys.su_mutex),
+};
+
+static int __init rpmsg_cfs_init(void)
+{
+	int ret;
+	struct config_group *root = &rpmsg_cfs_subsys.su_group;
+
+	config_group_init(root);
+
+	ret = configfs_register_subsystem(&rpmsg_cfs_subsys);
+	if (ret) {
+		pr_err("Error %d while registering subsystem %s\n",
+		       ret, root->cg_item.ci_namebuf);
+		goto err;
+	}
+
+	channel_group = configfs_register_default_group(root, "channel",
+							&rpmsg_channel_type);
+	if (IS_ERR(channel_group)) {
+		ret = PTR_ERR(channel_group);
+		pr_err("Error %d while registering channel group\n",
+		       ret);
+		goto err_channel_group;
+	}
+
+	virtproc_group =
+		configfs_register_default_group(root, "virtproc",
+						&rpmsg_virtproc_type);
+	if (IS_ERR(virtproc_group)) {
+		ret = PTR_ERR(virtproc_group);
+		pr_err("Error %d while registering virtproc group\n",
+		       ret);
+		goto err_virtproc_group;
+	}
+
+	return 0;
+
+err_virtproc_group:
+	configfs_unregister_default_group(channel_group);
+
+err_channel_group:
+	configfs_unregister_subsystem(&rpmsg_cfs_subsys);
+
+err:
+	return ret;
+}
+module_init(rpmsg_cfs_init);
+
+static void __exit rpmsg_cfs_exit(void)
+{
+	configfs_unregister_default_group(virtproc_group);
+	configfs_unregister_default_group(channel_group);
+	configfs_unregister_subsystem(&rpmsg_cfs_subsys);
+}
+module_exit(rpmsg_cfs_exit);
+
+MODULE_DESCRIPTION("PCI RPMSG CONFIGFS");
+MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c
index e330ec4dfc33..68569fec03e2 100644
--- a/drivers/rpmsg/rpmsg_core.c
+++ b/drivers/rpmsg/rpmsg_core.c
@@ -563,8 +563,15 @@ EXPORT_SYMBOL(rpmsg_unregister_device);
  */
 int __register_rpmsg_driver(struct rpmsg_driver *rpdrv, struct module *owner)
 {
+	const struct rpmsg_device_id *ids = rpdrv->id_table;
 	rpdrv->drv.bus = &rpmsg_bus;
 	rpdrv->drv.owner = owner;
+
+	while (ids && ids->name[0]) {
+		rpmsg_cfs_add_channel_group(ids->name);
+		ids++;
+	}
+
 	return driver_register(&rpdrv->drv);
 }
 EXPORT_SYMBOL(__register_rpmsg_driver);
diff --git a/drivers/rpmsg/rpmsg_internal.h b/drivers/rpmsg/rpmsg_internal.h
index 3fc83cd50e98..39b3a5caf242 100644
--- a/drivers/rpmsg/rpmsg_internal.h
+++ b/drivers/rpmsg/rpmsg_internal.h
@@ -68,6 +68,18 @@ struct rpmsg_endpoint_ops {
 			     poll_table *wait);
 };
 
+/**
+ * struct rpmsg_virtproc_ops - indirection table for rpmsg_virtproc operations
+ * @create_channel: Create a new rpdev channel
+ * @delete_channel: Delete the rpdev channel
+ * @owner: Owner of the module holding the ops
+ */
+struct rpmsg_virtproc_ops {
+	struct device *(*create_channel)(struct device *dev, const char *name);
+	void (*delete_channel)(struct device *dev);
+	struct module *owner;
+};
+
 int rpmsg_register_device(struct rpmsg_device *rpdev);
 int rpmsg_unregister_device(struct device *parent,
 			    struct rpmsg_channel_info *chinfo);
@@ -75,6 +87,10 @@ int rpmsg_unregister_device(struct device *parent,
 struct device *rpmsg_find_device(struct device *parent,
 				 struct rpmsg_channel_info *chinfo);
 
+struct config_group *
+rpmsg_cfs_add_virtproc_group(struct device *dev,
+			     const struct rpmsg_virtproc_ops *ops);
+
 /**
  * rpmsg_chrdev_register_device() - register chrdev device based on rpdev
  * @rpdev:	prepared rpdev to be used for creating endpoints
diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h
index 9fe156d1c018..b9d9283b46ac 100644
--- a/include/linux/rpmsg.h
+++ b/include/linux/rpmsg.h
@@ -135,6 +135,7 @@ int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst,
 __poll_t rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp,
 			poll_table *wait);
 
+struct config_group *rpmsg_cfs_add_channel_group(const char *name);
 #else
 
 static inline int register_rpmsg_device(struct rpmsg_device *dev)
@@ -242,6 +243,10 @@ static inline __poll_t rpmsg_poll(struct rpmsg_endpoint *ept,
 	return 0;
 }
 
+static inline struct config_group *rpmsg_cfs_add_channel_group(const char *name)
+{
+	return NULL;
+}
 #endif /* IS_ENABLED(CONFIG_RPMSG) */
 
 /* use a macro to avoid include chaining to get THIS_MODULE */
-- 
2.17.1


WARNING: multiple messages have this Message-ID (diff)
From: Kishon Vijay Abraham I <kishon@ti.com>
To: Ohad Ben-Cohen <ohad@wizery.com>,
	Bjorn Andersson <bjorn.andersson@linaro.org>,
	Jon Mason <jdmason@kudzu.us>, Dave Jiang <dave.jiang@intel.com>,
	Allen Hubbe <allenbh@gmail.com>,
	Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>,
	Bjorn Helgaas <bhelgaas@google.com>,
	"Michael S. Tsirkin" <mst@redhat.com>,
	Jason Wang <jasowang@redhat.com>,
	Paolo Bonzini <pbonzini@redhat.com>,
	Stefan Hajnoczi <stefanha@redhat.com>,
	Stefano Garzarella <sgarzare@redhat.com>
Cc: linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-remoteproc@vger.kernel.org, linux-ntb@googlegroups.com,
	linux-pci@vger.kernel.org, kvm@vger.kernel.org,
	virtualization@lists.linux-foundation.org,
	netdev@vger.kernel.org
Subject: [RFC PATCH 09/22] rpmsg: Introduce configfs entry for configuring rpmsg
Date: Thu, 2 Jul 2020 13:51:30 +0530	[thread overview]
Message-ID: <20200702082143.25259-10-kishon@ti.com> (raw)
In-Reply-To: <20200702082143.25259-1-kishon@ti.com>

Create a configfs entry for each "struct rpmsg_device_id" populated
in rpmsg client driver and create a configfs entry for each rpmsg
device. This will be used to bind a rpmsg client driver to a rpmsg
device in order to create a new rpmsg channel.

This is used for creating channel for VHOST based rpmsg bus (channels
are created in VIRTIO based bus during namespace announcement).

Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
 drivers/rpmsg/Makefile         |   2 +-
 drivers/rpmsg/rpmsg_cfs.c      | 394 +++++++++++++++++++++++++++++++++
 drivers/rpmsg/rpmsg_core.c     |   7 +
 drivers/rpmsg/rpmsg_internal.h |  16 ++
 include/linux/rpmsg.h          |   5 +
 5 files changed, 423 insertions(+), 1 deletion(-)
 create mode 100644 drivers/rpmsg/rpmsg_cfs.c

diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile
index ae92a7fb08f6..047acfda518a 100644
--- a/drivers/rpmsg/Makefile
+++ b/drivers/rpmsg/Makefile
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_RPMSG)		+= rpmsg_core.o
+obj-$(CONFIG_RPMSG)		+= rpmsg_core.o rpmsg_cfs.o
 obj-$(CONFIG_RPMSG_CHAR)	+= rpmsg_char.o
 obj-$(CONFIG_RPMSG_MTK_SCP)	+= mtk_rpmsg.o
 obj-$(CONFIG_RPMSG_QCOM_GLINK_RPM) += qcom_glink_rpm.o
diff --git a/drivers/rpmsg/rpmsg_cfs.c b/drivers/rpmsg/rpmsg_cfs.c
new file mode 100644
index 000000000000..a5c77aba00ee
--- /dev/null
+++ b/drivers/rpmsg/rpmsg_cfs.c
@@ -0,0 +1,394 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * configfs to configure RPMSG
+ *
+ * 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/idr.h>
+#include <linux/slab.h>
+
+#include "rpmsg_internal.h"
+
+static struct config_group *channel_group;
+static struct config_group *virtproc_group;
+
+enum rpmsg_channel_status {
+	STATUS_FREE,
+	STATUS_BUSY,
+};
+
+struct rpmsg_channel {
+	struct config_item item;
+	struct device *dev;
+	enum rpmsg_channel_status status;
+};
+
+struct rpmsg_channel_group {
+	struct config_group group;
+};
+
+struct rpmsg_virtproc_group {
+	struct config_group group;
+	struct device *dev;
+	const struct rpmsg_virtproc_ops *ops;
+};
+
+static inline
+struct rpmsg_channel *to_rpmsg_channel(struct config_item *channel_item)
+{
+	return container_of(channel_item, struct rpmsg_channel, item);
+}
+
+static inline struct rpmsg_channel_group
+*to_rpmsg_channel_group(struct config_group *channel_group)
+{
+	return container_of(channel_group, struct rpmsg_channel_group, group);
+}
+
+static inline
+struct rpmsg_virtproc_group *to_rpmsg_virtproc_group(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct rpmsg_virtproc_group,
+			    group);
+}
+
+/**
+ * rpmsg_virtproc_channel_link() - Create softlink of rpmsg client device
+ *   directory to virtproc configfs directory
+ * @virtproc_item: Config item representing configfs entry of virtual remote
+ *   processor
+ * @channel_item: Config item representing configfs entry of rpmsg client
+ *   driver
+ *
+ * Bind rpmsg client device to virtual remote processor by creating softlink
+ * between rpmsg client device directory to virtproc configfs directory
+ * in order to create a new rpmsg channel.
+ */
+static int rpmsg_virtproc_channel_link(struct config_item *virtproc_item,
+				       struct config_item *channel_item)
+{
+	struct rpmsg_virtproc_group *vgroup;
+	struct rpmsg_channel *channel;
+	struct config_group *cgroup;
+	struct device *dev;
+
+	vgroup = to_rpmsg_virtproc_group(virtproc_item);
+	channel = to_rpmsg_channel(channel_item);
+
+	if (channel->status == STATUS_BUSY)
+		return -EBUSY;
+
+	cgroup = channel_item->ci_group;
+
+	if (vgroup->ops && vgroup->ops->create_channel) {
+		dev = vgroup->ops->create_channel(vgroup->dev,
+						  cgroup->cg_item.ci_name);
+		if (IS_ERR_OR_NULL(dev))
+			return PTR_ERR(dev);
+	}
+
+	channel->dev = dev;
+	channel->status = STATUS_BUSY;
+
+	return 0;
+}
+
+/**
+ * rpmsg_virtproc_channel_unlink() - Remove softlink of rpmsg client device
+ *   directory from virtproc configfs directory
+ * @virtproc_item: Config item representing configfs entry of virtual remote
+ *   processor
+ * @channel_item: Config item representing configfs entry of rpmsg client
+ *   driver
+ *
+ * Unbind rpmsg client device from virtual remote processor by removing softlink
+ * of rpmsg client device directory from virtproc configfs directory which
+ * deletes the rpmsg channel.
+ */
+static void rpmsg_virtproc_channel_unlink(struct config_item *virtproc_item,
+					  struct config_item *channel_item)
+{
+	struct rpmsg_virtproc_group *vgroup;
+	struct rpmsg_channel *channel;
+
+	channel = to_rpmsg_channel(channel_item);
+	vgroup = to_rpmsg_virtproc_group(virtproc_item);
+
+	if (vgroup->ops && vgroup->ops->delete_channel)
+		vgroup->ops->delete_channel(channel->dev);
+
+	channel->status = STATUS_FREE;
+}
+
+static struct configfs_item_operations rpmsg_virtproc_item_ops = {
+	.allow_link	= rpmsg_virtproc_channel_link,
+	.drop_link	= rpmsg_virtproc_channel_unlink,
+};
+
+static const struct config_item_type rpmsg_virtproc_item_type = {
+	.ct_item_ops	= &rpmsg_virtproc_item_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+/**
+ * rpmsg_cfs_add_virtproc_group() - Add new configfs directory for virtproc
+ *   device
+ * @dev: Device representing the virtual remote processor
+ * @ops: rpmsg_virtproc_ops to create or delete rpmsg channel
+ *
+ * Add new configfs directory for virtproc device. The rpmsg client driver's
+ * configfs entry can be linked with this directory for creating a new
+ * rpmsg channel and the link can be removed for deleting the rpmsg channel.
+ */
+struct config_group *
+rpmsg_cfs_add_virtproc_group(struct device *dev,
+			     const struct rpmsg_virtproc_ops *ops)
+{
+	struct rpmsg_virtproc_group *vgroup;
+	struct config_group *group;
+	struct device *vdev;
+	int ret;
+
+	vgroup = kzalloc(sizeof(*vgroup), GFP_KERNEL);
+	if (!vgroup)
+		return ERR_PTR(-ENOMEM);
+
+	group = &vgroup->group;
+	config_group_init_type_name(group, dev_name(dev),
+				    &rpmsg_virtproc_item_type);
+	ret = configfs_register_group(virtproc_group, group);
+	if (ret)
+		goto err_register_group;
+
+	if (!try_module_get(ops->owner)) {
+		ret = -EPROBE_DEFER;
+		goto err_module_get;
+	}
+
+	vdev = get_device(dev);
+	vgroup->dev = vdev;
+	vgroup->ops = ops;
+
+	return group;
+
+err_module_get:
+	configfs_unregister_group(group);
+
+err_register_group:
+	kfree(vgroup);
+
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(rpmsg_cfs_add_virtproc_group);
+
+/**
+ * rpmsg_cfs_remove_virtproc_group() - Remove the configfs directory for
+ *   virtproc device
+ * @group: config_group of the virtproc device
+ *
+ * Remove the configfs directory for virtproc device.
+ */
+void rpmsg_cfs_remove_virtproc_group(struct config_group *group)
+{
+	struct rpmsg_virtproc_group *vgroup;
+
+	if (!group)
+		return;
+
+	vgroup = container_of(group, struct rpmsg_virtproc_group, group);
+	put_device(vgroup->dev);
+	module_put(vgroup->ops->owner);
+	configfs_unregister_group(&vgroup->group);
+	kfree(vgroup);
+}
+EXPORT_SYMBOL(rpmsg_cfs_remove_virtproc_group);
+
+static const struct config_item_type rpmsg_channel_item_type = {
+	.ct_owner	= THIS_MODULE,
+};
+
+/**
+ * rpmsg_channel_make() - Allow user to create sub-directory of rpmsg client
+ *   driver
+ * @name: Name of the sub-directory created by the user.
+ *
+ * Invoked when user creates a sub-directory to the configfs directory
+ * representing the rpmsg client driver. This can be linked with the virtproc
+ * directory for creating a new rpmsg channel.
+ */
+static struct config_item *
+rpmsg_channel_make(struct config_group *group, const char *name)
+{
+	struct rpmsg_channel *channel;
+
+	channel = kzalloc(sizeof(*channel), GFP_KERNEL);
+	if (!channel)
+		return ERR_PTR(-ENOMEM);
+
+	channel->status = STATUS_FREE;
+
+	config_item_init_type_name(&channel->item, name, &rpmsg_channel_item_type);
+	return &channel->item;
+}
+
+/**
+ * rpmsg_channel_drop() - Allow user to delete sub-directory of rpmsg client
+ *   driver
+ * @item: Config item representing the sub-directory the user created returned
+ *   by rpmsg_channel_make()
+ *
+ * Invoked when user creates a sub-directory to the configfs directory
+ * representing the rpmsg client driver. This can be linked with the virtproc
+ * directory for creating a new rpmsg channel.
+ */
+static void rpmsg_channel_drop(struct config_group *group, struct config_item *item)
+{
+	struct rpmsg_channel *channel;
+
+	channel = to_rpmsg_channel(item);
+	kfree(channel);
+}
+
+static struct configfs_group_operations rpmsg_channel_group_ops = {
+	.make_item     = &rpmsg_channel_make,
+	.drop_item      = &rpmsg_channel_drop,
+};
+
+static const struct config_item_type rpmsg_channel_group_type = {
+	.ct_group_ops	= &rpmsg_channel_group_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+/**
+ * rpmsg_cfs_add_channel_group() - Create a configfs directory for each
+ *   registered rpmsg client driver
+ * @name: The name of the rpmsg client driver
+ *
+ * Create a configfs directory for each registered rpmsg client driver. The
+ * user can create sub-directory within this directory for creating
+ * rpmsg channels to be used by the rpmsg client driver.
+ */
+struct config_group *rpmsg_cfs_add_channel_group(const char *name)
+{
+	struct rpmsg_channel_group *cgroup;
+	struct config_group *group;
+	int ret;
+
+	cgroup = kzalloc(sizeof(*cgroup), GFP_KERNEL);
+	if (!cgroup)
+		return ERR_PTR(-ENOMEM);
+
+	group = &cgroup->group;
+	config_group_init_type_name(group, name, &rpmsg_channel_group_type);
+	ret = configfs_register_group(channel_group, group);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return group;
+}
+EXPORT_SYMBOL(rpmsg_cfs_add_channel_group);
+
+/**
+ * rpmsg_cfs_remove_channel_group() - Remove the configfs directory associated
+ *   with the rpmsg client driver
+ * @group: Config group representing the rpmsg client driver
+ *
+ * Remove the configfs directory associated with the rpmsg client driver.
+ */
+void rpmsg_cfs_remove_channel_group(struct config_group *group)
+{
+	struct rpmsg_channel_group *cgroup;
+
+	if (IS_ERR_OR_NULL(group))
+		return;
+
+	cgroup = to_rpmsg_channel_group(group);
+	configfs_unregister_default_group(group);
+	kfree(cgroup);
+}
+EXPORT_SYMBOL(rpmsg_cfs_remove_channel_group);
+
+static const struct config_item_type rpmsg_channel_type = {
+	.ct_owner	= THIS_MODULE,
+};
+
+static const struct config_item_type rpmsg_virtproc_type = {
+	.ct_owner	= THIS_MODULE,
+};
+
+static const struct config_item_type rpmsg_type = {
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct configfs_subsystem rpmsg_cfs_subsys = {
+	.su_group = {
+		.cg_item = {
+			.ci_namebuf = "rpmsg",
+			.ci_type = &rpmsg_type,
+		},
+	},
+	.su_mutex = __MUTEX_INITIALIZER(rpmsg_cfs_subsys.su_mutex),
+};
+
+static int __init rpmsg_cfs_init(void)
+{
+	int ret;
+	struct config_group *root = &rpmsg_cfs_subsys.su_group;
+
+	config_group_init(root);
+
+	ret = configfs_register_subsystem(&rpmsg_cfs_subsys);
+	if (ret) {
+		pr_err("Error %d while registering subsystem %s\n",
+		       ret, root->cg_item.ci_namebuf);
+		goto err;
+	}
+
+	channel_group = configfs_register_default_group(root, "channel",
+							&rpmsg_channel_type);
+	if (IS_ERR(channel_group)) {
+		ret = PTR_ERR(channel_group);
+		pr_err("Error %d while registering channel group\n",
+		       ret);
+		goto err_channel_group;
+	}
+
+	virtproc_group =
+		configfs_register_default_group(root, "virtproc",
+						&rpmsg_virtproc_type);
+	if (IS_ERR(virtproc_group)) {
+		ret = PTR_ERR(virtproc_group);
+		pr_err("Error %d while registering virtproc group\n",
+		       ret);
+		goto err_virtproc_group;
+	}
+
+	return 0;
+
+err_virtproc_group:
+	configfs_unregister_default_group(channel_group);
+
+err_channel_group:
+	configfs_unregister_subsystem(&rpmsg_cfs_subsys);
+
+err:
+	return ret;
+}
+module_init(rpmsg_cfs_init);
+
+static void __exit rpmsg_cfs_exit(void)
+{
+	configfs_unregister_default_group(virtproc_group);
+	configfs_unregister_default_group(channel_group);
+	configfs_unregister_subsystem(&rpmsg_cfs_subsys);
+}
+module_exit(rpmsg_cfs_exit);
+
+MODULE_DESCRIPTION("PCI RPMSG CONFIGFS");
+MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c
index e330ec4dfc33..68569fec03e2 100644
--- a/drivers/rpmsg/rpmsg_core.c
+++ b/drivers/rpmsg/rpmsg_core.c
@@ -563,8 +563,15 @@ EXPORT_SYMBOL(rpmsg_unregister_device);
  */
 int __register_rpmsg_driver(struct rpmsg_driver *rpdrv, struct module *owner)
 {
+	const struct rpmsg_device_id *ids = rpdrv->id_table;
 	rpdrv->drv.bus = &rpmsg_bus;
 	rpdrv->drv.owner = owner;
+
+	while (ids && ids->name[0]) {
+		rpmsg_cfs_add_channel_group(ids->name);
+		ids++;
+	}
+
 	return driver_register(&rpdrv->drv);
 }
 EXPORT_SYMBOL(__register_rpmsg_driver);
diff --git a/drivers/rpmsg/rpmsg_internal.h b/drivers/rpmsg/rpmsg_internal.h
index 3fc83cd50e98..39b3a5caf242 100644
--- a/drivers/rpmsg/rpmsg_internal.h
+++ b/drivers/rpmsg/rpmsg_internal.h
@@ -68,6 +68,18 @@ struct rpmsg_endpoint_ops {
 			     poll_table *wait);
 };
 
+/**
+ * struct rpmsg_virtproc_ops - indirection table for rpmsg_virtproc operations
+ * @create_channel: Create a new rpdev channel
+ * @delete_channel: Delete the rpdev channel
+ * @owner: Owner of the module holding the ops
+ */
+struct rpmsg_virtproc_ops {
+	struct device *(*create_channel)(struct device *dev, const char *name);
+	void (*delete_channel)(struct device *dev);
+	struct module *owner;
+};
+
 int rpmsg_register_device(struct rpmsg_device *rpdev);
 int rpmsg_unregister_device(struct device *parent,
 			    struct rpmsg_channel_info *chinfo);
@@ -75,6 +87,10 @@ int rpmsg_unregister_device(struct device *parent,
 struct device *rpmsg_find_device(struct device *parent,
 				 struct rpmsg_channel_info *chinfo);
 
+struct config_group *
+rpmsg_cfs_add_virtproc_group(struct device *dev,
+			     const struct rpmsg_virtproc_ops *ops);
+
 /**
  * rpmsg_chrdev_register_device() - register chrdev device based on rpdev
  * @rpdev:	prepared rpdev to be used for creating endpoints
diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h
index 9fe156d1c018..b9d9283b46ac 100644
--- a/include/linux/rpmsg.h
+++ b/include/linux/rpmsg.h
@@ -135,6 +135,7 @@ int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst,
 __poll_t rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp,
 			poll_table *wait);
 
+struct config_group *rpmsg_cfs_add_channel_group(const char *name);
 #else
 
 static inline int register_rpmsg_device(struct rpmsg_device *dev)
@@ -242,6 +243,10 @@ static inline __poll_t rpmsg_poll(struct rpmsg_endpoint *ept,
 	return 0;
 }
 
+static inline struct config_group *rpmsg_cfs_add_channel_group(const char *name)
+{
+	return NULL;
+}
 #endif /* IS_ENABLED(CONFIG_RPMSG) */
 
 /* use a macro to avoid include chaining to get THIS_MODULE */
-- 
2.17.1


  parent reply	other threads:[~2020-07-02  8:25 UTC|newest]

Thread overview: 96+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-07-02  8:21 [RFC PATCH 00/22] Enhance VHOST to enable SoC-to-SoC communication Kishon Vijay Abraham I
2020-07-02  8:21 ` Kishon Vijay Abraham I
2020-07-02  8:21 ` [RFC PATCH 01/22] vhost: Make _feature_ bits a property of vhost device Kishon Vijay Abraham I
2020-07-02  8:21   ` Kishon Vijay Abraham I
2020-07-02  8:21 ` [RFC PATCH 02/22] vhost: Introduce standard Linux driver model in VHOST Kishon Vijay Abraham I
2020-07-02  8:21   ` Kishon Vijay Abraham I
2020-07-02  8:21 ` [RFC PATCH 03/22] vhost: Add ops for the VHOST driver to configure VHOST device Kishon Vijay Abraham I
2020-07-02  8:21   ` Kishon Vijay Abraham I
2020-07-02  8:21 ` [RFC PATCH 04/22] vringh: Add helpers to access vring in MMIO Kishon Vijay Abraham I
2020-07-02  8:21   ` Kishon Vijay Abraham I
2020-07-02  8:21 ` [RFC PATCH 05/22] vhost: Add MMIO helpers for operations on vhost virtqueue Kishon Vijay Abraham I
2020-07-02  8:21   ` Kishon Vijay Abraham I
2020-07-02  8:21 ` [RFC PATCH 06/22] vhost: Introduce configfs entry for configuring VHOST Kishon Vijay Abraham I
2020-07-02  8:21   ` Kishon Vijay Abraham I
2020-07-02  8:21 ` [RFC PATCH 07/22] virtio_pci: Use request_threaded_irq() instead of request_irq() Kishon Vijay Abraham I
2020-07-02  8:21   ` Kishon Vijay Abraham I
2020-07-02  8:21 ` [RFC PATCH 08/22] rpmsg: virtio_rpmsg_bus: Disable receive virtqueue callback when reading messages Kishon Vijay Abraham I
2020-07-02  8:21   ` Kishon Vijay Abraham I
2020-07-02  8:21 ` Kishon Vijay Abraham I [this message]
2020-07-02  8:21   ` [RFC PATCH 09/22] rpmsg: Introduce configfs entry for configuring rpmsg Kishon Vijay Abraham I
2020-07-02  8:21 ` [RFC PATCH 10/22] rpmsg: virtio_rpmsg_bus: Add Address Service Notification support Kishon Vijay Abraham I
2020-07-02  8:21   ` Kishon Vijay Abraham I
2020-07-02  8:21 ` [RFC PATCH 11/22] rpmsg: virtio_rpmsg_bus: Move generic rpmsg structure to rpmsg_internal.h Kishon Vijay Abraham I
2020-07-02  8:21   ` Kishon Vijay Abraham I
2020-07-02  8:21 ` [RFC PATCH 12/22] virtio: Add ops to allocate and free buffer Kishon Vijay Abraham I
2020-07-02  8:21   ` Kishon Vijay Abraham I
2020-07-02  8:21 ` [RFC PATCH 13/22] rpmsg: virtio_rpmsg_bus: Use virtio_alloc_buffer() and virtio_free_buffer() Kishon Vijay Abraham I
2020-07-02  8:21   ` Kishon Vijay Abraham I
2020-07-02  8:21 ` [RFC PATCH 14/22] rpmsg: Add VHOST based remote processor messaging bus Kishon Vijay Abraham I
2020-07-02  8:21   ` Kishon Vijay Abraham I
2020-07-02  8:21 ` [RFC PATCH 15/22] samples/rpmsg: Setup delayed work to send message Kishon Vijay Abraham I
2020-07-02  8:21   ` Kishon Vijay Abraham I
2020-07-02  8:21 ` [RFC PATCH 16/22] samples/rpmsg: Wait for address to be bound to rpdev for sending message Kishon Vijay Abraham I
2020-07-02  8:21   ` Kishon Vijay Abraham I
2020-07-02  8:21 ` [RFC PATCH 17/22] rpmsg.txt: Add Documentation to configure rpmsg using configfs Kishon Vijay Abraham I
2020-07-02  8:21   ` Kishon Vijay Abraham I
2020-07-02  8:21 ` [RFC PATCH 18/22] virtio_pci: Add VIRTIO driver for VHOST on Configurable PCIe Endpoint device Kishon Vijay Abraham I
2020-07-02  8:21   ` Kishon Vijay Abraham I
2020-07-02  8:21 ` [RFC PATCH 19/22] PCI: endpoint: Add EP function driver to provide VHOST interface Kishon Vijay Abraham I
2020-07-02  8:21   ` Kishon Vijay Abraham I
2020-07-02  8:21 ` [RFC PATCH 20/22] NTB: Add a new NTB client driver to implement VIRTIO functionality Kishon Vijay Abraham I
2020-07-02  8:21   ` Kishon Vijay Abraham I
2020-07-02  8:21 ` [RFC PATCH 21/22] NTB: Add a new NTB client driver to implement VHOST functionality Kishon Vijay Abraham I
2020-07-02  8:21   ` Kishon Vijay Abraham I
2020-07-02  8:21 ` [RFC PATCH 22/22] NTB: Describe ntb_virtio and ntb_vhost client in the documentation Kishon Vijay Abraham I
2020-07-02  8:21   ` Kishon Vijay Abraham I
2020-07-02  9:51 ` [RFC PATCH 00/22] Enhance VHOST to enable SoC-to-SoC communication Michael S. Tsirkin
2020-07-02 10:10   ` Jason Wang
2020-07-02 13:35     ` Kishon Vijay Abraham I
2020-07-02 13:35       ` Kishon Vijay Abraham I
2020-07-03  7:16       ` Jason Wang
2020-07-03  7:16         ` Jason Wang
2020-07-06  9:32         ` Kishon Vijay Abraham I
2020-07-06  9:32           ` Kishon Vijay Abraham I
2020-07-07  9:47           ` Jason Wang
2020-07-07  9:47             ` Jason Wang
2020-07-07 14:45             ` Kishon Vijay Abraham I
2020-07-07 14:45               ` Kishon Vijay Abraham I
2020-07-08 11:22               ` Jason Wang
2020-07-08 13:13                 ` Kishon Vijay Abraham I
2020-07-08 13:13                   ` Kishon Vijay Abraham I
2020-07-09  6:26                   ` Jason Wang
2020-08-28 10:34                     ` Cornelia Huck
2020-08-28 10:34                       ` Cornelia Huck
2020-09-01  5:24                       ` Kishon Vijay Abraham I
2020-09-01  5:24                         ` Kishon Vijay Abraham I
2020-09-01  8:50                         ` Jason Wang
2020-09-01  8:50                           ` Jason Wang
2020-09-08 16:37                           ` Cornelia Huck
2020-09-08 16:37                             ` Cornelia Huck
2020-09-09  8:41                             ` Jason Wang
2020-09-09  8:41                               ` Jason Wang
2020-09-14  7:23                           ` Kishon Vijay Abraham I
2020-09-14  7:23                             ` Kishon Vijay Abraham I
2020-09-15  8:18                             ` Jason Wang
2020-09-15  8:18                               ` Jason Wang
2020-09-15 15:47                               ` Kishon Vijay Abraham I
2020-09-15 15:47                                 ` Kishon Vijay Abraham I
2020-09-16  3:10                                 ` Jason Wang
2020-09-16  3:10                                   ` Jason Wang
2020-09-16 11:47                                   ` Kishon Vijay Abraham I
2020-09-16 11:47                                     ` Kishon Vijay Abraham I
2020-09-16 11:47                                     ` Kishon Vijay Abraham I
2020-09-16 11:47                                     ` Kishon Vijay Abraham I
2020-09-18  4:04                                     ` Jason Wang
2020-09-18  4:04                                       ` Jason Wang
2020-07-15 17:15                   ` Mathieu Poirier
2020-09-01  4:40                     ` Kishon Vijay Abraham I
2020-09-01  4:40                       ` Kishon Vijay Abraham I
2020-07-02 10:25   ` Kishon Vijay Abraham I
2020-07-02 10:25     ` Kishon Vijay Abraham I
2020-07-02 17:31   ` Mathieu Poirier
2020-07-02 17:31     ` Mathieu Poirier
2020-07-03  6:17     ` Kishon Vijay Abraham I
2020-07-03  6:17       ` Kishon Vijay Abraham I
2020-07-03  6:17       ` Kishon Vijay Abraham I

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200702082143.25259-10-kishon@ti.com \
    --to=kishon@ti.com \
    --cc=allenbh@gmail.com \
    --cc=bhelgaas@google.com \
    --cc=bjorn.andersson@linaro.org \
    --cc=dave.jiang@intel.com \
    --cc=jasowang@redhat.com \
    --cc=jdmason@kudzu.us \
    --cc=kvm@vger.kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-ntb@googlegroups.com \
    --cc=linux-pci@vger.kernel.org \
    --cc=linux-remoteproc@vger.kernel.org \
    --cc=lorenzo.pieralisi@arm.com \
    --cc=mst@redhat.com \
    --cc=netdev@vger.kernel.org \
    --cc=ohad@wizery.com \
    --cc=pbonzini@redhat.com \
    --cc=sgarzare@redhat.com \
    --cc=stefanha@redhat.com \
    --cc=virtualization@lists.linux-foundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.