qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [RFC PATCH v2 0/4] s390: virtio-ccw guest kernel support.
@ 2012-09-04 15:13 Cornelia Huck
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 1/4] s390/kvm: Handle hosts not supporting s390-virtio Cornelia Huck
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Cornelia Huck @ 2012-09-04 15:13 UTC (permalink / raw)
  To: KVM, linux-s390, qemu-devel
  Cc: Carsten Otte, Anthony Liguori, Rusty Russell, Sebastian Ott,
	Marcelo Tosatti, Heiko Carstens, Alexander Graf,
	Christian Borntraeger, Avi Kivity, Martin Schwidefsky

Hi,

here's the second revision of the guest support for virtio-ccw.

The first patch has gotten several changes and now handles checking
for s390-virtio support much more nicely.

The third patch has been adapted to the changed virtio-ccw interface.

Cornelia Huck (4):
  s390/kvm: Handle hosts not supporting s390-virtio.
  s390: Add a mechanism to get the subchannel id.
  s390/kvm: Add a channel I/O based virtio transport driver.
  s390/kvm: Split out early console code.

 arch/s390/include/asm/ccwdev.h  |   5 +
 arch/s390/include/asm/irq.h     |   1 +
 arch/s390/kernel/irq.c          |   1 +
 drivers/s390/cio/device_ops.c   |  12 +
 drivers/s390/kvm/Makefile       |   2 +-
 drivers/s390/kvm/early_printk.c |  42 +++
 drivers/s390/kvm/kvm_virtio.c   |  64 ++--
 drivers/s390/kvm/virtio_ccw.c   | 789 ++++++++++++++++++++++++++++++++++++++++
 8 files changed, 882 insertions(+), 34 deletions(-)
 create mode 100644 drivers/s390/kvm/early_printk.c
 create mode 100644 drivers/s390/kvm/virtio_ccw.c

-- 
1.7.11.5

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [Qemu-devel] [PATCH v2 1/4] s390/kvm: Handle hosts not supporting s390-virtio.
  2012-09-04 15:13 [Qemu-devel] [RFC PATCH v2 0/4] s390: virtio-ccw guest kernel support Cornelia Huck
@ 2012-09-04 15:13 ` Cornelia Huck
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 2/4] s390: Add a mechanism to get the subchannel id Cornelia Huck
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 7+ messages in thread
From: Cornelia Huck @ 2012-09-04 15:13 UTC (permalink / raw)
  To: KVM, linux-s390, qemu-devel
  Cc: Carsten Otte, Anthony Liguori, Rusty Russell, Sebastian Ott,
	Marcelo Tosatti, Heiko Carstens, Alexander Graf,
	Christian Borntraeger, Avi Kivity, Martin Schwidefsky

Running under a kvm host does not necessarily imply the presence of
a page mapped above the main memory with the virtio information;
however, the code includes a hard coded access to that page.

Instead, check for the presence of the page and exit gracefully
before we hit an addressing exception if it does not exist.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---

Changes v1->v2:

- check for the presence of the patch with lura
- reorder init sequence
- comments

---
 drivers/s390/kvm/kvm_virtio.c | 39 +++++++++++++++++++++++++++++++--------
 1 file changed, 31 insertions(+), 8 deletions(-)

diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c
index 47cccd5..76b95f3 100644
--- a/drivers/s390/kvm/kvm_virtio.c
+++ b/drivers/s390/kvm/kvm_virtio.c
@@ -419,6 +419,26 @@ static void kvm_extint_handler(struct ext_code ext_code,
 }
 
 /*
+ * For s390-virtio, we expect a page above main storage containing
+ * the virtio configuration. Try to actually load from this area
+ * in order to figure out if the host provides this page.
+ */
+static int __init test_devices_support(unsigned long addr)
+{
+	int ret = -EIO;
+
+	asm volatile(
+		"0:	lura	0,%1\n"
+		"1:	xgr	%0,%0\n"
+		"2:\n"
+		EX_TABLE(0b,2b)
+		EX_TABLE(1b,2b)
+		: "+d" (ret)
+		: "a" (addr)
+		: "0", "cc");
+	return ret;
+}
+/*
  * Init function for virtio
  * devices are in a single page above top of "normal" mem
  */
@@ -429,21 +449,24 @@ static int __init kvm_devices_init(void)
 	if (!MACHINE_IS_KVM)
 		return -ENODEV;
 
+	if (test_devices_support(real_memory_size) < 0)
+		/* No error. */
+		return 0;
+
+	rc = vmem_add_mapping(real_memory_size, PAGE_SIZE);
+	if (rc)
+		return rc;
+
+	kvm_devices = (void *) real_memory_size;
+
 	kvm_root = root_device_register("kvm_s390");
 	if (IS_ERR(kvm_root)) {
 		rc = PTR_ERR(kvm_root);
 		printk(KERN_ERR "Could not register kvm_s390 root device");
+		vmem_remove_mapping(real_memory_size, PAGE_SIZE);
 		return rc;
 	}
 
-	rc = vmem_add_mapping(real_memory_size, PAGE_SIZE);
-	if (rc) {
-		root_device_unregister(kvm_root);
-		return rc;
-	}
-
-	kvm_devices = (void *) real_memory_size;
-
 	INIT_WORK(&hotplug_work, hotplug_devices);
 
 	service_subclass_irq_register();
-- 
1.7.11.5

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [Qemu-devel] [PATCH v2 2/4] s390: Add a mechanism to get the subchannel id.
  2012-09-04 15:13 [Qemu-devel] [RFC PATCH v2 0/4] s390: virtio-ccw guest kernel support Cornelia Huck
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 1/4] s390/kvm: Handle hosts not supporting s390-virtio Cornelia Huck
@ 2012-09-04 15:13 ` Cornelia Huck
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 3/4] s390/kvm: Add a channel I/O based virtio transport driver Cornelia Huck
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 4/4] s390/kvm: Split out early console code Cornelia Huck
  3 siblings, 0 replies; 7+ messages in thread
From: Cornelia Huck @ 2012-09-04 15:13 UTC (permalink / raw)
  To: KVM, linux-s390, qemu-devel
  Cc: Carsten Otte, Anthony Liguori, Rusty Russell, Sebastian Ott,
	Marcelo Tosatti, Heiko Carstens, Alexander Graf,
	Christian Borntraeger, Avi Kivity, Martin Schwidefsky

This will be needed by the new virtio-ccw transport.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---

Changes v1->v2:
- make it EXPORT_SYMBOL_GPL to get in line with other interfaces

---
 arch/s390/include/asm/ccwdev.h |  5 +++++
 drivers/s390/cio/device_ops.c  | 12 ++++++++++++
 2 files changed, 17 insertions(+)

diff --git a/arch/s390/include/asm/ccwdev.h b/arch/s390/include/asm/ccwdev.h
index 1cb4bb3..9ad79f7 100644
--- a/arch/s390/include/asm/ccwdev.h
+++ b/arch/s390/include/asm/ccwdev.h
@@ -18,6 +18,9 @@ struct irb;
 struct ccw1;
 struct ccw_dev_id;
 
+/* from asm/schid.h */
+struct subchannel_id;
+
 /* simplified initializers for struct ccw_device:
  * CCW_DEVICE and CCW_DEVICE_DEVTYPE initialize one
  * entry in your MODULE_DEVICE_TABLE and set the match_flag correctly */
@@ -226,5 +229,7 @@ int ccw_device_siosl(struct ccw_device *);
 // FIXME: these have to go
 extern int _ccw_device_get_subchannel_number(struct ccw_device *);
 
+extern void ccw_device_get_schid(struct ccw_device *, struct subchannel_id *);
+
 extern void *ccw_device_get_chp_desc(struct ccw_device *, int);
 #endif /* _S390_CCWDEV_H_ */
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index ec7fb6d..2ad832f 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -763,6 +763,18 @@ _ccw_device_get_subchannel_number(struct ccw_device *cdev)
 	return cdev->private->schid.sch_no;
 }
 
