[PATCH/RFC 6/9] virtual block device driver

Carsten Otte
2007-05-11T17:36:03Z

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

This driver provides access to virtual block devices. It does use its own
make_request function which passes the bio to a workqueue thread. The workqueue
thread does use the diagnose hypervisor call to call the hosting Linux.
The hypervisor code in host userspace does use aio_submit to initiate the IO.
Once the IO is done, the host will use io_getevents and then generate an
interrupt to the guest. The interrupt handler calls bio_endio.
This device driver is currently architecture dependent. We intend to move the
host API to hypercall instead of the diagnose instuction. Please review.

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

---
drivers/s390/block/Kconfig | 7
drivers/s390/guest/Makefile | 1
drivers/s390/guest/vdisk.c | 153 +++++++++++++++++
drivers/s390/guest/vdisk.h | 230 ++++++++++++++++++++++++++
drivers/s390/guest/vdisk_blk.c | 355 +++++++++++++++++++++++++++++++++++++++++
5 files changed, 746 insertions(+)

Index: linux-2.6.21/drivers/s390/guest/vdisk.c
===================================================================
--- /dev/null
+++ linux-2.6.21/drivers/s390/guest/vdisk.c
@@ -0,0 +1,153 @@
+/*
+ * guest virtual block device driver
+ * Copyright IBM Corp. 2007
+ * Author: Carsten Otte <cotte-tA70FqPdS9bQT0dZR+AlfA@public.gmane.org>
+ */
+
+#include <linux/blkdev.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <asm/ptrace.h>
+#include <asm/s390_ext.h>
+#include <asm/vdev.h>
+#include "vdisk.h"
+
+MODULE_DESCRIPTION("Guest virtual block device driver");
+MODULE_AUTHOR("Carsten Otte <cotte-tA70FqPdS9bQT0dZR+AlfA@public.gmane.org>");
+MODULE_LICENSE("GPL");
+
+static struct vdev_driver vdisk_driver;
+
+static int __find_fd(struct device *dev, void* fdptr) {
+ int fd = (long)fdptr;
+
+ struct vdev *vdev = to_vdev(dev);
+ struct vdisk_device *vdisk = (struct vdisk_device *)vdev->drv_private;
+
+ if (vdisk->vfd == fd)
+ return 1;
+ else
+ return 0;
+}
+
+vdisk_irq_t vdisk_get_irqpage(int fd)
+{
+ struct device *dev;
+ struct vdev *vdev;
+ struct vdisk_device *vdisk;
+
+ dev = driver_find_device(&vdisk_driver.driver, NULL, (void*)(long)fd, __find_fd);
+ if (!dev)
+ return NULL;
+ vdev = to_vdev(dev);
+ vdisk = (struct vdisk_device *)vdev->drv_private;
+ return vdisk->irq_page;
+}
+
+struct vdisk_device * vdisk_get_device_by_fd(int fd)
+{
+ struct device *dev;
+ struct vdev *vdev;
+ struct vdisk_device *vdisk;
+
+ dev = driver_find_device(&vdisk_driver.driver, NULL, (void*)(long)fd, __find_fd);
+ if (!dev)
+ return NULL;
+ vdev = to_vdev(dev);
+ vdisk = (struct vdisk_device *)vdev->drv_private;
+ return vdisk;
+}
+
+
+static int vdisk_probe(struct vdev *vdev)
+{
+ struct vdisk_device *vdisk;
+ int rc;
+
+ vdisk = kzalloc(sizeof(struct vdisk_device), GFP_ATOMIC);
+ if (!vdisk)
+ return -ENOMEM;
+
+ vdisk->vdev = vdev;
+ vdev->drv_private = vdisk;
+ vdisk->submit_page = (void*)get_zeroed_page(GFP_KERNEL);
+
+ if (!vdisk->submit_page) {
+ rc = -ENOMEM;
+ goto out_free;
+ }
+
+ vdisk->irq_page = (void*)get_zeroed_page(GFP_KERNEL);
+
+ if (!vdisk->irq_page) {
+ rc = -ENOMEM;
+ goto out_free;
+ }
+
+ rc = diag_vdisk_disk_info(vdisk->vdev->hostid,
+ &vdisk->blocksize, &vdisk->size,
+ &vdisk->read_only);
+ if (rc)
+ goto out_free;
+ spin_lock_init(&vdisk->lock);
+ init_rwsem(&vdisk->pump_sem);
+ init_waitqueue_head(&vdisk->wait);
+
+ vdisk_init_blockdev(vdisk);
+ goto out;
+
+out_free:
+ if (vdisk->irq_page)
+ free_page((unsigned long)(vdisk->irq_page));
+ if (vdisk->submit_page)
+ free_page((unsigned long)(vdisk->submit_page));
+ kfree(vdisk);
+out:
+ return rc;
+}
+
+static struct vdev_driver vdisk_driver = {
+ .name = "vdisk",
+ .owner = THIS_MODULE,
+ .vdev_type = VDEV_TYPE_DISK,
+ .probe = vdisk_probe,
+};
+
+static int __init vdisk_init(void)
+{
+ int rc;
+ if (!MACHINE_IS_GUEST)
+ return -ENODEV;
+
+ rc = register_blkdev(VDISK_MAJOR, "vdisk");
+ if (rc) {
+ printk(KERN_WARNING "vdisk: cannot register block device\n");
+ return rc;
+ }
+ rc = register_external_interrupt(0x1235, vdisk_ext_handler);
+ if (rc)
+ goto unregister_blk;
+ rc = vdev_driver_register(&vdisk_driver);
+ if (rc)
+ goto unregister_irq;
+ goto out;
+unregister_irq:
+ unregister_external_interrupt(0x1235, vdisk_ext_handler);
+unregister_blk:
+ unregister_blkdev(VDISK_MAJOR, "vdisk");
+ printk (KERN_WARNING "vdisk: initialization failed\n");
+out:
+ return rc;
+}
+
+static void __exit vdisk_exit(void)
+{
+ unregister_external_interrupt(0x1235, vdisk_ext_handler);
+ unregister_blkdev(VDISK_MAJOR, "vdisk");
+ return;
+}
+
+module_init(vdisk_init);
+module_exit(vdisk_exit);
Index: linux-2.6.21/drivers/s390/guest/vdisk.h
===================================================================
--- /dev/null
+++ linux-2.6.21/drivers/s390/guest/vdisk.h
@@ -0,0 +1,230 @@
+/*
+ * guest virtual block device driver header file
+ * Copyright IBM Corp.
+ * Author: Carsten Otte <cotte-tA70FqPdS9bQT0dZR+AlfA@public.gmane.org>
+ */
+
+#include <linux/list.h>
+#include <linux/genhd.h>
+#include <linux/types.h>
+#include <linux/aio_abi.h>
+#include <linux/wait.h>
+#include <asm/vdev.h>
+
+#define VDISK_MAJOR 95
+#define SECTOR_SHIFT 9
+#define VDISK_NR_REQ 256
+#define VDISK_NR_RES 170
+
+#define VDISK_WRITE 1
+#define VDISK_READ 0
+
+struct vdisk_request {
+ unsigned long buf;
+ unsigned long count;
+};
+
+typedef struct vdisk_request (*vdisk_req_t)[VDISK_NR_REQ];
+
+struct vdisk_response {
+ unsigned long intparm;
+ unsigned long count;
+ unsigned long failed;
+};
+
+typedef struct vdisk_response (*vdisk_irq_t)[VDISK_NR_RES];
+
+struct vdisk_device {
+ struct list_head head;
+ int blocksize;
+ long size;
+ int read_only;
+ struct gendisk *gd;
+ struct vdev *vdev;
+ spinlock_t lock;
+ struct rw_semaphore pump_sem;
+ int open_count;
+ int vfd;
+ struct vdisk_request (*submit_page)[VDISK_NR_REQ];
+ struct workqueue_struct *wq;
+ vdisk_irq_t irq_page;
+ wait_queue_head_t wait;
+};
+
+struct vdisk_work {
+ struct work_struct work;
+ struct bio* bio;
+};
+
+struct vdisk_elem {
+ unsigned int fd;
+ unsigned int command;
+ unsigned long offset;
+ unsigned long buffer;
+ unsigned long nbytes;
+};
+
+struct vdisk_iocb_container {
+ struct iocb iocb;
+ struct bio *bio;
+ struct vdisk_device *dev;
+ int ctx_index;
+ unsigned long context;
+ struct list_head list;
+};
+
+// from aio_abi.h
+typedef enum io_iocb_cmd {
+ IO_CMD_PREAD = 0,
+ IO_CMD_PWRITE = 1,
+
+ IO_CMD_FSYNC = 2,
+ IO_CMD_FDSYNC = 3,
+
+ IO_CMD_POLL = 5,
+ IO_CMD_NOOP = 6,
+} io_iocb_cmd_t;
+
+static inline int
+diag_vdisk_disk_info(char name[256], int* blocksize,
+ long* size, int* read_only)
+{
+ register char* __arg1 asm("2") = name;
+ register int* __arg2 asm("3") = blocksize;
+ register long* __arg3 asm("4") = size;
+ register int* __arg4 asm("5") = read_only;
+ register int __svcres asm("2");
+ int __res;
+
+ __asm__ __volatile__ (
+ "diag 0,0,5"
+ : "=d" (__svcres)
+ : "0" (__arg1),
+ "d" (__arg2),
+ "d" (__arg3),
+ "d" (__arg4)
+ : "cc", "memory");
+ __res = __svcres;
+ return __res;
+}
+
+static inline int
+diag_vdisk_open(const char* name, int read_only, void* irq_page)
+{
+ register const char* __arg1 asm("2") = name;
+ register long __arg2 asm("3") = read_only;
+ register unsigned long __arg3 asm("4") = (unsigned long) irq_page;
+ register int __svcres asm("2");
+ int __res;
+
+ __asm__ __volatile__ (
+ "diag 0,0,7"
+ : "=d" (__svcres)
+ : "0" (__arg1),
+ "d" (__arg2),
+ "d" (__arg3)
+ : "cc", "memory");
+ __res = __svcres;
+ return __res;
+}
+
+static inline int
+diag_vdisk_close(int fd)
+{
+ register long __arg1 asm("2") = fd;
+ register int __svcres asm("2");
+ int __res;
+
+ __asm__ __volatile__ (
+ "diag 0,0,9"
+ : "=d" (__svcres)
+ : "0" (__arg1)
+ : "cc", "memory");
+ __res = __svcres;
+ return __res;
+}
+
+static inline int
+diag_vdisk_aio_setup(unsigned int index, unsigned int num_events,
+ unsigned long *context, void *int_page)
+{
+ register unsigned long __arg1 asm("2") = index;
+ register unsigned long __arg2 asm("3") = num_events;
+ register unsigned long* __arg3 asm("4") = context;
+ register void* __arg4 asm("5") = int_page;
+ register int __svcres asm("2");
+ int __res;
+
+ __asm__ __volatile__ (
+ "diag 0,0,0x0a"
+ : "=d" (__svcres)
+ : "0" (__arg1),
+ "d" (__arg2),
+ "d" (__arg3),
+ "d" (__arg4)
+ : "cc", "memory");
+ __res = __svcres;
+ return __res;
+}
+
+static inline void
+diag_vdisk_aio_destroy(unsigned int index)
+{
+ register unsigned long __arg1 asm("2") = index;
+ __asm__ __volatile__ (
+ "diag 0,0,0x12"
+ :: "d" (__arg1)
+ : "cc", "memory");
+}
+
+static inline int
+diag_vdisk_submit_request(int fd, void *submit_page, int op,
+ loff_t start_offset, int nrreq, void* parm)
+{
+ register long __arg1 asm("2") = fd;
+ register unsigned long __arg2 asm("3") = (unsigned long)submit_page;
+ register long __arg3 asm("4") = op;
+ register unsigned long __arg4 asm("5") = start_offset;
+ register long __arg5 asm("6") = nrreq;
+ register unsigned long __arg6 asm("7") = (unsigned long)parm;
+ register int __svcres asm("2");
+ int __res;
+
+ __asm__ __volatile__ (
+ "diag 0,0,0x0b"
+ : "=d" (__svcres)
+ : "0" (__arg1),
+ "d" (__arg2),
+ "d" (__arg3),
+ "d" (__arg4),
+ "d" (__arg5),
+ "d" (__arg6)
+ : "cc", "memory");
+ __res = __svcres;
+ return __res;
+}
+
+static inline int
+diag_vdisk_getevents(int index) {
+ register long __arg1 asm("2") = index;
+ register int __svcres asm("2");
+ int __res;
+
+ __asm__ __volatile__ (
+ "diag 0,0,0x0d"
+ : "=d" (__svcres)
+ : "0" (__arg1)
+ : "cc", "memory");
+ __res = __svcres;
+ return __res;
+}
+
+// in vdisk.c
+extern struct device *vdisk_sysfs_root;
+int vdisk_disk_info(struct vdisk_device *dev);
+vdisk_irq_t vdisk_get_irqpage(int fd);
+struct vdisk_device * vdisk_get_device_by_fd(int fd);
+
+// in vdisk_blk.c
+void vdisk_init_blockdev(struct vdisk_device *dev);
+void vdisk_ext_handler(__u16 code);
Index: linux-2.6.21/drivers/s390/guest/vdisk_blk.c
===================================================================
--- /dev/null
+++ linux-2.6.21/drivers/s390/guest/vdisk_blk.c
@@ -0,0 +1,355 @@
+/*
+ * guest virtual block device driver
+ * Copyright IBM Corp. 2007
+ * Author: Carsten Otte <cotte-tA70FqPdS9bQT0dZR+AlfA@public.gmane.org>
+ */
+
+#include <linux/blkdev.h>
+#include "vdisk.h"
+
+static int vdisk_open(struct inode *inode, struct file *filp);
+static int vdisk_release(struct inode *inode, struct file *filp);
+static int vdisk_make_request(request_queue_t *q, struct bio *bio);
+
+static struct block_device_operations vdisk_fops = {
+ .owner = THIS_MODULE,
+ .open = vdisk_open,
+ .release = vdisk_release,
+};
+
+void vdisk_init_blockdev(struct vdisk_device *dev)
+{
+ static int lastminor = 0;
+
+ dev->gd = alloc_disk(1);
+ if (!dev->gd) {
+ printk (KERN_WARNING "vdisk: out of memory while " \
+ "initializing %s\n", dev->vdev->symname);
+ return;
+ }
+ dev->open_count = 0;
+ dev->gd->first_minor = lastminor++;
+ dev->gd->queue = blk_alloc_queue(GFP_KERNEL);
+ if (!dev->gd->queue) {
+ printk (KERN_WARNING "vdisk: out of memory while " \
+ "initializing %s\n", dev->vdev->symname);
+ goto free_gd;
+ }
+ blk_queue_max_segment_size(dev->gd->queue, 15 * dev->blocksize);
+ strlcpy(dev->gd->disk_name, dev->vdev->symname, 32);
+ dev->gd->disk_name[32] = '\0';
+ dev->gd->major = VDISK_MAJOR;
+ dev->gd->fops = &vdisk_fops;
+ dev->gd->private_data = dev;
+ dev->gd->driverfs_dev = &dev->vdev->dev;
+ set_capacity(dev->gd, dev->size);
+ get_device(&dev->vdev->dev);
+ add_disk(dev->gd);
+ blk_queue_make_request(dev->gd->queue, vdisk_make_request);
+ blk_queue_hardsect_size(dev->gd->queue, dev->blocksize);
+ set_disk_ro(dev->gd, dev->read_only);
+ if (dev->blocksize)
+ printk(KERN_INFO "vdisk: device %s(%d:%d) active with " \
+ "block size %d and %ld sectors\n", dev->vdev->symname,
+ VDISK_MAJOR, dev->gd->first_minor, dev->blocksize,
+ dev->size);
+ else
+ printk(KERN_INFO "vdisk: device %s(%d:%d) inactive\n",
+ dev->vdev->symname, VDISK_MAJOR, dev->gd->first_minor);
+ return;
+ free_gd:
+ kfree(dev->gd);
+ dev->gd = NULL;
+ return;
+}
+
+static int vdisk_open(struct inode *inode, struct file *filp)
+{
+ struct vdisk_device *dev = inode->i_bdev->bd_disk->private_data;
+ unsigned long flags;
+ char* wq_name;
+ struct workqueue_struct *new_wq;
+ int rc;
+
+ if (!dev) {
+ rc = -ENODEV;
+ goto out;
+ }
+
+ down_write(&dev->pump_sem);
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->open_count) {
+ dev->open_count++;
+ rc = 0;
+ goto unlock;
+ }
+
+
+ wq_name = kmalloc(strlen(dev->gd->disk_name)+9, GFP_ATOMIC);
+ if (!wq_name) {
+ rc = -ENOMEM;
+ goto unlock;
+ }
+ memcpy(wq_name, "IO_pump_", 8);
+ strcpy(wq_name+8, dev->gd->disk_name);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+ new_wq = create_singlethread_workqueue(wq_name);
+ spin_lock_irqsave(&dev->lock, flags);
+
+ dev->wq = new_wq;
+ kfree (wq_name);
+
+ if (!dev->wq) {
+ rc = -EIO;
+ goto unlock;
+ }
+
+ rc = diag_vdisk_disk_info(dev->vdev->hostid, &dev->blocksize,
+ &dev->size, &dev->read_only);
+ if (rc) {
+ printk(KERN_ERR "vdisk: error querying %s\n", dev->vdev->hostid);
+ goto cleanup;
+ }
+ inode->i_bdev->bd_block_size = dev->blocksize;
+ dev->vfd = diag_vdisk_open(dev->vdev->hostid, dev->read_only,
+ dev->irq_page);
+
+ if (dev->vfd < 0) {
+ rc = dev->vfd;
+ printk(KERN_ERR "vdisk: error opening %s\n", dev->vdev->hostid);
+ goto cleanup;
+ } else {
+ dev->open_count++;
+ rc = 0;
+ }
+ goto unlock;
+
+ cleanup:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ destroy_workqueue(new_wq);
+ spin_lock_irqsave(&dev->lock, flags);
+ unlock:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ up_write(&dev->pump_sem);
+ out:
+ return rc;
+}
+
+static int
+vdisk_release(struct inode *inode, struct file *filp)
+{
+ int rc;
+ unsigned long flags;
+ struct vdisk_device *dev = inode->i_bdev->bd_disk->private_data;
+ struct workqueue_struct *old_wq;
+
+ if (!dev) {
+ rc = -ENODEV;
+ goto out;
+ }
+
+ down_write(&dev->pump_sem);
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->open_count--;
+
+ if (dev->open_count) {
+ rc = 0;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ goto up;
+ }
+ rc = diag_vdisk_close(dev->vfd);
+
+ old_wq = dev->wq;
+ dev->wq = NULL;
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ destroy_workqueue(old_wq);
+
+ up:
+ up_write(&dev->pump_sem);
+ out:
+ return rc;
+}
+
+static void vdisk_pump_bvecs(struct vdisk_device *dev, int op,
+ loff_t start_offset, int requestno,
+ struct bio* bio, struct bio_vec *(vectors[256]))
+{
+ int i, rc;
+ loff_t offset = start_offset;
+ int nr_done = 0;
+ long size;
+ long flags=0;
+ DEFINE_WAIT(wait);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ prepare_to_wait_exclusive(&dev->wait, &wait,
+ TASK_UNINTERRUPTIBLE);
+
+ while (nr_done < requestno) {
+ memset(dev->submit_page, 0, PAGE_SIZE);
+ for (i=nr_done; i<requestno; i++) {
+ (*dev->submit_page)[i-nr_done].buf =
+ (unsigned long)page_address(vectors[i]->bv_page) +
+ vectors[i]->bv_offset;
+ (*dev->submit_page)[i-nr_done].count = vectors[i]->bv_len;
+ }
+
+ rc = diag_vdisk_submit_request(dev->vfd,
+ dev->submit_page,
+ op, offset,
+ requestno-nr_done, bio);
+
+ if (rc < 0) {
+ // error case
+ size = 0;
+ for (i=0; i<(requestno-nr_done); i++)
+ size += (*dev->submit_page)[i].count;
+ bio_io_error(bio, size);
+ break;
+ }
+
+ if (rc == requestno - nr_done)
+ // everything was submitted propper
+ break;
+
+ if (rc) {
+ //request was partly submitted
+ for (i=0; i<rc; i++)
+ offset += (*dev->submit_page)[i].count;
+ nr_done += rc;
+ }
+ // we need to throttle IO, and retry submission later
+ spin_unlock_irqrestore(&dev->lock, flags);
+ io_schedule();
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ finish_wait(&dev->wait, &wait);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return;
+}
+
+static void vdisk_pump_bio(struct work_struct *zw)
+{
+ struct vdisk_work *work =
+ container_of(zw, struct vdisk_work, work);
+
+ struct bio *bio = work->bio;
+ struct bio_vec *bvec;
+ struct bio_vec *(vectors[256]);
+ struct vdisk_device *dev = bio->bi_bdev->bd_disk->private_data;
+ int i, op, requestno=0;
+ loff_t start_offset, offset;
+
+ BUG_ON(!dev);
+
+ kfree (zw);
+
+ if (bio_data_dir(bio))
+ op = VDISK_WRITE;
+ else
+ op = VDISK_READ;
+
+ offset = start_offset = ((loff_t)bio->bi_sector)<<SECTOR_SHIFT;
+
+ bio_for_each_segment(bvec, bio, i) {
+ if (bvec->bv_len & (dev->blocksize - 1)) //FIXME: Zugriff auf dev ohne lock
+ goto out;
+
+ vectors[requestno] = bvec;
+ offset += bvec->bv_len;
+ requestno++;
+ if (requestno == 255) {
+ vdisk_pump_bvecs(dev, op, start_offset, requestno,
+ bio, vectors);
+ start_offset = offset;
+ requestno = 0;
+ }
+ }
+
+ if (requestno)
+ vdisk_pump_bvecs(dev, op, start_offset, requestno, bio, vectors);
+
+out:
+ return;
+}
+
+static int vdisk_make_request(request_queue_t *q, struct bio *bio)
+{
+ struct vdisk_device *dev = bio->bi_bdev->bd_disk->private_data;
+ struct vdisk_work *work;
+ int rc;
+
+ if (!dev) {
+ rc = -ENODEV;
+ goto out;
+ }
+
+ if (bio_barrier(bio)) {
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+
+ work = kmalloc(sizeof(struct vdisk_work), GFP_KERNEL);
+ if (!work) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ work->bio = bio;
+
+ INIT_WORK(&work->work, vdisk_pump_bio);
+
+ if (!queue_work(dev->wq, &work->work)) {
+ rc = -EIO;
+ kfree(work);
+ } else
+ rc = 0;
+
+out:
+ return rc;
+}
+
+static void __post_response(vdisk_irq_t irq_page)
+{
+ int i;
+ struct bio *bio;
+
+ for (i=0; i<VDISK_NR_RES; i++) {
+ if (!(*irq_page)[i].intparm)
+ break;
+ bio = (struct bio *)((*irq_page)[i].intparm);
+ if ((*irq_page)[i].count)
+ bio_endio(bio, (*irq_page)[i].count, 0);
+ if ((*irq_page)[i].failed)
+ bio_endio(bio, (*irq_page)[i].failed, 1);
+ }
+}
+
+void vdisk_ext_handler(__u16 code)
+{
+ int rc=0; //FIXME: no initialization here
+ int fd = S390_lowcore.ext_params;
+ vdisk_irq_t irq_page;
+ struct vdisk_device *vdev;
+
+ irq_page = vdisk_get_irqpage(fd);
+
+ if (irq_page) {
+ do {
+ __post_response(irq_page);
+ rc = diag_vdisk_getevents(fd); // get more interrupts
+ } while(rc > 0);
+ vdev = vdisk_get_device_by_fd(fd);
+ if (!vdev)
+ panic("cannot find vdisk device while in interrupt");
+ spin_lock(&vdev->lock);
+ if (waitqueue_active(&vdev->wait))
+ wake_up(&vdev->wait);
+ spin_unlock(&vdev->lock);
+ } else
+ printk (KERN_WARNING "vdisk got interrupt for non-existing" \
+ " aio context id %d\n", fd);
+}
Index: linux-2.6.21/drivers/s390/block/Kconfig
===================================================================
--- linux-2.6.21.orig/drivers/s390/block/Kconfig
+++ linux-2.6.21/drivers/s390/block/Kconfig
@@ -63,4 +63,11 @@ config DASD_EER
DASD extended error reporting. This is only needed if you want to
use applications written for the EER facility.

+
+config VDISK
+ tristate "guest disk device support"
+ depends on S390_GUEST
+ help
+ This driver provides access to block devices for Linux systems running
+ under non z/VM hosts. If you are running on LPAR or z/VM only, say N.
endif
Index: linux-2.6.21/drivers/s390/guest/Makefile
===================================================================
--- linux-2.6.21.orig/drivers/s390/guest/Makefile
+++ linux-2.6.21/drivers/s390/guest/Makefile
@@ -4,4 +4,5 @@

obj-$(CONFIG_GUEST_CONSOLE) += guest_console.o guest_tty.o
obj-$(CONFIG_S390_GUEST) += vdev.o vdev_device.o
+obj-$(CONFIG_VDISK) += vdisk.o vdisk_blk.o