include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit...
[safe/jmp/linux-2.6] / drivers / block / xen-blkfront.c
index ffa0b43..82ed403 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/blkdev.h>
 #include <linux/hdreg.h>
+#include <linux/cdrom.h>
 #include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/scatterlist.h>
 
+#include <xen/xen.h>
 #include <xen/xenbus.h>
 #include <xen/grant_table.h>
 #include <xen/events.h>
@@ -63,7 +67,7 @@ struct blk_shadow {
        unsigned long frame[BLKIF_MAX_SEGMENTS_PER_REQUEST];
 };
 
-static struct block_device_operations xlvbd_block_fops;
+static const struct block_device_operations xlvbd_block_fops;
 
 #define BLK_RING_SIZE __RING_SIZE((struct blkif_sring *)0, PAGE_SIZE)
 
@@ -81,6 +85,7 @@ struct blkfront_info
        enum blkif_state connected;
        int ring_ref;
        struct blkif_front_ring ring;
+       struct scatterlist sg[BLKIF_MAX_SEGMENTS_PER_REQUEST];
        unsigned int evtchn, irq;
        struct request_queue *rq;
        struct work_struct work;
@@ -104,20 +109,22 @@ static DEFINE_SPINLOCK(blkif_io_lock);
 #define GRANT_INVALID_REF      0
 
 #define PARTS_PER_DISK         16
+#define PARTS_PER_EXT_DISK      256
 
 #define BLKIF_MAJOR(dev) ((dev)>>8)
 #define BLKIF_MINOR(dev) ((dev) & 0xff)
 
-#define DEV_NAME       "xvd"   /* name in /dev */
+#define EXT_SHIFT 28
+#define EXTENDED (1<<EXT_SHIFT)
+#define VDEV_IS_EXTENDED(dev) ((dev)&(EXTENDED))
+#define BLKIF_MINOR_EXT(dev) ((dev)&(~EXTENDED))
 
-/* Information about our VBDs. */
-#define MAX_VBDS 64
-static LIST_HEAD(vbds_list);
+#define DEV_NAME       "xvd"   /* name in /dev */
 
 static int get_id_from_freelist(struct blkfront_info *info)
 {
        unsigned long free = info->shadow_free;
-       BUG_ON(free > BLK_RING_SIZE);
+       BUG_ON(free >= BLK_RING_SIZE);
        info->shadow_free = info->shadow[free].req.id;
        info->shadow[free].req.id = 0x0fffffee; /* debug */
        return free;
@@ -137,7 +144,7 @@ static void blkif_restart_queue_callback(void *arg)
        schedule_work(&info->work);
 }
 
-int blkif_getgeo(struct block_device *bd, struct hd_geometry *hg)
+static int blkif_getgeo(struct block_device *bd, struct hd_geometry *hg)
 {
        /* We don't have real geometry info, but let's at least return
           values consistent with the size of the device */
@@ -153,6 +160,39 @@ int blkif_getgeo(struct block_device *bd, struct hd_geometry *hg)
        return 0;
 }
 