+/**
+ * ccw_device_get_schid - obtain a subchannel id
+ * @cdev: device to obtain the id for
+ * @schid: where to fill in the values
+ */
+void ccw_device_get_schid(struct ccw_device *cdev, struct subchannel_id *schid)
+{
+	struct subchannel *sch = to_subchannel(cdev->dev.parent);
+
+	*schid = sch->schid;
+}
+EXPORT_SYMBOL_GPL(ccw_device_get_schid);
 
 MODULE_LICENSE("GPL");
 EXPORT_SYMBOL(ccw_device_set_options_mask);
-- 
1.7.11.5

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [Qemu-devel] [PATCH v2 3/4] s390/kvm: Add a channel I/O based virtio transport driver.
  2012-09-04 15:13 [Qemu-devel] [RFC PATCH v2 0/4] s390: virtio-ccw guest kernel support Cornelia Huck
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 1/4] s390/kvm: Handle hosts not supporting s390-virtio Cornelia Huck
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 2/4] s390: Add a mechanism to get the subchannel id Cornelia Huck
@ 2012-09-04 15:13 ` Cornelia Huck
  2012-09-19 16:38   ` Alexander Graf
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 4/4] s390/kvm: Split out early console code Cornelia Huck
  3 siblings, 1 reply; 7+ messages in thread
From: Cornelia Huck @ 2012-09-04 15:13 UTC (permalink / raw)
  To: KVM, linux-s390, qemu-devel
  Cc: Carsten Otte, Anthony Liguori, Rusty Russell, Sebastian Ott,
	Marcelo Tosatti, Heiko Carstens, Alexander Graf,
	Christian Borntraeger, Avi Kivity, Martin Schwidefsky

Add a driver for kvm guests that matches virtual ccw devices provided
by the host as virtio bridge devices.

These virtio-ccw devices use a special set of channel commands in order
to perform virtio functions.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---

Changes v1->v2:
- adapt to improved virtio-ccw channel commands
- fix unregistration of online devices
- add a missing spinlock initialization

---
 arch/s390/include/asm/irq.h   |   1 +
 arch/s390/kernel/irq.c        |   1 +
 drivers/s390/kvm/Makefile     |   2 +-
 drivers/s390/kvm/virtio_ccw.c | 790 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 793 insertions(+), 1 deletion(-)
 create mode 100644 drivers/s390/kvm/virtio_ccw.c

diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h
index 2b9d418..b4bea53 100644
--- a/arch/s390/include/asm/irq.h
+++ b/arch/s390/include/asm/irq.h
@@ -31,6 +31,7 @@ enum interruption_class {
 	IOINT_CTC,
 	IOINT_APB,
 	IOINT_CSC,
+	IOINT_VIR,
 	NMI_NMI,
 	NR_IRQS,
 };
diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c
index dd7630d..2cc7eed 100644
--- a/arch/s390/kernel/irq.c
+++ b/arch/s390/kernel/irq.c
@@ -56,6 +56,7 @@ static const struct irq_class intrclass_names[] = {
 	{.name = "CTC", .desc = "[I/O] CTC" },
 	{.name = "APB", .desc = "[I/O] AP Bus" },
 	{.name = "CSC", .desc = "[I/O] CHSC Subchannel" },
+	{.name = "VIR", .desc = "[I/O] Virtual I/O Devices" },
 	{.name = "NMI", .desc = "[NMI] Machine Check" },
 };
 
diff --git a/drivers/s390/kvm/Makefile b/drivers/s390/kvm/Makefile
index 0815690..241891a 100644
--- a/drivers/s390/kvm/Makefile
+++ b/drivers/s390/kvm/Makefile
@@ -6,4 +6,4 @@
 # it under the terms of the GNU General Public License (version 2 only)
 # as published by the Free Software Foundation.
 
