diff options
Diffstat (limited to 'drivers/staging/hv/storvsc_drv.c')
-rw-r--r-- | drivers/staging/hv/storvsc_drv.c | 310 |
1 files changed, 208 insertions, 102 deletions
diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c index ae8c33e7849c..eb853f71089a 100644 --- a/drivers/staging/hv/storvsc_drv.c +++ b/drivers/staging/hv/storvsc_drv.c @@ -32,6 +32,7 @@ #include <linux/module.h> #include <linux/device.h> #include <linux/hyperv.h> +#include <linux/mempool.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_host.h> @@ -42,6 +43,7 @@ #include <scsi/scsi_dbg.h> +#define STORVSC_MIN_BUF_NR 64 #define STORVSC_RING_BUFFER_SIZE (20*PAGE_SIZE) static int storvsc_ringbuffer_size = STORVSC_RING_BUFFER_SIZE; @@ -78,7 +80,7 @@ MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)"); /* V1 Beta 0.1 */ /* V1 RC < 2008/1/31 1.0 */ /* V1 RC > 2008/1/31 2.0 */ -#define VMSTOR_PROTOCOL_VERSION_CURRENT VMSTOR_PROTOCOL_VERSION(2, 0) +#define VMSTOR_PROTOCOL_VERSION_CURRENT VMSTOR_PROTOCOL_VERSION(4, 2) @@ -104,7 +106,8 @@ enum vstor_packet_operation { VSTOR_OPERATION_END_INITIALIZATION = 8, VSTOR_OPERATION_QUERY_PROTOCOL_VERSION = 9, VSTOR_OPERATION_QUERY_PROPERTIES = 10, - VSTOR_OPERATION_MAXIMUM = 10 + VSTOR_OPERATION_ENUMERATE_BUS = 11, + VSTOR_OPERATION_MAXIMUM = 11 }; /* @@ -234,8 +237,6 @@ struct vstor_packet { #define STORVSC_MAX_CHANNELS 1 #define STORVSC_MAX_CMD_LEN 16 -struct hv_storvsc_request; - /* Matches Windows-end */ enum storvsc_request_type { WRITE_TYPE, @@ -284,9 +285,13 @@ struct storvsc_device { struct hv_storvsc_request reset_request; }; +struct stor_mem_pools { + struct kmem_cache *request_pool; + mempool_t *request_mempool; +}; + struct hv_host_device { struct hv_device *dev; - struct kmem_cache *request_pool; unsigned int port; unsigned char path; unsigned char target; @@ -302,6 +307,51 @@ struct storvsc_cmd_request { struct hv_storvsc_request request; }; +struct storvsc_scan_work { + struct work_struct work; + struct Scsi_Host *host; + uint lun; +}; + +static void storvsc_bus_scan(struct work_struct *work) +{ + struct storvsc_scan_work *wrk; + int id, order_id; + + wrk = container_of(work, struct storvsc_scan_work, work); + for (id = 0; id < wrk->host->max_id; ++id) { + if (wrk->host->reverse_ordering) + order_id = wrk->host->max_id - id - 1; + else + order_id = id; + + scsi_scan_target(&wrk->host->shost_gendev, 0, + order_id, SCAN_WILD_CARD, 1); + } + kfree(wrk); +} + +static void storvsc_remove_lun(struct work_struct *work) +{ + struct storvsc_scan_work *wrk; + struct scsi_device *sdev; + + wrk = container_of(work, struct storvsc_scan_work, work); + if (!scsi_host_get(wrk->host)) + goto done; + + sdev = scsi_device_lookup(wrk->host, 0, 0, wrk->lun); + + if (sdev) { + scsi_remove_device(sdev); + scsi_device_put(sdev); + } + scsi_host_put(wrk->host); + +done: + kfree(wrk); +} + static inline struct storvsc_device *get_out_stor_device( struct hv_device *device) { @@ -549,11 +599,25 @@ static void storvsc_on_receive(struct hv_device *device, struct vstor_packet *vstor_packet, struct hv_storvsc_request *request) { + struct storvsc_scan_work *work; + struct storvsc_device *stor_device; + switch (vstor_packet->operation) { case VSTOR_OPERATION_COMPLETE_IO: storvsc_on_io_completion(device, vstor_packet, request); break; + case VSTOR_OPERATION_REMOVE_DEVICE: + case VSTOR_OPERATION_ENUMERATE_BUS: + stor_device = get_in_stor_device(device); + work = kmalloc(sizeof(struct storvsc_scan_work), GFP_ATOMIC); + if (!work) + return; + + INIT_WORK(&work->work, storvsc_bus_scan); + work->host = stor_device->host; + schedule_work(&work->work); + break; default: break; @@ -729,19 +793,48 @@ static void storvsc_get_ide_info(struct hv_device *dev, int *target, int *path) static int storvsc_device_alloc(struct scsi_device *sdevice) { - /* - * This enables luns to be located sparsely. Otherwise, we may not - * discovered them. - */ - sdevice->sdev_bflags |= BLIST_SPARSELUN | BLIST_LARGELUN; + struct stor_mem_pools *memp; + int number = STORVSC_MIN_BUF_NR; + + memp = kzalloc(sizeof(struct stor_mem_pools), GFP_KERNEL); + if (!memp) + return -ENOMEM; + + memp->request_pool = + kmem_cache_create(dev_name(&sdevice->sdev_dev), + sizeof(struct storvsc_cmd_request), 0, + SLAB_HWCACHE_ALIGN, NULL); + + if (!memp->request_pool) + goto err0; + + memp->request_mempool = mempool_create(number, mempool_alloc_slab, + mempool_free_slab, + memp->request_pool); + + if (!memp->request_mempool) + goto err1; + + sdevice->hostdata = memp; + return 0; + +err1: + kmem_cache_destroy(memp->request_pool); + +err0: + kfree(memp); + return -ENOMEM; } -static int storvsc_merge_bvec(struct request_queue *q, - struct bvec_merge_data *bmd, struct bio_vec *bvec) +static void storvsc_device_destroy(struct scsi_device *sdevice) { - /* checking done by caller. */ - return bvec->bv_len; + struct stor_mem_pools *memp = sdevice->hostdata; + + mempool_destroy(memp->request_mempool); + kmem_cache_destroy(memp->request_pool); + kfree(memp); + sdevice->hostdata = NULL; } static int storvsc_device_configure(struct scsi_device *sdevice) @@ -751,8 +844,6 @@ static int storvsc_device_configure(struct scsi_device *sdevice) blk_queue_max_segment_size(sdevice->request_queue, PAGE_SIZE); - blk_queue_merge_bvec(sdevice->request_queue, storvsc_merge_bvec); - blk_queue_bounce_limit(sdevice->request_queue, BLK_BOUNCE_ANY); return 0; @@ -802,12 +893,14 @@ static int do_bounce_buffer(struct scatterlist *sgl, unsigned int sg_count) static struct scatterlist *create_bounce_buffer(struct scatterlist *sgl, unsigned int sg_count, - unsigned int len) + unsigned int len, + int write) { int i; int num_pages; struct scatterlist *bounce_sgl; struct page *page_buf; + unsigned int buf_len = ((write == WRITE_TYPE) ? 0 : PAGE_SIZE); num_pages = ALIGN(len, PAGE_SIZE) >> PAGE_SHIFT; @@ -819,7 +912,7 @@ static struct scatterlist *create_bounce_buffer(struct scatterlist *sgl, page_buf = alloc_page(GFP_ATOMIC); if (!page_buf) goto cleanup; - sg_set_page(&bounce_sgl[i], page_buf, 0, 0); + sg_set_page(&bounce_sgl[i], page_buf, buf_len, 0); } return bounce_sgl; @@ -833,7 +926,8 @@ cleanup: /* 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) + unsigned int orig_sgl_count, + unsigned int bounce_sgl_count) { int i; int j = 0; @@ -874,6 +968,24 @@ static unsigned int copy_from_bounce_buffer(struct scatterlist *orig_sgl, kunmap_atomic((void *)bounce_addr, KM_IRQ0); j++; + /* + * It is possible that the number of elements + * in the bounce buffer may not be equal to + * the number of elements in the original + * scatter list. Handle this correctly. + */ + + if (j == bounce_sgl_count) { + /* + * We are done; cleanup and return. + */ + kunmap_atomic((void *)(dest_addr - + orig_sgl[i].offset), + KM_IRQ0); + local_irq_restore(flags); + return total_copied; + } + /* if we need to use another bounce buffer */ if (destlen || i != orig_sgl_count - 1) bounce_addr = @@ -965,18 +1077,13 @@ static int storvsc_remove(struct hv_device *dev) { struct storvsc_device *stor_device = hv_get_drvdata(dev); struct Scsi_Host *host = stor_device->host; - struct hv_host_device *host_dev = - (struct hv_host_device *)host->hostdata; scsi_remove_host(host); scsi_host_put(host); storvsc_dev_remove(dev); - if (host_dev->request_pool) { - kmem_cache_destroy(host_dev->request_pool); - host_dev->request_pool = NULL; - } + return 0; } @@ -1014,7 +1121,7 @@ static int storvsc_host_reset(struct hv_device *device) stor_device = get_out_stor_device(device); if (!stor_device) - return -ENODEV; + return FAILED; request = &stor_device->reset_request; vstor_packet = &request->vstor_packet; @@ -1031,13 +1138,11 @@ static int storvsc_host_reset(struct hv_device *device) VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); if (ret != 0) - goto cleanup; + return FAILED; t = wait_for_completion_timeout(&request->wait_event, 5*HZ); - if (t == 0) { - ret = -ETIMEDOUT; - goto cleanup; - } + if (t == 0) + return TIMEOUT_ERROR; /* @@ -1045,8 +1150,7 @@ static int storvsc_host_reset(struct hv_device *device) * should have been flushed out and return to us */ -cleanup: - return ret; + return SUCCESS; } @@ -1055,16 +1159,10 @@ cleanup: */ 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_host_device *host_dev = shost_priv(scmnd->device->host); struct hv_device *dev = host_dev->dev; - ret = storvsc_host_reset(dev); - if (ret != 0) - return ret; - - return ret; + return storvsc_host_reset(dev); } @@ -1076,21 +1174,22 @@ static void storvsc_command_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; + struct hv_host_device *host_dev = shost_priv(scmnd->device->host); void (*scsi_done_fn)(struct scsi_cmnd *); struct scsi_sense_hdr sense_hdr; struct vmscsi_request *vm_srb; + struct storvsc_scan_work *wrk; + struct stor_mem_pools *memp = scmnd->device->hostdata; vm_srb = &request->vstor_packet.vm_srb; if (cmd_request->bounce_sgl_count) { - if (vm_srb->data_in == READ_TYPE) { + if (vm_srb->data_in == READ_TYPE) copy_from_bounce_buffer(scsi_sglist(scmnd), cmd_request->bounce_sgl, - scsi_sg_count(scmnd)); - destroy_bounce_buffer(cmd_request->bounce_sgl, + scsi_sg_count(scmnd), + cmd_request->bounce_sgl_count); + destroy_bounce_buffer(cmd_request->bounce_sgl, cmd_request->bounce_sgl_count); - } } /* @@ -1103,6 +1202,29 @@ static void storvsc_command_completion(struct hv_storvsc_request *request) else scmnd->result = vm_srb->scsi_status; + /* + * If the LUN is invalid; remove the device. + */ + if (vm_srb->srb_status == 0x20) { + struct storvsc_device *stor_dev; + struct hv_device *dev = host_dev->dev; + struct Scsi_Host *host; + + stor_dev = get_in_stor_device(dev); + host = stor_dev->host; + + wrk = kmalloc(sizeof(struct storvsc_scan_work), + GFP_ATOMIC); + if (!wrk) { + scmnd->result = DID_TARGET_FAILURE << 16; + } else { + wrk->host = host; + wrk->lun = vm_srb->lun; + INIT_WORK(&wrk->work, storvsc_remove_lun); + schedule_work(&wrk->work); + } + } + if (scmnd->result) { if (scsi_normalize_sense(scmnd->sense_buffer, SCSI_SENSE_BUFFERSIZE, &sense_hdr)) @@ -1120,7 +1242,7 @@ static void storvsc_command_completion(struct hv_storvsc_request *request) scsi_done_fn(scmnd); - kmem_cache_free(host_dev->request_pool, cmd_request); + mempool_free(cmd_request, memp->request_mempool); } static bool storvsc_check_scsi_cmd(struct scsi_cmnd *scmnd) @@ -1131,7 +1253,7 @@ static bool storvsc_check_scsi_cmd(struct scsi_cmnd *scmnd) switch (scsi_op) { /* smartd sends this command, which will offline the device */ case SET_WINDOW: - scmnd->result = DID_ERROR << 16; + scmnd->result = ILLEGAL_REQUEST << 16; allowed = false; break; default: @@ -1143,12 +1265,10 @@ static bool storvsc_check_scsi_cmd(struct scsi_cmnd *scmnd) /* * storvsc_queuecommand - Initiate command processing */ -static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, - void (*done)(struct scsi_cmnd *)) +static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) { int ret; - struct hv_host_device *host_dev = - (struct hv_host_device *)scmnd->device->host->hostdata; + struct hv_host_device *host_dev = shost_priv(host); struct hv_device *dev = host_dev->dev; struct hv_storvsc_request *request; struct storvsc_cmd_request *cmd_request; @@ -1157,9 +1277,10 @@ static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, struct scatterlist *sgl; unsigned int sg_count = 0; struct vmscsi_request *vm_srb; + struct stor_mem_pools *memp = scmnd->device->hostdata; if (storvsc_check_scsi_cmd(scmnd) == false) { - done(scmnd); + scmnd->scsi_done(scmnd); return 0; } @@ -1172,16 +1293,14 @@ static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, goto retry_request; } - scmnd->scsi_done = done; - request_size = sizeof(struct storvsc_cmd_request); - cmd_request = kmem_cache_zalloc(host_dev->request_pool, + cmd_request = mempool_alloc(memp->request_mempool, GFP_ATOMIC); - if (!cmd_request) { - scmnd->scsi_done = NULL; + if (!cmd_request) return SCSI_MLQUEUE_DEVICE_BUSY; - } + + memset(cmd_request, 0, sizeof(struct storvsc_cmd_request)); /* Setup the cmd request */ cmd_request->bounce_sgl_count = 0; @@ -1231,12 +1350,12 @@ static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, if (do_bounce_buffer(sgl, scsi_sg_count(scmnd)) != -1) { cmd_request->bounce_sgl = create_bounce_buffer(sgl, scsi_sg_count(scmnd), - scsi_bufflen(scmnd)); + scsi_bufflen(scmnd), + vm_srb->data_in); if (!cmd_request->bounce_sgl) { - scmnd->scsi_done = NULL; scmnd->host_scribble = NULL; - kmem_cache_free(host_dev->request_pool, - cmd_request); + mempool_free(cmd_request, + memp->request_mempool); return SCSI_MLQUEUE_HOST_BUSY; } @@ -1278,9 +1397,8 @@ retry_request: destroy_bounce_buffer(cmd_request->bounce_sgl, cmd_request->bounce_sgl_count); - kmem_cache_free(host_dev->request_pool, cmd_request); + mempool_free(cmd_request, memp->request_mempool); - scmnd->scsi_done = NULL; scmnd->host_scribble = NULL; ret = SCSI_MLQUEUE_DEVICE_BUSY; @@ -1289,9 +1407,6 @@ retry_request: return ret; } -static DEF_SCSI_QCMD(storvsc_queuecommand) - - /* Scsi driver */ static struct scsi_host_template scsi_driver = { .module = THIS_MODULE, @@ -1300,6 +1415,7 @@ static struct scsi_host_template scsi_driver = { .queuecommand = storvsc_queuecommand, .eh_host_reset_handler = storvsc_host_reset_handler, .slave_alloc = storvsc_device_alloc, + .slave_destroy = storvsc_device_destroy, .slave_configure = storvsc_device_configure, .cmd_per_lun = 1, /* 64 max_queue * 1 target */ @@ -1308,14 +1424,7 @@ static struct scsi_host_template scsi_driver = { /* no use setting to 0 since ll_blk_rw reset it to 1 */ /* currently 32 */ .sg_tablesize = MAX_MULTIPAGE_BUFFER_COUNT, - /* - * 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 - */ - /* physically contig pfns (ie sg[x].length > PAGE_SIZE). */ - .use_clustering = ENABLE_CLUSTERING, + .use_clustering = DISABLE_CLUSTERING, /* Make sure we dont get a sg segment crosses a page boundary */ .dma_boundary = PAGE_SIZE-1, }; @@ -1360,27 +1469,17 @@ static int storvsc_probe(struct hv_device *device, if (!host) return -ENOMEM; - host_dev = (struct hv_host_device *)host->hostdata; + host_dev = shost_priv(host); memset(host_dev, 0, sizeof(struct hv_host_device)); host_dev->port = host->host_no; host_dev->dev = device; - host_dev->request_pool = - kmem_cache_create(dev_name(&device->device), - sizeof(struct storvsc_cmd_request), 0, - SLAB_HWCACHE_ALIGN, NULL); - - if (!host_dev->request_pool) { - scsi_host_put(host); - return -ENOMEM; - } stor_device = kzalloc(sizeof(struct storvsc_device), GFP_KERNEL); if (!stor_device) { - kmem_cache_destroy(host_dev->request_pool); - scsi_host_put(host); - return -ENOMEM; + ret = -ENOMEM; + goto err_out0; } stor_device->destroy = false; @@ -1391,12 +1490,8 @@ static int storvsc_probe(struct hv_device *device, stor_device->port_number = host->host_no; ret = storvsc_connect_to_vsp(device, storvsc_ringbuffer_size); - if (ret) { - kmem_cache_destroy(host_dev->request_pool); - scsi_host_put(host); - kfree(stor_device); - return ret; - } + if (ret) + goto err_out1; if (dev_is_ide) storvsc_get_ide_info(device, &target, &path); @@ -1416,7 +1511,7 @@ static int storvsc_probe(struct hv_device *device, /* Register the HBA and start the scsi bus scan */ ret = scsi_add_host(host, &device->device); if (ret != 0) - goto err_out; + goto err_out2; if (!dev_is_ide) { scsi_scan_host(host); @@ -1425,21 +1520,32 @@ static int storvsc_probe(struct hv_device *device, ret = scsi_add_device(host, 0, target, 0); if (ret) { scsi_remove_host(host); - goto err_out; + goto err_out2; } return 0; -err_out: +err_out2: + /* + * Once we have connected with the host, we would need to + * to invoke storvsc_dev_remove() to rollback this state and + * this call also frees up the stor_device; hence the jump around + * err_out1 label. + */ storvsc_dev_remove(device); - kmem_cache_destroy(host_dev->request_pool); + goto err_out0; + +err_out1: + kfree(stor_device); + +err_out0: scsi_host_put(host); - return -ENODEV; + return ret; } /* The one and only one */ static struct hv_driver storvsc_drv = { - .name = "storvsc", + .name = KBUILD_MODNAME, .id_table = id_table, .probe = storvsc_probe, .remove = storvsc_remove, |