+static int blkif_ioctl(struct block_device *bdev, fmode_t mode,
+                      unsigned command, unsigned long argument)
+{
+       struct blkfront_info *info = bdev->bd_disk->private_data;
+       int i;
+
+       dev_dbg(&info->xbdev->dev, "command: 0x%x, argument: 0x%lx\n",
+               command, (long)argument);
+
+       switch (command) {
+       case CDROMMULTISESSION:
+               dev_dbg(&info->xbdev->dev, "FIXME: support multisession CDs later\n");
+               for (i = 0; i < sizeof(struct cdrom_multisession); i++)
+                       if (put_user(0, (char __user *)(argument + i)))
+                               return -EFAULT;
+               return 0;
+
+       case CDROM_GET_CAPABILITY: {
+               struct gendisk *gd = info->gd;
+               if (gd->flags & GENHD_FL_CD)
+                       return 0;
+               return -EINVAL;
+       }
+
+       default:
+               /*printk(KERN_ALERT "ioctl %08x not supported by Xen blkdev\n",
+                 command);*/
+               return -EINVAL; /* same return as native Linux */
+       }
+
+       return 0;
+}
+
 /*
  * blkif_queue_request
  *
@@ -168,12 +208,11 @@ static int blkif_queue_request(struct request *req)
        struct blkfront_info *info = req->rq_disk->private_data;
        unsigned long buffer_mfn;
        struct blkif_request *ring_req;
-       struct req_iterator iter;
-       struct bio_vec *bvec;
        unsigned long id;
        unsigned int fsect, lsect;
-       int ref;
+       int i, ref;
        grant_ref_t gref_head;
+       struct scatterlist *sg;
 
        if (unlikely(info->connected != BLKIF_STATE_CONNECTED))
                return 1;
@@ -194,7 +233,7 @@ static int blkif_queue_request(struct request *req)
        info->shadow[id].request = (unsigned long)req;
 
        ring_req->id = id;
-       ring_req->sector_number = (blkif_sector_t)req->sector;
+       ring_req->sector_number = (blkif_sector_t)blk_rq_pos(req);
        ring_req->handle = info->handle;
 
        ring_req->operation = rq_data_dir(req) ?
@@ -202,12 +241,13 @@ static int blkif_queue_request(struct request *req)
        if (blk_barrier_rq(req))
                ring_req->operation = BLKIF_OP_WRITE_BARRIER;
 
-       ring_req->nr_segments = 0;
-       rq_for_each_segment(bvec, req, iter) {
-               BUG_ON(ring_req->nr_segments == BLKIF_MAX_SEGMENTS_PER_REQUEST);
-               buffer_mfn = pfn_to_mfn(page_to_pfn(bvec->bv_page));
-               fsect = bvec->bv_offset >> 9;
-               lsect = fsect + (bvec->bv_len >> 9) - 1;
+       ring_req->nr_segments = blk_rq_map_sg(req->q, req, info->sg);
+       BUG_ON(ring_req->nr_segments > BLKIF_MAX_SEGMENTS_PER_REQUEST);
+
+       for_each_sg(info->sg, sg, ring_req->nr_segments, i) {
+               buffer_mfn = pfn_to_mfn(page_to_pfn(sg_page(sg)));
+               fsect = sg->offset >> 9;
+               lsect = fsect + (sg->length >> 9) - 1;
                /* install a grant reference. */
                ref = gnttab_claim_grant_reference(&gref_head);
                BUG_ON(ref == -ENOSPC);
@@ -218,16 +258,12 @@ static int blkif_queue_request(struct request *req)
                                buffer_mfn,
                                rq_data_dir(req) );
 
-               info->shadow[id].frame[ring_req->nr_segments] =
-                               mfn_to_pfn(buffer_mfn);
-
-               ring_req->seg[ring_req->nr_segments] =
+               info->shadow[id].frame[i] = mfn_to_pfn(buffer_mfn);
+               ring_req->seg[i] =
                                (struct blkif_request_segment) {
                                        .gref       = ref,
                                        .first_sect = fsect,
                                        .last_sect  = lsect };
-
-               ring_req->nr_segments++;
        }
 
        info->ring.req_prod_pvt++;
@@ -265,25 +301,25 @@ static void do_blkif_request(struct request_queue *rq)
 
        queued = 0;
 