-obj-$(CONFIG_S390_GUEST) += kvm_virtio.o
+obj-$(CONFIG_S390_GUEST) += kvm_virtio.o virtio_ccw.o
diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c
new file mode 100644
index 0000000..1c9af22
--- /dev/null
+++ b/drivers/s390/kvm/virtio_ccw.c
@@ -0,0 +1,790 @@
+/*
+ * ccw based virtio transport
+ *
+ * Copyright IBM Corp. 2012
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (version 2 only)
+ * as published by the Free Software Foundation.
+ *
+ *    Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ */
+
+#include <linux/kernel_stat.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/err.h>
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+#include <linux/slab.h>
+#include <linux/virtio_console.h>
+#include <linux/interrupt.h>
+#include <linux/virtio_ring.h>
+#include <linux/pfn.h>
+#include <linux/async.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <asm/io.h>
+#include <asm/kvm_para.h>
+#include <asm/setup.h>
+#include <asm/irq.h>
+#include <asm/cio.h>
+#include <asm/ccwdev.h>
+
+/*
+ * virtio related functions
+ */
+
+struct vq_config_block {
+	__u16 index;
+	__u16 num;
+} __attribute__ ((packed));
+
+#define VIRTIO_CCW_CONFIG_SIZE 0x100
+/* same as PCI config space size, should be enough for all drivers */
+
+struct virtio_ccw_device {
+	struct virtio_device vdev;
+	__u8 status;
+	__u8 config[VIRTIO_CCW_CONFIG_SIZE];
+	struct ccw_device *cdev;
+	struct ccw1 ccw;
+	__u32 area;
+	__u32 curr_io;
+	int err;
+	wait_queue_head_t wait_q;
+	spinlock_t lock;
+	struct list_head virtqueues;
+	unsigned long indicators; /* XXX - works because we're under 64 bit */
+	struct vq_config_block *config_block;
+};
+
+struct vq_info_block {
+	__u64 queue;
+	__u32 align;
+	__u16 index;
+	__u16 num;
+} __attribute__ ((packed));
+
+struct virtio_feature_desc {
+	__u32 features;
+	__u8 index;
+} __attribute__ ((packed));
+
+struct virtio_ccw_vq_info {
+	struct virtqueue *vq;
+	int num;
+	int queue_index;
+	void *queue;
+	struct vq_info_block *info_block;
+	struct list_head node;
+};
+
+#define KVM_VIRTIO_CCW_RING_ALIGN 4096
+
+#define CCW_CMD_SET_VQ 0x13
+#define CCW_CMD_VDEV_RESET 0x33
+#define CCW_CMD_SET_IND 0x43
+#define CCW_CMD_READ_FEAT 0x12
+#define CCW_CMD_WRITE_FEAT 0x11
+#define CCW_CMD_READ_CONF 0x22
+#define CCW_CMD_WRITE_CONF 0x21
+#define CCW_CMD_WRITE_STATUS 0x31
+#define CCW_CMD_READ_VQ_CONF 0x32
+
+#define VIRTIO_CCW_DOING_SET_VQ 0x00010000
+#define VIRTIO_CCW_DOING_RESET 0x00040000
+#define VIRTIO_CCW_DOING_READ_FEAT 0x00080000
+#define VIRTIO_CCW_DOING_WRITE_FEAT 0x00100000
+#define VIRTIO_CCW_DOING_READ_CONFIG 0x00200000
+#define VIRTIO_CCW_DOING_WRITE_CONFIG 0x00400000
+#define VIRTIO_CCW_DOING_WRITE_STATUS 0x00800000
+#define VIRTIO_CCW_DOING_SET_IND 0x01000000
+#define VIRTIO_CCW_DOING_READ_VQ_CONF 0x02000000
+#define VIRTIO_CCW_INTPARM_MASK 0xffff0000
+
+static struct virtio_ccw_device *to_vc_device(struct virtio_device *vdev)
+{
+	return container_of(vdev, struct virtio_ccw_device, vdev);
+}
+
+static int doing_io(struct virtio_ccw_device *vcdev, __u32 flag)
+{
+	unsigned long flags;
+	__u32 ret;
+
+	spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags);
+	if (vcdev->err)
+		ret = vcdev->err;
+	else
+		ret = vcdev->curr_io & flag;
+	spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags);
+	return ret;
+}
+
+static int ccw_io_helper(struct virtio_ccw_device *vcdev, __u32 intparm)
+{
+	int ret;
+	unsigned long flags;
+	int flag = intparm & VIRTIO_CCW_INTPARM_MASK;
+
+	spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags);
+	ret = ccw_device_start(vcdev->cdev, &vcdev->ccw, intparm, 0, 0);
+	if (!ret)
+		vcdev->curr_io |= flag;
+	spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags);
+	wait_event(vcdev->wait_q, doing_io(vcdev, flag) == 0);
+	return ret ? ret : vcdev->err;
+}
+
+static void virtio_ccw_kvm_notify(struct virtqueue *vq)
+{
+	struct virtio_ccw_vq_info *info = vq->priv;
+	struct virtio_ccw_device *vcdev;
+	struct subchannel_id schid;
+	__u32 reg2;
+
+	vcdev = to_vc_device(info->vq->vdev);
+	ccw_device_get_schid(vcdev->cdev, &schid);
+	reg2 = *(__u32 *)&schid;
+	kvm_hypercall2(3 /* CCW_NOTIFY */, reg2, info->queue_index);
+}
+
+static int virtio_ccw_read_vq_conf(struct virtio_ccw_device *vcdev, int index)
+{
+	vcdev->config_block->index = index;
+	vcdev->ccw.cmd_code = CCW_CMD_READ_VQ_CONF;
+	vcdev->ccw.flags = 0;
+	vcdev->ccw.count = sizeof(struct vq_config_block);
+	vcdev->ccw.cda = (__u32)(unsigned long)(vcdev->config_block);
+	ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_VQ_CONF);
+	return vcdev->config_block->num;
+}
+
+static void virtio_ccw_del_vq(struct virtqueue *vq)
+{
+	struct virtio_ccw_device *vcdev = to_vc_device(vq->vdev);
+	struct virtio_ccw_vq_info *info = vq->priv;
+	unsigned long flags;
+	unsigned long size;
+	int ret;
+
+	/* Remove from our list. */
+	spin_lock_irqsave(&vcdev->lock, flags);
+	list_del(&info->node);
+	spin_unlock_irqrestore(&vcdev->lock, flags);
+
+	/* Release from host. */
+	info->info_block->queue = 0;
+	info->info_block->align = 0;
+	info->info_block->index = info->queue_index;
+	info->info_block->num = 0;
+	vcdev->ccw.cmd_code = CCW_CMD_SET_VQ;
+	vcdev->ccw.flags = 0;
+	vcdev->ccw.count = sizeof(*info->info_block);
+	vcdev->ccw.cda = (__u32)(unsigned long)(info->info_block);
+	ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_VQ | info->queue_index);
+	if (ret)
+		dev_warn(&vq->vdev->dev, "Error %x while deleting queue %d",
+			 ret, info->queue_index);
+
+	vring_del_virtqueue(vq);
+	size = PAGE_ALIGN(vring_size(info->num, KVM_VIRTIO_CCW_RING_ALIGN));
+	free_pages_exact(info->queue, size);
+	kfree(info->info_block);
+	kfree(info);
+}
+
+static void virtio_ccw_del_vqs(struct virtio_device *vdev)
+{
+	struct virtqueue *vq, *n;
+
+	list_for_each_entry_safe(vq, n, &vdev->vqs, list)
+		virtio_ccw_del_vq(vq);
+}
+
+static struct virtqueue *virtio_ccw_setup_vq(struct virtio_device *vdev,
+					     int i, vq_callback_t *callback,
+					     const char *name)
+{
+	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
+	int err;
+	struct virtqueue *vq;
+	struct virtio_ccw_vq_info *info;
+	unsigned long size;
+	unsigned long flags;
+
+	/* Allocate queue. */
+	info = kzalloc(sizeof(struct virtio_ccw_vq_info), GFP_KERNEL);
+	if (!info) {
+		dev_warn(&vcdev->cdev->dev, "no info\n");
+		err = -ENOMEM;
+		goto out_err;
+	}
+	info->info_block = kzalloc(sizeof(*info->info_block),
+				   GFP_DMA | GFP_KERNEL);
+	if (!info->info_block) {
+		dev_warn(&vcdev->cdev->dev, "no info block\n");
+		err = -ENOMEM;
+		goto out_err;
+	}
+	info->queue_index = i;
+	info->num = virtio_ccw_read_vq_conf(vcdev, i);
+	size = PAGE_ALIGN(vring_size(info->num, KVM_VIRTIO_CCW_RING_ALIGN));
+	info->queue = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);
+	if (info->queue == NULL) {
+		dev_warn(&vcdev->cdev->dev, "no queue\n");
+		err = -ENOMEM;
+		goto out_err;
+	}
+	vq = vring_new_virtqueue(info->num, KVM_VIRTIO_CCW_RING_ALIGN, vdev,
+				 true, info->queue, virtio_ccw_kvm_notify,
+				 callback, name);
+	if (!vq) {
+		/* For now, we fail if we can't get the requested size. */
+		dev_warn(&vcdev->cdev->dev, "no vq\n");
+		err = -ENOMEM;
+		free_pages_exact(info->queue, size);
+		goto out_err;
+	}
+	info->vq = vq;
+	vq->priv = info;
+
+	/* Register it with the host. */
+	info->info_block->queue = (__u64)info->queue;
+	info->info_block->align = KVM_VIRTIO_CCW_RING_ALIGN;
+	info->info_block->index = info->queue_index;
+	info->info_block->num = info->num;
+	vcdev->ccw.cmd_code = CCW_CMD_SET_VQ;
+	vcdev->ccw.flags = 0;
+	vcdev->ccw.count = sizeof(*info->info_block);
+	vcdev->ccw.cda = (__u32)(unsigned long)(info->info_block);
+	err = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_VQ | info->queue_index);
+	if (err) {
+		dev_warn(&vcdev->cdev->dev, "SET_VQ failed\n");
+		free_pages_exact(info->queue, size);
+		info->vq = NULL;
+		vq->priv = NULL;
+		goto out_err;
+	}
+
+	/* Save it to our list. */
+	spin_lock_irqsave(&vcdev->lock, flags);
+	list_add(&info->node, &vcdev->virtqueues);
+	spin_unlock_irqrestore(&vcdev->lock, flags);
+
+	return vq;
+
+out_err:
+	if (info)
+		kfree(info->info_block);
+	kfree(info);
+	return ERR_PTR(err);
+}
+
+static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs,
+			       struct virtqueue *vqs[],
+			       vq_callback_t *callbacks[],
+			       const char *names[])
+{
+	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
+	int ret, i;
+
+	for (i = 0; i < nvqs; ++i) {
+		vqs[i] = virtio_ccw_setup_vq(vdev, i, callbacks[i], names[i]);
+		if (IS_ERR(vqs[i])) {
+			ret = PTR_ERR(vqs[i]);
+			vqs[i] = NULL;
+			goto out;
+		}
+	}
+	/* Register queue indicators with host. */
+	vcdev->indicators = 0;
+	vcdev->ccw.cmd_code = CCW_CMD_SET_IND;
+	vcdev->ccw.flags = 0;
+	vcdev->ccw.count = sizeof(vcdev->indicators);
+	vcdev->ccw.cda = (__u32)(unsigned long)(&vcdev->indicators);
+	ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_IND);
+	if (ret)
+		goto out;
+	return 0;
+out:
+	virtio_ccw_del_vqs(vdev);
+	return ret;
+}
+
+static void virtio_ccw_reset(struct virtio_device *vdev)
+{
+	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
+
+	/* Send a reset ccw on device. */
+	vcdev->ccw.cmd_code = CCW_CMD_VDEV_RESET;
+	vcdev->ccw.flags = 0;
+	vcdev->ccw.count = 0;
+	vcdev->ccw.cda = 0;
+	ccw_io_helper(vcdev, VIRTIO_CCW_DOING_RESET);
+}
+
+static u32 virtio_ccw_get_features(struct virtio_device *vdev)
+{
+	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
+	struct virtio_feature_desc features;
+	int ret;
+
+	/* Read the feature bits from the host. */
+	/* TODO: Features > 32 bits */
+	features.index = 0;
+	vcdev->ccw.cmd_code = CCW_CMD_READ_FEAT;
+	vcdev->ccw.flags = 0;
+	vcdev->ccw.count = sizeof(features);
+	vcdev->ccw.cda = vcdev->area;
+	ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_FEAT);
+	if (ret)
+		return 0;
+
+	memcpy(&features, (void *)(unsigned long)vcdev->area,
+	       sizeof(features));
+	return le32_to_cpu(features.features);
+}
+
+static void virtio_ccw_finalize_features(struct virtio_device *vdev)
+{
+	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
+	struct virtio_feature_desc features;
+	int i;
+
+	/* Give virtio_ring a chance to accept features. */
+	vring_transport_features(vdev);
+
+	for (i = 0; i < sizeof(*vdev->features) / sizeof(features.features);
+	     i++) {
+		features.index = i;
+		memcpy(&features.features,
+		       vdev->features + i * sizeof(features.features),
+		       sizeof(features.features));
+		memcpy((void *)(unsigned long)vcdev->area, &features,
+		       sizeof(features));
+		/* Write the feature bits to the host. */
+		vcdev->ccw.cmd_code = CCW_CMD_WRITE_FEAT;
+		vcdev->ccw.flags = 0;
+		vcdev->ccw.count = sizeof(features);
+		vcdev->ccw.cda = vcdev->area;
+		ccw_io_helper(vcdev, VIRTIO_CCW_DOING_WRITE_FEAT);
+	}
+}
+
+static void virtio_ccw_get_config(struct virtio_device *vdev,
+				  unsigned int offset, void *buf, unsigned len)
+{
+	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
+	int ret;
+
+	/* Read the config area from the host. */
+	vcdev->ccw.cmd_code = CCW_CMD_READ_CONF;
+	vcdev->ccw.flags = 0;
+	vcdev->ccw.count = offset + len;
+	vcdev->ccw.cda = vcdev->area;
+	ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_CONFIG);
+	if (ret)
+		return;
+
+	memcpy(vcdev->config, (void *)(unsigned long)vcdev->area,
+	       sizeof(vcdev->config));
+	memcpy(buf, &vcdev->config[offset], len);
+}
+
+static void virtio_ccw_set_config(struct virtio_device *vdev,
+				  unsigned int offset, const void *buf,
+				  unsigned len)
+{
+	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
+
+	memcpy(&vcdev->config[offset], buf, len);
+	/* Write the config area to the host. */
+	memcpy((void *)(unsigned long)vcdev->area, vcdev->config,
+	       sizeof(vcdev->config));
+	vcdev->ccw.cmd_code = CCW_CMD_WRITE_CONF;
+	vcdev->ccw.flags = 0;
+	vcdev->ccw.count = offset + len;
+	vcdev->ccw.cda = vcdev->area;
+	ccw_io_helper(vcdev, VIRTIO_CCW_DOING_WRITE_CONFIG);
+}
+
+static u8 virtio_ccw_get_status(struct virtio_device *vdev)
+{
+	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
+
+	return vcdev->status;
+}
+
+static void virtio_ccw_set_status(struct virtio_device *vdev, u8 status)
+{
+	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
+
+	/* Write the status to the host. */
+	vcdev->status = status;
+	memcpy((void *)(unsigned long)vcdev->area, &status, sizeof(status));
+	vcdev->ccw.cmd_code = CCW_CMD_WRITE_STATUS;
+	vcdev->ccw.flags = 0;
+	vcdev->ccw.count = sizeof(status);
+	vcdev->ccw.cda = vcdev->area;
+	ccw_io_helper(vcdev, VIRTIO_CCW_DOING_WRITE_STATUS);
+}
+
+static struct virtio_config_ops virtio_ccw_config_ops = {
+	.get_features = virtio_ccw_get_features,
+	.finalize_features = virtio_ccw_finalize_features,
+	.get = virtio_ccw_get_config,
+	.set = virtio_ccw_set_config,
+	.get_status = virtio_ccw_get_status,
+	.set_status = virtio_ccw_set_status,
+	.reset = virtio_ccw_reset,
+	.find_vqs = virtio_ccw_find_vqs,
+	.del_vqs = virtio_ccw_del_vqs,
+};
+
+
+/*
+ * ccw bus driver related functions
+ */
+
+static void virtio_ccw_release_dev(struct device *_d)
+{
+	struct virtio_device *dev = container_of(_d, struct virtio_device,
+						 dev);
+	struct virtio_ccw_device *vcdev = to_vc_device(dev);
+
+	kfree((void *)(unsigned long)vcdev->area);
+	kfree(vcdev->config_block);
+	kfree(vcdev);
+}
+
+static int irb_is_error(struct irb *irb)
+{
+	if (scsw_cstat(&irb->scsw) != 0)
+		return 1;
+	if (scsw_dstat(&irb->scsw) & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END))
+		return 1;
+	if (scsw_cc(&irb->scsw) != 0)
+		return 1;
+	return 0;
+}
+
+static struct virtqueue *virtio_ccw_vq_by_ind(struct virtio_ccw_device *vcdev,
+					      int index)
+{
+	struct virtio_ccw_vq_info *info;
+	unsigned long flags;
+	struct virtqueue *vq;
+
+	vq = NULL;
+	spin_lock_irqsave(&vcdev->lock, flags);
+	list_for_each_entry(info, &vcdev->virtqueues, node) {
+		if (info->queue_index == index) {
+			vq = info->vq;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&vcdev->lock, flags);
+	return vq;
+}
+
+static void virtio_ccw_int_handler(struct ccw_device *cdev,
+				   unsigned long intparm,
+				   struct irb *irb)
+{
+	__u32 activity = intparm & VIRTIO_CCW_INTPARM_MASK;
+	struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev);
+	int i;
+	struct virtqueue *vq;
+
+	/* Check if it's a notification from the host. */
+	if ((intparm == 0) &&
+	    (scsw_stctl(&irb->scsw) ==
+	     (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND))) {
+		/* OK */
+	}
+	if (irb_is_error(irb))
+		vcdev->err = -EIO; /* XXX - use real error */
+	if (vcdev->curr_io & activity) {
+		switch (activity) {
+		case VIRTIO_CCW_DOING_READ_FEAT:
+		case VIRTIO_CCW_DOING_WRITE_FEAT:
+		case VIRTIO_CCW_DOING_READ_CONFIG:
+		case VIRTIO_CCW_DOING_WRITE_CONFIG:
+		case VIRTIO_CCW_DOING_WRITE_STATUS:
+		case VIRTIO_CCW_DOING_SET_VQ:
+		case VIRTIO_CCW_DOING_SET_IND:
+		case VIRTIO_CCW_DOING_RESET:
+		case VIRTIO_CCW_DOING_READ_VQ_CONF:
+			vcdev->curr_io &= ~activity;
+			wake_up(&vcdev->wait_q);
+			break;
+		default:
+			/* don't know what to do... */
+			dev_warn(&cdev->dev, "Suspicious activity '%08x'\n",
+				 activity);
+			WARN_ON(1);
+			break;
+		}
+	}
+	for_each_set_bit(i, &vcdev->indicators,
+			 sizeof(vcdev->indicators)) {
+		vq = virtio_ccw_vq_by_ind(vcdev, i);
+		vring_interrupt(0, vq);
+		clear_bit(i, &vcdev->indicators);
+	}
+}
+
+/*
+ * We usually want to autoonline all devices, but give the admin
+ * a way to exempt devices from this.
+ */
+#define __DEV_WORDS ((__MAX_SUBCHANNEL + (8*sizeof(long) - 1)) / \
+		     (8*sizeof(long)))
+static unsigned long devs_no_auto[__MAX_SSID + 1][__DEV_WORDS];
+
+static char *no_auto = "";
+
+module_param(no_auto, charp, 0444);
+MODULE_PARM_DESC(no_auto, "list of ccw bus id ranges not to be auto-onlined");
+
+static int virtio_ccw_check_autoonline(struct ccw_device *cdev)
+{
+	struct ccw_dev_id id;
+
+	ccw_device_get_id(cdev, &id);
+	if (test_bit(id.devno, devs_no_auto[id.ssid]))
+		return 0;
+	return 1;
+}
+
+static void virtio_ccw_auto_online(void *data, async_cookie_t cookie)
+{
+	struct ccw_device *cdev = data;
+	int ret;
+
+	ret = ccw_device_set_online(cdev);
+	if (ret)
+		dev_warn(&cdev->dev, "Failed to set online: %d\n", ret);
+}
+
+static int virtio_ccw_probe(struct ccw_device *cdev)
+{
+	cdev->handler = virtio_ccw_int_handler;
+
+	if (virtio_ccw_check_autoonline(cdev))
+		async_schedule(virtio_ccw_auto_online, cdev);
+	return 0;
+}
+
+static void virtio_ccw_remove(struct ccw_device *cdev)
+{
+	struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev);
+
+	if (cdev->online) {
+		unregister_virtio_device(&vcdev->vdev);
+		dev_set_drvdata(&cdev->dev, NULL);
+	}
+	cdev->handler = NULL;
+}
+
+static int virtio_ccw_offline(struct ccw_device *cdev)
+{
+	struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev);
+
+	unregister_virtio_device(&vcdev->vdev);
+	dev_set_drvdata(&cdev->dev, NULL);
+	return 0;
+}
+
+
+/* Area needs to be big enough to fit status, features or configuration. */
+#define VIRTIO_AREA_SIZE VIRTIO_CCW_CONFIG_SIZE /* biggest possible use */
+
+static int virtio_ccw_online(struct ccw_device *cdev)
+{
+	int ret;
+	struct virtio_ccw_device *vcdev;
+
+	vcdev = kzalloc(sizeof(*vcdev), GFP_KERNEL);
+	if (!vcdev) {
+		dev_warn(&cdev->dev, "Could not get memory for virtio\n");
+		ret = -ENOMEM;
+		goto out_free;
+	}
+	vcdev->area = (__u32)(unsigned long)kzalloc(VIRTIO_AREA_SIZE,
+						    GFP_DMA | GFP_KERNEL);
+	if (!vcdev->area) {
+		dev_warn(&cdev->dev, "Cound not get memory for virtio\n");
+		ret = -ENOMEM;
+		goto out_free;
+	}
+	vcdev->config_block = kzalloc(sizeof(*vcdev->config_block),
+				   GFP_DMA | GFP_KERNEL);
+	if (!vcdev->config_block) {
+		ret = -ENOMEM;
+		goto out_free;
+	}
+	vcdev->vdev.dev.parent = &cdev->dev;
+	vcdev->vdev.dev.release = virtio_ccw_release_dev;
+	vcdev->vdev.config = &virtio_ccw_config_ops;
+	vcdev->cdev = cdev;
+	init_waitqueue_head(&vcdev->wait_q);
+	INIT_LIST_HEAD(&vcdev->virtqueues);
+	spin_lock_init(&vcdev->lock);
+
+	dev_set_drvdata(&cdev->dev, vcdev);
+	vcdev->vdev.id.vendor = cdev->id.cu_type;
+	vcdev->vdev.id.device = cdev->id.cu_model;
+	ret = register_virtio_device(&vcdev->vdev);
+	if (ret) {
+		dev_warn(&cdev->dev, "Failed to register virtio device: %d\n",
+			 ret);
+		goto out_put;
+	}
+	return 0;
+out_put:
+	dev_set_drvdata(&cdev->dev, NULL);
+	put_device(&vcdev->vdev.dev);
+	return ret;
+out_free:
+	if (vcdev) {
+		kfree((void *)(unsigned long)vcdev->area);
+		kfree(vcdev->config_block);
+	}
+	kfree(vcdev);
+	return ret;
+}
+
+static int virtio_ccw_cio_notify(struct ccw_device *cdev, int event)
+{
+	/* TODO: Check whether we need special handling here. */
+	return 0;
+}
+
+static struct ccw_device_id virtio_ids[] = {
+	{ CCW_DEVICE(0x3832, 0) },
+	{},
+};
+MODULE_DEVICE_TABLE(ccw, virtio_ids);
+
+static struct ccw_driver virtio_ccw_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "virtio_ccw",
+	},
+	.ids = virtio_ids,
+	.probe = virtio_ccw_probe,
+	.remove = virtio_ccw_remove,
+	.set_offline = virtio_ccw_offline,
+	.set_online = virtio_ccw_online,
+	.notify = virtio_ccw_cio_notify,
+	.int_class = IOINT_VIR,
+};
+
+static int __init pure_hex(char **cp, unsigned int *val, int min_digit,
+			   int max_digit, int max_val)
+{
+	int diff;
+
+	diff = 0;
+	*val = 0;
+
+	while (diff <= max_digit) {
+		int value = hex_to_bin(**cp);
+
+		if (value < 0)
+			break;
+		*val = *val * 16 + value;
+		(*cp)++;
+		diff++;
+	}
+
+	if ((diff < min_digit) || (diff > max_digit) || (*val > max_val))
+		return 1;
+
+	return 0;
+}
+
+static int __init parse_busid(char *str, unsigned int *cssid,
+			      unsigned int *ssid, unsigned int *devno)
+{
+	char *str_work;
+	int rc, ret;
+
+	rc = 1;
+
+	if (*str == '\0')
+		goto out;
+
+	str_work = str;
+	ret = pure_hex(&str_work, cssid, 1, 2, __MAX_CSSID);
+	if (ret || (str_work[0] != '.'))
+		goto out;
+	str_work++;
+	ret = pure_hex(&str_work, ssid, 1, 1, __MAX_SSID);
+	if (ret || (str_work[0] != '.'))
+		goto out;
+	str_work++;
+	ret = pure_hex(&str_work, devno, 4, 4, __MAX_SUBCHANNEL);
+	if (ret || (str_work[0] != '\0'))
+		goto out;
+
+	rc = 0;
+out:
+	return rc;
+}
+
+static void __init no_auto_parse(void)
+{
+	unsigned int from_cssid, to_cssid, from_ssid, to_ssid, from, to;
+	char *parm, *str;
+	int rc;
+
+	str = no_auto;
+	while ((parm = strsep(&str, ","))) {
+		rc = parse_busid(strsep(&parm, "-"), &from_cssid,
+				 &from_ssid, &from);
+		if (rc)
+			continue;
+		if (parm != NULL) {
+			rc = parse_busid(parm, &to_cssid,
+					 &to_ssid, &to);
+			if ((from_ssid > to_ssid) ||
+			    ((from_ssid == to_ssid) && (from > to)))
+				rc = -EINVAL;
+		} else {
+			to_cssid = from_cssid;
+			to_ssid = from_ssid;
+			to = from;
+		}
+		if (rc)
+			continue;
+		while ((from_ssid < to_ssid) ||
+		       ((from_ssid == to_ssid) && (from <= to))) {
+			set_bit(from, devs_no_auto[from_ssid]);
+			from++;
+			if (from > __MAX_SUBCHANNEL) {
+				from_ssid++;
+				from = 0;
+			}
+		}
+	}
+}
+
+static int __init virtio_ccw_init(void)
+{
+	/* parse no_auto string before we do anything further */
+	no_auto_parse();
+	return ccw_driver_register(&virtio_ccw_driver);
+}
+module_init(virtio_ccw_init);
+
+static void __exit virtio_ccw_exit(void)
+{
+	ccw_driver_unregister(&virtio_ccw_driver);
+}
+module_exit(virtio_ccw_exit);
-- 
1.7.11.5

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [Qemu-devel] [PATCH v2 4/4] s390/kvm: Split out early console code.
  2012-09-04 15:13 [Qemu-devel] [RFC PATCH v2 0/4] s390: virtio-ccw guest kernel support Cornelia Huck
                   ` (2 preceding siblings ...)
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 3/4] s390/kvm: Add a channel I/O based virtio transport driver Cornelia Huck
@ 2012-09-04 15:13 ` Cornelia Huck
  3 siblings, 0 replies; 7+ messages in thread
