diff options
Diffstat (limited to 'drivers/staging/hv/storvsc_drv.c')
-rw-r--r-- | drivers/staging/hv/storvsc_drv.c | 967 |
1 files changed, 423 insertions, 544 deletions
diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c index e6462a2fe9ab..942cc5f98db1 100644 --- a/drivers/staging/hv/storvsc_drv.c +++ b/drivers/staging/hv/storvsc_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/slab.h> @@ -31,18 +32,27 @@ #include <scsi/scsi_eh.h> #include <scsi/scsi_devinfo.h> #include <scsi/scsi_dbg.h> -#include "hv_api.h" -#include "logging.h" -#include "version_info.h" -#include "vmbus.h" -#include "storvsc_api.h" - - -struct host_device_context { - /* must be 1st field - * FIXME this is a bug */ - /* point back to our device context */ - struct hv_device *device_ctx; + +#include "hyperv.h" +#include "hyperv_storage.h" + +static int storvsc_ringbuffer_size = STORVSC_RING_BUFFER_SIZE; + +module_param(storvsc_ringbuffer_size, int, S_IRUGO); +MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)"); + +static const char *driver_name = "storvsc"; + +/* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */ +static const struct hv_guid gStorVscDeviceType = { + .data = { + 0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, + 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f + } +}; + +struct hv_host_device { + struct hv_device *dev; struct kmem_cache *request_pool; unsigned int port; unsigned char path; @@ -57,331 +67,57 @@ struct storvsc_cmd_request { struct scatterlist *bounce_sgl; struct hv_storvsc_request request; - /* !!!DO NOT ADD ANYTHING BELOW HERE!!! */ - /* The extension buffer falls right here and is pointed to by - * request.Extension; - * Which sounds like a very bad design... */ }; -/* Static decl */ -static int storvsc_probe(struct device *dev); -static int storvsc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *scmnd); -static int storvsc_device_alloc(struct scsi_device *); -static int storvsc_device_configure(struct scsi_device *); -static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd); -static int storvsc_remove(struct device *dev); - -static struct scatterlist *create_bounce_buffer(struct scatterlist *sgl, - unsigned int sg_count, - unsigned int len); -static void destroy_bounce_buffer(struct scatterlist *sgl, - unsigned int sg_count); -static int do_bounce_buffer(struct scatterlist *sgl, unsigned int sg_count); -static unsigned int copy_from_bounce_buffer(struct scatterlist *orig_sgl, - struct scatterlist *bounce_sgl, - unsigned int orig_sgl_count); -static unsigned int copy_to_bounce_buffer(struct scatterlist *orig_sgl, - struct scatterlist *bounce_sgl, - unsigned int orig_sgl_count); - -static int storvsc_get_chs(struct scsi_device *sdev, struct block_device *bdev, - sector_t capacity, int *info); - - -static int storvsc_ringbuffer_size = STORVSC_RING_BUFFER_SIZE; -module_param(storvsc_ringbuffer_size, int, S_IRUGO); -MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)"); - -/* The one and only one */ -static struct storvsc_driver_object g_storvsc_drv; - -/* Scsi driver */ -static struct scsi_host_template scsi_driver = { - .module = THIS_MODULE, - .name = "storvsc_host_t", - .bios_param = storvsc_get_chs, - .queuecommand = storvsc_queuecommand, - .eh_host_reset_handler = storvsc_host_reset_handler, - .slave_alloc = storvsc_device_alloc, - .slave_configure = storvsc_device_configure, - .cmd_per_lun = 1, - /* 64 max_queue * 1 target */ - .can_queue = STORVSC_MAX_IO_REQUESTS*STORVSC_MAX_TARGETS, - .this_id = -1, - /* no use setting to 0 since ll_blk_rw reset it to 1 */ - /* currently 32 */ - .sg_tablesize = MAX_MULTIPAGE_BUFFER_COUNT, +static int storvsc_device_alloc(struct scsi_device *sdevice) +{ /* - * ENABLE_CLUSTERING allows mutiple physically contig bio_vecs to merge - * into 1 sg element. If set, we must limit the max_segment_size to - * PAGE_SIZE, otherwise we may get 1 sg element that represents - * multiple + * This enables luns to be located sparsely. Otherwise, we may not + * discovered them. */ - /* physically contig pfns (ie sg[x].length > PAGE_SIZE). */ - .use_clustering = ENABLE_CLUSTERING, - /* Make sure we dont get a sg segment crosses a page boundary */ - .dma_boundary = PAGE_SIZE-1, -}; - - -/* - * storvsc_drv_init - StorVsc driver initialization. - */ -static int storvsc_drv_init(int (*drv_init)(struct hv_driver *drv)) -{ - int ret; - struct storvsc_driver_object *storvsc_drv_obj = &g_storvsc_drv; - struct hv_driver *drv = &g_storvsc_drv.base; - - storvsc_drv_obj->ring_buffer_size = storvsc_ringbuffer_size; - - /* Callback to client driver to complete the initialization */ - drv_init(&storvsc_drv_obj->base); - - drv->priv = storvsc_drv_obj; - - DPRINT_INFO(STORVSC_DRV, - "request extension size %u, max outstanding reqs %u", - storvsc_drv_obj->request_ext_size, - storvsc_drv_obj->max_outstanding_req_per_channel); - - if (storvsc_drv_obj->max_outstanding_req_per_channel < - STORVSC_MAX_IO_REQUESTS) { - DPRINT_ERR(STORVSC_DRV, - "The number of outstanding io requests (%d) " - "is larger than that supported (%d) internally.", - STORVSC_MAX_IO_REQUESTS, - storvsc_drv_obj->max_outstanding_req_per_channel); - return -1; - } - - drv->driver.name = storvsc_drv_obj->base.name; - - drv->driver.probe = storvsc_probe; - drv->driver.remove = storvsc_remove; - - /* The driver belongs to vmbus */ - ret = vmbus_child_driver_register(&drv->driver); - - return ret; -} - -static int storvsc_drv_exit_cb(struct device *dev, void *data) -{ - struct device **curr = (struct device **)data; - *curr = dev; - return 1; /* stop iterating */ -} - -static void storvsc_drv_exit(void) -{ - struct storvsc_driver_object *storvsc_drv_obj = &g_storvsc_drv; - struct hv_driver *drv = &g_storvsc_drv.base; - struct device *current_dev = NULL; - int ret; - - while (1) { - current_dev = NULL; - - /* Get the device */ - ret = driver_for_each_device(&drv->driver, NULL, - (void *) ¤t_dev, - storvsc_drv_exit_cb); - - if (ret) - DPRINT_WARN(STORVSC_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; + sdevice->sdev_bflags |= BLIST_SPARSELUN | BLIST_LARGELUN; + return 0; } -/* - * storvsc_probe - Add a new device for this driver - */ -static int storvsc_probe(struct device *device) +static int storvsc_merge_bvec(struct request_queue *q, + struct bvec_merge_data *bmd, struct bio_vec *bvec) { - int ret; - 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 Scsi_Host *host; - struct host_device_context *host_device_ctx; - struct storvsc_device_info device_info; - - if (!storvsc_drv_obj->base.dev_add) - return -1; - - host = scsi_host_alloc(&scsi_driver, - sizeof(struct host_device_context)); - if (!host) { - DPRINT_ERR(STORVSC_DRV, "unable to allocate scsi host object"); - return -ENOMEM; - } - - dev_set_drvdata(device, host); - - host_device_ctx = (struct host_device_context *)host->hostdata; - memset(host_device_ctx, 0, sizeof(struct host_device_context)); - - host_device_ctx->port = host->host_no; - host_device_ctx->device_ctx = device_obj; - - host_device_ctx->request_pool = - kmem_cache_create(dev_name(&device_obj->device), - sizeof(struct storvsc_cmd_request) + - storvsc_drv_obj->request_ext_size, 0, - SLAB_HWCACHE_ALIGN, NULL); - - if (!host_device_ctx->request_pool) { - scsi_host_put(host); - return -ENOMEM; - } - - device_info.port_number = host->host_no; - /* Call to the vsc driver to add the device */ - ret = storvsc_drv_obj->base.dev_add(device_obj, - (void *)&device_info); - if (ret != 0) { - DPRINT_ERR(STORVSC_DRV, "unable to add scsi vsc device"); - kmem_cache_destroy(host_device_ctx->request_pool); - scsi_host_put(host); - return -1; - } - - /* host_device_ctx->port = device_info.PortNumber; */ - host_device_ctx->path = device_info.path_id; - host_device_ctx->target = device_info.target_id; - - /* max # of devices per target */ - host->max_lun = STORVSC_MAX_LUNS_PER_TARGET; - /* max # of targets per channel */ - host->max_id = STORVSC_MAX_TARGETS; - /* max # of channels */ - host->max_channel = STORVSC_MAX_CHANNELS - 1; - - /* Register the HBA and start the scsi bus scan */ - ret = scsi_add_host(host, device); - if (ret != 0) { - DPRINT_ERR(STORVSC_DRV, "unable to add scsi host device"); - - storvsc_drv_obj->base.dev_rm(device_obj); - - kmem_cache_destroy(host_device_ctx->request_pool); - scsi_host_put(host); - return -1; - } - - scsi_scan_host(host); - return ret; + /* checking done by caller. */ + return bvec->bv_len; } -/* - * storvsc_remove - Callback when our device is removed - */ -static int storvsc_remove(struct device *device) +static int storvsc_device_configure(struct scsi_device *sdevice) { - int ret; - 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 Scsi_Host *host = dev_get_drvdata(device); - struct host_device_context *host_device_ctx = - (struct host_device_context *)host->hostdata; - - - if (!storvsc_drv_obj->base.dev_rm) - return -1; + scsi_adjust_queue_depth(sdevice, MSG_SIMPLE_TAG, + STORVSC_MAX_IO_REQUESTS); - /* - * 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(STORVSC, "unable to remove vsc device (ret %d)", - ret); - } + DPRINT_INFO(STORVSC_DRV, "sdev (%p) - setting max segment size to %ld", + sdevice, PAGE_SIZE); + blk_queue_max_segment_size(sdevice->request_queue, PAGE_SIZE); - if (host_device_ctx->request_pool) { - kmem_cache_destroy(host_device_ctx->request_pool); - host_device_ctx->request_pool = NULL; - } + DPRINT_INFO(STORVSC_DRV, "sdev (%p) - adding merge bio vec routine", + sdevice); + blk_queue_merge_bvec(sdevice->request_queue, storvsc_merge_bvec); - DPRINT_INFO(STORVSC, "removing host adapter (%p)...", host); - scsi_remove_host(host); + blk_queue_bounce_limit(sdevice->request_queue, BLK_BOUNCE_ANY); - DPRINT_INFO(STORVSC, "releasing host adapter (%p)...", host); - scsi_host_put(host); - return ret; + return 0; } -/* - * storvsc_commmand_completion - Command completion processing - */ -static void storvsc_commmand_completion(struct hv_storvsc_request *request) +static void destroy_bounce_buffer(struct scatterlist *sgl, + unsigned int sg_count) { - struct storvsc_cmd_request *cmd_request = - (struct storvsc_cmd_request *)request->context; - struct scsi_cmnd *scmnd = cmd_request->cmd; - struct host_device_context *host_device_ctx = - (struct host_device_context *)scmnd->device->host->hostdata; - void (*scsi_done_fn)(struct scsi_cmnd *); - struct scsi_sense_hdr sense_hdr; - - /* ASSERT(request == &cmd_request->request); */ - /* ASSERT(scmnd); */ - /* ASSERT((unsigned long)scmnd->host_scribble == */ - /* (unsigned long)cmd_request); */ - /* ASSERT(scmnd->scsi_done); */ - - if (cmd_request->bounce_sgl_count) { - /* using bounce buffer */ - /* printk("copy_from_bounce_buffer\n"); */ - - /* FIXME: We can optimize on writes by just skipping this */ - copy_from_bounce_buffer(scsi_sglist(scmnd), - cmd_request->bounce_sgl, - scsi_sg_count(scmnd)); - destroy_bounce_buffer(cmd_request->bounce_sgl, - cmd_request->bounce_sgl_count); - } - - scmnd->result = request->status; + int i; + struct page *page_buf; - if (scmnd->result) { - if (scsi_normalize_sense(scmnd->sense_buffer, - request->sense_buffer_size, &sense_hdr)) - scsi_print_sense_hdr("storvsc", &sense_hdr); + for (i = 0; i < sg_count; i++) { + page_buf = sg_page((&sgl[i])); + if (page_buf != NULL) + __free_page(page_buf); } - /* ASSERT(request->BytesXfer <= request->data_buffer.Length); */ - scsi_set_resid(scmnd, - request->data_buffer.len - request->bytes_xfer); - - scsi_done_fn = scmnd->scsi_done; - - scmnd->host_scribble = NULL; - scmnd->scsi_done = NULL; - - /* !!DO NOT MODIFY the scmnd after this call */ - scsi_done_fn(scmnd); - - kmem_cache_free(host_device_ctx->request_pool, cmd_request); + kfree(sgl); } static int do_bounce_buffer(struct scatterlist *sgl, unsigned int sg_count) @@ -440,21 +176,72 @@ cleanup: return NULL; } -static void destroy_bounce_buffer(struct scatterlist *sgl, - unsigned int sg_count) + +/* Assume the original sgl has enough room */ +static unsigned int copy_from_bounce_buffer(struct scatterlist *orig_sgl, + struct scatterlist *bounce_sgl, + unsigned int orig_sgl_count) { int i; - struct page *page_buf; + int j = 0; + unsigned long src, dest; + unsigned int srclen, destlen, copylen; + unsigned int total_copied = 0; + unsigned long bounce_addr = 0; + unsigned long dest_addr = 0; + unsigned long flags; - for (i = 0; i < sg_count; i++) { - page_buf = sg_page((&sgl[i])); - if (page_buf != NULL) - __free_page(page_buf); + local_irq_save(flags); + + for (i = 0; i < orig_sgl_count; i++) { + dest_addr = (unsigned long)kmap_atomic(sg_page((&orig_sgl[i])), + KM_IRQ0) + orig_sgl[i].offset; + dest = dest_addr; + destlen = orig_sgl[i].length; + + if (bounce_addr == 0) + bounce_addr = + (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), + KM_IRQ0); + + while (destlen) { + src = bounce_addr + bounce_sgl[j].offset; + srclen = bounce_sgl[j].length - bounce_sgl[j].offset; + + copylen = min(srclen, destlen); + memcpy((void *)dest, (void *)src, copylen); + + total_copied += copylen; + bounce_sgl[j].offset += copylen; + destlen -= copylen; + dest += copylen; + + if (bounce_sgl[j].offset == bounce_sgl[j].length) { + /* full */ + kunmap_atomic((void *)bounce_addr, KM_IRQ0); + j++; + + /* if we need to use another bounce buffer */ + if (destlen || i != orig_sgl_count - 1) + bounce_addr = + (unsigned long)kmap_atomic( + sg_page((&bounce_sgl[j])), KM_IRQ0); + } else if (destlen == 0 && i == orig_sgl_count - 1) { + /* unmap the last bounce that is < PAGE_SIZE */ + kunmap_atomic((void *)bounce_addr, KM_IRQ0); + } + } + + kunmap_atomic((void *)(dest_addr - orig_sgl[i].offset), + KM_IRQ0); } - kfree(sgl); + local_irq_restore(flags); + + return total_copied; } + /* Assume the bounce_sgl has enough room ie using the create_bounce_buffer() */ static unsigned int copy_to_bounce_buffer(struct scatterlist *orig_sgl, struct scatterlist *bounce_sgl, @@ -477,10 +264,10 @@ static unsigned int copy_to_bounce_buffer(struct scatterlist *orig_sgl, src = src_addr; srclen = orig_sgl[i].length; - /* ASSERT(orig_sgl[i].offset + orig_sgl[i].length <= PAGE_SIZE); */ - if (bounce_addr == 0) - bounce_addr = (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), KM_IRQ0); + bounce_addr = + (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), + KM_IRQ0); while (srclen) { /* assume bounce offset always == 0 */ @@ -502,7 +289,10 @@ static unsigned int copy_to_bounce_buffer(struct scatterlist *orig_sgl, /* if we need to use another bounce buffer */ if (srclen || i != orig_sgl_count - 1) - bounce_addr = (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), KM_IRQ0); + bounce_addr = + (unsigned long)kmap_atomic( + sg_page((&bounce_sgl[j])), KM_IRQ0); + } else if (srclen == 0 && i == orig_sgl_count - 1) { /* unmap the last bounce that is < PAGE_SIZE */ kunmap_atomic((void *)bounce_addr, KM_IRQ0); @@ -517,67 +307,185 @@ static unsigned int copy_to_bounce_buffer(struct scatterlist *orig_sgl, return total_copied; } -/* Assume the original sgl has enough room */ -static unsigned int copy_from_bounce_buffer(struct scatterlist *orig_sgl, - struct scatterlist *bounce_sgl, - unsigned int orig_sgl_count) + +/* + * storvsc_remove - Callback when our device is removed + */ +static int storvsc_remove(struct hv_device *dev) { - int i; - int j = 0; - unsigned long src, dest; - unsigned int srclen, destlen, copylen; - unsigned int total_copied = 0; - unsigned long bounce_addr = 0; - unsigned long dest_addr = 0; - unsigned long flags; + struct Scsi_Host *host = dev_get_drvdata(&dev->device); + struct hv_host_device *host_dev = + (struct hv_host_device *)host->hostdata; - local_irq_save(flags); + /* + * Call to the vsc driver to let it know that the device is being + * removed + */ + storvsc_dev_remove(dev); - for (i = 0; i < orig_sgl_count; i++) { - dest_addr = (unsigned long)kmap_atomic(sg_page((&orig_sgl[i])), - KM_IRQ0) + orig_sgl[i].offset; - dest = dest_addr; - destlen = orig_sgl[i].length; - /* ASSERT(orig_sgl[i].offset + orig_sgl[i].length <= PAGE_SIZE); */ + if (host_dev->request_pool) { + kmem_cache_destroy(host_dev->request_pool); + host_dev->request_pool = NULL; + } - if (bounce_addr == 0) - bounce_addr = (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), KM_IRQ0); + DPRINT_INFO(STORVSC, "removing host adapter (%p)...", host); + scsi_remove_host(host); - while (destlen) { - src = bounce_addr + bounce_sgl[j].offset; - srclen = bounce_sgl[j].length - bounce_sgl[j].offset; + DPRINT_INFO(STORVSC, "releasing host adapter (%p)...", host); + scsi_host_put(host); + return 0; +} - copylen = min(srclen, destlen); - memcpy((void *)dest, (void *)src, copylen); - total_copied += copylen; - bounce_sgl[j].offset += copylen; - destlen -= copylen; - dest += copylen; +static int storvsc_get_chs(struct scsi_device *sdev, struct block_device * bdev, + sector_t capacity, int *info) +{ + sector_t nsect = capacity; + sector_t cylinders = nsect; + int heads, sectors_pt; - if (bounce_sgl[j].offset == bounce_sgl[j].length) { - /* full */ - kunmap_atomic((void *)bounce_addr, KM_IRQ0); - j++; + /* + * We are making up these values; let us keep it simple. + */ + heads = 0xff; + sectors_pt = 0x3f; /* Sectors per track */ + sector_div(cylinders, heads * sectors_pt); + if ((sector_t)(cylinders + 1) * heads * sectors_pt < nsect) + cylinders = 0xffff; - /* if we need to use another bounce buffer */ - if (destlen || i != orig_sgl_count - 1) - bounce_addr = (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), KM_IRQ0); - } else if (destlen == 0 && i == orig_sgl_count - 1) { - /* unmap the last bounce that is < PAGE_SIZE */ - kunmap_atomic((void *)bounce_addr, KM_IRQ0); - } - } + info[0] = heads; + info[1] = sectors_pt; + info[2] = (int)cylinders; - kunmap_atomic((void *)(dest_addr - orig_sgl[i].offset), - KM_IRQ0); + DPRINT_INFO(STORVSC_DRV, "CHS (%d, %d, %d)", (int)cylinders, heads, + sectors_pt); + + return 0; +} + +static int storvsc_host_reset(struct hv_device *device) +{ + struct storvsc_device *stor_device; + struct hv_storvsc_request *request; + struct vstor_packet *vstor_packet; + int ret, t; + + DPRINT_INFO(STORVSC, "resetting host adapter..."); + + stor_device = get_stor_device(device); + if (!stor_device) + return -1; + + request = &stor_device->reset_request; + vstor_packet = &request->vstor_packet; + + init_completion(&request->wait_event); + + vstor_packet->operation = VSTOR_OPERATION_RESET_BUS; + vstor_packet->flags = REQUEST_COMPLETION_FLAG; + vstor_packet->vm_srb.path_id = stor_device->path_id; + + ret = vmbus_sendpacket(device->channel, vstor_packet, + sizeof(struct vstor_packet), + (unsigned long)&stor_device->reset_request, + VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (ret != 0) + goto cleanup; + + t = wait_for_completion_timeout(&request->wait_event, HZ); + if (t == 0) { + ret = -ETIMEDOUT; + goto cleanup; } - local_irq_restore(flags); + DPRINT_INFO(STORVSC, "host adapter reset completed"); - return total_copied; + /* + * At this point, all outstanding requests in the adapter + * should have been flushed out and return to us + */ + +cleanup: + put_stor_device(device); + return ret; +} + + +/* + * storvsc_host_reset_handler - Reset the scsi HBA + */ +static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd) +{ + int ret; + struct hv_host_device *host_dev = + (struct hv_host_device *)scmnd->device->host->hostdata; + struct hv_device *dev = host_dev->dev; + + DPRINT_INFO(STORVSC_DRV, "sdev (%p) dev obj (%p) - host resetting...", + scmnd->device, dev); + + /* Invokes the vsc to reset the host/bus */ + ret = storvsc_host_reset(dev); + if (ret != 0) + return ret; + + DPRINT_INFO(STORVSC_DRV, "sdev (%p) dev obj (%p) - host reseted", + scmnd->device, dev); + + return ret; } + +/* + * storvsc_commmand_completion - Command completion processing + */ +static void storvsc_commmand_completion(struct hv_storvsc_request *request) +{ + struct storvsc_cmd_request *cmd_request = + (struct storvsc_cmd_request *)request->context; + struct scsi_cmnd *scmnd = cmd_request->cmd; + struct hv_host_device *host_dev = + (struct hv_host_device *)scmnd->device->host->hostdata; + void (*scsi_done_fn)(struct scsi_cmnd *); + struct scsi_sense_hdr sense_hdr; + struct vmscsi_request *vm_srb; + + if (cmd_request->bounce_sgl_count) { + + /* FIXME: We can optimize on writes by just skipping this */ + copy_from_bounce_buffer(scsi_sglist(scmnd), + cmd_request->bounce_sgl, + scsi_sg_count(scmnd)); + destroy_bounce_buffer(cmd_request->bounce_sgl, + cmd_request->bounce_sgl_count); + } + + vm_srb = &request->vstor_packet.vm_srb; + scmnd->result = vm_srb->scsi_status; + + if (scmnd->result) { + if (scsi_normalize_sense(scmnd->sense_buffer, + SCSI_SENSE_BUFFERSIZE, &sense_hdr)) + scsi_print_sense_hdr("storvsc", &sense_hdr); + } + + scsi_set_resid(scmnd, + request->data_buffer.len - + vm_srb->data_transfer_length); + + scsi_done_fn = scmnd->scsi_done; + + scmnd->host_scribble = NULL; + scmnd->scsi_done = NULL; + + /* !!DO NOT MODIFY the scmnd after this call */ + scsi_done_fn(scmnd); + + kmem_cache_free(host_dev->request_pool, cmd_request); +} + + /* * storvsc_queuecommand - Initiate command processing */ @@ -585,28 +493,20 @@ static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, void (*done)(struct scsi_cmnd *)) { int ret; - struct host_device_context *host_device_ctx = - (struct host_device_context *)scmnd->device->host->hostdata; - struct hv_device *device_ctx = host_device_ctx->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_host_device *host_dev = + (struct hv_host_device *)scmnd->device->host->hostdata; + struct hv_device *dev = host_dev->dev; struct hv_storvsc_request *request; struct storvsc_cmd_request *cmd_request; unsigned int request_size = 0; int i; struct scatterlist *sgl; unsigned int sg_count = 0; + struct vmscsi_request *vm_srb; - DPRINT_DBG(STORVSC_DRV, "scmnd %p dir %d, use_sg %d buf %p len %d " - "queue depth %d tagged %d", scmnd, scmnd->sc_data_direction, - scsi_sg_count(scmnd), scsi_sglist(scmnd), - scsi_bufflen(scmnd), scmnd->device->queue_depth, - scmnd->device->tagged_supported); /* If retrying, no need to prep the cmd */ if (scmnd->host_scribble) { - /* ASSERT(scmnd->scsi_done != NULL); */ cmd_request = (struct storvsc_cmd_request *)scmnd->host_scribble; @@ -616,18 +516,13 @@ static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, goto retry_request; } - /* ASSERT(scmnd->scsi_done == NULL); */ - /* ASSERT(scmnd->host_scribble == NULL); */ - scmnd->scsi_done = done; request_size = sizeof(struct storvsc_cmd_request); - cmd_request = kmem_cache_alloc(host_device_ctx->request_pool, + cmd_request = kmem_cache_zalloc(host_dev->request_pool, GFP_ATOMIC); if (!cmd_request) { - DPRINT_ERR(STORVSC_DRV, "scmnd (%p) - unable to allocate " - "storvsc_cmd_request...marking queue busy", scmnd); scmnd->scsi_done = NULL; return SCSI_MLQUEUE_DEVICE_BUSY; } @@ -640,40 +535,35 @@ static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, scmnd->host_scribble = (unsigned char *)cmd_request; request = &cmd_request->request; + vm_srb = &request->vstor_packet.vm_srb; - request->extension = - (void *)((unsigned long)cmd_request + request_size); - DPRINT_DBG(STORVSC_DRV, "req %p size %d ext %d", request, request_size, - storvsc_drv_obj->request_ext_size); /* Build the SRB */ switch (scmnd->sc_data_direction) { case DMA_TO_DEVICE: - request->type = WRITE_TYPE; + vm_srb->data_in = WRITE_TYPE; break; case DMA_FROM_DEVICE: - request->type = READ_TYPE; + vm_srb->data_in = READ_TYPE; break; default: - request->type = UNKNOWN_TYPE; + vm_srb->data_in = UNKNOWN_TYPE; break; } request->on_io_completion = storvsc_commmand_completion; request->context = cmd_request;/* scmnd; */ - /* request->PortId = scmnd->device->channel; */ - request->host = host_device_ctx->port; - request->bus = scmnd->device->channel; - request->target_id = scmnd->device->id; - request->lun_id = scmnd->device->lun; + vm_srb->port_number = host_dev->port; + vm_srb->path_id = scmnd->device->channel; + vm_srb->target_id = scmnd->device->id; + vm_srb->lun = scmnd->device->lun; + + vm_srb->cdb_length = scmnd->cmd_len; - /* ASSERT(scmnd->cmd_len <= 16); */ - request->cdb_len = scmnd->cmd_len; - request->cdb = scmnd->cmnd; + memcpy(vm_srb->cdb, scmnd->cmnd, vm_srb->cdb_length); request->sense_buffer = scmnd->sense_buffer; - request->sense_buffer_size = SCSI_SENSE_BUFFERSIZE; request->data_buffer.len = scsi_bufflen(scmnd); @@ -683,20 +573,13 @@ static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, /* check if we need to bounce the sgl */ if (do_bounce_buffer(sgl, scsi_sg_count(scmnd)) != -1) { - DPRINT_INFO(STORVSC_DRV, - "need to bounce buffer for this scmnd %p", - scmnd); cmd_request->bounce_sgl = create_bounce_buffer(sgl, scsi_sg_count(scmnd), scsi_bufflen(scmnd)); if (!cmd_request->bounce_sgl) { - DPRINT_ERR(STORVSC_DRV, - "unable to create bounce buffer for " - "this scmnd %p", scmnd); - scmnd->scsi_done = NULL; scmnd->host_scribble = NULL; - kmem_cache_free(host_device_ctx->request_pool, + kmem_cache_free(host_dev->request_pool, cmd_request); return SCSI_MLQUEUE_HOST_BUSY; @@ -719,14 +602,11 @@ static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, request->data_buffer.offset = sgl[0].offset; - for (i = 0; i < sg_count; i++) { - DPRINT_DBG(STORVSC_DRV, "sgl[%d] len %d offset %d\n", - i, sgl[i].length, sgl[i].offset); + for (i = 0; i < sg_count; i++) request->data_buffer.pfn_array[i] = page_to_pfn(sg_page((&sgl[i]))); - } + } else if (scsi_sglist(scmnd)) { - /* ASSERT(scsi_bufflen(scmnd) <= PAGE_SIZE); */ request->data_buffer.offset = virt_to_phys(scsi_sglist(scmnd)) & (PAGE_SIZE-1); request->data_buffer.pfn_array[0] = @@ -735,13 +615,10 @@ static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, retry_request: /* Invokes the vsc to start an IO */ - ret = storvsc_drv_obj->on_io_request(device_ctx, - &cmd_request->request); + ret = storvsc_do_io(dev, &cmd_request->request); + if (ret == -1) { /* no more space */ - DPRINT_ERR(STORVSC_DRV, - "scmnd (%p) - queue FULL...marking queue busy", - scmnd); if (cmd_request->bounce_sgl_count) { /* @@ -755,7 +632,7 @@ retry_request: cmd_request->bounce_sgl_count); } - kmem_cache_free(host_device_ctx->request_pool, cmd_request); + kmem_cache_free(host_dev->request_pool, cmd_request); scmnd->scsi_done = NULL; scmnd->host_scribble = NULL; @@ -768,154 +645,156 @@ retry_request: static DEF_SCSI_QCMD(storvsc_queuecommand) -static int storvsc_merge_bvec(struct request_queue *q, - struct bvec_merge_data *bmd, struct bio_vec *bvec) -{ - /* checking done by caller. */ - return bvec->bv_len; -} -/* - * storvsc_device_configure - Configure the specified scsi device - */ -static int storvsc_device_alloc(struct scsi_device *sdevice) -{ - DPRINT_DBG(STORVSC_DRV, "sdev (%p) - setting device flag to %d", - sdevice, BLIST_SPARSELUN); +/* Scsi driver */ +static struct scsi_host_template scsi_driver = { + .module = THIS_MODULE, + .name = "storvsc_host_t", + .bios_param = storvsc_get_chs, + .queuecommand = storvsc_queuecommand, + .eh_host_reset_handler = storvsc_host_reset_handler, + .slave_alloc = storvsc_device_alloc, + .slave_configure = storvsc_device_configure, + .cmd_per_lun = 1, + /* 64 max_queue * 1 target */ + .can_queue = STORVSC_MAX_IO_REQUESTS*STORVSC_MAX_TARGETS, + .this_id = -1, + /* no use setting to 0 since ll_blk_rw reset it to 1 */ + /* currently 32 */ + .sg_tablesize = MAX_MULTIPAGE_BUFFER_COUNT, /* - * This enables luns to be located sparsely. Otherwise, we may not - * discovered them. + * ENABLE_CLUSTERING allows mutiple physically contig bio_vecs to merge + * into 1 sg element. If set, we must limit the max_segment_size to + * PAGE_SIZE, otherwise we may get 1 sg element that represents + * multiple */ - sdevice->sdev_bflags |= BLIST_SPARSELUN | BLIST_LARGELUN; - return 0; -} + /* physically contig pfns (ie sg[x].length > PAGE_SIZE). */ + .use_clustering = ENABLE_CLUSTERING, + /* Make sure we dont get a sg segment crosses a page boundary */ + .dma_boundary = PAGE_SIZE-1, +}; -static int storvsc_device_configure(struct scsi_device *sdevice) + +/* + * storvsc_probe - Add a new device for this driver + */ + +static int storvsc_probe(struct hv_device *device) { - DPRINT_INFO(STORVSC_DRV, "sdev (%p) - curr queue depth %d", sdevice, - sdevice->queue_depth); + int ret; + struct Scsi_Host *host; + struct hv_host_device *host_dev; + struct storvsc_device_info device_info; - DPRINT_INFO(STORVSC_DRV, "sdev (%p) - setting queue depth to %d", - sdevice, STORVSC_MAX_IO_REQUESTS); - scsi_adjust_queue_depth(sdevice, MSG_SIMPLE_TAG, - STORVSC_MAX_IO_REQUESTS); + host = scsi_host_alloc(&scsi_driver, + sizeof(struct hv_host_device)); + if (!host) + return -ENOMEM; - DPRINT_INFO(STORVSC_DRV, "sdev (%p) - setting max segment size to %ld", - sdevice, PAGE_SIZE); - blk_queue_max_segment_size(sdevice->request_queue, PAGE_SIZE); + dev_set_drvdata(&device->device, host); - DPRINT_INFO(STORVSC_DRV, "sdev (%p) - adding merge bio vec routine", - sdevice); - blk_queue_merge_bvec(sdevice->request_queue, storvsc_merge_bvec); + host_dev = (struct hv_host_device *)host->hostdata; + memset(host_dev, 0, sizeof(struct hv_host_device)); - blk_queue_bounce_limit(sdevice->request_queue, BLK_BOUNCE_ANY); - /* sdevice->timeout = (2000 * HZ);//(75 * HZ); */ + host_dev->port = host->host_no; + host_dev->dev = device; - return 0; -} + host_dev->request_pool = + kmem_cache_create(dev_name(&device->device), + sizeof(struct storvsc_cmd_request), 0, + SLAB_HWCACHE_ALIGN, NULL); -/* - * storvsc_host_reset_handler - Reset the scsi HBA - */ -static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd) -{ - int ret; - struct host_device_context *host_device_ctx = - (struct host_device_context *)scmnd->device->host->hostdata; - struct hv_device *device_ctx = host_device_ctx->device_ctx; + if (!host_dev->request_pool) { + scsi_host_put(host); + return -ENOMEM; + } - DPRINT_INFO(STORVSC_DRV, "sdev (%p) dev obj (%p) - host resetting...", - scmnd->device, device_ctx); + device_info.port_number = host->host_no; + device_info.ring_buffer_size = storvsc_ringbuffer_size; + /* Call to the vsc driver to add the device */ + ret = storvsc_dev_add(device, (void *)&device_info); - /* Invokes the vsc to reset the host/bus */ - ret = stor_vsc_on_host_reset(device_ctx); - if (ret != 0) - return ret; + if (ret != 0) { + kmem_cache_destroy(host_dev->request_pool); + scsi_host_put(host); + return -1; + } - DPRINT_INFO(STORVSC_DRV, "sdev (%p) dev obj (%p) - host reseted", - scmnd->device, device_ctx); + host_dev->path = device_info.path_id; + host_dev->target = device_info.target_id; - return ret; -} + /* max # of devices per target */ + host->max_lun = STORVSC_MAX_LUNS_PER_TARGET; + /* max # of targets per channel */ + host->max_id = STORVSC_MAX_TARGETS; + /* max # of channels */ + host->max_channel = STORVSC_MAX_CHANNELS - 1; -static int storvsc_get_chs(struct scsi_device *sdev, struct block_device * bdev, - sector_t capacity, int *info) -{ - sector_t total_sectors = capacity; - sector_t cylinder_times_heads = 0; - sector_t temp = 0; + /* Register the HBA and start the scsi bus scan */ + ret = scsi_add_host(host, &device->device); + if (ret != 0) { - int sectors_per_track = 0; - int heads = 0; - int cylinders = 0; - int rem = 0; + storvsc_dev_remove(device); - if (total_sectors > (65535 * 16 * 255)) - total_sectors = (65535 * 16 * 255); + kmem_cache_destroy(host_dev->request_pool); + scsi_host_put(host); + return -1; + } - if (total_sectors >= (65535 * 16 * 63)) { - sectors_per_track = 255; - heads = 16; + scsi_scan_host(host); + return ret; +} - 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; +/* The one and only one */ - cylinder_times_heads = total_sectors; - /* sector_div stores the quotient in cylinder_times_heads */ - rem = sector_div(cylinder_times_heads, sectors_per_track); +static struct hv_driver storvsc_drv = { + .probe = storvsc_probe, + .remove = storvsc_remove, +}; - temp = cylinder_times_heads + 1023; - /* sector_div stores the quotient in temp */ - rem = sector_div(temp, 1024); - heads = temp; +/* + * storvsc_drv_init - StorVsc driver initialization. + */ +static int storvsc_drv_init(void) +{ + int ret; + struct hv_driver *drv = &storvsc_drv; + u32 max_outstanding_req_per_channel; - if (heads < 4) - heads = 4; + /* + * Divide the ring buffer data size (which is 1 page less + * than the ring buffer size since that page is reserved for + * the ring buffer indices) by the max request size (which is + * vmbus_channel_packet_multipage_buffer + struct vstor_packet + u64) + */ - if (cylinder_times_heads >= (heads * 1024) || (heads > 16)) { - sectors_per_track = 31; - heads = 16; + max_outstanding_req_per_channel = + ((storvsc_ringbuffer_size - PAGE_SIZE) / + ALIGN(MAX_MULTIPAGE_BUFFER_PACKET + + sizeof(struct vstor_packet) + sizeof(u64), + sizeof(u64))); - cylinder_times_heads = total_sectors; - /* - * sector_div stores the quotient in - * cylinder_times_heads - */ - rem = sector_div(cylinder_times_heads, - sectors_per_track); - } + memcpy(&drv->dev_type, &gStorVscDeviceType, + sizeof(struct hv_guid)); - if (cylinder_times_heads >= (heads * 1024)) { - sectors_per_track = 63; - heads = 16; + if (max_outstanding_req_per_channel < + STORVSC_MAX_IO_REQUESTS) + return -1; - cylinder_times_heads = total_sectors; - /* - * sector_div stores the quotient in - * cylinder_times_heads - */ - rem = sector_div(cylinder_times_heads, - sectors_per_track); - } - } + drv->name = driver_name; + drv->driver.name = driver_name; - temp = cylinder_times_heads; - /* sector_div stores the quotient in temp */ - rem = sector_div(temp, heads); - cylinders = temp; - info[0] = heads; - info[1] = sectors_per_track; - info[2] = cylinders; + /* The driver belongs to vmbus */ + ret = vmbus_child_driver_register(&drv->driver); - DPRINT_INFO(STORVSC_DRV, "CHS (%d, %d, %d)", cylinders, heads, - sectors_per_track); + return ret; +} - return 0; +static void storvsc_drv_exit(void) +{ + vmbus_child_driver_unregister(&storvsc_drv.driver); } static int __init storvsc_init(void) @@ -923,7 +802,7 @@ static int __init storvsc_init(void) int ret; DPRINT_INFO(STORVSC_DRV, "Storvsc initializing...."); - ret = storvsc_drv_init(stor_vsc_initialize); + ret = storvsc_drv_init(); return ret; } |