-       while ((req = elv_next_request(rq)) != NULL) {
+       while ((req = blk_peek_request(rq)) != NULL) {
                info = req->rq_disk->private_data;
-               if (!blk_fs_request(req)) {
-                       end_request(req, 0);
-                       continue;
-               }
 
                if (RING_FULL(&info->ring))
                        goto wait;
 
-               pr_debug("do_blk_req %p: cmd %p, sec %lx, "
-                        "(%u/%li) buffer:%p [%s]\n",
-                        req, req->cmd, (unsigned long)req->sector,
-                        req->current_nr_sectors,
-                        req->nr_sectors, req->buffer,
-                        rq_data_dir(req) ? "write" : "read");
+               blk_start_request(req);
 
+               if (!blk_fs_request(req)) {
+                       __blk_end_request_all(req, -EIO);
+                       continue;
+               }
+
+               pr_debug("do_blk_req %p: cmd %p, sec %lx, "
+                        "(%u/%u) buffer:%p [%s]\n",
+                        req, req->cmd, (unsigned long)blk_rq_pos(req),
+                        blk_rq_cur_sectors(req), blk_rq_sectors(req),
+                        req->buffer, rq_data_dir(req) ? "write" : "read");
 
-               blkdev_dequeue_request(req);
                if (blkif_queue_request(req)) {
                        blk_requeue_request(rq, req);
 wait:
@@ -307,23 +343,25 @@ static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size)
        if (rq == NULL)
                return -1;
 
-       elevator_init(rq, "noop");
+       queue_flag_set_unlocked(QUEUE_FLAG_VIRT, rq);
 
        /* Hard sector size and max sectors impersonate the equiv. hardware. */
-       blk_queue_hardsect_size(rq, sector_size);
-       blk_queue_max_sectors(rq, 512);
+       blk_queue_logical_block_size(rq, sector_size);
+       blk_queue_max_hw_sectors(rq, 512);
 
        /* Each segment in a request is up to an aligned page in size. */
        blk_queue_segment_boundary(rq, PAGE_SIZE - 1);
        blk_queue_max_segment_size(rq, PAGE_SIZE);
 
        /* Ensure a merged request will fit in a single I/O ring slot. */
-       blk_queue_max_phys_segments(rq, BLKIF_MAX_SEGMENTS_PER_REQUEST);
-       blk_queue_max_hw_segments(rq, BLKIF_MAX_SEGMENTS_PER_REQUEST);
+       blk_queue_max_segments(rq, BLKIF_MAX_SEGMENTS_PER_REQUEST);
 
        /* Make sure buffer addresses are sector-aligned. */
        blk_queue_dma_alignment(rq, 511);
 
+       /* Make sure we don't use bounce buffers. */
+       blk_queue_bounce_limit(rq, BLK_BOUNCE_ANY);
+
        gd->queue = rq;
 
        return 0;
@@ -348,31 +386,60 @@ static int xlvbd_barrier(struct blkfront_info *info)
 }
 
 
-static int xlvbd_alloc_gendisk(int minor, blkif_sector_t capacity,
-                              int vdevice, u16 vdisk_info, u16 sector_size,
-                              struct blkfront_info *info)
+static int xlvbd_alloc_gendisk(blkif_sector_t capacity,
+                              struct blkfront_info *info,
+                              u16 vdisk_info, u16 sector_size)
 {
        struct gendisk *gd;
        int nr_minors = 1;
        int err = -ENODEV;
+       unsigned int offset;
+       int minor;
+       int nr_parts;
 
        BUG_ON(info->gd != NULL);
        BUG_ON(info->rq != NULL);
 
-       if ((minor % PARTS_PER_DISK) == 0)
-               nr_minors = PARTS_PER_DISK;
+       if ((info->vdevice>>EXT_SHIFT) > 1) {
+               /* this is above the extended range; something is wrong */
+               printk(KERN_WARNING "blkfront: vdevice 0x%x is above the extended range; ignoring\n", info->vdevice);
+               return -ENODEV;
+       }
+
+       if (!VDEV_IS_EXTENDED(info->vdevice)) {
+               minor = BLKIF_MINOR(info->vdevice);
+               nr_parts = PARTS_PER_DISK;
+       } else {
+               minor = BLKIF_MINOR_EXT(info->vdevice);
+               nr_parts = PARTS_PER_EXT_DISK;
+       }
+
+       if ((minor % nr_parts) == 0)
+               nr_minors = nr_parts;
 
        gd = alloc_disk(nr_minors);
        if (gd == NULL)
                goto out;
 
-       if (nr_minors > 1)
-               sprintf(gd->disk_name, "%s%c", DEV_NAME,
-                       'a' + minor / PARTS_PER_DISK);
-       else
-               sprintf(gd->disk_name, "%s%c%d", DEV_NAME,
-                       'a' + minor / PARTS_PER_DISK,
-                       minor % PARTS_PER_DISK);
+       offset = minor / nr_parts;
+
+       if (nr_minors > 1) {
+               if (offset < 26)
+                       sprintf(gd->disk_name, "%s%c", DEV_NAME, 'a' + offset);
+               else
+                       sprintf(gd->disk_name, "%s%c%c", DEV_NAME,
+                               'a' + ((offset / 26)-1), 'a' + (offset % 26));
+       } else {
+               if (offset < 26)
+                       sprintf(gd->disk_name, "%s%c%d", DEV_NAME,
+                               'a' + offset,
+                               minor & (nr_parts - 1));
+               else
+                       sprintf(gd->disk_name, "%s%c%c%d", DEV_NAME,
+                               'a' + ((offset / 26) - 1),
+                               'a' + (offset % 26),
+                               minor & (nr_parts - 1));
+       }
 
        gd->major = XENVBD_MAJOR;
        gd->first_minor = minor;
@@ -485,7 +552,6 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id)
 
        for (i = info->ring.rsp_cons; i != rp; i++) {
                unsigned long id;
-               int ret;
 
                bret = RING_GET_RESPONSE(&info->ring, i);
                id   = bret->id;
@@ -512,8 +578,7 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id)
                                dev_dbg(&info->xbdev->dev, "Bad return from blkdev data "
                                        "request: %x\n", bret->status);
 
-                       ret = __blk_end_request(req, error, blk_rq_bytes(req));
-                       BUG_ON(ret);
+                       __blk_end_request_all(req, error);
                        break;
                default:
                        BUG();
@@ -546,7 +611,7 @@ static int setup_blkring(struct xenbus_device *dev,
 
        info->ring_ref = GRANT_INVALID_REF;
 
-       sring = (struct blkif_sring *)__get_free_page(GFP_KERNEL);
+       sring = (struct blkif_sring *)__get_free_page(GFP_NOIO | __GFP_HIGH);
        if (!sring) {
                xenbus_dev_fatal(dev, -ENOMEM, "allocating shared ring");
                return -ENOMEM;
@@ -554,6 +619,8 @@ static int setup_blkring(struct xenbus_device *dev,
        SHARED_RING_INIT(sring);
        FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE);
 
+       sg_init_table(info->sg, BLKIF_MAX_SEGMENTS_PER_REQUEST);
+
        err = xenbus_grant_ring(dev, virt_to_mfn(info->ring.sring));
        if (err < 0) {
                free_page((unsigned long)sring);
@@ -661,8 +728,13 @@ static int blkfront_probe(struct xenbus_device *dev,
        err = xenbus_scanf(XBT_NIL, dev->nodename,
                           "virtual-device", "%i", &vdevice);
        if (err != 1) {
-               xenbus_dev_fatal(dev, err, "reading virtual-device");
-               return err;
+               /* go looking in the extended area instead */
+               err = xenbus_scanf(XBT_NIL, dev->nodename, "virtual-device-ext",
+                                  "%i", &vdevice);
+               if (err != 1) {
+                       xenbus_dev_fatal(dev, err, "reading virtual-device");
+                       return err;
+               }
        }
 
        info = kzalloc(sizeof(*info), GFP_KERNEL);
@@ -682,12 +754,12 @@ static int blkfront_probe(struct xenbus_device *dev,
 
        /* Front end dir is a number, which is used as the id. */
        info->handle = simple_strtoul(strrchr(dev->nodename, '/')+1, NULL, 0);
-       dev->dev.driver_data = info;
+       dev_set_drvdata(&dev->dev, info);
 
        err = talk_to_backend(dev, info);
        if (err) {
                kfree(info);
-               dev->dev.driver_data = NULL;
+               dev_set_drvdata(&dev->dev, NULL);
                return err;
        }
 
@@ -703,7 +775,8 @@ static int blkif_recover(struct blkfront_info *info)
        int j;
 
        /* Stage 1: Make a safe copy of the shadow state. */
-       copy = kmalloc(sizeof(info->shadow), GFP_KERNEL);
+       copy = kmalloc(sizeof(info->shadow),
+                      GFP_NOIO | __GFP_REPEAT | __GFP_HIGH);
        if (!copy)
                return -ENOMEM;
        memcpy(copy, info->shadow, sizeof(info->shadow));
@@ -771,7 +844,7 @@ static int blkif_recover(struct blkfront_info *info)
  */
 static int blkfront_resume(struct xenbus_device *dev)
 {
-       struct blkfront_info *info = dev->dev.driver_data;
+       struct blkfront_info *info = dev_get_drvdata(&dev->dev);
        int err;
 
        dev_dbg(&dev->dev, "blkfront_resume: %s\n", dev->nodename);
@@ -822,9 +895,7 @@ static void blkfront_connect(struct blkfront_info *info)
        if (err)
                info->feature_barrier = 0;
 
-       err = xlvbd_alloc_gendisk(BLKIF_MINOR(info->vdevice),
-                                 sectors, info->vdevice,
-                                 binfo, sector_size, info);
+       err = xlvbd_alloc_gendisk(sectors, info, binfo, sector_size);
        if (err) {
                xenbus_dev_fatal(info->xbdev, err, "xlvbd_add at %s",
                                 info->xbdev->otherend);
@@ -852,7 +923,7 @@ static void blkfront_connect(struct blkfront_info *info)
  */
 static void blkfront_closing(struct xenbus_device *dev)
 {
-       struct blkfront_info *info = dev->dev.driver_data;
+       struct blkfront_info *info = dev_get_drvdata(&dev->dev);
        unsigned long flags;
 
        dev_dbg(&dev->dev, "blkfront_closing: %s removed\n", dev->nodename);
@@ -862,8 +933,6 @@ static void blkfront_closing(struct xenbus_device *dev)
 
        spin_lock_irqsave(&blkif_io_lock, flags);
 
-       del_gendisk(info->gd);
-
        /* No more blkif_request(). */
        blk_stop_queue(info->rq);
 
@@ -877,6 +946,8 @@ static void blkfront_closing(struct xenbus_device *dev)
        blk_cleanup_queue(info->rq);
        info->rq = NULL;
 
+       del_gendisk(info->gd);
+
  out:
        xenbus_frontend_closed(dev);
 }
@@ -887,7 +958,7 @@ static void blkfront_closing(struct xenbus_device *dev)
 static void backend_changed(struct xenbus_device *dev,
                            enum xenbus_state backend_state)
 {
-       struct blkfront_info *info = dev->dev.driver_data;
+       struct blkfront_info *info = dev_get_drvdata(&dev->dev);
        struct block_device *bd;
 
        dev_dbg(&dev->dev, "blkfront:backend_changed.\n");
@@ -905,6 +976,10 @@ static void backend_changed(struct xenbus_device *dev,
                break;
 
        case XenbusStateClosing:
+               if (info->gd == NULL) {
+                       xenbus_frontend_closed(dev);
+                       break;
+               }
                bd = bdget_disk(info->gd, 0);
                if (bd == NULL)
                        xenbus_dev_fatal(dev, -ENODEV, "bdget failed");
@@ -923,7 +998,7 @@ static void backend_changed(struct xenbus_device *dev,
 
 static int blkfront_remove(struct xenbus_device *dev)
 {
-       struct blkfront_info *info = dev->dev.driver_data;
+       struct blkfront_info *info = dev_get_drvdata(&dev->dev);
 
        dev_dbg(&dev->dev, "blkfront_remove: %s removed\n", dev->nodename);
 
@@ -936,21 +1011,21 @@ static int blkfront_remove(struct xenbus_device *dev)
 
 static int blkfront_is_ready(struct xenbus_device *dev)
 {
-       struct blkfront_info *info = dev->dev.driver_data;
+       struct blkfront_info *info = dev_get_drvdata(&dev->dev);
 
        return info->is_ready;
 }
 
-static int blkif_open(struct inode *inode, struct file *filep)
+static int blkif_open(struct block_device *bdev, fmode_t mode)
 {
-       struct blkfront_info *info = inode->i_bdev->bd_disk->private_data;
+       struct blkfront_info *info = bdev->bd_disk->private_data;
        info->users++;
        return 0;
 }
 
-static int blkif_release(struct inode *inode, struct file *filep)
+static int blkif_release(struct gendisk *disk, fmode_t mode)
 {
-       struct blkfront_info *info = inode->i_bdev->bd_disk->private_data;
+       struct blkfront_info *info = disk->private_data;
        info->users--;
        if (info->users == 0) {
                /* Check whether we have been instructed to close.  We will
@@ -959,22 +1034,23 @@ static int blkif_release(struct inode *inode, struct file *filep)
                struct xenbus_device *dev = info->xbdev;
                enum xenbus_state state = xenbus_read_driver_state(dev->otherend);
 
-               if (state == XenbusStateClosing)
+               if (state == XenbusStateClosing && info->is_ready)
                        blkfront_closing(dev);
        }
        return 0;
 }
 
-static struct block_device_operations xlvbd_block_fops =
+static const struct block_device_operations xlvbd_block_fops =
 {
        .owner = THIS_MODULE,
        .open = blkif_open,
        .release = blkif_release,
        .getgeo = blkif_getgeo,
+       .locked_ioctl = blkif_ioctl,
 };
 
 
-static struct xenbus_device_id blkfront_ids[] = {
+static const struct xenbus_device_id blkfront_ids[] = {
        { "vbd" },
        { "" }
 };
@@ -992,7 +1068,7 @@ static struct xenbus_driver blkfront = {
 
 static int __init xlblk_init(void)
 {
-       if (!is_running_on_xen())
+       if (!xen_domain())
                return -ENODEV;
 
        if (register_blkdev(XENVBD_MAJOR, DEV_NAME)) {
@@ -1006,7 +1082,7 @@ static int __init xlblk_init(void)
 module_init(xlblk_init);
 
 
-static void xlblk_exit(void)
+static void __exit xlblk_exit(void)
 {
        return xenbus_unregister_driver(&blkfront);
 }
@@ -1016,3 +1092,4 @@ MODULE_DESCRIPTION("Xen virtual block device frontend");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS_BLOCKDEV_MAJOR(XENVBD_MAJOR);
 MODULE_ALIAS("xen:vbd");
+MODULE_ALIAS("xenblk");