From: Cornelia Huck @ 2012-09-04 15:13 UTC (permalink / raw)
  To: KVM, linux-s390, qemu-devel
  Cc: Carsten Otte, Anthony Liguori, Rusty Russell, Sebastian Ott,
	Marcelo Tosatti, Heiko Carstens, Alexander Graf,
	Christian Borntraeger, Avi Kivity, Martin Schwidefsky

This code is transport agnostic and can be used by both the legacy
virtio code and virtio_ccw.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
 drivers/s390/kvm/Makefile       |  2 +-
 drivers/s390/kvm/early_printk.c | 42 +++++++++++++++++++++++++++++++++++++++++
 drivers/s390/kvm/kvm_virtio.c   | 29 ++--------------------------
 drivers/s390/kvm/virtio_ccw.c   |  1 -
 4 files changed, 45 insertions(+), 29 deletions(-)
 create mode 100644 drivers/s390/kvm/early_printk.c

diff --git a/drivers/s390/kvm/Makefile b/drivers/s390/kvm/Makefile
index 241891a..a3c8fc4 100644
--- a/drivers/s390/kvm/Makefile
+++ b/drivers/s390/kvm/Makefile
@@ -6,4 +6,4 @@
 # it under the terms of the GNU General Public License (version 2 only)
 # as published by the Free Software Foundation.
 
