[PATCH/RFC 4/9] Basic guest virtual devices infrastructure

Carsten Otte
2007-05-11T17:35:58Z

From: Carsten Otte <cotte-tA70FqPdS9bQT0dZR+AlfA@public.gmane.org>

This patch adds support for a new bus type that manages paravirtualized
devices. The bus uses the s390 diagnose instruction to query devices, and
match them with the corresponding drivers.
Future enhancements should include hotplug and hotremoval of virtual devices
triggered by the host, and supend/resume of virtual devices for migration.

This code is s390 architecture specific, please review.

Signed-off-by: Carsten Otte <cotte-tA70FqPdS9bQT0dZR+AlfA@public.gmane.org>

---
arch/s390/Kconfig | 6 +
drivers/s390/Makefile | 2
drivers/s390/guest/Makefile | 6 +
drivers/s390/guest/vdev.c | 158 +++++++++++++++++++++++++++++++++++++++
drivers/s390/guest/vdev_device.c | 50 ++++++++++++
include/asm-s390/vdev.h | 53 +++++++++++++
6 files changed, 274 insertions(+), 1 deletion(-)

Index: linux-2.6.21/arch/s390/Kconfig
===================================================================
--- linux-2.6.21.orig/arch/s390/Kconfig
+++ linux-2.6.21/arch/s390/Kconfig
@@ -521,6 +521,12 @@ config S390_HOST
select S390_SWITCH_AMODE
help
Select this option if you want to host guest Linux images
+
+config S390_GUEST
+ bool "s390 guest support (EXPERIMENTAL)"
+ depends on 64BIT && EXPERIMENTAL
+ help
+ Select this option if you want to run the kernel under s390 linux
endmenu

source "net/Kconfig"
Index: linux-2.6.21/drivers/s390/Makefile
===================================================================
--- linux-2.6.21.orig/drivers/s390/Makefile
+++ linux-2.6.21/drivers/s390/Makefile
@@ -5,7 +5,7 @@
CFLAGS_sysinfo.o += -Iinclude/math-emu -Iarch/s390/math-emu -w

obj-y += s390mach.o sysinfo.o s390_rdev.o
-obj-y += cio/ block/ char/ crypto/ net/ scsi/
+obj-y += cio/ block/ char/ crypto/ net/ scsi/ guest/

drivers-y += drivers/s390/built-in.o

