diff options
Diffstat (limited to 'drivers/staging/hv/blkvsc_drv.c')
-rw-r--r-- | drivers/staging/hv/blkvsc_drv.c | 1465 |
1 files changed, 497 insertions, 968 deletions
diff --git a/drivers/staging/hv/blkvsc_drv.c b/drivers/staging/hv/blkvsc_drv.c index 68ad17d67098..46daade7a9e2 100644 --- a/drivers/staging/hv/blkvsc_drv.c +++ b/drivers/staging/hv/blkvsc_drv.c @@ -17,6 +17,7 @@ * Authors: * Haiyang Zhang <haiyangz@microsoft.com> * Hank Janssen <hjanssen@microsoft.com> + * K. Y. Srinivasan <kys@microsoft.com> */ #include <linux/init.h> #include <linux/module.h> @@ -25,17 +26,14 @@ #include <linux/major.h> #include <linux/delay.h> #include <linux/hdreg.h> -#include <linux/mutex.h> #include <linux/slab.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_eh.h> #include <scsi/scsi_dbg.h> -#include "hv_api.h" -#include "logging.h" -#include "version_info.h" -#include "vmbus.h" -#include "storvsc_api.h" + +#include "hyperv.h" +#include "hyperv_storage.h" #define BLKVSC_MINORS 64 @@ -46,6 +44,12 @@ enum blkvsc_device_type { DVD_TYPE, }; +enum blkvsc_op_type { + DO_INQUIRY, + DO_CAPACITY, + DO_FLUSH, +}; + /* * This request ties the struct request and struct * blkvsc_request/hv_storvsc_request together A struct request may be @@ -72,9 +76,6 @@ struct blkvsc_request { /* The group this request is part of. Maybe null */ struct blkvsc_request_group *group; - wait_queue_head_t wevent; - int cond; - int write; sector_t sector_start; unsigned long sector_count; @@ -84,12 +85,6 @@ struct blkvsc_request { unsigned char cmnd[MAX_COMMAND_SIZE]; struct hv_storvsc_request request; - /* - * !!!DO NOT ADD ANYTHING BELOW HERE!!! Otherwise, memory can overlap, - * because - The extension buffer falls right here and is pointed to by - * request.Extension; - * Which sounds like a horrible idea, who designed this? - */ }; /* Per device structure */ @@ -106,7 +101,6 @@ struct block_device_context { unsigned int device_id_len; int num_outstanding_reqs; int shutting_down; - int media_not_present; unsigned int sector_size; sector_t capacity; unsigned int port; @@ -115,515 +109,314 @@ struct block_device_context { int users; }; +static const char *drv_name = "blkvsc"; -/* Static decl */ -static DEFINE_MUTEX(blkvsc_mutex); -static int blkvsc_probe(struct device *dev); -static int blkvsc_remove(struct device *device); -static void blkvsc_shutdown(struct device *device); +/* {32412632-86cb-44a2-9b5c-50d1417354f5} */ +static const struct hv_guid dev_type = { + .data = { + 0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, + 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5 + } +}; -static int blkvsc_open(struct block_device *bdev, fmode_t mode); -static int blkvsc_release(struct gendisk *disk, fmode_t mode); -static unsigned int blkvsc_check_events(struct gendisk *gd, - unsigned int clearing); -static int blkvsc_revalidate_disk(struct gendisk *gd); -static int blkvsc_getgeo(struct block_device *bd, struct hd_geometry *hg); -static int blkvsc_ioctl(struct block_device *bd, fmode_t mode, - unsigned cmd, unsigned long argument); -static void blkvsc_request(struct request_queue *queue); +/* + * There is a circular dependency involving blkvsc_request_completion() + * and blkvsc_do_request(). + */ static void blkvsc_request_completion(struct hv_storvsc_request *request); -static int blkvsc_do_request(struct block_device_context *blkdev, - struct request *req); -static int blkvsc_submit_request(struct blkvsc_request *blkvsc_req, - void (*request_completion)(struct hv_storvsc_request *)); -static void blkvsc_init_rw(struct blkvsc_request *blkvsc_req); -static void blkvsc_cmd_completion(struct hv_storvsc_request *request); -static int blkvsc_do_inquiry(struct block_device_context *blkdev); -static int blkvsc_do_read_capacity(struct block_device_context *blkdev); -static int blkvsc_do_read_capacity16(struct block_device_context *blkdev); -static int blkvsc_do_flush(struct block_device_context *blkdev); -static int blkvsc_cancel_pending_reqs(struct block_device_context *blkdev); -static int blkvsc_do_pending_reqs(struct block_device_context *blkdev); static int blkvsc_ringbuffer_size = BLKVSC_RING_BUFFER_SIZE; + module_param(blkvsc_ringbuffer_size, int, S_IRUGO); MODULE_PARM_DESC(ring_size, "Ring buffer size (in bytes)"); -/* The one and only one */ -static struct storvsc_driver_object g_blkvsc_drv; - -static const struct block_device_operations block_ops = { - .owner = THIS_MODULE, - .open = blkvsc_open, - .release = blkvsc_release, - .check_events = blkvsc_check_events, - .revalidate_disk = blkvsc_revalidate_disk, - .getgeo = blkvsc_getgeo, - .ioctl = blkvsc_ioctl, -}; - /* - * blkvsc_drv_init - BlkVsc driver initialization. + * There is a circular dependency involving blkvsc_probe() + * and block_ops. */ -static int blkvsc_drv_init(int (*drv_init)(struct hv_driver *drv)) -{ - struct storvsc_driver_object *storvsc_drv_obj = &g_blkvsc_drv; - struct hv_driver *drv = &g_blkvsc_drv.base; - int ret; +static int blkvsc_probe(struct hv_device *dev); - storvsc_drv_obj->ring_buffer_size = blkvsc_ringbuffer_size; +static int blkvsc_device_add(struct hv_device *device, + void *additional_info) +{ + struct storvsc_device_info *device_info; + int ret = 0; - drv->priv = storvsc_drv_obj; + device_info = (struct storvsc_device_info *)additional_info; - /* Callback to client driver to complete the initialization */ - drv_init(&storvsc_drv_obj->base); + device_info->ring_buffer_size = blkvsc_ringbuffer_size; - drv->driver.name = storvsc_drv_obj->base.name; + ret = storvsc_dev_add(device, additional_info); + if (ret != 0) + return ret; - drv->driver.probe = blkvsc_probe; - drv->driver.remove = blkvsc_remove; - drv->driver.shutdown = blkvsc_shutdown; + /* + * We need to use the device instance guid to set the path and target + * id. For IDE devices, the device instance id is formatted as + * <bus id> * - <device id> - 8899 - 000000000000. + */ + device_info->path_id = device->dev_instance.data[3] << 24 | + device->dev_instance.data[2] << 16 | + device->dev_instance.data[1] << 8 | + device->dev_instance.data[0]; - /* The driver belongs to vmbus */ - ret = vmbus_child_driver_register(&drv->driver); + device_info->target_id = device->dev_instance.data[5] << 8 | + device->dev_instance.data[4]; return ret; } -static int blkvsc_drv_exit_cb(struct device *dev, void *data) -{ - struct device **curr = (struct device **)data; - *curr = dev; - return 1; /* stop iterating */ -} - -static void blkvsc_drv_exit(void) +static int blkvsc_submit_request(struct blkvsc_request *blkvsc_req, + void (*request_completion)(struct hv_storvsc_request *)) { - struct storvsc_driver_object *storvsc_drv_obj = &g_blkvsc_drv; - struct hv_driver *drv = &g_blkvsc_drv.base; - struct device *current_dev; + struct block_device_context *blkdev = blkvsc_req->dev; + struct hv_storvsc_request *storvsc_req; + struct vmscsi_request *vm_srb; int ret; - while (1) { - current_dev = NULL; - - /* Get the device */ - ret = driver_for_each_device(&drv->driver, NULL, - (void *) ¤t_dev, - blkvsc_drv_exit_cb); - - if (ret) - DPRINT_WARN(BLKVSC_DRV, - "driver_for_each_device returned %d", ret); - - - if (current_dev == NULL) - break; - - /* Initiate removal from the top-down */ - device_unregister(current_dev); - } - - if (storvsc_drv_obj->base.cleanup) - storvsc_drv_obj->base.cleanup(&storvsc_drv_obj->base); - vmbus_child_driver_unregister(&drv->driver); - - return; -} + storvsc_req = &blkvsc_req->request; + vm_srb = &storvsc_req->vstor_packet.vm_srb; -/* - * blkvsc_probe - Add a new device for this driver - */ -static int blkvsc_probe(struct device *device) -{ - struct hv_driver *drv = - drv_to_hv_drv(device->driver); - struct storvsc_driver_object *storvsc_drv_obj = - drv->priv; - struct hv_device *device_obj = device_to_hv_device(device); + vm_srb->data_in = blkvsc_req->write ? WRITE_TYPE : READ_TYPE; - struct block_device_context *blkdev = NULL; - struct storvsc_device_info device_info; - int major = 0; - int devnum = 0; - int ret = 0; - static int ide0_registered; - static int ide1_registered; + storvsc_req->on_io_completion = request_completion; + storvsc_req->context = blkvsc_req; - DPRINT_DBG(BLKVSC_DRV, "blkvsc_probe - enter"); + vm_srb->port_number = blkdev->port; + vm_srb->path_id = blkdev->path; + vm_srb->target_id = blkdev->target; + vm_srb->lun = 0; /* this is not really used at all */ - if (!storvsc_drv_obj->base.dev_add) { - DPRINT_ERR(BLKVSC_DRV, "OnDeviceAdd() not set"); - ret = -1; - goto Cleanup; - } + vm_srb->cdb_length = blkvsc_req->cmd_len; - blkdev = kzalloc(sizeof(struct block_device_context), GFP_KERNEL); - if (!blkdev) { - ret = -ENOMEM; - goto Cleanup; - } - - INIT_LIST_HEAD(&blkdev->pending_list); + memcpy(vm_srb->cdb, blkvsc_req->cmnd, vm_srb->cdb_length); - /* Initialize what we can here */ - spin_lock_init(&blkdev->lock); + storvsc_req->sense_buffer = blkvsc_req->sense_buffer; - /* ASSERT(sizeof(struct blkvsc_request_group) <= */ - /* sizeof(struct blkvsc_request)); */ + ret = storvsc_do_io(blkdev->device_ctx, + &blkvsc_req->request); + if (ret == 0) + blkdev->num_outstanding_reqs++; - blkdev->request_pool = kmem_cache_create(dev_name(&device_obj->device), - sizeof(struct blkvsc_request) + - storvsc_drv_obj->request_ext_size, 0, - SLAB_HWCACHE_ALIGN, NULL); - if (!blkdev->request_pool) { - ret = -ENOMEM; - goto Cleanup; - } + return ret; +} - /* Call to the vsc driver to add the device */ - ret = storvsc_drv_obj->base.dev_add(device_obj, &device_info); - if (ret != 0) { - DPRINT_ERR(BLKVSC_DRV, "unable to add blkvsc device"); - goto Cleanup; - } +static int blkvsc_open(struct block_device *bdev, fmode_t mode) +{ + struct block_device_context *blkdev = bdev->bd_disk->private_data; + unsigned long flags; - blkdev->device_ctx = device_obj; - /* this identified the device 0 or 1 */ - blkdev->target = device_info.target_id; - /* this identified the ide ctrl 0 or 1 */ - blkdev->path = device_info.path_id; + spin_lock_irqsave(&blkdev->lock, flags); - dev_set_drvdata(device, blkdev); + blkdev->users++; - /* Calculate the major and device num */ - if (blkdev->path == 0) { - major = IDE0_MAJOR; - devnum = blkdev->path + blkdev->target; /* 0 or 1 */ + spin_unlock_irqrestore(&blkdev->lock, flags); - if (!ide0_registered) { - ret = register_blkdev(major, "ide"); - if (ret != 0) { - DPRINT_ERR(BLKVSC_DRV, - "register_blkdev() failed! ret %d", - ret); - goto Remove; - } + return 0; +} - ide0_registered = 1; - } - } else if (blkdev->path == 1) { - major = IDE1_MAJOR; - devnum = blkdev->path + blkdev->target + 1; /* 2 or 3 */ - - if (!ide1_registered) { - ret = register_blkdev(major, "ide"); - if (ret != 0) { - DPRINT_ERR(BLKVSC_DRV, - "register_blkdev() failed! ret %d", - ret); - goto Remove; - } - ide1_registered = 1; - } - } else { - DPRINT_ERR(BLKVSC_DRV, "invalid pathid"); - ret = -1; - goto Cleanup; - } +static int blkvsc_getgeo(struct block_device *bd, struct hd_geometry *hg) +{ + sector_t nsect = get_capacity(bd->bd_disk); + sector_t cylinders = nsect; - DPRINT_INFO(BLKVSC_DRV, "blkvsc registered for major %d!!", major); + /* + * We are making up these values; let us keep it simple. + */ + hg->heads = 0xff; + hg->sectors = 0x3f; + sector_div(cylinders, hg->heads * hg->sectors); + hg->cylinders = cylinders; + if ((sector_t)(hg->cylinders + 1) * hg->heads * hg->sectors < nsect) + hg->cylinders = 0xffff; + return 0; - blkdev->gd = alloc_disk(BLKVSC_MINORS); - if (!blkdev->gd) { - DPRINT_ERR(BLKVSC_DRV, "register_blkdev() failed! ret %d", ret); - ret = -1; - goto Cleanup; - } +} - blkdev->gd->queue = blk_init_queue(blkvsc_request, &blkdev->lock); - blk_queue_max_segment_size(blkdev->gd->queue, PAGE_SIZE); - blk_queue_max_segments(blkdev->gd->queue, MAX_MULTIPAGE_BUFFER_COUNT); - blk_queue_segment_boundary(blkdev->gd->queue, PAGE_SIZE-1); - blk_queue_bounce_limit(blkdev->gd->queue, BLK_BOUNCE_ANY); - blk_queue_dma_alignment(blkdev->gd->queue, 511); +static void blkvsc_init_rw(struct blkvsc_request *blkvsc_req) +{ - blkdev->gd->major = major; - if (devnum == 1 || devnum == 3) - blkdev->gd->first_minor = BLKVSC_MINORS; - else - blkdev->gd->first_minor = 0; - blkdev->gd->fops = &block_ops; - blkdev->gd->events = DISK_EVENT_MEDIA_CHANGE; - blkdev->gd->private_data = blkdev; - blkdev->gd->driverfs_dev = &(blkdev->device_ctx->device); - sprintf(blkdev->gd->disk_name, "hd%c", 'a' + devnum); + blkvsc_req->cmd_len = 16; - blkvsc_do_inquiry(blkdev); - if (blkdev->device_type == DVD_TYPE) { - set_disk_ro(blkdev->gd, 1); - blkdev->gd->flags |= GENHD_FL_REMOVABLE; - blkvsc_do_read_capacity(blkdev); + if (rq_data_dir(blkvsc_req->req)) { + blkvsc_req->write = 1; + blkvsc_req->cmnd[0] = WRITE_16; } else { - blkvsc_do_read_capacity16(blkdev); + blkvsc_req->write = 0; + blkvsc_req->cmnd[0] = READ_16; } - set_capacity(blkdev->gd, blkdev->capacity * (blkdev->sector_size/512)); - blk_queue_logical_block_size(blkdev->gd->queue, blkdev->sector_size); - /* go! */ - add_disk(blkdev->gd); + blkvsc_req->cmnd[1] |= + (blkvsc_req->req->cmd_flags & REQ_FUA) ? 0x8 : 0; - DPRINT_INFO(BLKVSC_DRV, "%s added!! capacity %lu sector_size %d", - blkdev->gd->disk_name, (unsigned long)blkdev->capacity, - blkdev->sector_size); + *(unsigned long long *)&blkvsc_req->cmnd[2] = + cpu_to_be64(blkvsc_req->sector_start); + *(unsigned int *)&blkvsc_req->cmnd[10] = + cpu_to_be32(blkvsc_req->sector_count); +} - return ret; -Remove: - storvsc_drv_obj->base.dev_rm(device_obj); +static int blkvsc_ioctl(struct block_device *bd, fmode_t mode, + unsigned cmd, unsigned long arg) +{ + struct block_device_context *blkdev = bd->bd_disk->private_data; + int ret = 0; -Cleanup: - if (blkdev) { - if (blkdev->request_pool) { - kmem_cache_destroy(blkdev->request_pool); - blkdev->request_pool = NULL; - } - kfree(blkdev); - blkdev = NULL; + switch (cmd) { + case HDIO_GET_IDENTITY: + if (copy_to_user((void __user *)arg, blkdev->device_id, + blkdev->device_id_len)) + ret = -EFAULT; + break; + default: + ret = -EINVAL; + break; } return ret; } -static void blkvsc_shutdown(struct device *device) +static void blkvsc_cmd_completion(struct hv_storvsc_request *request) { - struct block_device_context *blkdev = dev_get_drvdata(device); + struct blkvsc_request *blkvsc_req = + (struct blkvsc_request *)request->context; + struct block_device_context *blkdev = + (struct block_device_context *)blkvsc_req->dev; + struct scsi_sense_hdr sense_hdr; + struct vmscsi_request *vm_srb; unsigned long flags; - if (!blkdev) - return; - DPRINT_DBG(BLKVSC_DRV, "blkvsc_shutdown - users %d disk %s\n", - blkdev->users, blkdev->gd->disk_name); + vm_srb = &blkvsc_req->request.vstor_packet.vm_srb; spin_lock_irqsave(&blkdev->lock, flags); - - blkdev->shutting_down = 1; - - blk_stop_queue(blkdev->gd->queue); - + blkdev->num_outstanding_reqs--; spin_unlock_irqrestore(&blkdev->lock, flags); - while (blkdev->num_outstanding_reqs) { - DPRINT_INFO(STORVSC, "waiting for %d requests to complete...", - blkdev->num_outstanding_reqs); - udelay(100); - } - - blkvsc_do_flush(blkdev); - - spin_lock_irqsave(&blkdev->lock, flags); - - blkvsc_cancel_pending_reqs(blkdev); + if (vm_srb->scsi_status) + if (scsi_normalize_sense(blkvsc_req->sense_buffer, + SCSI_SENSE_BUFFERSIZE, &sense_hdr)) + scsi_print_sense_hdr("blkvsc", &sense_hdr); - spin_unlock_irqrestore(&blkdev->lock, flags); + complete(&blkvsc_req->request.wait_event); } -static int blkvsc_do_flush(struct block_device_context *blkdev) -{ - struct blkvsc_request *blkvsc_req; - - DPRINT_DBG(BLKVSC_DRV, "blkvsc_do_flush()\n"); - if (blkdev->device_type != HARDDISK_TYPE) - return 0; - - blkvsc_req = kmem_cache_alloc(blkdev->request_pool, GFP_KERNEL); - if (!blkvsc_req) - return -ENOMEM; - - memset(blkvsc_req, 0, sizeof(struct blkvsc_request)); - init_waitqueue_head(&blkvsc_req->wevent); - blkvsc_req->dev = blkdev; - blkvsc_req->req = NULL; - blkvsc_req->write = 0; - - blkvsc_req->request.data_buffer.pfn_array[0] = 0; - blkvsc_req->request.data_buffer.offset = 0; - blkvsc_req->request.data_buffer.len = 0; - - blkvsc_req->cmnd[0] = SYNCHRONIZE_CACHE; - blkvsc_req->cmd_len = 10; - - /* - * Set this here since the completion routine may be invoked and - * completed before we return - */ - blkvsc_req->cond = 0; - blkvsc_submit_request(blkvsc_req, blkvsc_cmd_completion); - - wait_event_interruptible(blkvsc_req->wevent, blkvsc_req->cond); - - kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req); - - return 0; -} - -/* Do a scsi INQUIRY cmd here to get the device type (ie disk or dvd) */ -static int blkvsc_do_inquiry(struct block_device_context *blkdev) +static int blkvsc_do_operation(struct block_device_context *blkdev, + enum blkvsc_op_type op) { struct blkvsc_request *blkvsc_req; struct page *page_buf; unsigned char *buf; unsigned char device_type; + struct scsi_sense_hdr sense_hdr; + struct vmscsi_request *vm_srb; + unsigned long flags; - DPRINT_DBG(BLKVSC_DRV, "blkvsc_do_inquiry()\n"); + int ret = 0; - blkvsc_req = kmem_cache_alloc(blkdev->request_pool, GFP_KERNEL); + blkvsc_req = kmem_cache_zalloc(blkdev->request_pool, GFP_KERNEL); if (!blkvsc_req) return -ENOMEM; - memset(blkvsc_req, 0, sizeof(struct blkvsc_request)); page_buf = alloc_page(GFP_KERNEL); if (!page_buf) { kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req); return -ENOMEM; } - init_waitqueue_head(&blkvsc_req->wevent); + vm_srb = &blkvsc_req->request.vstor_packet.vm_srb; + init_completion(&blkvsc_req->request.wait_event); blkvsc_req->dev = blkdev; blkvsc_req->req = NULL; blkvsc_req->write = 0; - blkvsc_req->request.data_buffer.pfn_array[0] = page_to_pfn(page_buf); + blkvsc_req->request.data_buffer.pfn_array[0] = + page_to_pfn(page_buf); blkvsc_req->request.data_buffer.offset = 0; - blkvsc_req->request.data_buffer.len = 64; - - blkvsc_req->cmnd[0] = INQUIRY; - blkvsc_req->cmnd[1] = 0x1; /* Get product data */ - blkvsc_req->cmnd[2] = 0x83; /* mode page 83 */ - blkvsc_req->cmnd[4] = 64; - blkvsc_req->cmd_len = 6; - - /* - * Set this here since the completion routine may be invoked and - * completed before we return - */ - blkvsc_req->cond = 0; - blkvsc_submit_request(blkvsc_req, blkvsc_cmd_completion); - - DPRINT_DBG(BLKVSC_DRV, "waiting %p to complete - cond %d\n", - blkvsc_req, blkvsc_req->cond); - - wait_event_interruptible(blkvsc_req->wevent, blkvsc_req->cond); + switch (op) { + case DO_INQUIRY: + blkvsc_req->cmnd[0] = INQUIRY; + blkvsc_req->cmnd[1] = 0x1; /* Get product data */ + blkvsc_req->cmnd[2] = 0x83; /* mode page 83 */ + blkvsc_req->cmnd[4] = 64; + blkvsc_req->cmd_len = 6; + blkvsc_req->request.data_buffer.len = 64; + break; - buf = kmap(page_buf); + case DO_CAPACITY: + blkdev->sector_size = 0; + blkdev->capacity = 0; - /* print_hex_dump_bytes("", DUMP_PREFIX_NONE, buf, 64); */ - /* be to le */ - device_type = buf[0] & 0x1F; + blkvsc_req->cmnd[0] = READ_CAPACITY; + blkvsc_req->cmd_len = 16; + blkvsc_req->request.data_buffer.len = 8; + break; - if (device_type == 0x0) { - blkdev->device_type = HARDDISK_TYPE; - } else if (device_type == 0x5) { - blkdev->device_type = DVD_TYPE; - } else { - /* TODO: this is currently unsupported device type */ - blkdev->device_type = UNKNOWN_DEV_TYPE; + case DO_FLUSH: + blkvsc_req->cmnd[0] = SYNCHRONIZE_CACHE; + blkvsc_req->cmd_len = 10; + blkvsc_req->request.data_buffer.pfn_array[0] = 0; + blkvsc_req->request.data_buffer.len = 0; + break; + default: + ret = -EINVAL; + goto cleanup; } - DPRINT_DBG(BLKVSC_DRV, "device type %d\n", device_type); - - blkdev->device_id_len = buf[7]; - if (blkdev->device_id_len > 64) - blkdev->device_id_len = 64; - - memcpy(blkdev->device_id, &buf[8], blkdev->device_id_len); - /* printk_hex_dump_bytes("", DUMP_PREFIX_NONE, blkdev->device_id, - * blkdev->device_id_len); */ - - kunmap(page_buf); - - __free_page(page_buf); - - kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req); - - return 0; -} - -/* Do a scsi READ_CAPACITY cmd here to get the size of the disk */ -static int blkvsc_do_read_capacity(struct block_device_context *blkdev) -{ - struct blkvsc_request *blkvsc_req; - struct page *page_buf; - unsigned char *buf; - struct scsi_sense_hdr sense_hdr; - - DPRINT_DBG(BLKVSC_DRV, "blkvsc_do_read_capacity()\n"); + spin_lock_irqsave(&blkdev->lock, flags); + blkvsc_submit_request(blkvsc_req, blkvsc_cmd_completion); + spin_unlock_irqrestore(&blkdev->lock, flags); - blkdev->sector_size = 0; - blkdev->capacity = 0; - blkdev->media_not_present = 0; /* assume a disk is present */ + wait_for_completion_interruptible(&blkvsc_req->request.wait_event); - blkvsc_req = kmem_cache_alloc(blkdev->request_pool, GFP_KERNEL); - if (!blkvsc_req) - return -ENOMEM; + /* check error */ + if (vm_srb->scsi_status) { + scsi_normalize_sense(blkvsc_req->sense_buffer, + SCSI_SENSE_BUFFERSIZE, &sense_hdr); - memset(blkvsc_req, 0, sizeof(struct blkvsc_request)); - page_buf = alloc_page(GFP_KERNEL); - if (!page_buf) { - kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req); - return -ENOMEM; + return 0; } - init_waitqueue_head(&blkvsc_req->wevent); - blkvsc_req->dev = blkdev; - blkvsc_req->req = NULL; - blkvsc_req->write = 0; - - blkvsc_req->request.data_buffer.pfn_array[0] = page_to_pfn(page_buf); - blkvsc_req->request.data_buffer.offset = 0; - blkvsc_req->request.data_buffer.len = 8; + buf = kmap(page_buf); - blkvsc_req->cmnd[0] = READ_CAPACITY; - blkvsc_req->cmd_len = 16; + switch (op) { + case DO_INQUIRY: + device_type = buf[0] & 0x1F; - /* - * Set this here since the completion routine may be invoked - * and completed before we return - */ - blkvsc_req->cond = 0; + if (device_type == 0x0) + blkdev->device_type = HARDDISK_TYPE; + else + blkdev->device_type = UNKNOWN_DEV_TYPE; - blkvsc_submit_request(blkvsc_req, blkvsc_cmd_completion); + blkdev->device_id_len = buf[7]; + if (blkdev->device_id_len > 64) + blkdev->device_id_len = 64; - DPRINT_DBG(BLKVSC_DRV, "waiting %p to complete - cond %d\n", - blkvsc_req, blkvsc_req->cond); + memcpy(blkdev->device_id, &buf[8], blkdev->device_id_len); + break; - wait_event_interruptible(blkvsc_req->wevent, blkvsc_req->cond); + case DO_CAPACITY: + /* be to le */ + blkdev->capacity = + ((buf[0] << 24) | (buf[1] << 16) | + (buf[2] << 8) | buf[3]) + 1; - /* check error */ - if (blkvsc_req->request.status) { - scsi_normalize_sense(blkvsc_req->sense_buffer, - SCSI_SENSE_BUFFERSIZE, &sense_hdr); + blkdev->sector_size = + (buf[4] << 24) | (buf[5] << 16) | + (buf[6] << 8) | buf[7]; + break; + default: + break; - if (sense_hdr.asc == 0x3A) { - /* Medium not present */ - blkdev->media_not_present = 1; - } - return 0; } - buf = kmap(page_buf); - /* be to le */ - blkdev->capacity = ((buf[0] << 24) | (buf[1] << 16) | - (buf[2] << 8) | buf[3]) + 1; - blkdev->sector_size = (buf[4] << 24) | (buf[5] << 16) | - (buf[6] << 8) | buf[7]; +cleanup: kunmap(page_buf); @@ -631,119 +424,86 @@ static int blkvsc_do_read_capacity(struct block_device_context *blkdev) kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req); - return 0; + return ret; } -static int blkvsc_do_read_capacity16(struct block_device_context *blkdev) -{ - struct blkvsc_request *blkvsc_req; - struct page *page_buf; - unsigned char *buf; - struct scsi_sense_hdr sense_hdr; - - DPRINT_DBG(BLKVSC_DRV, "blkvsc_do_read_capacity16()\n"); - blkdev->sector_size = 0; - blkdev->capacity = 0; - blkdev->media_not_present = 0; /* assume a disk is present */ - - blkvsc_req = kmem_cache_alloc(blkdev->request_pool, GFP_KERNEL); - if (!blkvsc_req) - return -ENOMEM; - - memset(blkvsc_req, 0, sizeof(struct blkvsc_request)); - page_buf = alloc_page(GFP_KERNEL); - if (!page_buf) { - kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req); - return -ENOMEM; - } +static int blkvsc_cancel_pending_reqs(struct block_device_context *blkdev) +{ + struct blkvsc_request *pend_req, *tmp; + struct blkvsc_request *comp_req, *tmp2; + struct vmscsi_request *vm_srb; - init_waitqueue_head(&blkvsc_req->wevent); - blkvsc_req->dev = blkdev; - blkvsc_req->req = NULL; - blkvsc_req->write = 0; + int ret = 0; - blkvsc_req->request.data_buffer.pfn_array[0] = page_to_pfn(page_buf); - blkvsc_req->request.data_buffer.offset = 0; - blkvsc_req->request.data_buffer.len = 12; - blkvsc_req->cmnd[0] = 0x9E; /* READ_CAPACITY16; */ - blkvsc_req->cmd_len = 16; + /* Flush the pending list first */ + list_for_each_entry_safe(pend_req, tmp, &blkdev->pending_list, + pend_entry) { + /* + * The pend_req could be part of a partially completed + * request. If so, complete those req first until we + * hit the pend_req + */ + list_for_each_entry_safe(comp_req, tmp2, + &pend_req->group->blkvsc_req_list, + req_entry) { - /* - * Set this here since the completion routine may be invoked - * and completed before we return - */ - blkvsc_req->cond = 0; + if (comp_req == pend_req) + break; - blkvsc_submit_request(blkvsc_req, blkvsc_cmd_completion); + list_del(&comp_req->req_entry); - DPRINT_DBG(BLKVSC_DRV, "waiting %p to complete - cond %d\n", - blkvsc_req, blkvsc_req->cond); + if (comp_req->req) { + vm_srb = + &comp_req->request.vstor_packet. + vm_srb; + ret = __blk_end_request(comp_req->req, + (!vm_srb->scsi_status ? 0 : -EIO), + comp_req->sector_count * + blkdev->sector_size); - wait_event_interruptible(blkvsc_req->wevent, blkvsc_req->cond); + /* FIXME: shouldn't this do more than return? */ + if (ret) + goto out; + } - /* check error */ - if (blkvsc_req->request.status) { - scsi_normalize_sense(blkvsc_req->sense_buffer, - SCSI_SENSE_BUFFERSIZE, &sense_hdr); - if (sense_hdr.asc == 0x3A) { - /* Medium not present */ - blkdev->media_not_present = 1; + kmem_cache_free(blkdev->request_pool, comp_req); } - return 0; - } - buf = kmap(page_buf); - - /* be to le */ - blkdev->capacity = be64_to_cpu(*(unsigned long long *) &buf[0]) + 1; - blkdev->sector_size = be32_to_cpu(*(unsigned int *)&buf[8]); -#if 0 - blkdev->capacity = ((buf[0] << 24) | (buf[1] << 16) | - (buf[2] << 8) | buf[3]) + 1; - blkdev->sector_size = (buf[4] << 24) | (buf[5] << 16) | - (buf[6] << 8) | buf[7]; -#endif + list_del(&pend_req->pend_entry); - kunmap(page_buf); + list_del(&pend_req->req_entry); - __free_page(page_buf); + if (comp_req->req) { + if (!__blk_end_request(pend_req->req, -EIO, + pend_req->sector_count * + blkdev->sector_size)) { + /* + * All the sectors have been xferred ie the + * request is done + */ + kmem_cache_free(blkdev->request_pool, + pend_req->group); + } + } - kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req); + kmem_cache_free(blkdev->request_pool, pend_req); + } - return 0; +out: + return ret; } + /* * blkvsc_remove() - Callback when our device is removed */ -static int blkvsc_remove(struct device *device) +static int blkvsc_remove(struct hv_device *dev) { - struct hv_driver *drv = - drv_to_hv_drv(device->driver); - struct storvsc_driver_object *storvsc_drv_obj = - drv->priv; - struct hv_device *device_obj = device_to_hv_device(device); - struct block_device_context *blkdev = dev_get_drvdata(device); + struct block_device_context *blkdev = dev_get_drvdata(&dev->device); unsigned long flags; - int ret; - - DPRINT_DBG(BLKVSC_DRV, "blkvsc_remove()\n"); - if (!storvsc_drv_obj->base.dev_rm) - return -1; - - /* - * Call to the vsc driver to let it know that the device is being - * removed - */ - ret = storvsc_drv_obj->base.dev_rm(device_obj); - if (ret != 0) { - /* TODO: */ - DPRINT_ERR(BLKVSC_DRV, - "unable to remove blkvsc device (ret %d)", ret); - } /* Get to a known state */ spin_lock_irqsave(&blkdev->lock, flags); @@ -752,149 +512,77 @@ static int blkvsc_remove(struct device *device) blk_stop_queue(blkdev->gd->queue); - spin_unlock_irqrestore(&blkdev->lock, flags); - - while (blkdev->num_outstanding_reqs) { - DPRINT_INFO(STORVSC, "waiting for %d requests to complete...", - blkdev->num_outstanding_reqs); - udelay(100); - } - - blkvsc_do_flush(blkdev); - - spin_lock_irqsave(&blkdev->lock, flags); - blkvsc_cancel_pending_reqs(blkdev); spin_unlock_irqrestore(&blkdev->lock, flags); + blkvsc_do_operation(blkdev, DO_FLUSH); + blk_cleanup_queue(blkdev->gd->queue); + /* + * Call to the vsc driver to let it know that the device is being + * removed + */ + storvsc_dev_remove(dev); + del_gendisk(blkdev->gd); kmem_cache_destroy(blkdev->request_pool); kfree(blkdev); - return ret; + return 0; + } -static void blkvsc_init_rw(struct blkvsc_request *blkvsc_req) +static void blkvsc_shutdown(struct hv_device *dev) { - /* ASSERT(blkvsc_req->req); */ - /* ASSERT(blkvsc_req->sector_count <= (MAX_MULTIPAGE_BUFFER_COUNT*8)); */ - - blkvsc_req->cmd_len = 16; - - if (blkvsc_req->sector_start > 0xffffffff) { - if (rq_data_dir(blkvsc_req->req)) { - blkvsc_req->write = 1; - blkvsc_req->cmnd[0] = WRITE_16; - } else { - blkvsc_req->write = 0; - blkvsc_req->cmnd[0] = READ_16; - } - - blkvsc_req->cmnd[1] |= - (blkvsc_req->req->cmd_flags & REQ_FUA) ? 0x8 : 0; - - *(unsigned long long *)&blkvsc_req->cmnd[2] = - cpu_to_be64(blkvsc_req->sector_start); - *(unsigned int *)&blkvsc_req->cmnd[10] = - cpu_to_be32(blkvsc_req->sector_count); - } else if ((blkvsc_req->sector_count > 0xff) || - (blkvsc_req->sector_start > 0x1fffff)) { - if (rq_data_dir(blkvsc_req->req)) { - blkvsc_req->write = 1; - blkvsc_req->cmnd[0] = WRITE_10; - } else { - blkvsc_req->write = 0; - blkvsc_req->cmnd[0] = READ_10; - } + struct block_device_context *blkdev = dev_get_drvdata(&dev->device); + unsigned long flags; - blkvsc_req->cmnd[1] |= - (blkvsc_req->req->cmd_flags & REQ_FUA) ? 0x8 : 0; + if (!blkdev) + return; - *(unsigned int *)&blkvsc_req->cmnd[2] = - cpu_to_be32(blkvsc_req->sector_start); - *(unsigned short *)&blkvsc_req->cmnd[7] = - cpu_to_be16(blkvsc_req->sector_count); - } else { - if (rq_data_dir(blkvsc_req->req)) { - blkvsc_req->write = 1; - blkvsc_req->cmnd[0] = WRITE_6; - } else { - blkvsc_req->write = 0; - blkvsc_req->cmnd[0] = READ_6; - } + spin_lock_irqsave(&blkdev->lock, flags); - *(unsigned int *)&blkvsc_req->cmnd[1] = - cpu_to_be32(blkvsc_req->sector_start) >> 8; - blkvsc_req->cmnd[1] &= 0x1f; - blkvsc_req->cmnd[4] = (unsigned char)blkvsc_req->sector_count; - } -} + blkdev->shutting_down = 1; -static int blkvsc_submit_request(struct blkvsc_request *blkvsc_req, - void (*request_completion)(struct hv_storvsc_request *)) -{ - struct block_device_context *blkdev = blkvsc_req->dev; - struct hv_device *device_ctx = blkdev->device_ctx; - struct hv_driver *drv = - drv_to_hv_drv(device_ctx->device.driver); - struct storvsc_driver_object *storvsc_drv_obj = - drv->priv; - struct hv_storvsc_request *storvsc_req; - int ret; + blk_stop_queue(blkdev->gd->queue); - DPRINT_DBG(BLKVSC_DRV, "blkvsc_submit_request() - " - "req %p type %s start_sector %lu count %ld offset %d " - "len %d\n", blkvsc_req, - (blkvsc_req->write) ? "WRITE" : "READ", - (unsigned long) blkvsc_req->sector_start, - blkvsc_req->sector_count, - blkvsc_req->request.data_buffer.offset, - blkvsc_req->request.data_buffer.len); -#if 0 - for (i = 0; i < (blkvsc_req->request.data_buffer.len >> 12); i++) { - DPRINT_DBG(BLKVSC_DRV, "blkvsc_submit_request() - " - "req %p pfn[%d] %llx\n", - blkvsc_req, i, - blkvsc_req->request.data_buffer.pfn_array[i]); - } -#endif + blkvsc_cancel_pending_reqs(blkdev); - storvsc_req = &blkvsc_req->request; - storvsc_req->extension = (void *)((unsigned long)blkvsc_req + - sizeof(struct blkvsc_request)); + spin_unlock_irqrestore(&blkdev->lock, flags); - storvsc_req->type = blkvsc_req->write ? WRITE_TYPE : READ_TYPE; + blkvsc_do_operation(blkdev, DO_FLUSH); - storvsc_req->on_io_completion = request_completion; - storvsc_req->context = blkvsc_req; + /* + * Now wait for all outgoing I/O to be drained. + */ + storvsc_wait_to_drain((struct storvsc_device *)dev->ext); - storvsc_req->host = blkdev->port; - storvsc_req->bus = blkdev->path; - storvsc_req->target_id = blkdev->target; - storvsc_req->lun_id = 0; /* this is not really used at all */ +} - storvsc_req->cdb_len = blkvsc_req->cmd_len; - storvsc_req->cdb = blkvsc_req->cmnd; +static int blkvsc_release(struct gendisk *disk, fmode_t mode) +{ + struct block_device_context *blkdev = disk->private_data; + unsigned long flags; - storvsc_req->sense_buffer = blkvsc_req->sense_buffer; - storvsc_req->sense_buffer_size = SCSI_SENSE_BUFFERSIZE; + if (blkdev->users == 1) { + blkvsc_do_operation(blkdev, DO_FLUSH); + } - ret = storvsc_drv_obj->on_io_request(blkdev->device_ctx, - &blkvsc_req->request); - if (ret == 0) - blkdev->num_outstanding_reqs++; + spin_lock_irqsave(&blkdev->lock, flags); + blkdev->users--; + spin_unlock_irqrestore(&blkdev->lock, flags); - return ret; + return 0; } + /* * We break the request into 1 or more blkvsc_requests and submit - * them. If we can't submit them all, we put them on the + * them. If we cant submit them all, we put them on the * pending_list. The blkvsc_request() will work on the pending_list. */ static int blkvsc_do_request(struct block_device_context *blkdev, @@ -913,11 +601,8 @@ static int blkvsc_do_request(struct block_device_context *blkdev, int pending = 0; struct blkvsc_request_group *group = NULL; - DPRINT_DBG(BLKVSC_DRV, "blkdev %p req %p sect %lu\n", blkdev, req, - (unsigned long)blk_rq_pos(req)); - /* Create a group to tie req to list of blkvsc_reqs */ - group = kmem_cache_alloc(blkdev->request_pool, GFP_ATOMIC); + group = kmem_cache_zalloc(blkdev->request_pool, GFP_ATOMIC); if (!group) return -ENOMEM; @@ -933,11 +618,6 @@ static int blkvsc_do_request(struct block_device_context *blkdev, * Map this bio into an existing or new storvsc request */ bio_for_each_segment(bvec, bio, seg_idx) { - DPRINT_DBG(BLKVSC_DRV, "bio_for_each_segment() " - "- req %p bio %p bvec %p seg_idx %d " - "databuf_idx %d\n", req, bio, bvec, - seg_idx, databuf_idx); - /* Get a new storvsc request */ /* 1st-time */ if ((!blkvsc_req) || @@ -949,10 +629,15 @@ static int blkvsc_do_request(struct block_device_context *blkdev, (prev_bvec->bv_len != PAGE_SIZE))) { /* submit the prev one */ if (blkvsc_req) { - blkvsc_req->sector_start = start_sector; - sector_div(blkvsc_req->sector_start, (blkdev->sector_size >> 9)); - - blkvsc_req->sector_count = num_sectors / (blkdev->sector_size >> 9); + blkvsc_req->sector_start = + start_sector; + sector_div( + blkvsc_req->sector_start, + (blkdev->sector_size >> 9)); + + blkvsc_req->sector_count = + num_sectors / + (blkdev->sector_size >> 9); blkvsc_init_rw(blkvsc_req); } @@ -960,18 +645,24 @@ static int blkvsc_do_request(struct block_device_context *blkdev, * Create new blkvsc_req to represent * the current bvec */ - blkvsc_req = kmem_cache_alloc(blkdev->request_pool, GFP_ATOMIC); + blkvsc_req = + kmem_cache_zalloc( + blkdev->request_pool, GFP_ATOMIC); if (!blkvsc_req) { /* free up everything */ list_for_each_entry_safe( blkvsc_req, tmp, &group->blkvsc_req_list, req_entry) { - list_del(&blkvsc_req->req_entry); - kmem_cache_free(blkdev->request_pool, blkvsc_req); + list_del( + &blkvsc_req->req_entry); + kmem_cache_free( + blkdev->request_pool, + blkvsc_req); } - kmem_cache_free(blkdev->request_pool, group); + kmem_cache_free( + blkdev->request_pool, group); return -ENOMEM; } @@ -980,23 +671,27 @@ static int blkvsc_do_request(struct block_device_context *blkdev, blkvsc_req->dev = blkdev; blkvsc_req->req = req; - blkvsc_req->request.data_buffer.offset - = bvec->bv_offset; - blkvsc_req->request.data_buffer.len - = 0; + blkvsc_req->request. + data_buffer.offset + = bvec->bv_offset; + blkvsc_req->request. + data_buffer.len = 0; /* Add to the group */ blkvsc_req->group = group; blkvsc_req->group->outstanding++; list_add_tail(&blkvsc_req->req_entry, - &blkvsc_req->group->blkvsc_req_list); + &blkvsc_req->group->blkvsc_req_list); start_sector += num_sectors; num_sectors = 0; databuf_idx = 0; } - /* Add the curr bvec/segment to the curr blkvsc_req */ + /* + * Add the curr bvec/segment to the curr + * blkvsc_req + */ blkvsc_req->request.data_buffer. pfn_array[databuf_idx] = page_to_pfn(bvec->bv_page); @@ -1015,10 +710,6 @@ static int blkvsc_do_request(struct block_device_context *blkdev, /* Handle the last one */ if (blkvsc_req) { - DPRINT_DBG(BLKVSC_DRV, "blkdev %p req %p group %p count %d\n", - blkdev, req, blkvsc_req->group, - blkvsc_req->group->outstanding); - blkvsc_req->sector_start = start_sector; sector_div(blkvsc_req->sector_start, (blkdev->sector_size >> 9)); @@ -1031,13 +722,6 @@ static int blkvsc_do_request(struct block_device_context *blkdev, list_for_each_entry(blkvsc_req, &group->blkvsc_req_list, req_entry) { if (pending) { - DPRINT_DBG(BLKVSC_DRV, "adding blkvsc_req to " - "pending_list - blkvsc_req %p start_sect %lu" - " sect_count %ld (%lu %ld)\n", blkvsc_req, - (unsigned long)blkvsc_req->sector_start, - blkvsc_req->sector_count, - (unsigned long)start_sector, - (unsigned long)num_sectors); list_add_tail(&blkvsc_req->pend_entry, &blkdev->pending_list); @@ -1050,186 +734,12 @@ static int blkvsc_do_request(struct block_device_context *blkdev, &blkdev->pending_list); } - DPRINT_DBG(BLKVSC_DRV, "submitted blkvsc_req %p " - "start_sect %lu sect_count %ld (%lu %ld) " - "ret %d\n", blkvsc_req, - (unsigned long)blkvsc_req->sector_start, - blkvsc_req->sector_count, - (unsigned long)start_sector, - num_sectors, ret); } } return pending; } -static void blkvsc_cmd_completion(struct hv_storvsc_request *request) -{ - struct blkvsc_request *blkvsc_req = - (struct blkvsc_request *)request->context; - struct block_device_context *blkdev = - (struct block_device_context *)blkvsc_req->dev; - struct scsi_sense_hdr sense_hdr; - - DPRINT_DBG(BLKVSC_DRV, "blkvsc_cmd_completion() - req %p\n", - blkvsc_req); - - blkdev->num_outstanding_reqs--; - - if (blkvsc_req->request.status) - if (scsi_normalize_sense(blkvsc_req->sense_buffer, - SCSI_SENSE_BUFFERSIZE, &sense_hdr)) - scsi_print_sense_hdr("blkvsc", &sense_hdr); - - blkvsc_req->cond = 1; - wake_up_interruptible(&blkvsc_req->wevent); -} - -static void blkvsc_request_completion(struct hv_storvsc_request *request) -{ - struct blkvsc_request *blkvsc_req = - (struct blkvsc_request *)request->context; - struct block_device_context *blkdev = - (struct block_device_context *)blkvsc_req->dev; - unsigned long flags; - struct blkvsc_request *comp_req, *tmp; - - /* ASSERT(blkvsc_req->group); */ - - DPRINT_DBG(BLKVSC_DRV, "blkdev %p blkvsc_req %p group %p type %s " - "sect_start %lu sect_count %ld len %d group outstd %d " - "total outstd %d\n", - blkdev, blkvsc_req, blkvsc_req->group, - (blkvsc_req->write) ? "WRITE" : "READ", - (unsigned long)blkvsc_req->sector_start, - blkvsc_req->sector_count, - blkvsc_req->request.data_buffer.len, - blkvsc_req->group->outstanding, - blkdev->num_outstanding_reqs); - - spin_lock_irqsave(&blkdev->lock, flags); - - blkdev->num_outstanding_reqs--; - blkvsc_req->group->outstanding--; - - /* - * Only start processing when all the blkvsc_reqs are - * completed. This guarantees no out-of-order blkvsc_req - * completion when calling end_that_request_first() - */ - if (blkvsc_req->group->outstanding == 0) { - list_for_each_entry_safe(comp_req, tmp, - &blkvsc_req->group->blkvsc_req_list, - req_entry) { - DPRINT_DBG(BLKVSC_DRV, "completing blkvsc_req %p " - "sect_start %lu sect_count %ld\n", - comp_req, - (unsigned long)comp_req->sector_start, - comp_req->sector_count); - - list_del(&comp_req->req_entry); - - if (!__blk_end_request(comp_req->req, - (!comp_req->request.status ? 0 : -EIO), - comp_req->sector_count * blkdev->sector_size)) { - /* - * All the sectors have been xferred ie the - * request is done - */ - DPRINT_DBG(BLKVSC_DRV, "req %p COMPLETED\n", - comp_req->req); - kmem_cache_free(blkdev->request_pool, - comp_req->group); - } - - kmem_cache_free(blkdev->request_pool, comp_req); - } - - if (!blkdev->shutting_down) { - blkvsc_do_pending_reqs(blkdev); - blk_start_queue(blkdev->gd->queue); - blkvsc_request(blkdev->gd->queue); - } - } - - spin_unlock_irqrestore(&blkdev->lock, flags); -} - -static int blkvsc_cancel_pending_reqs(struct block_device_context *blkdev) -{ - struct blkvsc_request *pend_req, *tmp; - struct blkvsc_request *comp_req, *tmp2; - - int ret = 0; - - DPRINT_DBG(BLKVSC_DRV, "blkvsc_cancel_pending_reqs()"); - - /* Flush the pending list first */ - list_for_each_entry_safe(pend_req, tmp, &blkdev->pending_list, - pend_entry) { - /* - * The pend_req could be part of a partially completed - * request. If so, complete those req first until we - * hit the pend_req - */ - list_for_each_entry_safe(comp_req, tmp2, - &pend_req->group->blkvsc_req_list, - req_entry) { - DPRINT_DBG(BLKVSC_DRV, "completing blkvsc_req %p " - "sect_start %lu sect_count %ld\n", - comp_req, - (unsigned long) comp_req->sector_start, - comp_req->sector_count); - - if (comp_req == pend_req) - break; - - list_del(&comp_req->req_entry); - - if (comp_req->req) { - ret = __blk_end_request(comp_req->req, - (!comp_req->request.status ? 0 : -EIO), - comp_req->sector_count * - blkdev->sector_size); - - /* FIXME: shouldn't this do more than return? */ - if (ret) - goto out; - } - - kmem_cache_free(blkdev->request_pool, comp_req); - } - - DPRINT_DBG(BLKVSC_DRV, "cancelling pending request - %p\n", - pend_req); - - list_del(&pend_req->pend_entry); - - list_del(&pend_req->req_entry); - - if (comp_req->req) { - if (!__blk_end_request(pend_req->req, -EIO, - pend_req->sector_count * - blkdev->sector_size)) { - /* - * All the sectors have been xferred ie the - * request is done - */ - DPRINT_DBG(BLKVSC_DRV, - "blkvsc_cancel_pending_reqs() - " - "req %p COMPLETED\n", pend_req->req); - kmem_cache_free(blkdev->request_pool, - pend_req->group); - } - } - - kmem_cache_free(blkdev->request_pool, pend_req); - } - -out: - return ret; -} - static int blkvsc_do_pending_reqs(struct block_device_context *blkdev) { struct blkvsc_request *pend_req, *tmp; @@ -1238,8 +748,6 @@ static int blkvsc_do_pending_reqs(struct block_device_context *blkdev) /* Flush the pending list first */ list_for_each_entry_safe(pend_req, tmp, &blkdev->pending_list, pend_entry) { - DPRINT_DBG(BLKVSC_DRV, "working off pending_list - %p\n", - pend_req); ret = blkvsc_submit_request(pend_req, blkvsc_request_completion); @@ -1252,19 +760,17 @@ static int blkvsc_do_pending_reqs(struct block_device_context *blkdev) return ret; } + static void blkvsc_request(struct request_queue *queue) { struct block_device_context *blkdev = NULL; struct request *req; int ret = 0; - DPRINT_DBG(BLKVSC_DRV, "- enter\n"); while ((req = blk_peek_request(queue)) != NULL) { - DPRINT_DBG(BLKVSC_DRV, "- req %p\n", req); blkdev = req->rq_disk->private_data; - if (blkdev->shutting_down || req->cmd_type != REQ_TYPE_FS || - blkdev->media_not_present) { + if (blkdev->shutting_down || req->cmd_type != REQ_TYPE_FS) { __blk_end_request_cur(req, 0); continue; } @@ -1272,8 +778,6 @@ static void blkvsc_request(struct request_queue *queue) ret = blkvsc_do_pending_reqs(blkdev); if (ret != 0) { - DPRINT_DBG(BLKVSC_DRV, - "- stop queue - pending_list not empty\n"); blk_stop_queue(queue); break; } @@ -1282,11 +786,9 @@ static void blkvsc_request(struct request_queue *queue) ret = blkvsc_do_request(blkdev, req); if (ret > 0) { - DPRINT_DBG(BLKVSC_DRV, "- stop queue - no room\n"); blk_stop_queue(queue); break; } else if (ret < 0) { - DPRINT_DBG(BLKVSC_DRV, "- stop queue - no mem\n"); blk_requeue_request(queue, req); blk_stop_queue(queue); break; @@ -1294,191 +796,218 @@ static void blkvsc_request(struct request_queue *queue) } } -static int blkvsc_open(struct block_device *bdev, fmode_t mode) -{ - struct block_device_context *blkdev = bdev->bd_disk->private_data; - - DPRINT_DBG(BLKVSC_DRV, "- users %d disk %s\n", blkdev->users, - blkdev->gd->disk_name); - mutex_lock(&blkvsc_mutex); - spin_lock(&blkdev->lock); - if (!blkdev->users && blkdev->device_type == DVD_TYPE) { - spin_unlock(&blkdev->lock); - check_disk_change(bdev); - spin_lock(&blkdev->lock); - } - - blkdev->users++; +/* The one and only one */ +static struct hv_driver blkvsc_drv = { + .probe = blkvsc_probe, + .remove = blkvsc_remove, + .shutdown = blkvsc_shutdown, +}; - spin_unlock(&blkdev->lock); - mutex_unlock(&blkvsc_mutex); - return 0; -} +static const struct block_device_operations block_ops = { + .owner = THIS_MODULE, + .open = blkvsc_open, + .release = blkvsc_release, + .getgeo = blkvsc_getgeo, + .ioctl = blkvsc_ioctl, +}; -static int blkvsc_release(struct gendisk *disk, fmode_t mode) +/* + * blkvsc_drv_init - BlkVsc driver initialization. + */ +static int blkvsc_drv_init(void) { - struct block_device_context *blkdev = disk->private_data; + struct hv_driver *drv = &blkvsc_drv; + int ret; - DPRINT_DBG(BLKVSC_DRV, "- users %d disk %s\n", blkdev->users, - blkdev->gd->disk_name); + BUILD_BUG_ON(sizeof(sector_t) != 8); - mutex_lock(&blkvsc_mutex); - spin_lock(&blkdev->lock); - if (blkdev->users == 1) { - spin_unlock(&blkdev->lock); - blkvsc_do_flush(blkdev); - spin_lock(&blkdev->lock); - } + memcpy(&drv->dev_type, &dev_type, sizeof(struct hv_guid)); + drv->name = drv_name; + drv->driver.name = drv_name; - blkdev->users--; + /* The driver belongs to vmbus */ + ret = vmbus_child_driver_register(&drv->driver); - spin_unlock(&blkdev->lock); - mutex_unlock(&blkvsc_mutex); - return 0; + return ret; } -static unsigned int blkvsc_check_events(struct gendisk *gd, - unsigned int clearing) + +static void blkvsc_drv_exit(void) { - DPRINT_DBG(BLKVSC_DRV, "- enter\n"); - return DISK_EVENT_MEDIA_CHANGE; + + vmbus_child_driver_unregister(&blkvsc_drv.driver); } -static int blkvsc_revalidate_disk(struct gendisk *gd) +/* + * blkvsc_probe - Add a new device for this driver + */ +static int blkvsc_probe(struct hv_device *dev) { - struct block_device_context *blkdev = gd->private_data; - - DPRINT_DBG(BLKVSC_DRV, "- enter\n"); + struct block_device_context *blkdev = NULL; + struct storvsc_device_info device_info; + struct storvsc_major_info major_info; + int ret = 0; - if (blkdev->device_type == DVD_TYPE) { - blkvsc_do_read_capacity(blkdev); - set_capacity(blkdev->gd, blkdev->capacity * - (blkdev->sector_size/512)); - blk_queue_logical_block_size(gd->queue, blkdev->sector_size); + blkdev = kzalloc(sizeof(struct block_device_context), GFP_KERNEL); + if (!blkdev) { + ret = -ENOMEM; + goto cleanup; } - return 0; -} -static int blkvsc_getgeo(struct block_device *bd, struct hd_geometry *hg) -{ - sector_t total_sectors = get_capacity(bd->bd_disk); - sector_t cylinder_times_heads = 0; - sector_t temp = 0; + INIT_LIST_HEAD(&blkdev->pending_list); - int sectors_per_track = 0; - int heads = 0; - int cylinders = 0; - int rem = 0; + /* Initialize what we can here */ + spin_lock_init(&blkdev->lock); - if (total_sectors > (65535 * 16 * 255)) - total_sectors = (65535 * 16 * 255); - if (total_sectors >= (65535 * 16 * 63)) { - sectors_per_track = 255; - heads = 16; + blkdev->request_pool = kmem_cache_create(dev_name(&dev->device), + sizeof(struct blkvsc_request), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!blkdev->request_pool) { + ret = -ENOMEM; + goto cleanup; + } - cylinder_times_heads = total_sectors; - /* sector_div stores the quotient in cylinder_times_heads */ - rem = sector_div(cylinder_times_heads, sectors_per_track); - } else { - sectors_per_track = 17; - cylinder_times_heads = total_sectors; - /* sector_div stores the quotient in cylinder_times_heads */ - rem = sector_div(cylinder_times_heads, sectors_per_track); + ret = blkvsc_device_add(dev, &device_info); + if (ret != 0) + goto cleanup; - temp = cylinder_times_heads + 1023; - /* sector_div stores the quotient in temp */ - rem = sector_div(temp, 1024); + blkdev->device_ctx = dev; + /* this identified the device 0 or 1 */ + blkdev->target = device_info.target_id; + /* this identified the ide ctrl 0 or 1 */ + blkdev->path = device_info.path_id; - heads = temp; + dev_set_drvdata(&dev->device, blkdev); - if (heads < 4) - heads = 4; + ret = storvsc_get_major_info(&device_info, &major_info); + if (ret) + goto cleanup; - if (cylinder_times_heads >= (heads * 1024) || (heads > 16)) { - sectors_per_track = 31; - heads = 16; + if (major_info.do_register) { + ret = register_blkdev(major_info.major, major_info.devname); - cylinder_times_heads = total_sectors; - /* - * sector_div stores the quotient in - * cylinder_times_heads - */ - rem = sector_div(cylinder_times_heads, - sectors_per_track); + if (ret != 0) { + DPRINT_ERR(BLKVSC_DRV, + "register_blkdev() failed! ret %d", ret); + goto remove; } + } - if (cylinder_times_heads >= (heads * 1024)) { - sectors_per_track = 63; - heads = 16; + DPRINT_INFO(BLKVSC_DRV, "blkvsc registered for major %d!!", + major_info.major); - cylinder_times_heads = total_sectors; - /* - * sector_div stores the quotient in - * cylinder_times_heads - */ - rem = sector_div(cylinder_times_heads, - sectors_per_track); - } + blkdev->gd = alloc_disk(BLKVSC_MINORS); + if (!blkdev->gd) { + ret = -1; + goto cleanup; } - temp = cylinder_times_heads; - /* sector_div stores the quotient in temp */ - rem = sector_div(temp, heads); - cylinders = temp; + blkdev->gd->queue = blk_init_queue(blkvsc_request, &blkdev->lock); - hg->heads = heads; - hg->sectors = sectors_per_track; - hg->cylinders = cylinders; + blk_queue_max_segment_size(blkdev->gd->queue, PAGE_SIZE); + blk_queue_max_segments(blkdev->gd->queue, MAX_MULTIPAGE_BUFFER_COUNT); + blk_queue_segment_boundary(blkdev->gd->queue, PAGE_SIZE-1); + blk_queue_bounce_limit(blkdev->gd->queue, BLK_BOUNCE_ANY); + blk_queue_dma_alignment(blkdev->gd->queue, 511); - DPRINT_INFO(BLKVSC_DRV, "CHS (%d, %d, %d)", cylinders, heads, - sectors_per_track); + blkdev->gd->major = major_info.major; + if (major_info.index == 1 || major_info.index == 3) + blkdev->gd->first_minor = BLKVSC_MINORS; + else + blkdev->gd->first_minor = 0; + blkdev->gd->fops = &block_ops; + blkdev->gd->events = DISK_EVENT_MEDIA_CHANGE; + blkdev->gd->private_data = blkdev; + blkdev->gd->driverfs_dev = &(blkdev->device_ctx->device); + sprintf(blkdev->gd->disk_name, "hd%c", 'a' + major_info.index); - return 0; -} + blkvsc_do_operation(blkdev, DO_INQUIRY); + blkvsc_do_operation(blkdev, DO_CAPACITY); -static int blkvsc_ioctl(struct block_device *bd, fmode_t mode, - unsigned cmd, unsigned long argument) -{ -/* struct block_device_context *blkdev = bd->bd_disk->private_data; */ - int ret; + set_capacity(blkdev->gd, blkdev->capacity * (blkdev->sector_size/512)); + blk_queue_logical_block_size(blkdev->gd->queue, blkdev->sector_size); + /* go! */ + add_disk(blkdev->gd); - switch (cmd) { - /* - * TODO: I think there is certain format for HDIO_GET_IDENTITY rather - * than just a GUID. Commented it out for now. - */ -#if 0 - case HDIO_GET_IDENTITY: - DPRINT_INFO(BLKVSC_DRV, "HDIO_GET_IDENTITY\n"); - if (copy_to_user((void __user *)arg, blkdev->device_id, - blkdev->device_id_len)) - ret = -EFAULT; - break; -#endif - default: - ret = -EINVAL; - break; + DPRINT_INFO(BLKVSC_DRV, "%s added!! capacity %lu sector_size %d", + blkdev->gd->disk_name, (unsigned long)blkdev->capacity, + blkdev->sector_size); + + return ret; + +remove: + storvsc_dev_remove(dev); + +cleanup: + if (blkdev) { + if (blkdev->request_pool) { + kmem_cache_destroy(blkdev->request_pool); + blkdev->request_pool = NULL; + } + kfree(blkdev); + blkdev = NULL; } return ret; } -static int __init blkvsc_init(void) +static void blkvsc_request_completion(struct hv_storvsc_request *request) { - int ret; + struct blkvsc_request *blkvsc_req = + (struct blkvsc_request *)request->context; + struct block_device_context *blkdev = + (struct block_device_context *)blkvsc_req->dev; + unsigned long flags; + struct blkvsc_request *comp_req, *tmp; + struct vmscsi_request *vm_srb; - BUILD_BUG_ON(sizeof(sector_t) != 8); - DPRINT_INFO(BLKVSC_DRV, "Blkvsc initializing...."); + spin_lock_irqsave(&blkdev->lock, flags); - ret = blkvsc_drv_init(blk_vsc_initialize); + blkdev->num_outstanding_reqs--; + blkvsc_req->group->outstanding--; - return ret; + /* + * Only start processing when all the blkvsc_reqs are + * completed. This guarantees no out-of-order blkvsc_req + * completion when calling end_that_request_first() + */ + if (blkvsc_req->group->outstanding == 0) { + list_for_each_entry_safe(comp_req, tmp, + &blkvsc_req->group->blkvsc_req_list, + req_entry) { + + list_del(&comp_req->req_entry); + + vm_srb = + &comp_req->request.vstor_packet.vm_srb; + if (!__blk_end_request(comp_req->req, + (!vm_srb->scsi_status ? 0 : -EIO), + comp_req->sector_count * blkdev->sector_size)) { + /* + * All the sectors have been xferred ie the + * request is done + */ + kmem_cache_free(blkdev->request_pool, + comp_req->group); + } + + kmem_cache_free(blkdev->request_pool, comp_req); + } + + if (!blkdev->shutting_down) { + blkvsc_do_pending_reqs(blkdev); + blk_start_queue(blkdev->gd->queue); + blkvsc_request(blkdev->gd->queue); + } + } + + spin_unlock_irqrestore(&blkdev->lock, flags); } static void __exit blkvsc_exit(void) @@ -1489,5 +1018,5 @@ static void __exit blkvsc_exit(void) MODULE_LICENSE("GPL"); MODULE_VERSION(HV_DRV_VERSION); MODULE_DESCRIPTION("Microsoft Hyper-V virtual block driver"); -module_init(blkvsc_init); +module_init(blkvsc_drv_init); module_exit(blkvsc_exit); |