-obj-$(CONFIG_S390_GUEST) += kvm_virtio.o virtio_ccw.o
+obj-$(CONFIG_S390_GUEST) += kvm_virtio.o early_printk.o virtio_ccw.o
diff --git a/drivers/s390/kvm/early_printk.c b/drivers/s390/kvm/early_printk.c
new file mode 100644
index 0000000..7831530
--- /dev/null
+++ b/drivers/s390/kvm/early_printk.c
@@ -0,0 +1,42 @@
+/*
+ * early_printk.c - code for early console output with virtio_console
+ * split off from kvm_virtio.c
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (version 2 only)
+ * as published by the Free Software Foundation.
+ *
+ *    Author(s): Christian Borntraeger <borntraeger@de.ibm.com>
+ */
+
+#include <linux/kernel_stat.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/virtio_console.h>
+#include <asm/kvm_para.h>
+#include <asm/kvm_virtio.h>
+#include <asm/setup.h>
+#include <asm/sclp.h>
+
+static __init int early_put_chars(u32 vtermno, const char *buf, int count)
+{
+	char scratch[17];
+	unsigned int len = count;
+
+	if (len > sizeof(scratch) - 1)
+		len = sizeof(scratch) - 1;
+	scratch[len] = '\0';
+	memcpy(scratch, buf, len);
+	kvm_hypercall1(KVM_S390_VIRTIO_NOTIFY, __pa(scratch));
+	return len;
+}
+
+static int __init s390_virtio_console_init(void)
+{
+	if (sclp_has_vt220() || sclp_has_linemode())
+		return -ENODEV;
+	return virtio_cons_early_init(early_put_chars);
+}
+console_initcall(s390_virtio_console_init);
diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c
index 76b95f3..6cdc66a 100644
--- a/drivers/s390/kvm/kvm_virtio.c
+++ b/drivers/s390/kvm/kvm_virtio.c
@@ -17,7 +17,6 @@
 #include <linux/virtio.h>
 #include <linux/virtio_config.h>
 #include <linux/slab.h>