Index: linux-2.6.21/drivers/s390/guest/Makefile
===================================================================
--- /dev/null
+++ linux-2.6.21/drivers/s390/guest/Makefile
@@ -0,0 +1,6 @@
+#
+# s390 Linux virtual environment
+#
+
+obj-$(CONFIG_S390_GUEST) += vdev.o vdev_device.o
+
Index: linux-2.6.21/drivers/s390/guest/vdev.c
===================================================================
--- /dev/null
+++ linux-2.6.21/drivers/s390/guest/vdev.c
@@ -0,0 +1,158 @@
+/*
+ * vdev - guest os layer for device virtualization
+ *
+ * Copyright IBM Corp. 2007
+ * Author: Carsten Otte <cotte-tA70FqPdS9bQT0dZR+AlfA@public.gmane.org>
+ *
+ */
+
+#include <asm/vdev.h>
+
+static void vdev_bus_release(struct device *);
+
+struct bus_type vdev_bus_type = {
+ .name = "vdev",
+ .match = vdev_match,
+ .probe = vdev_probe,
+};
+
+struct device vdev_bus = {
+ .bus_id = "vdev0",
+ .release = vdev_bus_release
+};
+
+int vdev_match(struct device * dev, struct device_driver *drv)
+{
+ struct vdev *vdev = to_vdev(dev);
+ struct vdev_driver *vdrv = to_vdrv(drv);
+
+ if (vdev->vdev_type == vdrv->vdev_type)
+ return 1;
+
+ return 0;
+}
+
+int vdev_probe(struct device * dev)
+{
+ struct vdev *vdev = to_vdev(dev);
+ struct vdev_driver *vdrv = to_vdrv(dev->driver);
+
+ return vdrv->probe(vdev);
+}
+
+static void vdev_bus_release (struct device *device)
+{
+ /* noop, static bus object */
+}
+
+static inline int vdev_diag_hotplug(char symname[128], char hostid[128])
+{
+ register char * __arg1 asm("2") = symname;
+ register char * __arg2 asm("3") = hostid;
+ register int __svcres asm("2");
+ int __res;
+
+ __asm__ __volatile__ (
+ "diag 0,0,0x1e"
+ : "=d" (__svcres)
+ : "0" (__arg1),
+ "d" (__arg2)
+ : "cc", "memory");
+ __res = __svcres;
+ return __res;
+}
+
+
+static int vdev_scan_coldplug(void)
+{
+ int rc;
+ struct vdev *device;
+
+ do {
+ device = kzalloc(sizeof(struct vdev), GFP_ATOMIC);
+ if (!device) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ rc = vdev_diag_hotplug(device->symname, device->hostid);
+ if (rc == -ENODEV)
+ break;
+ if (rc < 0) {
+ printk (KERN_WARNING "vdev: error %d detecting" \
+ " initial devices\n", rc);
+ break;
+ }
+ device->vdev_type = rc;
+
+ //sanity: are strings terminated?
+ if ((strnlen(device->symname, 128) == 128) ||
+ (strnlen(device->hostid, 128) == 128)) {
+ // warn and discard device
+ printk ("vdev: illegal device entry received\n");
+ break;
+ }
+
+ rc = vdevice_register(device);
+ if (rc) {
+ kfree(device);
+ } else
+ switch (device->vdev_type) {
+ case VDEV_TYPE_DISK:
+ printk (KERN_INFO "vdev: storage device " \
+ "detected: %s\n", device->symname);
+ break;
+ case VDEV_TYPE_NET:
+ printk (KERN_INFO "vdev: network device " \
+ "detected: %s\n", device->symname);
+ break;
+ default:
+ printk (KERN_INFO "vdev: unknown device " \
+ "detected: %s\n", device->symname);
+ }
+ } while(1);
+ kfree (device);
+ out:
+ return 0;
+}
+
+
+int __init vdev_init(void)
+{
+ int rc;
+
+ rc = bus_register(&vdev_bus_type);
+ if (rc) {
+ printk (KERN_WARNING "vdev: failed to register bus type\n");
+ goto out;
+ }
+ rc = device_register(&vdev_bus);
+ if (rc) {
+ printk (KERN_WARNING "vdev: failed to register bus device\n");
+ goto bunregister;
+ }
+ printk (KERN_INFO "vdev: initialization complete\n");
+ rc = vdev_scan_coldplug();
+ if (rc) {
+ printk (KERN_WARNING "vdev: failed to scan devices\n");
+ goto dunregister;
+ }
+ goto out;
+ dunregister:
+ device_unregister(&vdev_bus);
+
+ bunregister:
+ bus_unregister(&vdev_bus_type);
+ out:
+ return rc;
+}
+
+void vdev_exit(void)
+{
+ bus_unregister(&vdev_bus_type);
+}
+
+module_init(vdev_init);
+module_exit(vdev_exit);
+MODULE_DESCRIPTION("Guest layer for device virtualization");
+MODULE_AUTHOR("Copyright IBM Corp. 2007");
+MODULE_LICENSE("GPL");
Index: linux-2.6.21/drivers/s390/guest/vdev_device.c
===================================================================
--- /dev/null
+++ linux-2.6.21/drivers/s390/guest/vdev_device.c
@@ -0,0 +1,50 @@
+/*
+ * vdev - guest layer for device virtualization
+ *
+ * Copyright IBM Corp. 2007
+ * Author: Carsten Otte <cotte-tA70FqPdS9bQT0dZR+AlfA@public.gmane.org>
+ *
+ */
+
+#include <asm/vdev.h>
+
+int vdev_driver_register (struct vdev_driver *vdrv)
+{
+ struct device_driver *drv = &vdrv->driver;
+
+ drv->bus = &vdev_bus_type;
+ drv->name = vdrv->name;
+
+ return driver_register(drv);
+}
+
+int vdevice_register(struct vdev *vdev)
+{
+ struct device *dev = &vdev->dev;
+ int ret,typesize;
+
+ dev->bus = &vdev_bus_type;
+ dev->parent = &vdev_bus;
+ memset(dev->bus_id, 0, BUS_ID_SIZE);
+ switch (vdev->vdev_type) {
+ case VDEV_TYPE_DISK:
+ strncpy (dev->bus_id, "block:", 6);
+ typesize=6;
+ break;
+ case VDEV_TYPE_NET:
+ strncpy (dev->bus_id, "net:", 4);
+ typesize=4;
+ break;
+ default:
+ strncpy (dev->bus_id, "unknown:", 8);
+ typesize=8;
+ break;
+ }
+ strncpy (dev->bus_id+typesize, vdev->symname, BUS_ID_SIZE-typesize-1);
+
+ ret = device_register(dev);
+
+ //FIXME: add device attribs here
+
+ return ret;
+}
Index: linux-2.6.21/include/asm-s390/vdev.h
===================================================================
--- /dev/null
+++ linux-2.6.21/include/asm-s390/vdev.h
@@ -0,0 +1,53 @@
+/*
+ * vdev - guest layer for device virtualization
+ *
+ * Copyright IBM Corp. 2007
+ * Author: Carsten Otte <cotte-tA70FqPdS9bQT0dZR+AlfA@public.gmane.org>
+ *
+ */
+
+#ifndef __VDEV_H
+#define __VDEV_H
+#include <linux/device.h>
+
+/* in vdev.c */
+extern int vdev_match(struct device *, struct device_driver *);
+extern int vdev_probe (struct device *);
+
+extern struct device vdev_bus;
+extern struct bus_type vdev_bus_type;
+
+#define VDEV_TYPE_DISK 0
+#define VDEV_TYPE_NET 1
+
+struct vdev {
+ unsigned int vdev_type;
+ char symname[128];
+ char hostid[128];
+ struct vdev_driver *driver;
+ struct device dev;
+ void *drv_private;
+};
+
+struct vdev_driver {
+ struct module *owner;
+ int vdev_type;
+ int (*probe) (struct vdev *);
+ int (*set_online) (struct vdev *);
+ int (*set_offline) (struct vdev *);
+ int (*suspend) (struct vdev *);
+ int (*resume) (struct vdev *);
+ struct device_driver driver; /* higher level structure, don't init
+ this from your driver */
+ char *name;
+ void *drv_private;
+};
+
+#define to_vdev(n) container_of(n, struct vdev, dev)
+#define to_vdrv(n) container_of(n, struct vdev_driver, driver)
+
+
+/* in vdevice.c */
+extern int vdevice_register(struct vdev *);
+extern int vdev_driver_register(struct vdev_driver *);
+#endif