* 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>
#include "vstorage.h"
#include "channel.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 *g_driver_name = "storvsc";
+static const char *driver_name = "storvsc";
/* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */
static const struct hv_guid gStorVscDeviceType = {
}
};
-struct host_device_context {
- /* must be 1st field
- * FIXME this is a bug */
- /* point back to our device context */
- struct hv_device *device_ctx;
+struct hv_host_device {
+ struct hv_device *dev;
struct kmem_cache *request_pool;
unsigned int port;
unsigned char path;
/*
- * stor_vsc_initialize - Main entry point
+ * storvsc_initialize - Main entry point
*/
-static int stor_vsc_initialize(struct hv_driver *driver)
+static int storvsc_initialize(struct hv_driver *driver)
{
- struct storvsc_driver_object *stor_driver;
+ struct storvsc_driver *stor_driver;
stor_driver = hvdr_to_stordr(driver);
- DPRINT_DBG(STORVSC,
- "sizeof(struct hv_storvsc_request)=%zd "
- "sizeof(struct vstor_packet)=%zd, "
- "sizeof(struct vmscsi_request)=%zd",
- sizeof(struct hv_storvsc_request),
- sizeof(struct vstor_packet),
- sizeof(struct vmscsi_request));
/* Make sure we are at least 2 pages since 1 page is used for control */
- driver->name = g_driver_name;
+ driver->name = driver_name;
memcpy(&driver->dev_type, &gStorVscDeviceType,
sizeof(struct hv_guid));
sizeof(struct vstor_packet) + sizeof(u64),
sizeof(u64)));
- DPRINT_INFO(STORVSC, "max io %u, currently %u\n",
- stor_driver->max_outstanding_req_per_channel,
- STORVSC_MAX_IO_REQUESTS);
-
- /* Setup the dispatch table */
- stor_driver->base.dev_add = stor_vsc_on_device_add;
- stor_driver->base.dev_rm = stor_vsc_on_device_remove;
- stor_driver->base.cleanup = stor_vsc_on_cleanup;
-
- stor_driver->on_io_request = stor_vsc_on_io_request;
return 0;
}
-/* Static decl */
-static int storvsc_probe(struct hv_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,
- /*
- * 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,
- /* 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(void)
-{
- 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 */
- stor_vsc_initialize(&storvsc_drv_obj->base);
-
- DPRINT_INFO(STORVSC_DRV,
- "max outstanding reqs %u",
- 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->probe = storvsc_probe;
- drv->driver.remove = storvsc_remove;
-
- /* The driver belongs to vmbus */
- ret = vmbus_child_driver_register(&drv->driver);
-
- return ret;
-}
-
-
-static int stor_vsc_on_host_reset(struct hv_device *device)
+static int storvsc_device_alloc(struct scsi_device *sdevice)
{
- 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) {
- DPRINT_ERR(STORVSC, "unable to get stor device..."
- "device being destroyed?");
- 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) {
- DPRINT_ERR(STORVSC, "Unable to send reset packet %p ret %d",
- vstor_packet, ret);
- goto cleanup;
- }
-
- t = wait_for_completion_timeout(&request->wait_event, HZ);
- if (t == 0) {
- ret = -ETIMEDOUT;
- goto cleanup;
- }
-
- DPRINT_INFO(STORVSC, "host adapter reset completed");
-
/*
- * At this point, all outstanding requests in the adapter
- * should have been flushed out and return to us
+ * This enables luns to be located sparsely. Otherwise, we may not
+ * discovered them.
*/
-
-cleanup:
- put_stor_device(device);
- 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 hv_device *device)
+static int storvsc_merge_bvec(struct request_queue *q,
+ struct bvec_merge_data *bmd, struct bio_vec *bvec)
{
- int ret;
- struct storvsc_driver_object *storvsc_drv_obj =
- drv_to_stordrv(device->device.driver);
- 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->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;
-
- host_device_ctx->request_pool =
- kmem_cache_create(dev_name(&device->device),
- sizeof(struct storvsc_cmd_request), 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, (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->device);
- if (ret != 0) {
- DPRINT_ERR(STORVSC_DRV, "unable to add scsi host device");
-
- storvsc_drv_obj->base.dev_rm(device);
-
- 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)
{
- struct storvsc_driver_object *storvsc_drv_obj =
- drv_to_stordrv(device->driver);
- 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;
+ 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
- */
- storvsc_drv_obj->base.dev_rm(device_obj);
+ 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 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;
- struct vmscsi_request *vm_srb;
-
- /* 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);
- }
-
- vm_srb = &request->vstor_packet.vm_srb;
- scmnd->result = vm_srb->scsi_status;
+ int i;
+ struct page *page_buf;
- if (scmnd->result) {
- if (scsi_normalize_sense(scmnd->sense_buffer,
- SCSI_SENSE_BUFFERSIZE, &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 -
- 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_device_ctx->request_pool, cmd_request);
+ kfree(sgl);
}
static int do_bounce_buffer(struct scatterlist *sgl, unsigned int sg_count)
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,
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 */
/* 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);
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)
+{
+ struct Scsi_Host *host = dev_get_drvdata(&dev->device);
+ struct hv_host_device *host_dev =
+ (struct hv_host_device *)host->hostdata;
+
+ /*
+ * Call to the vsc driver to let it know that the device is being
+ * removed
+ */
+ storvsc_dev_remove(dev);
+
+ if (host_dev->request_pool) {
+ kmem_cache_destroy(host_dev->request_pool);
+ host_dev->request_pool = NULL;
+ }
+
+ DPRINT_INFO(STORVSC, "removing host adapter (%p)...", host);
+ scsi_remove_host(host);
+
+ DPRINT_INFO(STORVSC, "releasing host adapter (%p)...", host);
+ scsi_host_put(host);
+ return 0;
+}
+
+
+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;
+
+ /*
+ * 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;
+
+ info[0] = heads;
+ info[1] = sectors_pt;
+ info[2] = (int)cylinders;
+
+ 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;
+ }
+
+ DPRINT_INFO(STORVSC, "host adapter reset completed");
+
+ /*
+ * 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 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;
+ int ret;
+ struct hv_host_device *host_dev =
+ (struct hv_host_device *)scmnd->device->host->hostdata;
+ struct hv_device *dev = host_dev->dev;
- local_irq_save(flags);
+ DPRINT_INFO(STORVSC_DRV, "sdev (%p) dev obj (%p) - host resetting...",
+ scmnd->device, 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); */
+ /* Invokes the vsc to reset the host/bus */
+ ret = storvsc_host_reset(dev);
+ if (ret != 0)
+ return ret;
- if (bounce_addr == 0)
- bounce_addr = (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), KM_IRQ0);
+ DPRINT_INFO(STORVSC_DRV, "sdev (%p) dev obj (%p) - host reseted",
+ scmnd->device, dev);
- while (destlen) {
- src = bounce_addr + bounce_sgl[j].offset;
- srclen = bounce_sgl[j].length - bounce_sgl[j].offset;
+ return ret;
+}
- copylen = min(srclen, destlen);
- memcpy((void *)dest, (void *)src, copylen);
- total_copied += copylen;
- bounce_sgl[j].offset += copylen;
- destlen -= copylen;
- dest += copylen;
+/*
+ * 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 (bounce_sgl[j].offset == bounce_sgl[j].length) {
- /* full */
- kunmap_atomic((void *)bounce_addr, KM_IRQ0);
- j++;
+ if (cmd_request->bounce_sgl_count) {
- /* 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);
- }
- }
+ /* 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);
+ }
- kunmap_atomic((void *)(dest_addr - orig_sgl[i].offset),
- KM_IRQ0);
+ 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);
}
- local_irq_restore(flags);
+ scsi_set_resid(scmnd,
+ request->data_buffer.len -
+ vm_srb->data_transfer_length);
- return total_copied;
+ 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
*/
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 storvsc_driver_object *storvsc_drv_obj =
- drv_to_stordrv(device_ctx->device.driver);
+ 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;
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;
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_zalloc(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;
}
request = &cmd_request->request;
vm_srb = &request->vstor_packet.vm_srb;
- DPRINT_DBG(STORVSC_DRV, "req %p size %d", request, request_size);
/* Build the SRB */
switch (scmnd->sc_data_direction) {
request->on_io_completion = storvsc_commmand_completion;
request->context = cmd_request;/* scmnd; */
- /* request->PortId = scmnd->device->channel; */
- vm_srb->port_number = host_device_ctx->port;
+ 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;
- /* ASSERT(scmnd->cmd_len <= 16); */
vm_srb->cdb_length = scmnd->cmd_len;
memcpy(vm_srb->cdb, scmnd->cmnd, vm_srb->cdb_length);
/* 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;
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] =
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) {
/*
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;
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;
+ /* 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;
+
+ /* 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->device);
+ if (ret != 0) {
+
+ storvsc_dev_remove(device);
+
+ kmem_cache_destroy(host_dev->request_pool);
+ scsi_host_put(host);
+ return -1;
+ }
+ scsi_scan_host(host);
return ret;
}
-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;
+/* The one and only one */
- int sectors_per_track = 0;
- int heads = 0;
- int cylinders = 0;
- int rem = 0;
+static struct storvsc_driver storvsc_drv = {
+ .base.probe = storvsc_probe,
+ .base.remove = storvsc_remove,
+};
- if (total_sectors > (65535 * 16 * 255))
- total_sectors = (65535 * 16 * 255);
- if (total_sectors >= (65535 * 16 * 63)) {
- sectors_per_track = 255;
- heads = 16;
+/*
+ * storvsc_drv_init - StorVsc driver initialization.
+ */
+static int storvsc_drv_init(void)
+{
+ int ret;
+ struct storvsc_driver *storvsc_drv_obj = &storvsc_drv;
+ struct hv_driver *drv = &storvsc_drv.base;
- 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;
+ storvsc_drv_obj->ring_buffer_size = storvsc_ringbuffer_size;
- cylinder_times_heads = total_sectors;
- /* sector_div stores the quotient in cylinder_times_heads */
- rem = sector_div(cylinder_times_heads, sectors_per_track);
+ /* Callback to client driver to complete the initialization */
+ storvsc_initialize(&storvsc_drv_obj->base);
- temp = cylinder_times_heads + 1023;
- /* sector_div stores the quotient in temp */
- rem = sector_div(temp, 1024);
+ if (storvsc_drv_obj->max_outstanding_req_per_channel <
+ STORVSC_MAX_IO_REQUESTS)
+ return -1;
- heads = temp;
+ drv->driver.name = storvsc_drv_obj->base.name;
- if (heads < 4)
- heads = 4;
- if (cylinder_times_heads >= (heads * 1024) || (heads > 16)) {
- sectors_per_track = 31;
- heads = 16;
+ /* The driver belongs to vmbus */
+ ret = vmbus_child_driver_register(&drv->driver);
- cylinder_times_heads = total_sectors;
- /*
- * sector_div stores the quotient in
- * cylinder_times_heads
- */
- rem = sector_div(cylinder_times_heads,
- sectors_per_track);
- }
+ return ret;
+}
- if (cylinder_times_heads >= (heads * 1024)) {
- sectors_per_track = 63;
- heads = 16;
+static int storvsc_drv_exit_cb(struct device *dev, void *data)
+{
+ struct device **curr = (struct device **)data;
+ *curr = dev;
+ return 1; /* stop iterating */
+}
- cylinder_times_heads = total_sectors;
- /*
- * sector_div stores the quotient in
- * cylinder_times_heads
- */
- rem = sector_div(cylinder_times_heads,
- sectors_per_track);
- }
- }
+static void storvsc_drv_exit(void)
+{
+ struct hv_driver *drv = &storvsc_drv.base;
+ struct device *current_dev = NULL;
+ int ret;
- temp = cylinder_times_heads;
- /* sector_div stores the quotient in temp */
- rem = sector_div(temp, heads);
- cylinders = temp;
+ while (1) {
+ current_dev = NULL;
+
+ /* Get the device */
+ ret = driver_for_each_device(&drv->driver, NULL,
+ (void *) ¤t_dev,
+ storvsc_drv_exit_cb);
- info[0] = heads;
- info[1] = sectors_per_track;
- info[2] = cylinders;
- DPRINT_INFO(STORVSC_DRV, "CHS (%d, %d, %d)", cylinders, heads,
- sectors_per_track);
+ if (current_dev == NULL)
+ break;
+
+ /* Initiate removal from the top-down */
+ device_unregister(current_dev);
+ }
- return 0;
+ vmbus_child_driver_unregister(&drv->driver);
+ return;
}
static int __init storvsc_init(void)