-#include <linux/virtio_console.h>
 #include <linux/interrupt.h>
 #include <linux/virtio_ring.h>
 #include <linux/export.h>
@@ -25,9 +24,9 @@
 #include <asm/io.h>
 #include <asm/kvm_para.h>
 #include <asm/kvm_virtio.h>
-#include <asm/sclp.h>
 #include <asm/setup.h>
 #include <asm/irq.h>
+#include <asm/sclp.h>
 
 #define VIRTIO_SUBCODE_64 0x0D00
 
@@ -450,8 +449,7 @@ static int __init kvm_devices_init(void)
 		return -ENODEV;
 
 	if (test_devices_support(real_memory_size) < 0)
-		/* No error. */
-		return 0;
+		return -ENODEV;
 
 	rc = vmem_add_mapping(real_memory_size, PAGE_SIZE);
 	if (rc)
@@ -476,29 +474,6 @@ static int __init kvm_devices_init(void)
 	return 0;
 }
 
-/* code for early console output with virtio_console */
-static __init int early_put_chars(u32 vtermno, const char *buf, int count)
-{
-	char scratch[17];
-	unsigned int len = count;
-
-	if (len > sizeof(scratch) - 1)
-		len = sizeof(scratch) - 1;
-	scratch[len] = '\0';
-	memcpy(scratch, buf, len);
-	kvm_hypercall1(KVM_S390_VIRTIO_NOTIFY, __pa(scratch));
-	return len;
-}
-
-static int __init s390_virtio_console_init(void)
-{
-	if (sclp_has_vt220() || sclp_has_linemode())
-		return -ENODEV;
-	return virtio_cons_early_init(early_put_chars);
-}
-console_initcall(s390_virtio_console_init);
-
-
 /*
  * We do this after core stuff, but before the drivers.
  */
diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c
index 1c9af22..14ae293 100644
--- a/drivers/s390/kvm/virtio_ccw.c
+++ b/drivers/s390/kvm/virtio_ccw.c
@@ -17,7 +17,6 @@
 #include <linux/virtio.h>
 #include <linux/virtio_config.h>
 #include <linux/slab.h>
-#include <linux/virtio_console.h>
 #include <linux/interrupt.h>
 #include <linux/virtio_ring.h>
 #include <linux/pfn.h>
-- 
1.7.11.5

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [Qemu-devel] [PATCH v2 3/4] s390/kvm: Add a channel I/O based virtio transport driver.
  2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 3/4] s390/kvm: Add a channel I/O based virtio transport driver Cornelia Huck
@ 2012-09-19 16:38   ` Alexander Graf
  2012-10-09 14:30     ` Cornelia Huck
  0 siblings, 1 reply; 7+ messages in thread
From: Alexander Graf @ 2012-09-19 16:38 UTC (permalink / raw)
  To: Cornelia Huck
  Cc: linux-s390, Anthony Liguori, Rusty Russell, KVM, Carsten Otte,
	Sebastian Ott, Marcelo Tosatti, Heiko Carstens, qemu-devel,
	Christian Borntraeger, Avi Kivity, Martin Schwidefsky


On 04.09.2012, at 17:13, Cornelia Huck wrote:

> Add a driver for kvm guests that matches virtual ccw devices provided
> by the host as virtio bridge devices.
> 
> These virtio-ccw devices use a special set of channel commands in order
> to perform virtio functions.
> 
> Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
> ---
> 
> Changes v1->v2:
> - adapt to improved virtio-ccw channel commands
> - fix unregistration of online devices
> - add a missing spinlock initialization
> 
> ---
> arch/s390/include/asm/irq.h   |   1 +
> arch/s390/kernel/irq.c        |   1 +
> drivers/s390/kvm/Makefile     |   2 +-
> drivers/s390/kvm/virtio_ccw.c | 790 ++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 793 insertions(+), 1 deletion(-)
> create mode 100644 drivers/s390/kvm/virtio_ccw.c
> 
> diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h
> index 2b9d418..b4bea53 100644
> --- a/arch/s390/include/asm/irq.h
> +++ b/arch/s390/include/asm/irq.h
> @@ -31,6 +31,7 @@ enum interruption_class {
> 	IOINT_CTC,
> 	IOINT_APB,
> 	IOINT_CSC,
> +	IOINT_VIR,
> 	NMI_NMI,
> 	NR_IRQS,
> };
> diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c
> index dd7630d..2cc7eed 100644
> --- a/arch/s390/kernel/irq.c
> +++ b/arch/s390/kernel/irq.c
> @@ -56,6 +56,7 @@ static const struct irq_class intrclass_names[] = {
> 	{.name = "CTC", .desc = "[I/O] CTC" },
> 	{.name = "APB", .desc = "[I/O] AP Bus" },
> 	{.name = "CSC", .desc = "[I/O] CHSC Subchannel" },
> +	{.name = "VIR", .desc = "[I/O] Virtual I/O Devices" },
> 	{.name = "NMI", .desc = "[NMI] Machine Check" },
> };
> 
> diff --git a/drivers/s390/kvm/Makefile b/drivers/s390/kvm/Makefile
> index 0815690..241891a 100644
> --- a/drivers/s390/kvm/Makefile
> +++ b/drivers/s390/kvm/Makefile
> @@ -6,4 +6,4 @@
> # it under the terms of the GNU General Public License (version 2 only)
> # as published by the Free Software Foundation.
> 
> -obj-$(CONFIG_S390_GUEST) += kvm_virtio.o
> +obj-$(CONFIG_S390_GUEST) += kvm_virtio.o virtio_ccw.o
> diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c
> new file mode 100644
> index 0000000..1c9af22
> --- /dev/null
> +++ b/drivers/s390/kvm/virtio_ccw.c
> @@ -0,0 +1,790 @@
> +/*
> + * ccw based virtio transport
> + *
> + * Copyright IBM Corp. 2012
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License (version 2 only)
> + * as published by the Free Software Foundation.
> + *
> + *    Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
> + */
> +
> +#include <linux/kernel_stat.h>
> +#include <linux/init.h>
> +#include <linux/bootmem.h>
> +#include <linux/err.h>
> +#include <linux/virtio.h>
> +#include <linux/virtio_config.h>
> +#include <linux/slab.h>
> +#include <linux/virtio_console.h>
> +#include <linux/interrupt.h>
> +#include <linux/virtio_ring.h>
> +#include <linux/pfn.h>
> +#include <linux/async.h>
> +#include <linux/wait.h>
> +#include <linux/list.h>
> +#include <linux/bitops.h>
> +#include <linux/module.h>
> +#include <asm/io.h>
> +#include <asm/kvm_para.h>
> +#include <asm/setup.h>
> +#include <asm/irq.h>
> +#include <asm/cio.h>
> +#include <asm/ccwdev.h>
> +
> +/*
> + * virtio related functions
> + */
> +
> +struct vq_config_block {
> +	__u16 index;
> +	__u16 num;
> +} __attribute__ ((packed));
> +
> +#define VIRTIO_CCW_CONFIG_SIZE 0x100
> +/* same as PCI config space size, should be enough for all drivers */
> +
> +struct virtio_ccw_device {
> +	struct virtio_device vdev;
> +	__u8 status;
> +	__u8 config[VIRTIO_CCW_CONFIG_SIZE];
> +	struct ccw_device *cdev;
> +	struct ccw1 ccw;
> +	__u32 area;
> +	__u32 curr_io;
> +	int err;
> +	wait_queue_head_t wait_q;
> +	spinlock_t lock;
> +	struct list_head virtqueues;
> +	unsigned long indicators; /* XXX - works because we're under 64 bit */
> +	struct vq_config_block *config_block;
> +};
> +
> +struct vq_info_block {
> +	__u64 queue;
> +	__u32 align;
> +	__u16 index;
> +	__u16 num;
> +} __attribute__ ((packed));
> +
> +struct virtio_feature_desc {
> +	__u32 features;
> +	__u8 index;
> +} __attribute__ ((packed));
> +
> +struct virtio_ccw_vq_info {
> +	struct virtqueue *vq;
> +	int num;
> +	int queue_index;
> +	void *queue;
> +	struct vq_info_block *info_block;
> +	struct list_head node;
> +};
> +
> +#define KVM_VIRTIO_CCW_RING_ALIGN 4096
> +
> +#define CCW_CMD_SET_VQ 0x13
> +#define CCW_CMD_VDEV_RESET 0x33
> +#define CCW_CMD_SET_IND 0x43
> +#define CCW_CMD_READ_FEAT 0x12
> +#define CCW_CMD_WRITE_FEAT 0x11
> +#define CCW_CMD_READ_CONF 0x22
> +#define CCW_CMD_WRITE_CONF 0x21
> +#define CCW_CMD_WRITE_STATUS 0x31
> +#define CCW_CMD_READ_VQ_CONF 0x32
> +
> +#define VIRTIO_CCW_DOING_SET_VQ 0x00010000
> +#define VIRTIO_CCW_DOING_RESET 0x00040000
> +#define VIRTIO_CCW_DOING_READ_FEAT 0x00080000
> +#define VIRTIO_CCW_DOING_WRITE_FEAT 0x00100000
> +#define VIRTIO_CCW_DOING_READ_CONFIG 0x00200000
> +#define VIRTIO_CCW_DOING_WRITE_CONFIG 0x00400000
> +#define VIRTIO_CCW_DOING_WRITE_STATUS 0x00800000
> +#define VIRTIO_CCW_DOING_SET_IND 0x01000000
> +#define VIRTIO_CCW_DOING_READ_VQ_CONF 0x02000000
> +#define VIRTIO_CCW_INTPARM_MASK 0xffff0000
> +
> +static struct virtio_ccw_device *to_vc_device(struct virtio_device *vdev)
> +{
> +	return container_of(vdev, struct virtio_ccw_device, vdev);
> +}
> +
> +static int doing_io(struct virtio_ccw_device *vcdev, __u32 flag)
> +{
> +	unsigned long flags;
> +	__u32 ret;
> +
> +	spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags);
> +	if (vcdev->err)
> +		ret = vcdev->err;
> +	else
> +		ret = vcdev->curr_io & flag;
> +	spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags);
> +	return ret;
> +}
> +
> +static int ccw_io_helper(struct virtio_ccw_device *vcdev, __u32 intparm)
> +{
> +	int ret;
> +	unsigned long flags;
> +	int flag = intparm & VIRTIO_CCW_INTPARM_MASK;
> +
> +	spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags);
> +	ret = ccw_device_start(vcdev->cdev, &vcdev->ccw, intparm, 0, 0);
> +	if (!ret)
> +		vcdev->curr_io |= flag;
> +	spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags);
> +	wait_event(vcdev->wait_q, doing_io(vcdev, flag) == 0);
> +	return ret ? ret : vcdev->err;
> +}
> +
> +static void virtio_ccw_kvm_notify(struct virtqueue *vq)
> +{
> +	struct virtio_ccw_vq_info *info = vq->priv;
> +	struct virtio_ccw_device *vcdev;
> +	struct subchannel_id schid;
> +	__u32 reg2;
> +
> +	vcdev = to_vc_device(info->vq->vdev);
> +	ccw_device_get_schid(vcdev->cdev, &schid);
> +	reg2 = *(__u32 *)&schid;
> +	kvm_hypercall2(3 /* CCW_NOTIFY */, reg2, info->queue_index);
> +}
> +
> +static int virtio_ccw_read_vq_conf(struct virtio_ccw_device *vcdev, int index)
> +{
> +	vcdev->config_block->index = index;
> +	vcdev->ccw.cmd_code = CCW_CMD_READ_VQ_CONF;
> +	vcdev->ccw.flags = 0;
> +	vcdev->ccw.count = sizeof(struct vq_config_block);
> +	vcdev->ccw.cda = (__u32)(unsigned long)(vcdev->config_block);
> +	ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_VQ_CONF);
> +	return vcdev->config_block->num;
> +}
> +
> +static void virtio_ccw_del_vq(struct virtqueue *vq)
> +{
> +	struct virtio_ccw_device *vcdev = to_vc_device(vq->vdev);
> +	struct virtio_ccw_vq_info *info = vq->priv;
> +	unsigned long flags;
> +	unsigned long size;
> +	int ret;
> +
> +	/* Remove from our list. */
> +	spin_lock_irqsave(&vcdev->lock, flags);
> +	list_del(&info->node);
> +	spin_unlock_irqrestore(&vcdev->lock, flags);
> +
> +	/* Release from host. */
> +	info->info_block->queue = 0;
> +	info->info_block->align = 0;
> +	info->info_block->index = info->queue_index;
> +	info->info_block->num = 0;
> +	vcdev->ccw.cmd_code = CCW_CMD_SET_VQ;
> +	vcdev->ccw.flags = 0;
> +	vcdev->ccw.count = sizeof(*info->info_block);
> +	vcdev->ccw.cda = (__u32)(unsigned long)(info->info_block);
> +	ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_VQ | info->queue_index);
> +	if (ret)
> +		dev_warn(&vq->vdev->dev, "Error %x while deleting queue %d",
> +			 ret, info->queue_index);
> +
> +	vring_del_virtqueue(vq);
> +	size = PAGE_ALIGN(vring_size(info->num, KVM_VIRTIO_CCW_RING_ALIGN));
> +	free_pages_exact(info->queue, size);
> +	kfree(info->info_block);
> +	kfree(info);
> +}
> +
> +static void virtio_ccw_del_vqs(struct virtio_device *vdev)
> +{
> +	struct virtqueue *vq, *n;
> +
> +	list_for_each_entry_safe(vq, n, &vdev->vqs, list)
> +		virtio_ccw_del_vq(vq);
> +}
> +
> +static struct virtqueue *virtio_ccw_setup_vq(struct virtio_device *vdev,
> +					     int i, vq_callback_t *callback,
> +					     const char *name)
> +{
> +	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
> +	int err;
> +	struct virtqueue *vq;
> +	struct virtio_ccw_vq_info *info;
> +	unsigned long size;
> +	unsigned long flags;
> +
> +	/* Allocate queue. */
> +	info = kzalloc(sizeof(struct virtio_ccw_vq_info), GFP_KERNEL);
> +	if (!info) {
> +		dev_warn(&vcdev->cdev->dev, "no info\n");
> +		err = -ENOMEM;
> +		goto out_err;
> +	}
> +	info->info_block = kzalloc(sizeof(*info->info_block),
> +				   GFP_DMA | GFP_KERNEL);
> +	if (!info->info_block) {
> +		dev_warn(&vcdev->cdev->dev, "no info block\n");
> +		err = -ENOMEM;
> +		goto out_err;
> +	}
> +	info->queue_index = i;
> +	info->num = virtio_ccw_read_vq_conf(vcdev, i);
> +	size = PAGE_ALIGN(vring_size(info->num, KVM_VIRTIO_CCW_RING_ALIGN));
> +	info->queue = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);
> +	if (info->queue == NULL) {
> +		dev_warn(&vcdev->cdev->dev, "no queue\n");
> +		err = -ENOMEM;
> +		goto out_err;
> +	}
> +	vq = vring_new_virtqueue(info->num, KVM_VIRTIO_CCW_RING_ALIGN, vdev,
> +				 true, info->queue, virtio_ccw_kvm_notify,
> +				 callback, name);
> +	if (!vq) {
> +		/* For now, we fail if we can't get the requested size. */
> +		dev_warn(&vcdev->cdev->dev, "no vq\n");
> +		err = -ENOMEM;
> +		free_pages_exact(info->queue, size);
> +		goto out_err;
> +	}
> +	info->vq = vq;
> +	vq->priv = info;
> +
> +	/* Register it with the host. */
> +	info->info_block->queue = (__u64)info->queue;
> +	info->info_block->align = KVM_VIRTIO_CCW_RING_ALIGN;
> +	info->info_block->index = info->queue_index;
> +	info->info_block->num = info->num;
> +	vcdev->ccw.cmd_code = CCW_CMD_SET_VQ;
> +	vcdev->ccw.flags = 0;
> +	vcdev->ccw.count = sizeof(*info->info_block);
> +	vcdev->ccw.cda = (__u32)(unsigned long)(info->info_block);
> +	err = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_VQ | info->queue_index);
> +	if (err) {
> +		dev_warn(&vcdev->cdev->dev, "SET_VQ failed\n");
> +		free_pages_exact(info->queue, size);
> +		info->vq = NULL;
> +		vq->priv = NULL;
> +		goto out_err;
> +	}
> +
> +	/* Save it to our list. */
> +	spin_lock_irqsave(&vcdev->lock, flags);
> +	list_add(&info->node, &vcdev->virtqueues);
> +	spin_unlock_irqrestore(&vcdev->lock, flags);
> +
> +	return vq;
> +
> +out_err:
> +	if (info)
> +		kfree(info->info_block);
> +	kfree(info);
> +	return ERR_PTR(err);
> +}
> +
> +static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs,
> +			       struct virtqueue *vqs[],
> +			       vq_callback_t *callbacks[],
> +			       const char *names[])
> +{
> +	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
> +	int ret, i;
> +
> +	for (i = 0; i < nvqs; ++i) {
> +		vqs[i] = virtio_ccw_setup_vq(vdev, i, callbacks[i], names[i]);
> +		if (IS_ERR(vqs[i])) {
> +			ret = PTR_ERR(vqs[i]);
> +			vqs[i] = NULL;
> +			goto out;
> +		}
> +	}
> +	/* Register queue indicators with host. */
> +	vcdev->indicators = 0;
> +	vcdev->ccw.cmd_code = CCW_CMD_SET_IND;
> +	vcdev->ccw.flags = 0;
> +	vcdev->ccw.count = sizeof(vcdev->indicators);
> +	vcdev->ccw.cda = (__u32)(unsigned long)(&vcdev->indicators);
> +	ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_IND);
> +	if (ret)
> +		goto out;
> +	return 0;
> +out:
> +	virtio_ccw_del_vqs(vdev);
> +	return ret;
> +}
> +
> +static void virtio_ccw_reset(struct virtio_device *vdev)
> +{
> +	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
> +
> +	/* Send a reset ccw on device. */
> +	vcdev->ccw.cmd_code = CCW_CMD_VDEV_RESET;
> +	vcdev->ccw.flags = 0;
> +	vcdev->ccw.count = 0;
> +	vcdev->ccw.cda = 0;
> +	ccw_io_helper(vcdev, VIRTIO_CCW_DOING_RESET);
> +}
> +
> +static u32 virtio_ccw_get_features(struct virtio_device *vdev)
> +{
> +	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
> +	struct virtio_feature_desc features;
> +	int ret;
> +
> +	/* Read the feature bits from the host. */
> +	/* TODO: Features > 32 bits */
> +	features.index = 0;
> +	vcdev->ccw.cmd_code = CCW_CMD_READ_FEAT;
> +	vcdev->ccw.flags = 0;
> +	vcdev->ccw.count = sizeof(features);
> +	vcdev->ccw.cda = vcdev->area;
> +	ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_FEAT);
> +	if (ret)
> +		return 0;
> +
> +	memcpy(&features, (void *)(unsigned long)vcdev->area,
> +	       sizeof(features));
> +	return le32_to_cpu(features.features);

The fact that the features are LE is missing from the spec, right?


Alex

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [Qemu-devel] [PATCH v2 3/4] s390/kvm: Add a channel I/O based virtio transport driver.
  2012-09-19 16:38   ` Alexander Graf
@ 2012-10-09 14:30     ` Cornelia Huck
  0 siblings, 0 replies; 7+ messages in thread
From: Cornelia Huck @ 2012-10-09 14:30 UTC (permalink / raw)
  To: Alexander Graf
  Cc: linux-s390, Anthony Liguori, Rusty Russell, KVM, Carsten Otte,
	Sebastian Ott, Marcelo Tosatti, Heiko Carstens, qemu-devel,
	Christian Borntraeger, Avi Kivity, Martin Schwidefsky

On Wed, 19 Sep 2012 18:38:38 +0200
Alexander Graf <agraf@suse.de> wrote:

> 
> On 04.09.2012, at 17:13, Cornelia Huck wrote:

> > +static u32 virtio_ccw_get_features(struct virtio_device *vdev)
> > +{
> > +	struct virtio_ccw_device *vcdev = to_vc_device(vdev);
> > +	struct virtio_feature_desc features;
> > +	int ret;
> > +
> > +	/* Read the feature bits from the host. */
> > +	/* TODO: Features > 32 bits */
> > +	features.index = 0;
> > +	vcdev->ccw.cmd_code = CCW_CMD_READ_FEAT;
> > +	vcdev->ccw.flags = 0;
> > +	vcdev->ccw.count = sizeof(features);
> > +	vcdev->ccw.cda = vcdev->area;
> > +	ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_FEAT);
> > +	if (ret)
> > +		return 0;
> > +
> > +	memcpy(&features, (void *)(unsigned long)vcdev->area,
> > +	       sizeof(features));
> > +	return le32_to_cpu(features.features);
> 
> The fact that the features are LE is missing from the spec, right?

You're right, I thought it was there.

> 
> 
> Alex
> 

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2012-10-09 14:31 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-09-04 15:13 [Qemu-devel] [RFC PATCH v2 0/4] s390: virtio-ccw guest kernel support Cornelia Huck
2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 1/4] s390/kvm: Handle hosts not supporting s390-virtio Cornelia Huck
2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 2/4] s390: Add a mechanism to get the subchannel id Cornelia Huck
2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 3/4] s390/kvm: Add a channel I/O based virtio transport driver Cornelia Huck
2012-09-19 16:38   ` Alexander Graf
2012-10-09 14:30     ` Cornelia Huck
2012-09-04 15:13 ` [Qemu-devel] [PATCH v2 4/4] s390/kvm: Split out early console code Cornelia Huck

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).