diff options
Diffstat (limited to 'drivers/staging/hv')
40 files changed, 4786 insertions, 7629 deletions
diff --git a/drivers/staging/hv/Kconfig b/drivers/staging/hv/Kconfig index d41f380d188f..5e0c9f6c7457 100644 --- a/drivers/staging/hv/Kconfig +++ b/drivers/staging/hv/Kconfig @@ -1,6 +1,6 @@ config HYPERV tristate "Microsoft Hyper-V client drivers" - depends on X86 && m + depends on X86 && ACPI && PCI && m default n help Select this option to run Linux as a Hyper-V client operating @@ -31,7 +31,7 @@ config HYPERV_NET config HYPERV_UTILS tristate "Microsoft Hyper-V Utilities driver" - depends on CONNECTOR + depends on CONNECTOR && NLS default HYPERV help Select this option to enable the Hyper-V Utilities. diff --git a/drivers/staging/hv/Makefile b/drivers/staging/hv/Makefile index abeb2f7ef4e2..30046743a0b4 100644 --- a/drivers/staging/hv/Makefile +++ b/drivers/staging/hv/Makefile @@ -9,6 +9,6 @@ hv_vmbus-y := vmbus_drv.o \ hv.o connection.o channel.o \ channel_mgmt.o ring_buffer.o hv_storvsc-y := storvsc_drv.o storvsc.o -hv_blkvsc-y := blkvsc_drv.o blkvsc.o +hv_blkvsc-y := blkvsc_drv.o storvsc.o hv_netvsc-y := netvsc_drv.o netvsc.o rndis_filter.o hv_utils-y := hv_util.o hv_kvp.o diff --git a/drivers/staging/hv/blkvsc.c b/drivers/staging/hv/blkvsc.c deleted file mode 100644 index 7c8729bc8329..000000000000 --- a/drivers/staging/hv/blkvsc.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang <haiyangz@microsoft.com> - * Hank Janssen <hjanssen@microsoft.com> - * - */ -#include <linux/kernel.h> -#include <linux/mm.h> -#include "hv_api.h" -#include "storvsc.c" - -static const char *g_blk_driver_name = "blkvsc"; - -/* {32412632-86cb-44a2-9b5c-50d1417354f5} */ -static const struct hv_guid g_blk_device_type = { - .data = { - 0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, - 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5 - } -}; - -static int blk_vsc_on_device_add(struct hv_device *device, void *additional_info) -{ - struct storvsc_device_info *device_info; - int ret = 0; - - device_info = (struct storvsc_device_info *)additional_info; - - ret = stor_vsc_on_device_add(device, additional_info); - if (ret != 0) - return ret; - - /* - * 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]; - - device_info->target_id = device->dev_instance.data[5] << 8 | - device->dev_instance.data[4]; - - return ret; -} - -int blk_vsc_initialize(struct hv_driver *driver) -{ - struct storvsc_driver_object *stor_driver; - int ret = 0; - - stor_driver = (struct storvsc_driver_object *)driver; - - /* Make sure we are at least 2 pages since 1 page is used for control */ - /* ASSERT(stor_driver->RingBufferSize >= (PAGE_SIZE << 1)); */ - - driver->name = g_blk_driver_name; - memcpy(&driver->dev_type, &g_blk_device_type, sizeof(struct hv_guid)); - - stor_driver->request_ext_size = sizeof(struct storvsc_request_extension); - - /* - * 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) - */ - stor_driver->max_outstanding_req_per_channel = - ((stor_driver->ring_buffer_size - PAGE_SIZE) / - ALIGN(MAX_MULTIPAGE_BUFFER_PACKET + - sizeof(struct vstor_packet) + sizeof(u64), - sizeof(u64))); - - DPRINT_INFO(BLKVSC, "max io outstd %u", - stor_driver->max_outstanding_req_per_channel); - - /* Setup the dispatch table */ - stor_driver->base.dev_add = blk_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 ret; -} 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); diff --git a/drivers/staging/hv/channel.c b/drivers/staging/hv/channel.c index f7ce7d2494b3..f655e59a9a8f 100644 --- a/drivers/staging/hv/channel.c +++ b/drivers/staging/hv/channel.c @@ -18,15 +18,17 @@ * Haiyang Zhang <haiyangz@microsoft.com> * Hank Janssen <hjanssen@microsoft.com> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/sched.h> #include <linux/wait.h> #include <linux/mm.h> #include <linux/slab.h> #include <linux/module.h> -#include "hv_api.h" -#include "logging.h" -#include "vmbus_private.h" + +#include "hyperv.h" +#include "hyperv_vmbus.h" #define NUM_PAGES_SPANNED(addr, len) \ ((PAGE_ALIGN(addr + len) >> PAGE_SHIFT) - (addr >> PAGE_SHIFT)) @@ -40,37 +42,6 @@ static int create_gpadl_header( static void dump_vmbus_channel(struct vmbus_channel *channel); static void vmbus_setevent(struct vmbus_channel *channel); - -#if 0 -static void DumpMonitorPage(struct hv_monitor_page *MonitorPage) -{ - int i = 0; - int j = 0; - - DPRINT_DBG(VMBUS, "monitorPage - %p, trigger state - %d", - MonitorPage, MonitorPage->trigger_state); - - for (i = 0; i < 4; i++) - DPRINT_DBG(VMBUS, "trigger group (%d) - %llx", i, - MonitorPage->trigger_group[i].as_uint64); - - for (i = 0; i < 4; i++) { - for (j = 0; j < 32; j++) { - DPRINT_DBG(VMBUS, "latency (%d)(%d) - %llx", i, j, - MonitorPage->latency[i][j]); - } - } - for (i = 0; i < 4; i++) { - for (j = 0; j < 32; j++) { - DPRINT_DBG(VMBUS, "param-conn id (%d)(%d) - %d", i, j, - MonitorPage->parameter[i][j].connectionid.asu32); - DPRINT_DBG(VMBUS, "param-flag (%d)(%d) - %d", i, j, - MonitorPage->parameter[i][j].flag_number); - } - } -} -#endif - /* * vmbus_setevent- Trigger an event notification on the specified * channel. @@ -97,28 +68,6 @@ static void vmbus_setevent(struct vmbus_channel *channel) } } -#if 0 -static void VmbusChannelClearEvent(struct vmbus_channel *channel) -{ - struct hv_monitor_page *monitorPage; - - if (Channel->offermsg.monitor_allocated) { - /* Each u32 represents 32 channels */ - sync_clear_bit(Channel->offermsg.child_relid & 31, - (unsigned long *)vmbus_connection.send_int_page + - (Channel->offermsg.child_relid >> 5)); - - monitorPage = (struct hv_monitor_page *) - vmbus_connection.monitor_pages; - monitorPage++; /* Get the child to parent monitor page */ - - sync_clear_bit(Channel->monitor_bit, - (unsigned long *)&monitorPage->trigger_group - [Channel->monitor_grp].Pending); - } -} - -#endif /* * vmbus_get_debug_info -Retrieve various channel debug info */ @@ -160,8 +109,8 @@ void vmbus_get_debug_info(struct vmbus_channel *channel, monitorpage->parameter[monitor_group] [monitor_offset].connectionid.u.id; - ringbuffer_get_debuginfo(&channel->inbound, &debuginfo->inbound); - ringbuffer_get_debuginfo(&channel->outbound, &debuginfo->outbound); + hv_ringbuffer_get_debuginfo(&channel->inbound, &debuginfo->inbound); + hv_ringbuffer_get_debuginfo(&channel->outbound, &debuginfo->outbound); } /* @@ -175,11 +124,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, struct vmbus_channel_msginfo *openInfo = NULL; void *in, *out; unsigned long flags; - int ret, err = 0; - - /* Aligned to page size */ - /* ASSERT(!(SendRingBufferSize & (PAGE_SIZE - 1))); */ - /* ASSERT(!(RecvRingBufferSize & (PAGE_SIZE - 1))); */ + int ret, t, err = 0; newchannel->onchannel_callback = onchannelcallback; newchannel->channel_callback_context = context; @@ -191,7 +136,6 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, if (!out) return -ENOMEM; - /* ASSERT(((unsigned long)out & (PAGE_SIZE-1)) == 0); */ in = (void *)((unsigned long)out + send_ringbuffer_size); @@ -199,13 +143,16 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, newchannel->ringbuffer_pagecount = (send_ringbuffer_size + recv_ringbuffer_size) >> PAGE_SHIFT; - ret = ringbuffer_init(&newchannel->outbound, out, send_ringbuffer_size); + ret = hv_ringbuffer_init( + &newchannel->outbound, out, send_ringbuffer_size); + if (ret != 0) { err = ret; goto errorout; } - ret = ringbuffer_init(&newchannel->inbound, in, recv_ringbuffer_size); + ret = hv_ringbuffer_init( + &newchannel->inbound, in, recv_ringbuffer_size); if (ret != 0) { err = ret; goto errorout; @@ -213,9 +160,6 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, /* Establish the gpadl for the ring buffer */ - DPRINT_DBG(VMBUS, "Establishing ring buffer's gpadl for channel %p...", - newchannel); - newchannel->ringbuffer_gpadlhandle = 0; ret = vmbus_establish_gpadl(newchannel, @@ -229,16 +173,6 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, goto errorout; } - DPRINT_DBG(VMBUS, "channel %p <relid %d gpadl 0x%x send ring %p " - "size %d recv ring %p size %d, downstreamoffset %d>", - newchannel, newchannel->offermsg.child_relid, - newchannel->ringbuffer_gpadlhandle, - newchannel->outbound.ring_buffer, - newchannel->outbound.ring_size, - newchannel->inbound.ring_buffer, - newchannel->inbound.ring_size, - send_ringbuffer_size); - /* Create and init the channel open message */ openInfo = kmalloc(sizeof(*openInfo) + sizeof(struct vmbus_channel_open_channel), @@ -248,7 +182,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, goto errorout; } - init_waitqueue_head(&openInfo->waitevent); + init_completion(&openInfo->waitevent); openMsg = (struct vmbus_channel_open_channel *)openInfo->msg; openMsg->header.msgtype = CHANNELMSG_OPENCHANNEL; @@ -272,30 +206,21 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, &vmbus_connection.chn_msg_list); spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - DPRINT_DBG(VMBUS, "Sending channel open msg..."); - ret = vmbus_post_msg(openMsg, sizeof(struct vmbus_channel_open_channel)); - if (ret != 0) { - DPRINT_ERR(VMBUS, "unable to open channel - %d", ret); + + if (ret != 0) goto Cleanup; - } - openInfo->wait_condition = 0; - wait_event_timeout(openInfo->waitevent, - openInfo->wait_condition, - msecs_to_jiffies(1000)); - if (openInfo->wait_condition == 0) { + t = wait_for_completion_timeout(&openInfo->waitevent, HZ); + if (t == 0) { err = -ETIMEDOUT; goto errorout; } - if (openInfo->response.open_result.status == 0) - DPRINT_INFO(VMBUS, "channel <%p> open success!!", newchannel); - else - DPRINT_INFO(VMBUS, "channel <%p> open failed - %d!!", - newchannel, openInfo->response.open_result.status); + if (openInfo->response.open_result.status) + err = openInfo->response.open_result.status; Cleanup: spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); @@ -303,11 +228,11 @@ Cleanup: spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); kfree(openInfo); - return 0; + return err; errorout: - ringbuffer_cleanup(&newchannel->outbound); - ringbuffer_cleanup(&newchannel->inbound); + hv_ringbuffer_cleanup(&newchannel->outbound); + hv_ringbuffer_cleanup(&newchannel->inbound); free_pages((unsigned long)out, get_order(send_ringbuffer_size + recv_ringbuffer_size)); kfree(openInfo); @@ -326,6 +251,7 @@ static void dump_gpadl_body(struct vmbus_channel_gpadl_body *gpadl, u32 len) pfncount = (len - sizeof(struct vmbus_channel_gpadl_body)) / sizeof(u64); + DPRINT_DBG(VMBUS, "gpadl body - len %d pfn count %d", len, pfncount); for (i = 0; i < pfncount; i++) @@ -377,9 +303,6 @@ static int create_gpadl_header(void *kbuffer, u32 size, int pfnsum, pfncount, pfnleft, pfncurr, pfnsize; - /* ASSERT((kbuffer & (PAGE_SIZE-1)) == 0); */ - /* ASSERT((Size & (PAGE_SIZE-1)) == 0); */ - pagecount = size >> PAGE_SHIFT; pfn = virt_to_phys(kbuffer) >> PAGE_SHIFT; @@ -508,6 +431,7 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, u32 next_gpadl_handle; unsigned long flags; int ret = 0; + int t; next_gpadl_handle = atomic_read(&vmbus_connection.next_gpadl_handle); atomic_inc(&vmbus_connection.next_gpadl_handle); @@ -516,7 +440,7 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, if (ret) return ret; - init_waitqueue_head(&msginfo->waitevent); + init_completion(&msginfo->waitevent); gpadlmsg = (struct vmbus_channel_gpadl_header *)msginfo->msg; gpadlmsg->header.msgtype = CHANNELMSG_GPADL_HEADER; @@ -530,19 +454,11 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, &vmbus_connection.chn_msg_list); spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - DPRINT_DBG(VMBUS, "buffer %p, size %d msg cnt %d", - kbuffer, size, msgcount); - DPRINT_DBG(VMBUS, "Sending GPADL Header - len %zd", - msginfo->msgsize - sizeof(*msginfo)); - - msginfo->wait_condition = 0; ret = vmbus_post_msg(gpadlmsg, msginfo->msgsize - sizeof(*msginfo)); - if (ret != 0) { - DPRINT_ERR(VMBUS, "Unable to open channel - %d", ret); + if (ret != 0) goto Cleanup; - } if (msgcount > 1) { list_for_each(curr, &msginfo->submsglist) { @@ -556,10 +472,6 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, CHANNELMSG_GPADL_BODY; gpadl_body->gpadl = next_gpadl_handle; - DPRINT_DBG(VMBUS, "Sending GPADL Body - len %zd", - submsginfo->msgsize - - sizeof(*submsginfo)); - dump_gpadl_body(gpadl_body, submsginfo->msgsize - sizeof(*submsginfo)); ret = vmbus_post_msg(gpadl_body, @@ -570,19 +482,11 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, } } - wait_event_timeout(msginfo->waitevent, - msginfo->wait_condition, - msecs_to_jiffies(1000)); - BUG_ON(msginfo->wait_condition == 0); + t = wait_for_completion_timeout(&msginfo->waitevent, HZ); + BUG_ON(t == 0); /* At this point, we received the gpadl created msg */ - DPRINT_DBG(VMBUS, "Received GPADL created " - "(relid %d, status %d handle %x)", - channel->offermsg.child_relid, - msginfo->response.gpadl_created.creation_status, - gpadlmsg->gpadl); - *gpadl_handle = gpadlmsg->gpadl; Cleanup: @@ -603,7 +507,7 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) struct vmbus_channel_gpadl_teardown *msg; struct vmbus_channel_msginfo *info; unsigned long flags; - int ret; + int ret, t; /* ASSERT(gpadl_handle != 0); */ @@ -612,7 +516,7 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) if (!info) return -ENOMEM; - init_waitqueue_head(&info->waitevent); + init_completion(&info->waitevent); msg = (struct vmbus_channel_gpadl_teardown *)info->msg; @@ -624,14 +528,12 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) list_add_tail(&info->msglistentry, &vmbus_connection.chn_msg_list); spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - info->wait_condition = 0; ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_gpadl_teardown)); BUG_ON(ret != 0); - wait_event_timeout(info->waitevent, - info->wait_condition, msecs_to_jiffies(1000)); - BUG_ON(info->wait_condition == 0); + t = wait_for_completion_timeout(&info->waitevent, HZ); + BUG_ON(t == 0); /* Received a torndown response */ spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); @@ -681,8 +583,8 @@ void vmbus_close(struct vmbus_channel *channel) /* TODO: Send a msg to release the childRelId */ /* Cleanup the ring buffers for this channel */ - ringbuffer_cleanup(&channel->outbound); - ringbuffer_cleanup(&channel->inbound); + hv_ringbuffer_cleanup(&channel->outbound); + hv_ringbuffer_cleanup(&channel->inbound); free_pages((unsigned long)channel->ringbuffer_pages, get_order(channel->ringbuffer_pagecount * PAGE_SIZE)); @@ -730,13 +632,8 @@ int vmbus_sendpacket(struct vmbus_channel *channel, const void *buffer, u64 aligned_data = 0; int ret; - DPRINT_DBG(VMBUS, "channel %p buffer %p len %d", - channel, buffer, bufferlen); - dump_vmbus_channel(channel); - /* ASSERT((packetLenAligned - packetLen) < sizeof(u64)); */ - /* Setup the descriptor */ desc.type = type; /* VmbusPacketTypeDataInBand; */ desc.flags = flags; /* VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; */ @@ -751,10 +648,10 @@ int vmbus_sendpacket(struct vmbus_channel *channel, const void *buffer, sg_set_buf(&bufferlist[2], &aligned_data, packetlen_aligned - packetlen); - ret = ringbuffer_write(&channel->outbound, bufferlist, 3); + ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); /* TODO: We should determine if this is optional */ - if (ret == 0 && !get_ringbuffer_interrupt_mask(&channel->outbound)) + if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) vmbus_setevent(channel); return ret; @@ -794,8 +691,6 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, packetlen = descsize + bufferlen; packetlen_aligned = ALIGN(packetlen, sizeof(u64)); - /* ASSERT((packetLenAligned - packetLen) < sizeof(u64)); */ - /* Setup the descriptor */ desc.type = VM_PKT_DATA_USING_GPA_DIRECT; desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; @@ -816,10 +711,10 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, sg_set_buf(&bufferlist[2], &aligned_data, packetlen_aligned - packetlen); - ret = ringbuffer_write(&channel->outbound, bufferlist, 3); + ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); /* TODO: We should determine if this is optional */ - if (ret == 0 && !get_ringbuffer_interrupt_mask(&channel->outbound)) + if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) vmbus_setevent(channel); return ret; @@ -846,10 +741,6 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, dump_vmbus_channel(channel); - DPRINT_DBG(VMBUS, "data buffer - offset %u len %u pfn count %u", - multi_pagebuffer->offset, - multi_pagebuffer->len, pfncount); - if ((pfncount < 0) || (pfncount > MAX_MULTIPAGE_BUFFER_COUNT)) return -EINVAL; @@ -863,7 +754,6 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, packetlen = descsize + bufferlen; packetlen_aligned = ALIGN(packetlen, sizeof(u64)); - /* ASSERT((packetLenAligned - packetLen) < sizeof(u64)); */ /* Setup the descriptor */ desc.type = VM_PKT_DATA_USING_GPA_DIRECT; @@ -885,10 +775,10 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, sg_set_buf(&bufferlist[2], &aligned_data, packetlen_aligned - packetlen); - ret = ringbuffer_write(&channel->outbound, bufferlist, 3); + ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); /* TODO: We should determine if this is optional */ - if (ret == 0 && !get_ringbuffer_interrupt_mask(&channel->outbound)) + if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) vmbus_setevent(channel); return ret; @@ -922,32 +812,22 @@ int vmbus_recvpacket(struct vmbus_channel *channel, void *buffer, spin_lock_irqsave(&channel->inbound_lock, flags); - ret = ringbuffer_peek(&channel->inbound, &desc, + ret = hv_ringbuffer_peek(&channel->inbound, &desc, sizeof(struct vmpacket_descriptor)); if (ret != 0) { spin_unlock_irqrestore(&channel->inbound_lock, flags); - - /* DPRINT_DBG(VMBUS, "nothing to read!!"); */ return 0; } - /* VmbusChannelClearEvent(Channel); */ - packetlen = desc.len8 << 3; userlen = packetlen - (desc.offset8 << 3); - /* ASSERT(userLen > 0); */ - - DPRINT_DBG(VMBUS, "packet received on channel %p relid %d <type %d " - "flag %d tid %llx pktlen %d datalen %d> ", - channel, channel->offermsg.child_relid, desc.type, - desc.flags, desc.trans_id, packetlen, userlen); *buffer_actual_len = userlen; if (userlen > bufferlen) { spin_unlock_irqrestore(&channel->inbound_lock, flags); - DPRINT_ERR(VMBUS, "buffer too small - got %d needs %d", + pr_err("Buffer too small - got %d needs %d\n", bufferlen, userlen); return -1; } @@ -955,7 +835,7 @@ int vmbus_recvpacket(struct vmbus_channel *channel, void *buffer, *requestid = desc.trans_id; /* Copy over the packet to the user buffer */ - ret = ringbuffer_read(&channel->inbound, buffer, userlen, + ret = hv_ringbuffer_read(&channel->inbound, buffer, userlen, (desc.offset8 << 3)); spin_unlock_irqrestore(&channel->inbound_lock, flags); @@ -982,39 +862,32 @@ int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer, spin_lock_irqsave(&channel->inbound_lock, flags); - ret = ringbuffer_peek(&channel->inbound, &desc, + ret = hv_ringbuffer_peek(&channel->inbound, &desc, sizeof(struct vmpacket_descriptor)); if (ret != 0) { spin_unlock_irqrestore(&channel->inbound_lock, flags); - - /* DPRINT_DBG(VMBUS, "nothing to read!!"); */ return 0; } - /* VmbusChannelClearEvent(Channel); */ packetlen = desc.len8 << 3; userlen = packetlen - (desc.offset8 << 3); - DPRINT_DBG(VMBUS, "packet received on channel %p relid %d <type %d " - "flag %d tid %llx pktlen %d datalen %d> ", - channel, channel->offermsg.child_relid, desc.type, - desc.flags, desc.trans_id, packetlen, userlen); - *buffer_actual_len = packetlen; if (packetlen > bufferlen) { spin_unlock_irqrestore(&channel->inbound_lock, flags); - DPRINT_ERR(VMBUS, "buffer too small - needed %d bytes but " - "got space for only %d bytes", packetlen, bufferlen); + pr_err("Buffer too small - needed %d bytes but " + "got space for only %d bytes\n", + packetlen, bufferlen); return -2; } *requestid = desc.trans_id; /* Copy over the entire packet to the user buffer */ - ret = ringbuffer_read(&channel->inbound, buffer, packetlen, 0); + ret = hv_ringbuffer_read(&channel->inbound, buffer, packetlen, 0); spin_unlock_irqrestore(&channel->inbound_lock, flags); return 0; @@ -1027,7 +900,6 @@ EXPORT_SYMBOL_GPL(vmbus_recvpacket_raw); void vmbus_onchannel_event(struct vmbus_channel *channel) { dump_vmbus_channel(channel); - /* ASSERT(Channel->OnChannelCallback); */ channel->onchannel_callback(channel->channel_callback_context); @@ -1051,6 +923,6 @@ void vmbus_ontimer(unsigned long data) static void dump_vmbus_channel(struct vmbus_channel *channel) { DPRINT_DBG(VMBUS, "Channel (%d)", channel->offermsg.child_relid); - dump_ring_info(&channel->outbound, "Outbound "); - dump_ring_info(&channel->inbound, "Inbound "); + hv_dump_ring_info(&channel->outbound, "Outbound "); + hv_dump_ring_info(&channel->inbound, "Inbound "); } diff --git a/drivers/staging/hv/channel.h b/drivers/staging/hv/channel.h deleted file mode 100644 index de4f867de171..000000000000 --- a/drivers/staging/hv/channel.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang <haiyangz@microsoft.com> - * Hank Janssen <hjanssen@microsoft.com> - * - */ - - -#ifndef _CHANNEL_H_ -#define _CHANNEL_H_ - -#include "channel_mgmt.h" - -/* The format must be the same as struct vmdata_gpa_direct */ -struct vmbus_channel_packet_page_buffer { - u16 type; - u16 dataoffset8; - u16 length8; - u16 flags; - u64 transactionid; - u32 reserved; - u32 rangecount; - struct hv_page_buffer range[MAX_PAGE_BUFFER_COUNT]; -} __packed; - -/* The format must be the same as struct vmdata_gpa_direct */ -struct vmbus_channel_packet_multipage_buffer { - u16 type; - u16 dataoffset8; - u16 length8; - u16 flags; - u64 transactionid; - u32 reserved; - u32 rangecount; /* Always 1 in this case */ - struct hv_multipage_buffer range; -} __packed; - - -extern int vmbus_open(struct vmbus_channel *channel, - u32 send_ringbuffersize, - u32 recv_ringbuffersize, - void *userdata, - u32 userdatalen, - void(*onchannel_callback)(void *context), - void *context); - -extern void vmbus_close(struct vmbus_channel *channel); - -extern int vmbus_sendpacket(struct vmbus_channel *channel, - const void *buffer, - u32 bufferLen, - u64 requestid, - enum vmbus_packet_type type, - u32 flags); - -extern int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, - struct hv_page_buffer pagebuffers[], - u32 pagecount, - void *buffer, - u32 bufferlen, - u64 requestid); - -extern int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, - struct hv_multipage_buffer *mpb, - void *buffer, - u32 bufferlen, - u64 requestid); - -extern int vmbus_establish_gpadl(struct vmbus_channel *channel, - void *kbuffer, - u32 size, - u32 *gpadl_handle); - -extern int vmbus_teardown_gpadl(struct vmbus_channel *channel, - u32 gpadl_handle); - -extern int vmbus_recvpacket(struct vmbus_channel *channel, - void *buffer, - u32 bufferlen, - u32 *buffer_actual_len, - u64 *requestid); - -extern int vmbus_recvpacket_raw(struct vmbus_channel *channel, - void *buffer, - u32 bufferlen, - u32 *buffer_actual_len, - u64 *requestid); - -extern void vmbus_onchannel_event(struct vmbus_channel *channel); - -extern void vmbus_get_debug_info(struct vmbus_channel *channel, - struct vmbus_channel_debug_info *debug); - -extern void vmbus_ontimer(unsigned long data); - -#endif /* _CHANNEL_H_ */ diff --git a/drivers/staging/hv/channel_mgmt.c b/drivers/staging/hv/channel_mgmt.c index 06b573227e8d..957d61ee4ceb 100644 --- a/drivers/staging/hv/channel_mgmt.c +++ b/drivers/staging/hv/channel_mgmt.c @@ -18,6 +18,8 @@ * Haiyang Zhang <haiyangz@microsoft.com> * Hank Janssen <hjanssen@microsoft.com> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/sched.h> #include <linux/wait.h> @@ -26,21 +28,20 @@ #include <linux/list.h> #include <linux/module.h> #include <linux/completion.h> -#include "hv_api.h" -#include "logging.h" -#include "vmbus_private.h" -#include "utils.h" + +#include "hyperv.h" +#include "hyperv_vmbus.h" struct vmbus_channel_message_table_entry { - enum vmbus_channel_message_type messageType; - void (*messageHandler)(struct vmbus_channel_message_header *msg); + enum vmbus_channel_message_type message_type; + void (*message_handler)(struct vmbus_channel_message_header *msg); }; #define MAX_MSG_TYPES 4 #define MAX_NUM_DEVICE_CLASSES_SUPPORTED 8 static const struct hv_guid - gSupportedDeviceClasses[MAX_NUM_DEVICE_CLASSES_SUPPORTED] = { + supported_device_classes[MAX_NUM_DEVICE_CLASSES_SUPPORTED] = { /* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */ /* Storage - SCSI */ { @@ -180,6 +181,24 @@ void chn_cb_negotiate(void *context) struct icmsg_hdr *icmsghdrp; struct icmsg_negotiate *negop = NULL; + if (channel->util_index >= 0) { + /* + * This is a properly initialized util channel. + * Route this callback appropriately and setup state + * so that we don't need to reroute again. + */ + if (hv_cb_utils[channel->util_index].callback != NULL) { + /* + * The util driver has established a handler for + * this service; do the magic. + */ + channel->onchannel_callback = + hv_cb_utils[channel->util_index].callback; + (hv_cb_utils[channel->util_index].callback)(channel); + return; + } + } + buflen = PAGE_SIZE; buf = kmalloc(buflen, GFP_ATOMIC); @@ -216,7 +235,6 @@ struct hyperv_service_callback hv_cb_utils[MAX_MSG_TYPES] = { 0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49, 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB }, - .callback = chn_cb_negotiate, .log_msg = "Shutdown channel functionality initialized" }, @@ -228,7 +246,6 @@ struct hyperv_service_callback hv_cb_utils[MAX_MSG_TYPES] = { 0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf }, - .callback = chn_cb_negotiate, .log_msg = "Timesync channel functionality initialized" }, /* {57164f39-9115-4e78-ab55-382f3bd5422d} */ @@ -239,7 +256,6 @@ struct hyperv_service_callback hv_cb_utils[MAX_MSG_TYPES] = { 0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d }, - .callback = chn_cb_negotiate, .log_msg = "Heartbeat channel functionality initialized" }, /* {A9A0F4E7-5A45-4d96-B827-8A841E8C03E6} */ @@ -249,7 +265,6 @@ struct hyperv_service_callback hv_cb_utils[MAX_MSG_TYPES] = { 0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6 }, - .callback = chn_cb_negotiate, .log_msg = "KVP channel functionality initialized" }, }; @@ -290,9 +305,7 @@ static void release_channel(struct work_struct *work) struct vmbus_channel, work); - DPRINT_DBG(VMBUS, "releasing channel (%p)", channel); destroy_workqueue(channel->controlwq); - DPRINT_DBG(VMBUS, "channel released (%p)", channel); kfree(channel); } @@ -314,22 +327,6 @@ void free_channel(struct vmbus_channel *channel) } -DECLARE_COMPLETION(hv_channel_ready); - -/* - * Count initialized channels, and ensure all channels are ready when hv_vmbus - * module loading completes. - */ -static void count_hv_channel(void) -{ - static int counter; - unsigned long flags; - - spin_lock_irqsave(&vmbus_connection.channel_lock, flags); - if (++counter == MAX_MSG_TYPES) - complete(&hv_channel_ready); - spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); -} /* * vmbus_process_rescind_offer - @@ -384,8 +381,6 @@ static void vmbus_process_offer(struct work_struct *work) spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); if (!fnew) { - DPRINT_DBG(VMBUS, "Ignoring duplicate offer for relid (%d)", - newchannel->offermsg.child_relid); free_channel(newchannel); return; } @@ -400,9 +395,6 @@ static void vmbus_process_offer(struct work_struct *work) &newchannel->offermsg.offer.if_instance, newchannel); - DPRINT_DBG(VMBUS, "child device object allocated - %p", - newchannel->device_obj); - /* * Add the new device to the bus. This will kick off device-driver * binding which eventually invokes the device driver's AddDevice() @@ -410,8 +402,7 @@ static void vmbus_process_offer(struct work_struct *work) */ ret = vmbus_child_device_register(newchannel->device_obj); if (ret != 0) { - DPRINT_ERR(VMBUS, - "unable to add child device object (relid %d)", + pr_err("unable to add child device object (relid %d)\n", newchannel->offermsg.child_relid); spin_lock_irqsave(&vmbus_connection.channel_lock, flags); @@ -426,6 +417,7 @@ static void vmbus_process_offer(struct work_struct *work) * can cleanup properly */ newchannel->state = CHANNEL_OPEN_STATE; + newchannel->util_index = -1; /* Invalid index */ /* Open IC channels */ for (cnt = 0; cnt < MAX_MSG_TYPES; cnt++) { @@ -434,12 +426,13 @@ static void vmbus_process_offer(struct work_struct *work) sizeof(struct hv_guid)) == 0 && vmbus_open(newchannel, 2 * PAGE_SIZE, 2 * PAGE_SIZE, NULL, 0, - hv_cb_utils[cnt].callback, + chn_cb_negotiate, newchannel) == 0) { hv_cb_utils[cnt].channel = newchannel; - DPRINT_INFO(VMBUS, "%s", - hv_cb_utils[cnt].log_msg); - count_hv_channel(); + newchannel->util_index = cnt; + + pr_info("%s\n", hv_cb_utils[cnt].log_msg); + } } } @@ -464,55 +457,26 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr) offer = (struct vmbus_channel_offer_channel *)hdr; for (i = 0; i < MAX_NUM_DEVICE_CLASSES_SUPPORTED; i++) { if (memcmp(&offer->offer.if_type, - &gSupportedDeviceClasses[i], sizeof(struct hv_guid)) == 0) { + &supported_device_classes[i], + sizeof(struct hv_guid)) == 0) { fsupported = 1; break; } } - if (!fsupported) { - DPRINT_DBG(VMBUS, "Ignoring channel offer notification for " - "child relid %d", offer->child_relid); + if (!fsupported) return; - } guidtype = &offer->offer.if_type; guidinstance = &offer->offer.if_instance; - DPRINT_INFO(VMBUS, "Channel offer notification - " - "child relid %d monitor id %d allocated %d, " - "type {%02x%02x%02x%02x-%02x%02x-%02x%02x-" - "%02x%02x%02x%02x%02x%02x%02x%02x} " - "instance {%02x%02x%02x%02x-%02x%02x-%02x%02x-" - "%02x%02x%02x%02x%02x%02x%02x%02x}", - offer->child_relid, offer->monitorid, - offer->monitor_allocated, - guidtype->data[3], guidtype->data[2], - guidtype->data[1], guidtype->data[0], - guidtype->data[5], guidtype->data[4], - guidtype->data[7], guidtype->data[6], - guidtype->data[8], guidtype->data[9], - guidtype->data[10], guidtype->data[11], - guidtype->data[12], guidtype->data[13], - guidtype->data[14], guidtype->data[15], - guidinstance->data[3], guidinstance->data[2], - guidinstance->data[1], guidinstance->data[0], - guidinstance->data[5], guidinstance->data[4], - guidinstance->data[7], guidinstance->data[6], - guidinstance->data[8], guidinstance->data[9], - guidinstance->data[10], guidinstance->data[11], - guidinstance->data[12], guidinstance->data[13], - guidinstance->data[14], guidinstance->data[15]); - /* Allocate the channel object and save this offer. */ newchannel = alloc_channel(); if (!newchannel) { - DPRINT_ERR(VMBUS, "unable to allocate channel object"); + pr_err("Unable to allocate channel object\n"); return; } - DPRINT_DBG(VMBUS, "channel object allocated - %p", newchannel); - memcpy(&newchannel->offermsg, offer, sizeof(struct vmbus_channel_offer_channel)); newchannel->monitor_grp = (u8)offer->monitorid / 32; @@ -535,11 +499,10 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) rescind = (struct vmbus_channel_rescind_offer *)hdr; channel = relid2channel(rescind->child_relid); - if (channel == NULL) { - DPRINT_DBG(VMBUS, "channel not found for relId %d", - rescind->child_relid); + + if (channel == NULL) + /* Just return here, no channel found */ return; - } /* work is initialized for vmbus_process_rescind_offer() from * vmbus_process_offer() where the channel got created */ @@ -573,7 +536,6 @@ static void vmbus_onopen_result(struct vmbus_channel_message_header *hdr) unsigned long flags; result = (struct vmbus_channel_open_result *)hdr; - DPRINT_DBG(VMBUS, "vmbus open result - %d", result->status); /* * Find the open msg, copy the result and signal/unblock the wait event @@ -592,9 +554,9 @@ static void vmbus_onopen_result(struct vmbus_channel_message_header *hdr) openmsg->openid == result->openid) { memcpy(&msginfo->response.open_result, result, - sizeof(struct vmbus_channel_open_result)); - msginfo->wait_condition = 1; - wake_up(&msginfo->waitevent); + sizeof( + struct vmbus_channel_open_result)); + complete(&msginfo->waitevent); break; } } @@ -618,8 +580,6 @@ static void vmbus_ongpadl_created(struct vmbus_channel_message_header *hdr) unsigned long flags; gpadlcreated = (struct vmbus_channel_gpadl_created *)hdr; - DPRINT_DBG(VMBUS, "vmbus gpadl created result - %d", - gpadlcreated->creation_status); /* * Find the establish msg, copy the result and signal/unblock the wait @@ -641,9 +601,9 @@ static void vmbus_ongpadl_created(struct vmbus_channel_message_header *hdr) (gpadlcreated->gpadl == gpadlheader->gpadl)) { memcpy(&msginfo->response.gpadl_created, gpadlcreated, - sizeof(struct vmbus_channel_gpadl_created)); - msginfo->wait_condition = 1; - wake_up(&msginfo->waitevent); + sizeof( + struct vmbus_channel_gpadl_created)); + complete(&msginfo->waitevent); break; } } @@ -686,9 +646,9 @@ static void vmbus_ongpadl_torndown( if (gpadl_torndown->gpadl == gpadl_teardown->gpadl) { memcpy(&msginfo->response.gpadl_torndown, gpadl_torndown, - sizeof(struct vmbus_channel_gpadl_torndown)); - msginfo->wait_condition = 1; - wake_up(&msginfo->waitevent); + sizeof( + struct vmbus_channel_gpadl_torndown)); + complete(&msginfo->waitevent); break; } } @@ -727,8 +687,7 @@ static void vmbus_onversion_response( memcpy(&msginfo->response.version_response, version_response, sizeof(struct vmbus_channel_version_response)); - msginfo->wait_condition = 1; - wake_up(&msginfo->waitevent); + complete(&msginfo->waitevent); } } spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); @@ -736,7 +695,7 @@ static void vmbus_onversion_response( /* Channel message dispatch table */ static struct vmbus_channel_message_table_entry - gChannelMessageTable[CHANNELMSG_COUNT] = { + channel_message_table[CHANNELMSG_COUNT] = { {CHANNELMSG_INVALID, NULL}, {CHANNELMSG_OFFERCHANNEL, vmbus_onoffer}, {CHANNELMSG_RESCIND_CHANNELOFFER, vmbus_onoffer_rescind}, @@ -770,22 +729,18 @@ void vmbus_onmessage(void *context) hdr = (struct vmbus_channel_message_header *)msg->u.payload; size = msg->header.payload_size; - DPRINT_DBG(VMBUS, "message type %d size %d", hdr->msgtype, size); - if (hdr->msgtype >= CHANNELMSG_COUNT) { - DPRINT_ERR(VMBUS, - "Received invalid channel message type %d size %d", + pr_err("Received invalid channel message type %d size %d\n", hdr->msgtype, size); print_hex_dump_bytes("", DUMP_PREFIX_NONE, (unsigned char *)msg->u.payload, size); return; } - if (gChannelMessageTable[hdr->msgtype].messageHandler) - gChannelMessageTable[hdr->msgtype].messageHandler(hdr); + if (channel_message_table[hdr->msgtype].message_handler) + channel_message_table[hdr->msgtype].message_handler(hdr); else - DPRINT_ERR(VMBUS, "Unhandled channel message type %d", - hdr->msgtype); + pr_err("Unhandled channel message type %d\n", hdr->msgtype); } /* @@ -795,7 +750,7 @@ int vmbus_request_offers(void) { struct vmbus_channel_message_header *msg; struct vmbus_channel_msginfo *msginfo; - int ret; + int ret, t; msginfo = kmalloc(sizeof(*msginfo) + sizeof(struct vmbus_channel_message_header), @@ -803,7 +758,7 @@ int vmbus_request_offers(void) if (!msginfo) return -ENOMEM; - init_waitqueue_head(&msginfo->waitevent); + init_completion(&msginfo->waitevent); msg = (struct vmbus_channel_message_header *)msginfo->msg; @@ -813,15 +768,13 @@ int vmbus_request_offers(void) ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_message_header)); if (ret != 0) { - DPRINT_ERR(VMBUS, "Unable to request offers - %d", ret); + pr_err("Unable to request offers - %d\n", ret); goto cleanup; } - msginfo->wait_condition = 0; - wait_event_timeout(msginfo->waitevent, msginfo->wait_condition, - msecs_to_jiffies(1000)); - if (msginfo->wait_condition == 0) { + t = wait_for_completion_timeout(&msginfo->waitevent, HZ); + if (t == 0) { ret = -ETIMEDOUT; goto cleanup; } @@ -834,38 +787,4 @@ cleanup: return ret; } -/* - * vmbus_release_unattached_channels - Release channels that are - * unattached/unconnected ie (no drivers associated) - */ -void vmbus_release_unattached_channels(void) -{ - struct vmbus_channel *channel, *pos; - struct vmbus_channel *start = NULL; - unsigned long flags; - - spin_lock_irqsave(&vmbus_connection.channel_lock, flags); - - list_for_each_entry_safe(channel, pos, &vmbus_connection.chn_list, - listentry) { - if (channel == start) - break; - - if (!channel->device_obj->drv) { - list_del(&channel->listentry); - DPRINT_INFO(VMBUS, - "Releasing unattached device object %p", - channel->device_obj); - - vmbus_child_device_unregister(channel->device_obj); - free_channel(channel); - } else { - if (!start) - start = channel; - } - } - - spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); -} - /* eof */ diff --git a/drivers/staging/hv/channel_mgmt.h b/drivers/staging/hv/channel_mgmt.h deleted file mode 100644 index 96f74e2a3c7f..000000000000 --- a/drivers/staging/hv/channel_mgmt.h +++ /dev/null @@ -1,320 +0,0 @@ -/* - * - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang <haiyangz@microsoft.com> - * Hank Janssen <hjanssen@microsoft.com> - * - */ - - -#ifndef _CHANNEL_MGMT_H_ -#define _CHANNEL_MGMT_H_ - -#include <linux/list.h> -#include <linux/timer.h> -#include <linux/workqueue.h> -#include "ring_buffer.h" -#include "vmbus_channel_interface.h" -#include "vmbus_packet_format.h" - -/* Version 1 messages */ -enum vmbus_channel_message_type { - CHANNELMSG_INVALID = 0, - CHANNELMSG_OFFERCHANNEL = 1, - CHANNELMSG_RESCIND_CHANNELOFFER = 2, - CHANNELMSG_REQUESTOFFERS = 3, - CHANNELMSG_ALLOFFERS_DELIVERED = 4, - CHANNELMSG_OPENCHANNEL = 5, - CHANNELMSG_OPENCHANNEL_RESULT = 6, - CHANNELMSG_CLOSECHANNEL = 7, - CHANNELMSG_GPADL_HEADER = 8, - CHANNELMSG_GPADL_BODY = 9, - CHANNELMSG_GPADL_CREATED = 10, - CHANNELMSG_GPADL_TEARDOWN = 11, - CHANNELMSG_GPADL_TORNDOWN = 12, - CHANNELMSG_RELID_RELEASED = 13, - CHANNELMSG_INITIATE_CONTACT = 14, - CHANNELMSG_VERSION_RESPONSE = 15, - CHANNELMSG_UNLOAD = 16, -#ifdef VMBUS_FEATURE_PARENT_OR_PEER_MEMORY_MAPPED_INTO_A_CHILD - CHANNELMSG_VIEWRANGE_ADD = 17, - CHANNELMSG_VIEWRANGE_REMOVE = 18, -#endif - CHANNELMSG_COUNT -}; - -struct vmbus_channel_message_header { - enum vmbus_channel_message_type msgtype; - u32 padding; -} __packed; - -/* Query VMBus Version parameters */ -struct vmbus_channel_query_vmbus_version { - struct vmbus_channel_message_header header; - u32 version; -} __packed; - -/* VMBus Version Supported parameters */ -struct vmbus_channel_version_supported { - struct vmbus_channel_message_header header; - bool version_supported; -} __packed; - -/* Offer Channel parameters */ -struct vmbus_channel_offer_channel { - struct vmbus_channel_message_header header; - struct vmbus_channel_offer offer; - u32 child_relid; - u8 monitorid; - bool monitor_allocated; -} __packed; - -/* Rescind Offer parameters */ -struct vmbus_channel_rescind_offer { - struct vmbus_channel_message_header header; - u32 child_relid; -} __packed; - -/* - * Request Offer -- no parameters, SynIC message contains the partition ID - * Set Snoop -- no parameters, SynIC message contains the partition ID - * Clear Snoop -- no parameters, SynIC message contains the partition ID - * All Offers Delivered -- no parameters, SynIC message contains the partition - * ID - * Flush Client -- no parameters, SynIC message contains the partition ID - */ - -/* Open Channel parameters */ -struct vmbus_channel_open_channel { - struct vmbus_channel_message_header header; - - /* Identifies the specific VMBus channel that is being opened. */ - u32 child_relid; - - /* ID making a particular open request at a channel offer unique. */ - u32 openid; - - /* GPADL for the channel's ring buffer. */ - u32 ringbuffer_gpadlhandle; - - /* GPADL for the channel's server context save area. */ - u32 server_contextarea_gpadlhandle; - - /* - * The upstream ring buffer begins at offset zero in the memory - * described by RingBufferGpadlHandle. The downstream ring buffer - * follows it at this offset (in pages). - */ - u32 downstream_ringbuffer_pageoffset; - - /* User-specific data to be passed along to the server endpoint. */ - unsigned char userdata[MAX_USER_DEFINED_BYTES]; -} __packed; - -/* Open Channel Result parameters */ -struct vmbus_channel_open_result { - struct vmbus_channel_message_header header; - u32 child_relid; - u32 openid; - u32 status; -} __packed; - -/* Close channel parameters; */ -struct vmbus_channel_close_channel { - struct vmbus_channel_message_header header; - u32 child_relid; -} __packed; - -/* Channel Message GPADL */ -#define GPADL_TYPE_RING_BUFFER 1 -#define GPADL_TYPE_SERVER_SAVE_AREA 2 -#define GPADL_TYPE_TRANSACTION 8 - -/* - * The number of PFNs in a GPADL message is defined by the number of - * pages that would be spanned by ByteCount and ByteOffset. If the - * implied number of PFNs won't fit in this packet, there will be a - * follow-up packet that contains more. - */ -struct vmbus_channel_gpadl_header { - struct vmbus_channel_message_header header; - u32 child_relid; - u32 gpadl; - u16 range_buflen; - u16 rangecount; - struct gpa_range range[0]; -} __packed; - -/* This is the followup packet that contains more PFNs. */ -struct vmbus_channel_gpadl_body { - struct vmbus_channel_message_header header; - u32 msgnumber; - u32 gpadl; - u64 pfn[0]; -} __packed; - -struct vmbus_channel_gpadl_created { - struct vmbus_channel_message_header header; - u32 child_relid; - u32 gpadl; - u32 creation_status; -} __packed; - -struct vmbus_channel_gpadl_teardown { - struct vmbus_channel_message_header header; - u32 child_relid; - u32 gpadl; -} __packed; - -struct vmbus_channel_gpadl_torndown { - struct vmbus_channel_message_header header; - u32 gpadl; -} __packed; - -#ifdef VMBUS_FEATURE_PARENT_OR_PEER_MEMORY_MAPPED_INTO_A_CHILD -struct vmbus_channel_view_range_add { - struct vmbus_channel_message_header header; - PHYSICAL_ADDRESS viewrange_base; - u64 viewrange_length; - u32 child_relid; -} __packed; - -struct vmbus_channel_view_range_remove { - struct vmbus_channel_message_header header; - PHYSICAL_ADDRESS viewrange_base; - u32 child_relid; -} __packed; -#endif - -struct vmbus_channel_relid_released { - struct vmbus_channel_message_header header; - u32 child_relid; -} __packed; - -struct vmbus_channel_initiate_contact { - struct vmbus_channel_message_header header; - u32 vmbus_version_requested; - u32 padding2; - u64 interrupt_page; - u64 monitor_page1; - u64 monitor_page2; -} __packed; - -struct vmbus_channel_version_response { - struct vmbus_channel_message_header header; - bool version_supported; -} __packed; - -enum vmbus_channel_state { - CHANNEL_OFFER_STATE, - CHANNEL_OPENING_STATE, - CHANNEL_OPEN_STATE, -}; - -struct vmbus_channel { - struct list_head listentry; - - struct hv_device *device_obj; - - struct timer_list poll_timer; /* SA-111 workaround */ - struct work_struct work; - - enum vmbus_channel_state state; - - struct vmbus_channel_offer_channel offermsg; - /* - * These are based on the OfferMsg.MonitorId. - * Save it here for easy access. - */ - u8 monitor_grp; - u8 monitor_bit; - - u32 ringbuffer_gpadlhandle; - - /* Allocated memory for ring buffer */ - void *ringbuffer_pages; - u32 ringbuffer_pagecount; - struct hv_ring_buffer_info outbound; /* send to parent */ - struct hv_ring_buffer_info inbound; /* receive from parent */ - spinlock_t inbound_lock; - struct workqueue_struct *controlwq; - - /* Channel callback are invoked in this workqueue context */ - /* HANDLE dataWorkQueue; */ - - void (*onchannel_callback)(void *context); - void *channel_callback_context; -}; - -struct vmbus_channel_debug_info { - u32 relid; - enum vmbus_channel_state state; - struct hv_guid interfacetype; - struct hv_guid interface_instance; - u32 monitorid; - u32 servermonitor_pending; - u32 servermonitor_latency; - u32 servermonitor_connectionid; - u32 clientmonitor_pending; - u32 clientmonitor_latency; - u32 clientmonitor_connectionid; - - struct hv_ring_buffer_debug_info inbound; - struct hv_ring_buffer_debug_info outbound; -}; - -/* - * Represents each channel msg on the vmbus connection This is a - * variable-size data structure depending on the msg type itself - */ -struct vmbus_channel_msginfo { - /* Bookkeeping stuff */ - struct list_head msglistentry; - - /* So far, this is only used to handle gpadl body message */ - struct list_head submsglist; - - /* Synchronize the request/response if needed */ - int wait_condition; - wait_queue_head_t waitevent; - union { - struct vmbus_channel_version_supported version_supported; - struct vmbus_channel_open_result open_result; - struct vmbus_channel_gpadl_torndown gpadl_torndown; - struct vmbus_channel_gpadl_created gpadl_created; - struct vmbus_channel_version_response version_response; - } response; - - u32 msgsize; - /* - * The channel message that goes out on the "wire". - * It will contain at minimum the VMBUS_CHANNEL_MESSAGE_HEADER header - */ - unsigned char msg[0]; -}; - - -void free_channel(struct vmbus_channel *channel); - -void vmbus_onmessage(void *context); - -int vmbus_request_offers(void); - -void vmbus_release_unattached_channels(void); - -#endif /* _CHANNEL_MGMT_H_ */ diff --git a/drivers/staging/hv/connection.c b/drivers/staging/hv/connection.c index afc8116e7aa4..37bbf770ef11 100644 --- a/drivers/staging/hv/connection.c +++ b/drivers/staging/hv/connection.c @@ -20,15 +20,17 @@ * Hank Janssen <hjanssen@microsoft.com> * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/sched.h> #include <linux/wait.h> #include <linux/mm.h> #include <linux/slab.h> #include <linux/vmalloc.h> -#include "hv_api.h" -#include "logging.h" -#include "vmbus_private.h" + +#include "hyperv.h" +#include "hyperv_vmbus.h" struct vmbus_connection vmbus_connection = { @@ -42,6 +44,7 @@ struct vmbus_connection vmbus_connection = { int vmbus_connect(void) { int ret = 0; + int t; struct vmbus_channel_msginfo *msginfo = NULL; struct vmbus_channel_initiate_contact *msg; unsigned long flags; @@ -55,7 +58,7 @@ int vmbus_connect(void) vmbus_connection.work_queue = create_workqueue("hv_vmbus_con"); if (!vmbus_connection.work_queue) { ret = -1; - goto Cleanup; + goto cleanup; } INIT_LIST_HEAD(&vmbus_connection.chn_msg_list); @@ -72,7 +75,7 @@ int vmbus_connect(void) (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, 0); if (vmbus_connection.int_page == NULL) { ret = -1; - goto Cleanup; + goto cleanup; } vmbus_connection.recv_int_page = vmbus_connection.int_page; @@ -88,7 +91,7 @@ int vmbus_connect(void) (void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 1); if (vmbus_connection.monitor_pages == NULL) { ret = -1; - goto Cleanup; + goto cleanup; } msginfo = kzalloc(sizeof(*msginfo) + @@ -96,10 +99,10 @@ int vmbus_connect(void) GFP_KERNEL); if (msginfo == NULL) { ret = -ENOMEM; - goto Cleanup; + goto cleanup; } - init_waitqueue_head(&msginfo->waitevent); + init_completion(&msginfo->waitevent); msg = (struct vmbus_channel_initiate_contact *)msginfo->msg; @@ -121,11 +124,6 @@ int vmbus_connect(void) spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - DPRINT_DBG(VMBUS, "Vmbus connection - interrupt pfn %llx, " - "monitor1 pfn %llx,, monitor2 pfn %llx", - msg->interrupt_page, msg->monitor_page1, msg->monitor_page2); - - DPRINT_DBG(VMBUS, "Sending channel initiate msg..."); ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_initiate_contact)); if (ret != 0) { @@ -133,21 +131,19 @@ int vmbus_connect(void) list_del(&msginfo->msglistentry); spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - goto Cleanup; + goto cleanup; } /* Wait for the connection response */ - msginfo->wait_condition = 0; - wait_event_timeout(msginfo->waitevent, msginfo->wait_condition, - msecs_to_jiffies(1000)); - if (msginfo->wait_condition == 0) { + t = wait_for_completion_timeout(&msginfo->waitevent, HZ); + if (t == 0) { spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); list_del(&msginfo->msglistentry); spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); ret = -ETIMEDOUT; - goto Cleanup; + goto cleanup; } spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); @@ -156,21 +152,19 @@ int vmbus_connect(void) /* Check if successful */ if (msginfo->response.version_response.version_supported) { - DPRINT_INFO(VMBUS, "Vmbus connected!!"); vmbus_connection.conn_state = CONNECTED; - } else { - DPRINT_ERR(VMBUS, "Vmbus connection failed!!..." - "current version (%d) not supported", - VMBUS_REVISION_NUMBER); + pr_err("Unable to connect, " + "Version %d not supported by Hyper-V\n", + VMBUS_REVISION_NUMBER); ret = -1; - goto Cleanup; + goto cleanup; } kfree(msginfo); return 0; -Cleanup: +cleanup: vmbus_connection.conn_state = DISCONNECTED; if (vmbus_connection.work_queue) @@ -213,7 +207,7 @@ int vmbus_disconnect(void) ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_message_header)); if (ret != 0) - goto Cleanup; + goto cleanup; free_pages((unsigned long)vmbus_connection.int_page, 0); free_pages((unsigned long)vmbus_connection.monitor_pages, 1); @@ -223,9 +217,9 @@ int vmbus_disconnect(void) vmbus_connection.conn_state = DISCONNECTED; - DPRINT_INFO(VMBUS, "Vmbus disconnected!!"); + pr_info("hv_vmbus disconnected\n"); -Cleanup: +cleanup: kfree(msg); return ret; } @@ -255,10 +249,9 @@ struct vmbus_channel *relid2channel(u32 relid) /* * process_chn_event - Process a channel event notification */ -static void process_chn_event(void *context) +static void process_chn_event(u32 relid) { struct vmbus_channel *channel; - u32 relid = (u32)(unsigned long)context; /* ASSERT(relId > 0); */ @@ -270,13 +263,8 @@ static void process_chn_event(void *context) if (channel) { vmbus_onchannel_event(channel); - /* - * WorkQueueQueueWorkItem(channel->dataWorkQueue, - * vmbus_onchannel_event, - * (void*)channel); - */ } else { - DPRINT_ERR(VMBUS, "channel not found for relid - %d.", relid); + pr_err("channel not found for relid - %u\n", relid); } } @@ -285,39 +273,33 @@ static void process_chn_event(void *context) */ void vmbus_on_event(unsigned long data) { - int dword; - int maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5; + u32 dword; + u32 maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5; int bit; - int relid; + u32 relid; u32 *recv_int_page = vmbus_connection.recv_int_page; /* Check events */ - if (recv_int_page) { - for (dword = 0; dword < maxdword; dword++) { - if (recv_int_page[dword]) { - for (bit = 0; bit < 32; bit++) { - if (sync_test_and_clear_bit(bit, - (unsigned long *) - &recv_int_page[dword])) { - relid = (dword << 5) + bit; - DPRINT_DBG(VMBUS, "event detected for relid - %d", relid); - - if (relid == 0) { - /* special case - vmbus channel protocol msg */ - DPRINT_DBG(VMBUS, "invalid relid - %d", relid); - continue; - } else { - /* QueueWorkItem(VmbusProcessEvent, (void*)relid); */ - /* ret = WorkQueueQueueWorkItem(gVmbusConnection.workQueue, VmbusProcessChannelEvent, (void*)relid); */ - process_chn_event((void *) - (unsigned long)relid); - } - } + if (!recv_int_page) + return; + for (dword = 0; dword < maxdword; dword++) { + if (!recv_int_page[dword]) + continue; + for (bit = 0; bit < 32; bit++) { + if (sync_test_and_clear_bit(bit, (unsigned long *)&recv_int_page[dword])) { + relid = (dword << 5) + bit; + + if (relid == 0) { + /* + * Special case - vmbus + * channel protocol msg + */ + continue; } + process_chn_event(relid); } - } + } } - return; } /* diff --git a/drivers/staging/hv/hv.c b/drivers/staging/hv/hv.c index 0b06f4fe5838..a2cc0911de58 100644 --- a/drivers/staging/hv/hv.c +++ b/drivers/staging/hv/hv.c @@ -19,13 +19,15 @@ * Hank Janssen <hjanssen@microsoft.com> * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/mm.h> #include <linux/slab.h> #include <linux/vmalloc.h> -#include "hv_api.h" -#include "logging.h" -#include "vmbus_private.h" + +#include "hyperv.h" +#include "hyperv_vmbus.h" /* The one and only */ struct hv_context hv_context = { @@ -80,33 +82,7 @@ static int query_hypervisor_info(void) op = HVCPUID_VENDOR_MAXFUNCTION; cpuid(op, &eax, &ebx, &ecx, &edx); - DPRINT_INFO(VMBUS, "Vendor ID: %c%c%c%c%c%c%c%c%c%c%c%c", - (ebx & 0xFF), - ((ebx >> 8) & 0xFF), - ((ebx >> 16) & 0xFF), - ((ebx >> 24) & 0xFF), - (ecx & 0xFF), - ((ecx >> 8) & 0xFF), - ((ecx >> 16) & 0xFF), - ((ecx >> 24) & 0xFF), - (edx & 0xFF), - ((edx >> 8) & 0xFF), - ((edx >> 16) & 0xFF), - ((edx >> 24) & 0xFF)); - max_leaf = eax; - eax = 0; - ebx = 0; - ecx = 0; - edx = 0; - op = HVCPUID_INTERFACE; - cpuid(op, &eax, &ebx, &ecx, &edx); - - DPRINT_INFO(VMBUS, "Interface ID: %c%c%c%c", - (eax & 0xFF), - ((eax >> 8) & 0xFF), - ((eax >> 16) & 0xFF), - ((eax >> 24) & 0xFF)); if (max_leaf >= HVCPUID_VERSION) { eax = 0; @@ -115,7 +91,7 @@ static int query_hypervisor_info(void) edx = 0; op = HVCPUID_VERSION; cpuid(op, &eax, &ebx, &ecx, &edx); - DPRINT_INFO(VMBUS, "OS Build:%d-%d.%d-%d-%d.%d",\ + pr_info("Hyper-V Host OS Build:%d-%d.%d-%d-%d.%d\n", eax, ebx >> 16, ebx & 0xFFFF, @@ -137,18 +113,11 @@ static u64 do_hypercall(u64 control, void *input, void *output) u64 output_address = (output) ? virt_to_phys(output) : 0; volatile void *hypercall_page = hv_context.hypercall_page; - DPRINT_DBG(VMBUS, "Hypercall <control %llx input phys %llx virt %p " - "output phys %llx virt %p hypercall %p>", - control, input_address, input, - output_address, output, hypercall_page); - __asm__ __volatile__("mov %0, %%r8" : : "r" (output_address) : "r8"); __asm__ __volatile__("call *%3" : "=a" (hv_status) : "c" (control), "d" (input_address), "m" (hypercall_page)); - DPRINT_DBG(VMBUS, "Hypercall <return %llx>", hv_status); - return hv_status; #else @@ -165,18 +134,12 @@ static u64 do_hypercall(u64 control, void *input, void *output) u32 output_address_lo = output_address & 0xFFFFFFFF; volatile void *hypercall_page = hv_context.hypercall_page; - DPRINT_DBG(VMBUS, "Hypercall <control %llx input %p output %p>", - control, input, output); - __asm__ __volatile__ ("call *%8" : "=d"(hv_status_hi), "=a"(hv_status_lo) : "d" (control_hi), "a" (control_lo), "b" (input_address_hi), "c" (input_address_lo), "D"(output_address_hi), "S"(output_address_lo), "m" (hypercall_page)); - DPRINT_DBG(VMBUS, "Hypercall <return %llx>", - hv_status_lo | ((u64)hv_status_hi << 32)); - return hv_status_lo | ((u64)hv_status_hi << 32); #endif /* !x86_64 */ } @@ -197,13 +160,8 @@ int hv_init(void) memset(hv_context.synic_message_page, 0, sizeof(void *) * MAX_NUM_CPUS); - if (!query_hypervisor_presence()) { - DPRINT_ERR(VMBUS, "No Windows hypervisor detected!!"); - goto Cleanup; - } - - DPRINT_INFO(VMBUS, - "Windows hypervisor detected! Retrieving more info..."); + if (!query_hypervisor_presence()) + goto cleanup; max_leaf = query_hypervisor_info(); /* HvQueryHypervisorFeatures(maxLeaf); */ @@ -213,11 +171,8 @@ int hv_init(void) */ rdmsrl(HV_X64_MSR_GUEST_OS_ID, hv_context.guestid); - if (hv_context.guestid != 0) { - DPRINT_ERR(VMBUS, "Unknown guest id (0x%llx)!!", - hv_context.guestid); - goto Cleanup; - } + if (hv_context.guestid != 0) + goto cleanup; /* Write our OS info */ wrmsrl(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID); @@ -232,11 +187,8 @@ int hv_init(void) */ virtaddr = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_EXEC); - if (!virtaddr) { - DPRINT_ERR(VMBUS, - "unable to allocate hypercall page!!"); - goto Cleanup; - } + if (!virtaddr) + goto cleanup; hypercall_msr.enable = 1; @@ -247,23 +199,17 @@ int hv_init(void) hypercall_msr.as_uint64 = 0; rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); - if (!hypercall_msr.enable) { - DPRINT_ERR(VMBUS, "unable to set hypercall page!!"); - goto Cleanup; - } + if (!hypercall_msr.enable) + goto cleanup; hv_context.hypercall_page = virtaddr; - DPRINT_INFO(VMBUS, "Hypercall page VA=%p, PA=0x%0llx", - hv_context.hypercall_page, - (u64)hypercall_msr.guest_physical_address << PAGE_SHIFT); - /* Setup the global signal event param for the signal event hypercall */ hv_context.signal_event_buffer = kmalloc(sizeof(struct hv_input_signal_event_buffer), GFP_KERNEL); if (!hv_context.signal_event_buffer) - goto Cleanup; + goto cleanup; hv_context.signal_event_param = (struct hv_input_signal_event *) @@ -278,7 +224,7 @@ int hv_init(void) return ret; -Cleanup: +cleanup: if (virtaddr) { if (hypercall_msr.enable) { hypercall_msr.as_uint64 = 0; @@ -394,24 +340,20 @@ void hv_synic_init(void *irqarg) /* Check the version */ rdmsrl(HV_X64_MSR_SVERSION, version); - DPRINT_INFO(VMBUS, "SynIC version: %llx", version); - hv_context.synic_message_page[cpu] = (void *)get_zeroed_page(GFP_ATOMIC); if (hv_context.synic_message_page[cpu] == NULL) { - DPRINT_ERR(VMBUS, - "unable to allocate SYNIC message page!!"); - goto Cleanup; + pr_err("Unable to allocate SYNIC message page\n"); + goto cleanup; } hv_context.synic_event_page[cpu] = (void *)get_zeroed_page(GFP_ATOMIC); if (hv_context.synic_event_page[cpu] == NULL) { - DPRINT_ERR(VMBUS, - "unable to allocate SYNIC event page!!"); - goto Cleanup; + pr_err("Unable to allocate SYNIC event page\n"); + goto cleanup; } /* Setup the Synic's message page */ @@ -420,8 +362,6 @@ void hv_synic_init(void *irqarg) simp.base_simp_gpa = virt_to_phys(hv_context.synic_message_page[cpu]) >> PAGE_SHIFT; - DPRINT_DBG(VMBUS, "HV_X64_MSR_SIMP msr set to: %llx", simp.as_uint64); - wrmsrl(HV_X64_MSR_SIMP, simp.as_uint64); /* Setup the Synic's event page */ @@ -430,14 +370,8 @@ void hv_synic_init(void *irqarg) siefp.base_siefp_gpa = virt_to_phys(hv_context.synic_event_page[cpu]) >> PAGE_SHIFT; - DPRINT_DBG(VMBUS, "HV_X64_MSR_SIEFP msr set to: %llx", siefp.as_uint64); - wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); - /* Setup the interception SINT. */ - /* wrmsrl((HV_X64_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX), */ - /* interceptionSint.as_uint64); */ - /* Setup the shared SINT. */ rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); @@ -446,9 +380,6 @@ void hv_synic_init(void *irqarg) shared_sint.masked = false; shared_sint.auto_eoi = true; - DPRINT_DBG(VMBUS, "HV_X64_MSR_SINT1 msr set to: %llx", - shared_sint.as_uint64); - wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); /* Enable the global synic bit */ @@ -460,7 +391,7 @@ void hv_synic_init(void *irqarg) hv_context.synic_initialized = true; return; -Cleanup: +cleanup: if (hv_context.synic_event_page[cpu]) free_page((unsigned long)hv_context.synic_event_page[cpu]); diff --git a/drivers/staging/hv/hv.h b/drivers/staging/hv/hv.h deleted file mode 100644 index 829aff81bb30..000000000000 --- a/drivers/staging/hv/hv.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang <haiyangz@microsoft.com> - * Hank Janssen <hjanssen@microsoft.com> - * - */ - - -#ifndef __HV_H__ -#define __HV_H__ - -#include "hv_api.h" - -enum { - VMBUS_MESSAGE_CONNECTION_ID = 1, - VMBUS_MESSAGE_PORT_ID = 1, - VMBUS_EVENT_CONNECTION_ID = 2, - VMBUS_EVENT_PORT_ID = 2, - VMBUS_MONITOR_CONNECTION_ID = 3, - VMBUS_MONITOR_PORT_ID = 3, - VMBUS_MESSAGE_SINT = 2, -}; - -/* #defines */ - -#define HV_PRESENT_BIT 0x80000000 - -#define HV_LINUX_GUEST_ID_LO 0x00000000 -#define HV_LINUX_GUEST_ID_HI 0xB16B00B5 -#define HV_LINUX_GUEST_ID (((u64)HV_LINUX_GUEST_ID_HI << 32) | \ - HV_LINUX_GUEST_ID_LO) - -#define HV_CPU_POWER_MANAGEMENT (1 << 0) -#define HV_RECOMMENDATIONS_MAX 4 - -#define HV_X64_MAX 5 -#define HV_CAPS_MAX 8 - - -#define HV_HYPERCALL_PARAM_ALIGN sizeof(u64) - - -/* Service definitions */ - -#define HV_SERVICE_PARENT_PORT (0) -#define HV_SERVICE_PARENT_CONNECTION (0) - -#define HV_SERVICE_CONNECT_RESPONSE_SUCCESS (0) -#define HV_SERVICE_CONNECT_RESPONSE_INVALID_PARAMETER (1) -#define HV_SERVICE_CONNECT_RESPONSE_UNKNOWN_SERVICE (2) -#define HV_SERVICE_CONNECT_RESPONSE_CONNECTION_REJECTED (3) - -#define HV_SERVICE_CONNECT_REQUEST_MESSAGE_ID (1) -#define HV_SERVICE_CONNECT_RESPONSE_MESSAGE_ID (2) -#define HV_SERVICE_DISCONNECT_REQUEST_MESSAGE_ID (3) -#define HV_SERVICE_DISCONNECT_RESPONSE_MESSAGE_ID (4) -#define HV_SERVICE_MAX_MESSAGE_ID (4) - -#define HV_SERVICE_PROTOCOL_VERSION (0x0010) -#define HV_CONNECT_PAYLOAD_BYTE_COUNT 64 - -/* #define VMBUS_REVISION_NUMBER 6 */ - -/* Our local vmbus's port and connection id. Anything >0 is fine */ -/* #define VMBUS_PORT_ID 11 */ - -/* 628180B8-308D-4c5e-B7DB-1BEB62E62EF4 */ -static const struct hv_guid VMBUS_SERVICE_ID = { - .data = { - 0xb8, 0x80, 0x81, 0x62, 0x8d, 0x30, 0x5e, 0x4c, - 0xb7, 0xdb, 0x1b, 0xeb, 0x62, 0xe6, 0x2e, 0xf4 - }, -}; - -#define MAX_NUM_CPUS 32 - - -struct hv_input_signal_event_buffer { - u64 align8; - struct hv_input_signal_event event; -}; - -struct hv_context { - /* We only support running on top of Hyper-V - * So at this point this really can only contain the Hyper-V ID - */ - u64 guestid; - - void *hypercall_page; - - bool synic_initialized; - - /* - * This is used as an input param to HvCallSignalEvent hypercall. The - * input param is immutable in our usage and must be dynamic mem (vs - * stack or global). */ - struct hv_input_signal_event_buffer *signal_event_buffer; - /* 8-bytes aligned of the buffer above */ - struct hv_input_signal_event *signal_event_param; - - void *synic_message_page[MAX_NUM_CPUS]; - void *synic_event_page[MAX_NUM_CPUS]; -}; - -extern struct hv_context hv_context; - - -/* Hv Interface */ - -extern int hv_init(void); - -extern void hv_cleanup(void); - -extern u16 hv_post_message(union hv_connection_id connection_id, - enum hv_message_type message_type, - void *payload, size_t payload_size); - -extern u16 hv_signal_event(void); - -extern void hv_synic_init(void *irqarg); - -extern void hv_synic_cleanup(void *arg); - -#endif /* __HV_H__ */ diff --git a/drivers/staging/hv/hv_api.h b/drivers/staging/hv/hv_api.h deleted file mode 100644 index 43a722888dc4..000000000000 --- a/drivers/staging/hv/hv_api.h +++ /dev/null @@ -1,910 +0,0 @@ -/* - * - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang <haiyangz@microsoft.com> - * Hank Janssen <hjanssen@microsoft.com> - * - */ -#ifndef __HV_API_H -#define __HV_API_H - -struct hv_guid { - unsigned char data[16]; -}; - - - -/* Status codes for hypervisor operations. */ - -/* - * HV_STATUS_SUCCESS - * The specified hypercall succeeded - */ -#define HV_STATUS_SUCCESS ((u16)0x0000) - -/* - * HV_STATUS_INVALID_HYPERCALL_CODE - * The hypervisor does not support the operation because the specified - * hypercall code is not supported. - */ -#define HV_STATUS_INVALID_HYPERCALL_CODE ((u16)0x0002) - -/* - * HV_STATUS_INVALID_HYPERCALL_INPUT - * The hypervisor does not support the operation because the encoding for the - * hypercall input register is not supported. - */ -#define HV_STATUS_INVALID_HYPERCALL_INPUT ((u16)0x0003) - -/* - * HV_STATUS_INVALID_ALIGNMENT - * The hypervisor could not perform the operation because a parameter has an - * invalid alignment. - */ -#define HV_STATUS_INVALID_ALIGNMENT ((u16)0x0004) - -/* - * HV_STATUS_INVALID_PARAMETER - * The hypervisor could not perform the operation because an invalid parameter - * was specified. - */ -#define HV_STATUS_INVALID_PARAMETER ((u16)0x0005) - -/* - * HV_STATUS_ACCESS_DENIED - * Access to the specified object was denied. - */ -#define HV_STATUS_ACCESS_DENIED ((u16)0x0006) - -/* - * HV_STATUS_INVALID_PARTITION_STATE - * The hypervisor could not perform the operation because the partition is - * entering or in an invalid state. - */ -#define HV_STATUS_INVALID_PARTITION_STATE ((u16)0x0007) - -/* - * HV_STATUS_OPERATION_DENIED - * The operation is not allowed in the current state. - */ -#define HV_STATUS_OPERATION_DENIED ((u16)0x0008) - -/* - * HV_STATUS_UNKNOWN_PROPERTY - * The hypervisor does not recognize the specified partition property. - */ -#define HV_STATUS_UNKNOWN_PROPERTY ((u16)0x0009) - -/* - * HV_STATUS_PROPERTY_VALUE_OUT_OF_RANGE - * The specified value of a partition property is out of range or violates an - * invariant. - */ -#define HV_STATUS_PROPERTY_VALUE_OUT_OF_RANGE ((u16)0x000A) - -/* - * HV_STATUS_INSUFFICIENT_MEMORY - * There is not enough memory in the hypervisor pool to complete the operation. - */ -#define HV_STATUS_INSUFFICIENT_MEMORY ((u16)0x000B) - -/* - * HV_STATUS_PARTITION_TOO_DEEP - * The maximum partition depth has been exceeded for the partition hierarchy. - */ -#define HV_STATUS_PARTITION_TOO_DEEP ((u16)0x000C) - -/* - * HV_STATUS_INVALID_PARTITION_ID - * A partition with the specified partition Id does not exist. - */ -#define HV_STATUS_INVALID_PARTITION_ID ((u16)0x000D) - -/* - * HV_STATUS_INVALID_VP_INDEX - * The hypervisor could not perform the operation because the specified VP - * index is invalid. - */ -#define HV_STATUS_INVALID_VP_INDEX ((u16)0x000E) - -/* - * HV_STATUS_NOT_FOUND - * The iteration is complete; no addition items in the iteration could be - * found. - */ -#define HV_STATUS_NOT_FOUND ((u16)0x0010) - -/* - * HV_STATUS_INVALID_PORT_ID - * The hypervisor could not perform the operation because the specified port - * identifier is invalid. - */ -#define HV_STATUS_INVALID_PORT_ID ((u16)0x0011) - -/* - * HV_STATUS_INVALID_CONNECTION_ID - * The hypervisor could not perform the operation because the specified - * connection identifier is invalid. - */ -#define HV_STATUS_INVALID_CONNECTION_ID ((u16)0x0012) - -/* - * HV_STATUS_INSUFFICIENT_BUFFERS - * You did not supply enough message buffers to send a message. - */ -#define HV_STATUS_INSUFFICIENT_BUFFERS ((u16)0x0013) - -/* - * HV_STATUS_NOT_ACKNOWLEDGED - * The previous virtual interrupt has not been acknowledged. - */ -#define HV_STATUS_NOT_ACKNOWLEDGED ((u16)0x0014) - -/* - * HV_STATUS_INVALID_VP_STATE - * A virtual processor is not in the correct state for the performance of the - * indicated operation. - */ -#define HV_STATUS_INVALID_VP_STATE ((u16)0x0015) - -/* - * HV_STATUS_ACKNOWLEDGED - * The previous virtual interrupt has already been acknowledged. - */ -#define HV_STATUS_ACKNOWLEDGED ((u16)0x0016) - -/* - * HV_STATUS_INVALID_SAVE_RESTORE_STATE - * The indicated partition is not in a valid state for saving or restoring. - */ -#define HV_STATUS_INVALID_SAVE_RESTORE_STATE ((u16)0x0017) - -/* - * HV_STATUS_INVALID_SYNIC_STATE - * The hypervisor could not complete the operation because a required feature - * of the synthetic interrupt controller (SynIC) was disabled. - */ -#define HV_STATUS_INVALID_SYNIC_STATE ((u16)0x0018) - -/* - * HV_STATUS_OBJECT_IN_USE - * The hypervisor could not perform the operation because the object or value - * was either already in use or being used for a purpose that would not permit - * completing the operation. - */ -#define HV_STATUS_OBJECT_IN_USE ((u16)0x0019) - -/* - * HV_STATUS_INVALID_PROXIMITY_DOMAIN_INFO - * The proximity domain information is invalid. - */ -#define HV_STATUS_INVALID_PROXIMITY_DOMAIN_INFO ((u16)0x001A) - -/* - * HV_STATUS_NO_DATA - * An attempt to retrieve debugging data failed because none was available. - */ -#define HV_STATUS_NO_DATA ((u16)0x001B) - -/* - * HV_STATUS_INACTIVE - * The physical connection being used for debuggging has not recorded any - * receive activity since the last operation. - */ -#define HV_STATUS_INACTIVE ((u16)0x001C) - -/* - * HV_STATUS_NO_RESOURCES - * There are not enough resources to complete the operation. - */ -#define HV_STATUS_NO_RESOURCES ((u16)0x001D) - -/* - * HV_STATUS_FEATURE_UNAVAILABLE - * A hypervisor feature is not available to the user. - */ -#define HV_STATUS_FEATURE_UNAVAILABLE ((u16)0x001E) - -/* - * HV_STATUS_UNSUCCESSFUL - * {Operation Failed} The requested operation was unsuccessful. - */ -#define HV_STATUS_UNSUCCESSFUL ((u16)0x1001) - -/* - * HV_STATUS_INSUFFICIENT_BUFFER - * The specified buffer was too small to contain all of the requested data. - */ -#define HV_STATUS_INSUFFICIENT_BUFFER ((u16)0x1002) - -/* - * HV_STATUS_GPA_NOT_PRESENT - * The guest physical address is not currently associated with a system - * physical address. - */ -#define HV_STATUS_GPA_NOT_PRESENT ((u16)0x1003) - -/* - * HV_STATUS_GUEST_PAGE_FAULT - * The operation would have resulted in a page fault in the guest. - */ -#define HV_STATUS_GUEST_PAGE_FAULT ((u16)0x1004) - -/* - * HV_STATUS_RUNDOWN_DISABLED - * The operation cannot proceed as the rundown object was marked disabled. - */ -#define HV_STATUS_RUNDOWN_DISABLED ((u16)0x1005) - -/* - * HV_STATUS_KEY_ALREADY_EXISTS - * The entry cannot be added as another entry with the same key already exists. - */ -#define HV_STATUS_KEY_ALREADY_EXISTS ((u16)0x1006) - -/* - * HV_STATUS_GPA_INTERCEPT - * The operation resulted an intercept on a region of guest physical memory. - */ -#define HV_STATUS_GPA_INTERCEPT ((u16)0x1007) - -/* - * HV_STATUS_GUEST_GENERAL_PROTECTION_FAULT - * The operation would have resulted in a general protection fault in the - * guest. - */ -#define HV_STATUS_GUEST_GENERAL_PROTECTION_FAULT ((u16)0x1008) - -/* - * HV_STATUS_GUEST_STACK_FAULT - * The operation would have resulted in a stack fault in the guest. - */ -#define HV_STATUS_GUEST_STACK_FAULT ((u16)0x1009) - -/* - * HV_STATUS_GUEST_INVALID_OPCODE_FAULT - * The operation would have resulted in an invalid opcode fault in the guest. - */ -#define HV_STATUS_GUEST_INVALID_OPCODE_FAULT ((u16)0x100A) - -/* - * HV_STATUS_FINALIZE_INCOMPLETE - * The partition is not completely finalized. - */ -#define HV_STATUS_FINALIZE_INCOMPLETE ((u16)0x100B) - -/* - * HV_STATUS_GUEST_MACHINE_CHECK_ABORT - * The operation would have resulted in an machine check abort in the guest. - */ -#define HV_STATUS_GUEST_MACHINE_CHECK_ABORT ((u16)0x100C) - -/* - * HV_STATUS_ILLEGAL_OVERLAY_ACCESS - * An illegal access was attempted to an overlay page. - */ -#define HV_STATUS_ILLEGAL_OVERLAY_ACCESS ((u16)0x100D) - -/* - * HV_STATUS_INSUFFICIENT_SYSTEM_VA - * There is not enough system VA space available to satisfy the request, - */ -#define HV_STATUS_INSUFFICIENT_SYSTEM_VA ((u16)0x100E) - -/* - * HV_STATUS_VIRTUAL_ADDRESS_NOT_MAPPED - * The passed virtual address was not mapped in the hypervisor address space. - */ -#define HV_STATUS_VIRTUAL_ADDRESS_NOT_MAPPED ((u16)0x100F) - -/* - * HV_STATUS_NOT_IMPLEMENTED - * The requested operation is not implemented in this version of the - * hypervisor. - */ -#define HV_STATUS_NOT_IMPLEMENTED ((u16)0x1010) - -/* - * HV_STATUS_VMX_INSTRUCTION_FAILED - * The requested VMX instruction failed to complete successfully. - */ -#define HV_STATUS_VMX_INSTRUCTION_FAILED ((u16)0x1011) - -/* - * HV_STATUS_VMX_INSTRUCTION_FAILED_WITH_STATUS - * The requested VMX instruction failed to complete successfully indicating - * status. - */ -#define HV_STATUS_VMX_INSTRUCTION_FAILED_WITH_STATUS ((u16)0x1012) - -/* - * HV_STATUS_MSR_ACCESS_FAILED - * The requested access to the model specific register failed. - */ -#define HV_STATUS_MSR_ACCESS_FAILED ((u16)0x1013) - -/* - * HV_STATUS_CR_ACCESS_FAILED - * The requested access to the control register failed. - */ -#define HV_STATUS_CR_ACCESS_FAILED ((u16)0x1014) - -/* - * HV_STATUS_TIMEOUT - * The specified timeout expired before the operation completed. - */ -#define HV_STATUS_TIMEOUT ((u16)0x1016) - -/* - * HV_STATUS_MSR_INTERCEPT - * The requested access to the model specific register generated an intercept. - */ -#define HV_STATUS_MSR_INTERCEPT ((u16)0x1017) - -/* - * HV_STATUS_CPUID_INTERCEPT - * The CPUID instruction generated an intercept. - */ -#define HV_STATUS_CPUID_INTERCEPT ((u16)0x1018) - -/* - * HV_STATUS_REPEAT_INSTRUCTION - * The current instruction should be repeated and the instruction pointer not - * advanced. - */ -#define HV_STATUS_REPEAT_INSTRUCTION ((u16)0x1019) - -/* - * HV_STATUS_PAGE_PROTECTION_VIOLATION - * The current instruction should be repeated and the instruction pointer not - * advanced. - */ -#define HV_STATUS_PAGE_PROTECTION_VIOLATION ((u16)0x101A) - -/* - * HV_STATUS_PAGE_TABLE_INVALID - * The current instruction should be repeated and the instruction pointer not - * advanced. - */ -#define HV_STATUS_PAGE_TABLE_INVALID ((u16)0x101B) - -/* - * HV_STATUS_PAGE_NOT_PRESENT - * The current instruction should be repeated and the instruction pointer not - * advanced. - */ -#define HV_STATUS_PAGE_NOT_PRESENT ((u16)0x101C) - -/* - * HV_STATUS_IO_INTERCEPT - * The requested access to the I/O port generated an intercept. - */ -#define HV_STATUS_IO_INTERCEPT ((u16)0x101D) - -/* - * HV_STATUS_NOTHING_TO_DO - * There is nothing to do. - */ -#define HV_STATUS_NOTHING_TO_DO ((u16)0x101E) - -/* - * HV_STATUS_THREAD_TERMINATING - * The requested thread is terminating. - */ -#define HV_STATUS_THREAD_TERMINATING ((u16)0x101F) - -/* - * HV_STATUS_SECTION_ALREADY_CONSTRUCTED - * The specified section was already constructed. - */ -#define HV_STATUS_SECTION_ALREADY_CONSTRUCTED ((u16)0x1020) - -/* HV_STATUS_SECTION_NOT_ALREADY_CONSTRUCTED - * The specified section was not already constructed. - */ -#define HV_STATUS_SECTION_NOT_ALREADY_CONSTRUCTED ((u16)0x1021) - -/* - * HV_STATUS_PAGE_ALREADY_COMMITTED - * The specified virtual address was already backed by physical memory. - */ -#define HV_STATUS_PAGE_ALREADY_COMMITTED ((u16)0x1022) - -/* - * HV_STATUS_PAGE_NOT_ALREADY_COMMITTED - * The specified virtual address was not already backed by physical memory. - */ -#define HV_STATUS_PAGE_NOT_ALREADY_COMMITTED ((u16)0x1023) - -/* - * HV_STATUS_COMMITTED_PAGES_REMAIN - * Committed pages remain in the section. - */ -#define HV_STATUS_COMMITTED_PAGES_REMAIN ((u16)0x1024) - -/* - * HV_STATUS_NO_REMAINING_COMMITTED_PAGES - * No additional committed pages beyond the specified page exist in the - * section. - */ -#define HV_STATUS_NO_REMAINING_COMMITTED_PAGES ((u16)0x1025) - -/* - * HV_STATUS_INSUFFICIENT_COMPARTMENT_VA - * The VA space of the compartment is exhausted. - */ -#define HV_STATUS_INSUFFICIENT_COMPARTMENT_VA ((u16)0x1026) - -/* - * HV_STATUS_DEREF_SPA_LIST_FULL - * The SPA dereference list is full, and there are additional entries to be - * added to it. - */ -#define HV_STATUS_DEREF_SPA_LIST_FULL ((u16)0x1027) - -/* - * HV_STATUS_GPA_OUT_OF_RANGE - * The supplied GPA is out of range. - */ -#define HV_STATUS_GPA_OUT_OF_RANGE ((u16)0x1027) - -/* - * HV_STATUS_NONVOLATILE_XMM_STALE - * The XMM register that was being accessed is stale. - */ -#define HV_STATUS_NONVOLATILE_XMM_STALE ((u16)0x1028) - -/* HV_STATUS_UNSUPPORTED_PROCESSOR - * The hypervisor does not support the processors in this system. - */ -#define HV_STATUS_UNSUPPORTED_PROCESSOR ((u16)0x1029) - -/* - * HV_STATUS_INSUFFICIENT_CROM_SPACE - * Insufficient space existed for copying over the CROM contents. - */ -#define HV_STATUS_INSUFFICIENT_CROM_SPACE ((u16)0x2000) - -/* - * HV_STATUS_BAD_CROM_FORMAT - * The contents of the CROM failed validation attempts. - */ -#define HV_STATUS_BAD_CROM_FORMAT ((u16)0x2001) - -/* - * HV_STATUS_UNSUPPORTED_CROM_FORMAT - * The contents of the CROM contain contents the parser doesn't support. - */ -#define HV_STATUS_UNSUPPORTED_CROM_FORMAT ((u16)0x2002) - -/* - * HV_STATUS_UNSUPPORTED_CONTROLLER - * The register format of the OHCI controller specified for debugging is not - * supported. - */ -#define HV_STATUS_UNSUPPORTED_CONTROLLER ((u16)0x2003) - -/* - * HV_STATUS_CROM_TOO_LARGE - * The CROM contents were to large to copy over. - */ -#define HV_STATUS_CROM_TOO_LARGE ((u16)0x2004) - -/* - * HV_STATUS_CONTROLLER_IN_USE - * The OHCI controller specified for debugging cannot be used as it is already - * in use. - */ -#define HV_STATUS_CONTROLLER_IN_USE ((u16)0x2005) - - -/* - * The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent - * is set by CPUID(HVCPUID_VERSION_FEATURES). - */ -enum hv_cpuid_function { - HVCPUID_VERSION_FEATURES = 0x00000001, - HVCPUID_VENDOR_MAXFUNCTION = 0x40000000, - HVCPUID_INTERFACE = 0x40000001, - - /* - * The remaining functions depend on the value of - * HVCPUID_INTERFACE - */ - HVCPUID_VERSION = 0x40000002, - HVCPUID_FEATURES = 0x40000003, - HVCPUID_ENLIGHTENMENT_INFO = 0x40000004, - HVCPUID_IMPLEMENTATION_LIMITS = 0x40000005, -}; - -/* Define the virtual APIC registers */ -#define HV_X64_MSR_EOI (0x40000070) -#define HV_X64_MSR_ICR (0x40000071) -#define HV_X64_MSR_TPR (0x40000072) -#define HV_X64_MSR_APIC_ASSIST_PAGE (0x40000073) - -/* Define version of the synthetic interrupt controller. */ -#define HV_SYNIC_VERSION (1) - -/* Define synthetic interrupt controller model specific registers. */ -#define HV_X64_MSR_SCONTROL (0x40000080) -#define HV_X64_MSR_SVERSION (0x40000081) -#define HV_X64_MSR_SIEFP (0x40000082) -#define HV_X64_MSR_SIMP (0x40000083) -#define HV_X64_MSR_EOM (0x40000084) -#define HV_X64_MSR_SINT0 (0x40000090) -#define HV_X64_MSR_SINT1 (0x40000091) -#define HV_X64_MSR_SINT2 (0x40000092) -#define HV_X64_MSR_SINT3 (0x40000093) -#define HV_X64_MSR_SINT4 (0x40000094) -#define HV_X64_MSR_SINT5 (0x40000095) -#define HV_X64_MSR_SINT6 (0x40000096) -#define HV_X64_MSR_SINT7 (0x40000097) -#define HV_X64_MSR_SINT8 (0x40000098) -#define HV_X64_MSR_SINT9 (0x40000099) -#define HV_X64_MSR_SINT10 (0x4000009A) -#define HV_X64_MSR_SINT11 (0x4000009B) -#define HV_X64_MSR_SINT12 (0x4000009C) -#define HV_X64_MSR_SINT13 (0x4000009D) -#define HV_X64_MSR_SINT14 (0x4000009E) -#define HV_X64_MSR_SINT15 (0x4000009F) - -/* Define the expected SynIC version. */ -#define HV_SYNIC_VERSION_1 (0x1) - -/* Define synthetic interrupt controller message constants. */ -#define HV_MESSAGE_SIZE (256) -#define HV_MESSAGE_PAYLOAD_BYTE_COUNT (240) -#define HV_MESSAGE_PAYLOAD_QWORD_COUNT (30) -#define HV_ANY_VP (0xFFFFFFFF) - -/* Define synthetic interrupt controller flag constants. */ -#define HV_EVENT_FLAGS_COUNT (256 * 8) -#define HV_EVENT_FLAGS_BYTE_COUNT (256) -#define HV_EVENT_FLAGS_DWORD_COUNT (256 / sizeof(u32)) - -/* Define hypervisor message types. */ -enum hv_message_type { - HVMSG_NONE = 0x00000000, - - /* Memory access messages. */ - HVMSG_UNMAPPED_GPA = 0x80000000, - HVMSG_GPA_INTERCEPT = 0x80000001, - - /* Timer notification messages. */ - HVMSG_TIMER_EXPIRED = 0x80000010, - - /* Error messages. */ - HVMSG_INVALID_VP_REGISTER_VALUE = 0x80000020, - HVMSG_UNRECOVERABLE_EXCEPTION = 0x80000021, - HVMSG_UNSUPPORTED_FEATURE = 0x80000022, - - /* Trace buffer complete messages. */ - HVMSG_EVENTLOG_BUFFERCOMPLETE = 0x80000040, - - /* Platform-specific processor intercept messages. */ - HVMSG_X64_IOPORT_INTERCEPT = 0x80010000, - HVMSG_X64_MSR_INTERCEPT = 0x80010001, - HVMSG_X64_CPUID_INTERCEPT = 0x80010002, - HVMSG_X64_EXCEPTION_INTERCEPT = 0x80010003, - HVMSG_X64_APIC_EOI = 0x80010004, - HVMSG_X64_LEGACY_FP_ERROR = 0x80010005 -}; - -/* Define the number of synthetic interrupt sources. */ -#define HV_SYNIC_SINT_COUNT (16) -#define HV_SYNIC_STIMER_COUNT (4) - -/* Define invalid partition identifier. */ -#define HV_PARTITION_ID_INVALID ((u64)0x0) - -/* Define connection identifier type. */ -union hv_connection_id { - u32 asu32; - struct { - u32 id:24; - u32 reserved:8; - } u; -}; - -/* Define port identifier type. */ -union hv_port_id { - u32 asu32; - struct { - u32 id:24; - u32 reserved:8; - } u ; -}; - -/* Define port type. */ -enum hv_port_type { - HVPORT_MSG = 1, - HVPORT_EVENT = 2, - HVPORT_MONITOR = 3 -}; - -/* Define port information structure. */ -struct hv_port_info { - enum hv_port_type port_type; - u32 padding; - union { - struct { - u32 target_sint; - u32 target_vp; - u64 rsvdz; - } message_port_info; - struct { - u32 target_sint; - u32 target_vp; - u16 base_flag_bumber; - u16 flag_count; - u32 rsvdz; - } event_port_info; - struct { - u64 monitor_address; - u64 rsvdz; - } monitor_port_info; - }; -}; - -struct hv_connection_info { - enum hv_port_type port_type; - u32 padding; - union { - struct { - u64 rsvdz; - } message_connection_info; - struct { - u64 rsvdz; - } event_connection_info; - struct { - u64 monitor_address; - } monitor_connection_info; - }; -}; - -/* Define synthetic interrupt controller message flags. */ -union hv_message_flags { - u8 asu8; - struct { - u8 msg_pending:1; - u8 reserved:7; - }; -}; - -/* Define synthetic interrupt controller message header. */ -struct hv_message_header { - enum hv_message_type message_type; - u8 payload_size; - union hv_message_flags message_flags; - u8 reserved[2]; - union { - u64 sender; - union hv_port_id port; - }; -}; - -/* Define timer message payload structure. */ -struct hv_timer_message_payload { - u32 timer_index; - u32 reserved; - u64 expiration_time; /* When the timer expired */ - u64 delivery_time; /* When the message was delivered */ -}; - -/* Define synthetic interrupt controller message format. */ -struct hv_message { - struct hv_message_header header; - union { - u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT]; - } u ; -}; - -/* Define the number of message buffers associated with each port. */ -#define HV_PORT_MESSAGE_BUFFER_COUNT (16) - -/* Define the synthetic interrupt message page layout. */ -struct hv_message_page { - struct hv_message sint_message[HV_SYNIC_SINT_COUNT]; -}; - -/* Define the synthetic interrupt controller event flags format. */ -union hv_synic_event_flags { - u8 flags8[HV_EVENT_FLAGS_BYTE_COUNT]; - u32 flags32[HV_EVENT_FLAGS_DWORD_COUNT]; -}; - -/* Define the synthetic interrupt flags page layout. */ -struct hv_synic_event_flags_page { - union hv_synic_event_flags sintevent_flags[HV_SYNIC_SINT_COUNT]; -}; - -/* Define SynIC control register. */ -union hv_synic_scontrol { - u64 as_uint64; - struct { - u64 enable:1; - u64 reserved:63; - }; -}; - -/* Define synthetic interrupt source. */ -union hv_synic_sint { - u64 as_uint64; - struct { - u64 vector:8; - u64 reserved1:8; - u64 masked:1; - u64 auto_eoi:1; - u64 reserved2:46; - }; -}; - -/* Define the format of the SIMP register */ -union hv_synic_simp { - u64 as_uint64; - struct { - u64 simp_enabled:1; - u64 preserved:11; - u64 base_simp_gpa:52; - }; -}; - -/* Define the format of the SIEFP register */ -union hv_synic_siefp { - u64 as_uint64; - struct { - u64 siefp_enabled:1; - u64 preserved:11; - u64 base_siefp_gpa:52; - }; -}; - -/* Definitions for the monitored notification facility */ -union hv_monitor_trigger_group { - u64 as_uint64; - struct { - u32 pending; - u32 armed; - }; -}; - -struct hv_monitor_parameter { - union hv_connection_id connectionid; - u16 flagnumber; - u16 rsvdz; -}; - -union hv_monitor_trigger_state { - u32 asu32; - - struct { - u32 group_enable:4; - u32 rsvdz:28; - }; -}; - -/* struct hv_monitor_page Layout */ -/* ------------------------------------------------------ */ -/* | 0 | TriggerState (4 bytes) | Rsvd1 (4 bytes) | */ -/* | 8 | TriggerGroup[0] | */ -/* | 10 | TriggerGroup[1] | */ -/* | 18 | TriggerGroup[2] | */ -/* | 20 | TriggerGroup[3] | */ -/* | 28 | Rsvd2[0] | */ -/* | 30 | Rsvd2[1] | */ -/* | 38 | Rsvd2[2] | */ -/* | 40 | NextCheckTime[0][0] | NextCheckTime[0][1] | */ -/* | ... | */ -/* | 240 | Latency[0][0..3] | */ -/* | 340 | Rsvz3[0] | */ -/* | 440 | Parameter[0][0] | */ -/* | 448 | Parameter[0][1] | */ -/* | ... | */ -/* | 840 | Rsvd4[0] | */ -/* ------------------------------------------------------ */ -struct hv_monitor_page { - union hv_monitor_trigger_state trigger_state; - u32 rsvdz1; - - union hv_monitor_trigger_group trigger_group[4]; - u64 rsvdz2[3]; - - s32 next_checktime[4][32]; - - u16 latency[4][32]; - u64 rsvdz3[32]; - - struct hv_monitor_parameter parameter[4][32]; - - u8 rsvdz4[1984]; -}; - -/* Declare the various hypercall operations. */ -enum hv_call_code { - HVCALL_POST_MESSAGE = 0x005c, - HVCALL_SIGNAL_EVENT = 0x005d, -}; - -/* Definition of the hv_post_message hypercall input structure. */ -struct hv_input_post_message { - union hv_connection_id connectionid; - u32 reserved; - enum hv_message_type message_type; - u32 payload_size; - u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT]; -}; - -/* Definition of the hv_signal_event hypercall input structure. */ -struct hv_input_signal_event { - union hv_connection_id connectionid; - u16 flag_number; - u16 rsvdz; -}; - -/* - * Versioning definitions used for guests reporting themselves to the - * hypervisor, and visa versa. - */ - -/* Version info reported by guest OS's */ -enum hv_guest_os_vendor { - HVGUESTOS_VENDOR_MICROSOFT = 0x0001 -}; - -enum hv_guest_os_microsoft_ids { - HVGUESTOS_MICROSOFT_UNDEFINED = 0x00, - HVGUESTOS_MICROSOFT_MSDOS = 0x01, - HVGUESTOS_MICROSOFT_WINDOWS3X = 0x02, - HVGUESTOS_MICROSOFT_WINDOWS9X = 0x03, - HVGUESTOS_MICROSOFT_WINDOWSNT = 0x04, - HVGUESTOS_MICROSOFT_WINDOWSCE = 0x05 -}; - -/* - * Declare the MSR used to identify the guest OS. - */ -#define HV_X64_MSR_GUEST_OS_ID 0x40000000 - -union hv_x64_msr_guest_os_id_contents { - u64 as_uint64; - struct { - u64 build_number:16; - u64 service_version:8; /* Service Pack, etc. */ - u64 minor_version:8; - u64 major_version:8; - u64 os_id:8; /* enum hv_guest_os_microsoft_ids (if Vendor=MS) */ - u64 vendor_id:16; /* enum hv_guest_os_vendor */ - }; -}; - -/* - * Declare the MSR used to setup pages used to communicate with the hypervisor. - */ -#define HV_X64_MSR_HYPERCALL 0x40000001 - -union hv_x64_msr_hypercall_contents { - u64 as_uint64; - struct { - u64 enable:1; - u64 reserved:11; - u64 guest_physical_address:52; - }; -}; - -#endif diff --git a/drivers/staging/hv/hv_kvp.c b/drivers/staging/hv/hv_kvp.c index faf692e4126e..13b0ecf7d5d6 100644 --- a/drivers/staging/hv/hv_kvp.c +++ b/drivers/staging/hv/hv_kvp.c @@ -20,23 +20,14 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * */ - +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/net.h> #include <linux/nls.h> #include <linux/connector.h> #include <linux/workqueue.h> -#include "logging.h" -#include "hv_api.h" -#include "vmbus.h" -#include "vmbus_packet_format.h" -#include "vmbus_channel_interface.h" -#include "version_info.h" -#include "channel.h" -#include "vmbus_private.h" -#include "vmbus_api.h" -#include "utils.h" +#include "hyperv.h" #include "hv_kvp.h" @@ -114,7 +105,7 @@ kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) message = (struct hv_ku_msg *)msg->data; if (msg->seq == KVP_REGISTER) { - printk(KERN_INFO "KVP: user-mode registering done.\n"); + pr_info("KVP: user-mode registering done.\n"); kvp_register(); } @@ -174,7 +165,7 @@ kvp_respond_to_host(char *key, char *value, int error) /* * This is a spurious call! */ - printk(KERN_WARNING "KVP: Transaction not active\n"); + pr_warn("KVP: Transaction not active\n"); return; } /* @@ -259,9 +250,6 @@ void hv_kvp_onchannelcallback(void *context) vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE, &recvlen, &requestid); if (recvlen > 0) { - DPRINT_DBG(VMBUS, "KVP packet: len=%d, requestid=%lld", - recvlen, requestid); - icmsghdrp = (struct icmsg_hdr *)&recv_buffer[ sizeof(struct vmbuspipe_hdr)]; diff --git a/drivers/staging/hv/hv_mouse.c b/drivers/staging/hv/hv_mouse.c index 118c7be22562..359e73741c48 100644 --- a/drivers/staging/hv/hv_mouse.c +++ b/drivers/staging/hv/hv_mouse.c @@ -26,13 +26,7 @@ #include <linux/dmi.h> #include <linux/delay.h> -#include "hv_api.h" -#include "logging.h" -#include "version_info.h" -#include "vmbus.h" -#include "vmbus_api.h" -#include "channel.h" -#include "vmbus_packet_format.h" +#include "hyperv.h" /* @@ -45,13 +39,6 @@ struct hv_input_dev_info { char name[128]; }; -/* Represents the input vsc driver */ -/* FIXME - can be removed entirely */ -struct mousevsc_drv_obj { - struct hv_driver Base; -}; - - /* The maximum size of a synthetic input message. */ #define SYNTHHID_MAX_INPUT_REPORT_SIZE 16 @@ -169,23 +156,23 @@ struct mousevsc_prt_msg { * Represents an mousevsc device */ struct mousevsc_dev { - struct hv_device *Device; + struct hv_device *device; /* 0 indicates the device is being destroyed */ - atomic_t RefCount; - int NumOutstandingRequests; - unsigned char bInitializeComplete; - struct mousevsc_prt_msg ProtocolReq; - struct mousevsc_prt_msg ProtocolResp; + atomic_t ref_count; + int num_outstanding_req; + unsigned char init_complete; + struct mousevsc_prt_msg protocol_req; + struct mousevsc_prt_msg protocol_resp; /* Synchronize the request/response if needed */ - wait_queue_head_t ProtocolWaitEvent; - wait_queue_head_t DeviceInfoWaitEvent; + wait_queue_head_t protocol_wait_event; + wait_queue_head_t dev_info_wait_event; int protocol_wait_condition; int device_wait_condition; - int DeviceInfoStatus; + int dev_info_status; - struct hid_descriptor *HidDesc; - unsigned char *ReportDesc; - u32 ReportDescSize; + struct hid_descriptor *hid_desc; + unsigned char *report_desc; + u32 report_desc_size; struct hv_input_dev_info hid_dev_info; }; @@ -202,41 +189,41 @@ static void deviceinfo_callback(struct hv_device *dev, struct hv_input_dev_info static void inputreport_callback(struct hv_device *dev, void *packet, u32 len); static void reportdesc_callback(struct hv_device *dev, void *packet, u32 len); -static struct mousevsc_dev *AllocInputDevice(struct hv_device *Device) +static struct mousevsc_dev *alloc_input_device(struct hv_device *device) { - struct mousevsc_dev *inputDevice; + struct mousevsc_dev *input_dev; - inputDevice = kzalloc(sizeof(struct mousevsc_dev), GFP_KERNEL); + input_dev = kzalloc(sizeof(struct mousevsc_dev), GFP_KERNEL); - if (!inputDevice) + if (!input_dev) return NULL; /* * Set to 2 to allow both inbound and outbound traffics - * (ie GetInputDevice() and MustGetInputDevice()) to proceed. + * (ie get_input_device() and must_get_input_device()) to proceed. */ - atomic_cmpxchg(&inputDevice->RefCount, 0, 2); + atomic_cmpxchg(&input_dev->ref_count, 0, 2); - inputDevice->Device = Device; - Device->ext = inputDevice; + input_dev->device = device; + device->ext = input_dev; - return inputDevice; + return input_dev; } -static void FreeInputDevice(struct mousevsc_dev *Device) +static void free_input_device(struct mousevsc_dev *device) { - WARN_ON(atomic_read(&Device->RefCount) == 0); - kfree(Device); + WARN_ON(atomic_read(&device->ref_count) == 0); + kfree(device); } /* * Get the inputdevice object if exists and its refcount > 1 */ -static struct mousevsc_dev *GetInputDevice(struct hv_device *Device) +static struct mousevsc_dev *get_input_device(struct hv_device *device) { - struct mousevsc_dev *inputDevice; + struct mousevsc_dev *input_dev; - inputDevice = (struct mousevsc_dev *)Device->ext; + input_dev = (struct mousevsc_dev *)device->ext; /* * FIXME @@ -244,134 +231,137 @@ static struct mousevsc_dev *GetInputDevice(struct hv_device *Device) * what the intention is... * * printk(KERN_ERR "-------------------------> REFCOUNT = %d", - * inputDevice->RefCount); + * input_dev->ref_count); */ - if (inputDevice && atomic_read(&inputDevice->RefCount) > 1) - atomic_inc(&inputDevice->RefCount); + if (input_dev && atomic_read(&input_dev->ref_count) > 1) + atomic_inc(&input_dev->ref_count); else - inputDevice = NULL; + input_dev = NULL; - return inputDevice; + return input_dev; } /* * Get the inputdevice object iff exists and its refcount > 0 */ -static struct mousevsc_dev *MustGetInputDevice(struct hv_device *Device) +static struct mousevsc_dev *must_get_input_device(struct hv_device *device) { - struct mousevsc_dev *inputDevice; + struct mousevsc_dev *input_dev; - inputDevice = (struct mousevsc_dev *)Device->ext; + input_dev = (struct mousevsc_dev *)device->ext; - if (inputDevice && atomic_read(&inputDevice->RefCount)) - atomic_inc(&inputDevice->RefCount); + if (input_dev && atomic_read(&input_dev->ref_count)) + atomic_inc(&input_dev->ref_count); else - inputDevice = NULL; + input_dev = NULL; - return inputDevice; + return input_dev; } -static void PutInputDevice(struct hv_device *Device) +static void put_input_device(struct hv_device *device) { - struct mousevsc_dev *inputDevice; + struct mousevsc_dev *input_dev; - inputDevice = (struct mousevsc_dev *)Device->ext; + input_dev = (struct mousevsc_dev *)device->ext; - atomic_dec(&inputDevice->RefCount); + atomic_dec(&input_dev->ref_count); } /* - * Drop ref count to 1 to effectively disable GetInputDevice() + * Drop ref count to 1 to effectively disable get_input_device() */ -static struct mousevsc_dev *ReleaseInputDevice(struct hv_device *Device) +static struct mousevsc_dev *release_input_device(struct hv_device *device) { - struct mousevsc_dev *inputDevice; + struct mousevsc_dev *input_dev; - inputDevice = (struct mousevsc_dev *)Device->ext; + input_dev = (struct mousevsc_dev *)device->ext; /* Busy wait until the ref drop to 2, then set it to 1 */ - while (atomic_cmpxchg(&inputDevice->RefCount, 2, 1) != 2) + while (atomic_cmpxchg(&input_dev->ref_count, 2, 1) != 2) udelay(100); - return inputDevice; + return input_dev; } /* - * Drop ref count to 0. No one can use InputDevice object. + * Drop ref count to 0. No one can use input_device object. */ -static struct mousevsc_dev *FinalReleaseInputDevice(struct hv_device *Device) +static struct mousevsc_dev *final_release_input_device(struct hv_device *device) { - struct mousevsc_dev *inputDevice; + struct mousevsc_dev *input_dev; - inputDevice = (struct mousevsc_dev *)Device->ext; + input_dev = (struct mousevsc_dev *)device->ext; /* Busy wait until the ref drop to 1, then set it to 0 */ - while (atomic_cmpxchg(&inputDevice->RefCount, 1, 0) != 1) + while (atomic_cmpxchg(&input_dev->ref_count, 1, 0) != 1) udelay(100); - Device->ext = NULL; - return inputDevice; + device->ext = NULL; + return input_dev; } -static void MousevscOnSendCompletion(struct hv_device *Device, struct vmpacket_descriptor *Packet) +static void mousevsc_on_send_completion(struct hv_device *device, + struct vmpacket_descriptor *packet) { - struct mousevsc_dev *inputDevice; + struct mousevsc_dev *input_dev; void *request; - inputDevice = MustGetInputDevice(Device); - if (!inputDevice) { + input_dev = must_get_input_device(device); + if (!input_dev) { pr_err("unable to get input device...device being destroyed?"); return; } - request = (void *)(unsigned long)Packet->trans_id; + request = (void *)(unsigned long)packet->trans_id; - if (request == &inputDevice->ProtocolReq) { + if (request == &input_dev->protocol_req) { /* FIXME */ /* Shouldn't we be doing something here? */ } - PutInputDevice(Device); + put_input_device(device); } -static void MousevscOnReceiveDeviceInfo(struct mousevsc_dev *InputDevice, struct synthhid_device_info *DeviceInfo) +static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, + struct synthhid_device_info *device_info) { int ret = 0; struct hid_descriptor *desc; struct mousevsc_prt_msg ack; /* Assume success for now */ - InputDevice->DeviceInfoStatus = 0; + input_device->dev_info_status = 0; /* Save the device attr */ - memcpy(&InputDevice->hid_dev_info, &DeviceInfo->hid_dev_info, sizeof(struct hv_input_dev_info)); + memcpy(&input_device->hid_dev_info, &device_info->hid_dev_info, + sizeof(struct hv_input_dev_info)); /* Save the hid desc */ - desc = &DeviceInfo->hid_descriptor; + desc = &device_info->hid_descriptor; WARN_ON(desc->bLength > 0); - InputDevice->HidDesc = kzalloc(desc->bLength, GFP_KERNEL); + input_device->hid_desc = kzalloc(desc->bLength, GFP_KERNEL); - if (!InputDevice->HidDesc) { + if (!input_device->hid_desc) { pr_err("unable to allocate hid descriptor - size %d", desc->bLength); goto Cleanup; } - memcpy(InputDevice->HidDesc, desc, desc->bLength); + memcpy(input_device->hid_desc, desc, desc->bLength); /* Save the report desc */ - InputDevice->ReportDescSize = desc->desc[0].wDescriptorLength; - InputDevice->ReportDesc = kzalloc(InputDevice->ReportDescSize, + input_device->report_desc_size = desc->desc[0].wDescriptorLength; + input_device->report_desc = kzalloc(input_device->report_desc_size, GFP_KERNEL); - if (!InputDevice->ReportDesc) { + if (!input_device->report_desc) { pr_err("unable to allocate report descriptor - size %d", - InputDevice->ReportDescSize); + input_device->report_desc_size); goto Cleanup; } - memcpy(InputDevice->ReportDesc, + memcpy(input_device->report_desc, ((unsigned char *)desc) + desc->bLength, desc->desc[0].wDescriptorLength); @@ -385,7 +375,7 @@ static void MousevscOnReceiveDeviceInfo(struct mousevsc_dev *InputDevice, struct ack.ack.header.size = 1; ack.ack.reserved = 0; - ret = vmbus_sendpacket(InputDevice->Device->channel, + ret = vmbus_sendpacket(input_device->device->channel, &ack, sizeof(struct pipe_prt_msg) - sizeof(unsigned char) + sizeof(struct synthhid_device_info_ack), @@ -398,138 +388,143 @@ static void MousevscOnReceiveDeviceInfo(struct mousevsc_dev *InputDevice, struct goto Cleanup; } - InputDevice->device_wait_condition = 1; - wake_up(&InputDevice->DeviceInfoWaitEvent); + input_device->device_wait_condition = 1; + wake_up(&input_device->dev_info_wait_event); return; Cleanup: - kfree(InputDevice->HidDesc); - InputDevice->HidDesc = NULL; + kfree(input_device->hid_desc); + input_device->hid_desc = NULL; - kfree(InputDevice->ReportDesc); - InputDevice->ReportDesc = NULL; + kfree(input_device->report_desc); + input_device->report_desc = NULL; - InputDevice->DeviceInfoStatus = -1; - InputDevice->device_wait_condition = 1; - wake_up(&InputDevice->DeviceInfoWaitEvent); + input_device->dev_info_status = -1; + input_device->device_wait_condition = 1; + wake_up(&input_device->dev_info_wait_event); } -static void MousevscOnReceiveInputReport(struct mousevsc_dev *InputDevice, struct synthhid_input_report *InputReport) +static void mousevsc_on_receive_input_report(struct mousevsc_dev *input_device, + struct synthhid_input_report *input_report) { - struct mousevsc_drv_obj *inputDriver; + struct hv_driver *input_drv; - if (!InputDevice->bInitializeComplete) { - pr_info("Initialization incomplete...ignoring InputReport msg"); + if (!input_device->init_complete) { + pr_info("Initialization incomplete...ignoring input_report msg"); return; } - inputDriver = (struct mousevsc_drv_obj *)InputDevice->Device->drv; + input_drv = drv_to_hv_drv(input_device->device->device.driver); - inputreport_callback(InputDevice->Device, - InputReport->buffer, - InputReport->header.size); + inputreport_callback(input_device->device, + input_report->buffer, + input_report->header.size); } -static void MousevscOnReceive(struct hv_device *Device, struct vmpacket_descriptor *Packet) +static void mousevsc_on_receive(struct hv_device *device, + struct vmpacket_descriptor *packet) { - struct pipe_prt_msg *pipeMsg; - struct synthhid_msg *hidMsg; - struct mousevsc_dev *inputDevice; + struct pipe_prt_msg *pipe_msg; + struct synthhid_msg *hid_msg; + struct mousevsc_dev *input_dev; - inputDevice = MustGetInputDevice(Device); - if (!inputDevice) { + input_dev = must_get_input_device(device); + if (!input_dev) { pr_err("unable to get input device...device being destroyed?"); return; } - pipeMsg = (struct pipe_prt_msg *)((unsigned long)Packet + (Packet->offset8 << 3)); + pipe_msg = (struct pipe_prt_msg *)((unsigned long)packet + + (packet->offset8 << 3)); - if (pipeMsg->type != PipeMessageData) { + if (pipe_msg->type != PipeMessageData) { pr_err("unknown pipe msg type - type %d len %d", - pipeMsg->type, pipeMsg->size); - PutInputDevice(Device); + pipe_msg->type, pipe_msg->size); + put_input_device(device); return ; } - hidMsg = (struct synthhid_msg *)&pipeMsg->data[0]; + hid_msg = (struct synthhid_msg *)&pipe_msg->data[0]; - switch (hidMsg->header.type) { + switch (hid_msg->header.type) { case SynthHidProtocolResponse: - memcpy(&inputDevice->ProtocolResp, pipeMsg, - pipeMsg->size + sizeof(struct pipe_prt_msg) - + memcpy(&input_dev->protocol_resp, pipe_msg, + pipe_msg->size + sizeof(struct pipe_prt_msg) - sizeof(unsigned char)); - inputDevice->protocol_wait_condition = 1; - wake_up(&inputDevice->ProtocolWaitEvent); + input_dev->protocol_wait_condition = 1; + wake_up(&input_dev->protocol_wait_event); break; case SynthHidInitialDeviceInfo: - WARN_ON(pipeMsg->size >= sizeof(struct hv_input_dev_info)); + WARN_ON(pipe_msg->size >= sizeof(struct hv_input_dev_info)); /* * Parse out the device info into device attr, * hid desc and report desc */ - MousevscOnReceiveDeviceInfo(inputDevice, - (struct synthhid_device_info *)&pipeMsg->data[0]); + mousevsc_on_receive_device_info(input_dev, + (struct synthhid_device_info *)&pipe_msg->data[0]); break; case SynthHidInputReport: - MousevscOnReceiveInputReport(inputDevice, - (struct synthhid_input_report *)&pipeMsg->data[0]); + mousevsc_on_receive_input_report(input_dev, + (struct synthhid_input_report *)&pipe_msg->data[0]); break; default: pr_err("unsupported hid msg type - type %d len %d", - hidMsg->header.type, hidMsg->header.size); + hid_msg->header.type, hid_msg->header.size); break; } - PutInputDevice(Device); + put_input_device(device); } -static void MousevscOnChannelCallback(void *Context) +static void mousevsc_on_channel_callback(void *context) { const int packetSize = 0x100; int ret = 0; - struct hv_device *device = (struct hv_device *)Context; - struct mousevsc_dev *inputDevice; + struct hv_device *device = (struct hv_device *)context; + struct mousevsc_dev *input_dev; - u32 bytesRecvd; - u64 requestId; - unsigned char packet[packetSize]; + u32 bytes_recvd; + u64 req_id; + unsigned char packet[0x100]; struct vmpacket_descriptor *desc; unsigned char *buffer = packet; int bufferlen = packetSize; - inputDevice = MustGetInputDevice(device); + input_dev = must_get_input_device(device); - if (!inputDevice) { + if (!input_dev) { pr_err("unable to get input device...device being destroyed?"); return; } do { - ret = vmbus_recvpacket_raw(device->channel, buffer, bufferlen, &bytesRecvd, &requestId); + ret = vmbus_recvpacket_raw(device->channel, buffer, + bufferlen, &bytes_recvd, &req_id); if (ret == 0) { - if (bytesRecvd > 0) { + if (bytes_recvd > 0) { desc = (struct vmpacket_descriptor *)buffer; switch (desc->type) { case VM_PKT_COMP: - MousevscOnSendCompletion(device, - desc); + mousevsc_on_send_completion( + device, desc); break; case VM_PKT_DATA_INBAND: - MousevscOnReceive(device, desc); + mousevsc_on_receive( + device, desc); break; default: pr_err("unhandled packet type %d, tid %llx len %d\n", desc->type, - requestId, - bytesRecvd); + req_id, + bytes_recvd); break; } @@ -555,8 +550,8 @@ static void MousevscOnChannelCallback(void *Context) } } else if (ret == -2) { /* Handle large packet */ - bufferlen = bytesRecvd; - buffer = kzalloc(bytesRecvd, GFP_KERNEL); + bufferlen = bytes_recvd; + buffer = kzalloc(bytes_recvd, GFP_KERNEL); if (buffer == NULL) { buffer = packet; @@ -564,35 +559,35 @@ static void MousevscOnChannelCallback(void *Context) /* Try again next time around */ pr_err("unable to allocate buffer of size %d!", - bytesRecvd); + bytes_recvd); break; } } } while (1); - PutInputDevice(device); + put_input_device(device); return; } -static int MousevscConnectToVsp(struct hv_device *Device) +static int mousevsc_connect_to_vsp(struct hv_device *device) { int ret = 0; - struct mousevsc_dev *inputDevice; + struct mousevsc_dev *input_dev; struct mousevsc_prt_msg *request; struct mousevsc_prt_msg *response; - inputDevice = GetInputDevice(Device); + input_dev = get_input_device(device); - if (!inputDevice) { + if (!input_dev) { pr_err("unable to get input device...device being destroyed?"); return -1; } - init_waitqueue_head(&inputDevice->ProtocolWaitEvent); - init_waitqueue_head(&inputDevice->DeviceInfoWaitEvent); + init_waitqueue_head(&input_dev->protocol_wait_event); + init_waitqueue_head(&input_dev->dev_info_wait_event); - request = &inputDevice->ProtocolReq; + request = &input_dev->protocol_req; /* * Now, initiate the vsc/vsp initialization protocol on the open channel @@ -608,7 +603,7 @@ static int MousevscConnectToVsp(struct hv_device *Device) pr_info("synthhid protocol request..."); - ret = vmbus_sendpacket(Device->channel, request, + ret = vmbus_sendpacket(device->channel, request, sizeof(struct pipe_prt_msg) - sizeof(unsigned char) + sizeof(struct synthhid_protocol_request), @@ -620,14 +615,15 @@ static int MousevscConnectToVsp(struct hv_device *Device) goto Cleanup; } - inputDevice->protocol_wait_condition = 0; - wait_event_timeout(inputDevice->ProtocolWaitEvent, inputDevice->protocol_wait_condition, msecs_to_jiffies(1000)); - if (inputDevice->protocol_wait_condition == 0) { + input_dev->protocol_wait_condition = 0; + wait_event_timeout(input_dev->protocol_wait_event, + input_dev->protocol_wait_condition, msecs_to_jiffies(1000)); + if (input_dev->protocol_wait_condition == 0) { ret = -ETIMEDOUT; goto Cleanup; } - response = &inputDevice->ProtocolResp; + response = &input_dev->protocol_resp; if (!response->response.approved) { pr_err("synthhid protocol request failed (version %d)", @@ -636,9 +632,10 @@ static int MousevscConnectToVsp(struct hv_device *Device) goto Cleanup; } - inputDevice->device_wait_condition = 0; - wait_event_timeout(inputDevice->DeviceInfoWaitEvent, inputDevice->device_wait_condition, msecs_to_jiffies(1000)); - if (inputDevice->device_wait_condition == 0) { + input_dev->device_wait_condition = 0; + wait_event_timeout(input_dev->dev_info_wait_event, + input_dev->device_wait_condition, msecs_to_jiffies(1000)); + if (input_dev->device_wait_condition == 0) { ret = -ETIMEDOUT; goto Cleanup; } @@ -647,94 +644,95 @@ static int MousevscConnectToVsp(struct hv_device *Device) * We should have gotten the device attr, hid desc and report * desc at this point */ - if (!inputDevice->DeviceInfoStatus) + if (!input_dev->dev_info_status) pr_info("**** input channel up and running!! ****"); else ret = -1; Cleanup: - PutInputDevice(Device); + put_input_device(device); return ret; } -static int MousevscOnDeviceAdd(struct hv_device *Device, void *AdditionalInfo) +static int mousevsc_on_device_add(struct hv_device *device, + void *additional_info) { int ret = 0; - struct mousevsc_dev *inputDevice; - struct mousevsc_drv_obj *inputDriver; + struct mousevsc_dev *input_dev; + struct hv_driver *input_drv; struct hv_input_dev_info dev_info; - inputDevice = AllocInputDevice(Device); + input_dev = alloc_input_device(device); - if (!inputDevice) { + if (!input_dev) { ret = -1; goto Cleanup; } - inputDevice->bInitializeComplete = false; + input_dev->init_complete = false; /* Open the channel */ - ret = vmbus_open(Device->channel, + ret = vmbus_open(device->channel, INPUTVSC_SEND_RING_BUFFER_SIZE, INPUTVSC_RECV_RING_BUFFER_SIZE, NULL, 0, - MousevscOnChannelCallback, - Device + mousevsc_on_channel_callback, + device ); if (ret != 0) { pr_err("unable to open channel: %d", ret); - FreeInputDevice(inputDevice); + free_input_device(input_dev); return -1; } pr_info("InputVsc channel open: %d", ret); - ret = MousevscConnectToVsp(Device); + ret = mousevsc_connect_to_vsp(device); if (ret != 0) { pr_err("unable to connect channel: %d", ret); - vmbus_close(Device->channel); - FreeInputDevice(inputDevice); + vmbus_close(device->channel); + free_input_device(input_dev); return ret; } - inputDriver = (struct mousevsc_drv_obj *)inputDevice->Device->drv; + input_drv = drv_to_hv_drv(input_dev->device->device.driver); - dev_info.vendor = inputDevice->hid_dev_info.vendor; - dev_info.product = inputDevice->hid_dev_info.product; - dev_info.version = inputDevice->hid_dev_info.version; + dev_info.vendor = input_dev->hid_dev_info.vendor; + dev_info.product = input_dev->hid_dev_info.product; + dev_info.version = input_dev->hid_dev_info.version; strcpy(dev_info.name, "Microsoft Vmbus HID-compliant Mouse"); /* Send the device info back up */ - deviceinfo_callback(Device, &dev_info); + deviceinfo_callback(device, &dev_info); /* Send the report desc back up */ /* workaround SA-167 */ - if (inputDevice->ReportDesc[14] == 0x25) - inputDevice->ReportDesc[14] = 0x29; + if (input_dev->report_desc[14] == 0x25) + input_dev->report_desc[14] = 0x29; - reportdesc_callback(Device, inputDevice->ReportDesc, - inputDevice->ReportDescSize); + reportdesc_callback(device, input_dev->report_desc, + input_dev->report_desc_size); - inputDevice->bInitializeComplete = true; + input_dev->init_complete = true; Cleanup: return ret; } -static int MousevscOnDeviceRemove(struct hv_device *Device) +static int mousevsc_on_device_remove(struct hv_device *device) { - struct mousevsc_dev *inputDevice; + struct mousevsc_dev *input_dev; int ret = 0; pr_info("disabling input device (%p)...", - Device->ext); + device->ext); - inputDevice = ReleaseInputDevice(Device); + input_dev = release_input_device(device); /* @@ -743,29 +741,27 @@ static int MousevscOnDeviceRemove(struct hv_device *Device) * * so that outstanding requests can be completed. */ - while (inputDevice->NumOutstandingRequests) { - pr_info("waiting for %d requests to complete...", inputDevice->NumOutstandingRequests); + while (input_dev->num_outstanding_req) { + pr_info("waiting for %d requests to complete...", + input_dev->num_outstanding_req); udelay(100); } - pr_info("removing input device (%p)...", Device->ext); + pr_info("removing input device (%p)...", device->ext); - inputDevice = FinalReleaseInputDevice(Device); + input_dev = final_release_input_device(device); - pr_info("input device (%p) safe to remove", inputDevice); + pr_info("input device (%p) safe to remove", input_dev); /* Close the channel */ - vmbus_close(Device->channel); + vmbus_close(device->channel); - FreeInputDevice(inputDevice); + free_input_device(input_dev); return ret; } -static void MousevscOnCleanup(struct hv_driver *drv) -{ -} /* * Data types @@ -778,8 +774,6 @@ struct input_device_context { }; -static struct mousevsc_drv_obj g_mousevsc_drv; - static void deviceinfo_callback(struct hv_device *dev, struct hv_input_dev_info *info) { struct input_device_context *input_device_ctx = @@ -813,24 +807,19 @@ static void mousevsc_hid_close(struct hid_device *hid) { } -static int mousevsc_probe(struct device *device) +static int mousevsc_probe(struct hv_device *dev) { int ret = 0; - struct hv_driver *drv = - drv_to_hv_drv(device->driver); - struct mousevsc_drv_obj *mousevsc_drv_obj = drv->priv; - - struct hv_device *device_obj = device_to_hv_device(device); struct input_device_context *input_dev_ctx; input_dev_ctx = kmalloc(sizeof(struct input_device_context), GFP_KERNEL); - dev_set_drvdata(device, input_dev_ctx); + dev_set_drvdata(&dev->device, input_dev_ctx); /* Call to the vsc driver to add the device */ - ret = mousevsc_drv_obj->Base.dev_add(device_obj, NULL); + ret = mousevsc_on_device_add(dev, NULL); if (ret != 0) { DPRINT_ERR(INPUTVSC_DRV, "unable to add input vsc device"); @@ -841,35 +830,27 @@ static int mousevsc_probe(struct device *device) return 0; } -static int mousevsc_remove(struct device *device) +static int mousevsc_remove(struct hv_device *dev) { int ret = 0; - struct hv_driver *drv = - drv_to_hv_drv(device->driver); - struct mousevsc_drv_obj *mousevsc_drv_obj = drv->priv; - - struct hv_device *device_obj = device_to_hv_device(device); struct input_device_context *input_dev_ctx; input_dev_ctx = kmalloc(sizeof(struct input_device_context), GFP_KERNEL); - dev_set_drvdata(device, input_dev_ctx); + dev_set_drvdata(&dev->device, input_dev_ctx); if (input_dev_ctx->connected) { hidinput_disconnect(input_dev_ctx->hid_device); input_dev_ctx->connected = 0; } - if (!mousevsc_drv_obj->Base.dev_rm) - return -1; - /* * Call to the vsc driver to let it know that the device * is being removed */ - ret = mousevsc_drv_obj->Base.dev_rm(device_obj); + ret = mousevsc_on_device_remove(dev); if (ret != 0) { DPRINT_ERR(INPUTVSC_DRV, @@ -934,81 +915,28 @@ static void reportdesc_callback(struct hv_device *dev, void *packet, u32 len) kfree(hid_dev); } -static int mousevsc_drv_exit_cb(struct device *dev, void *data) -{ - struct device **curr = (struct device **)data; - *curr = dev; - return 1; -} +static struct hv_driver mousevsc_drv = { + .probe = mousevsc_probe, + .remove = mousevsc_remove, +}; static void mousevsc_drv_exit(void) { - struct mousevsc_drv_obj *mousevsc_drv_obj = &g_mousevsc_drv; - struct hv_driver *drv = &g_mousevsc_drv.Base; - int ret; - - struct device *current_dev = NULL; - - while (1) { - current_dev = NULL; - - /* Get the device */ - ret = driver_for_each_device(&drv->driver, NULL, - (void *)¤t_dev, - mousevsc_drv_exit_cb); - if (ret) - printk(KERN_ERR "Can't find mouse device!\n"); - - if (current_dev == NULL) - break; - - /* Initiate removal from the top-down */ - device_unregister(current_dev); - } - - if (mousevsc_drv_obj->Base.cleanup) - mousevsc_drv_obj->Base.cleanup(&mousevsc_drv_obj->Base); - - vmbus_child_driver_unregister(&drv->driver); - - return; + vmbus_child_driver_unregister(&mousevsc_drv.driver); } -static int mouse_vsc_initialize(struct hv_driver *Driver) -{ - struct mousevsc_drv_obj *inputDriver = - (struct mousevsc_drv_obj *)Driver; - int ret = 0; - - Driver->name = driver_name; - memcpy(&Driver->dev_type, &mouse_guid, - sizeof(struct hv_guid)); - - /* Setup the dispatch table */ - inputDriver->Base.dev_add = MousevscOnDeviceAdd; - inputDriver->Base.dev_rm = MousevscOnDeviceRemove; - inputDriver->Base.cleanup = MousevscOnCleanup; - - return ret; -} - - static int __init mousevsc_init(void) { - struct mousevsc_drv_obj *input_drv_obj = &g_mousevsc_drv; - struct hv_driver *drv = &g_mousevsc_drv.Base; + struct hv_driver *drv = &mousevsc_drv; DPRINT_INFO(INPUTVSC_DRV, "Hyper-V Mouse driver initializing."); - /* Callback to client driver to complete the initialization */ - mouse_vsc_initialize(&input_drv_obj->Base); - - drv->driver.name = input_drv_obj->Base.name; - drv->priv = input_drv_obj; + memcpy(&drv->dev_type, &mouse_guid, + sizeof(struct hv_guid)); - drv->driver.probe = mousevsc_probe; - drv->driver.remove = mousevsc_remove; + drv->driver.name = driver_name; + drv->name = driver_name; /* The driver belongs to vmbus */ vmbus_child_driver_register(&drv->driver); diff --git a/drivers/staging/hv/hv_timesource.c b/drivers/staging/hv/hv_timesource.c index a7ee533303b4..0efb04915255 100644 --- a/drivers/staging/hv/hv_timesource.c +++ b/drivers/staging/hv/hv_timesource.c @@ -20,6 +20,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/version.h> #include <linux/clocksource.h> @@ -91,7 +92,7 @@ static int __init init_hv_clocksource(void) if (!dmi_check_system(hv_timesource_dmi_table)) return -ENODEV; - printk(KERN_INFO "Registering HyperV clock source\n"); + pr_info("Registering HyperV clock source\n"); return clocksource_register(&hyperv_cs); } diff --git a/drivers/staging/hv/hv_util.c b/drivers/staging/hv/hv_util.c index 2df15683f8fa..c164b54b4cd7 100644 --- a/drivers/staging/hv/hv_util.c +++ b/drivers/staging/hv/hv_util.c @@ -18,6 +18,8 @@ * Haiyang Zhang <haiyangz@microsoft.com> * Hank Janssen <hjanssen@microsoft.com> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> @@ -27,16 +29,7 @@ #include <linux/dmi.h> #include <linux/pci.h> -#include "logging.h" -#include "hv_api.h" -#include "vmbus.h" -#include "vmbus_packet_format.h" -#include "vmbus_channel_interface.h" -#include "version_info.h" -#include "channel.h" -#include "vmbus_private.h" -#include "vmbus_api.h" -#include "utils.h" +#include "hyperv.h" #include "hv_kvp.h" static u8 *shut_txf_buf; @@ -59,9 +52,6 @@ static void shutdown_onchannelcallback(void *context) PAGE_SIZE, &recvlen, &requestid); if (recvlen > 0) { - DPRINT_DBG(VMBUS, "shutdown packet: len=%d, requestid=%lld", - recvlen, requestid); - icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[ sizeof(struct vmbuspipe_hdr)]; @@ -79,17 +69,17 @@ static void shutdown_onchannelcallback(void *context) icmsghdrp->status = HV_S_OK; execute_shutdown = true; - DPRINT_INFO(VMBUS, "Shutdown request received -" - " graceful shutdown initiated"); + pr_info("Shutdown request received -" + " graceful shutdown initiated\n"); break; default: icmsghdrp->status = HV_E_FAIL; execute_shutdown = false; - DPRINT_INFO(VMBUS, "Shutdown request received -" - " Invalid request"); + pr_info("Shutdown request received -" + " Invalid request\n"); break; - }; + } } icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION @@ -159,9 +149,6 @@ static void timesync_onchannelcallback(void *context) PAGE_SIZE, &recvlen, &requestid); if (recvlen > 0) { - DPRINT_DBG(VMBUS, "timesync packet: recvlen=%d, requestid=%lld", - recvlen, requestid); - icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[ sizeof(struct vmbuspipe_hdr)]; @@ -200,9 +187,6 @@ static void heartbeat_onchannelcallback(void *context) PAGE_SIZE, &recvlen, &requestid); if (recvlen > 0) { - DPRINT_DBG(VMBUS, "heartbeat packet: len=%d, requestid=%lld", - recvlen, requestid); - icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[ sizeof(struct vmbuspipe_hdr)]; @@ -214,9 +198,6 @@ static void heartbeat_onchannelcallback(void *context) sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)]; - DPRINT_DBG(VMBUS, "heartbeat seq = %lld", - heartbeat_msg->seq_num); - heartbeat_msg->seq_num += 1; } @@ -254,7 +235,7 @@ MODULE_DEVICE_TABLE(dmi, hv_utils_dmi_table); static int __init init_hyperv_utils(void) { - printk(KERN_INFO "Registering HyperV Utility Driver\n"); + pr_info("Registering HyperV Utility Driver\n"); if (hv_kvp_init()) return -ENODEV; @@ -268,52 +249,48 @@ static int __init init_hyperv_utils(void) hbeat_txf_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!shut_txf_buf || !time_txf_buf || !hbeat_txf_buf) { - printk(KERN_INFO - "Unable to allocate memory for receive buffer\n"); + pr_info("Unable to allocate memory for receive buffer\n"); kfree(shut_txf_buf); kfree(time_txf_buf); kfree(hbeat_txf_buf); return -ENOMEM; } - hv_cb_utils[HV_SHUTDOWN_MSG].channel->onchannel_callback = - &shutdown_onchannelcallback; hv_cb_utils[HV_SHUTDOWN_MSG].callback = &shutdown_onchannelcallback; - hv_cb_utils[HV_TIMESYNC_MSG].channel->onchannel_callback = - ×ync_onchannelcallback; hv_cb_utils[HV_TIMESYNC_MSG].callback = ×ync_onchannelcallback; - hv_cb_utils[HV_HEARTBEAT_MSG].channel->onchannel_callback = - &heartbeat_onchannelcallback; hv_cb_utils[HV_HEARTBEAT_MSG].callback = &heartbeat_onchannelcallback; - hv_cb_utils[HV_KVP_MSG].channel->onchannel_callback = - &hv_kvp_onchannelcallback; - - + hv_cb_utils[HV_KVP_MSG].callback = &hv_kvp_onchannelcallback; return 0; } static void exit_hyperv_utils(void) { - printk(KERN_INFO "De-Registered HyperV Utility Driver\n"); + pr_info("De-Registered HyperV Utility Driver\n"); + + if (hv_cb_utils[HV_SHUTDOWN_MSG].channel != NULL) + hv_cb_utils[HV_SHUTDOWN_MSG].channel->onchannel_callback = + &chn_cb_negotiate; + hv_cb_utils[HV_SHUTDOWN_MSG].callback = NULL; - hv_cb_utils[HV_SHUTDOWN_MSG].channel->onchannel_callback = - &chn_cb_negotiate; - hv_cb_utils[HV_SHUTDOWN_MSG].callback = &chn_cb_negotiate; + if (hv_cb_utils[HV_TIMESYNC_MSG].channel != NULL) + hv_cb_utils[HV_TIMESYNC_MSG].channel->onchannel_callback = + &chn_cb_negotiate; + hv_cb_utils[HV_TIMESYNC_MSG].callback = NULL; - hv_cb_utils[HV_TIMESYNC_MSG].channel->onchannel_callback = - &chn_cb_negotiate; - hv_cb_utils[HV_TIMESYNC_MSG].callback = &chn_cb_negotiate; + if (hv_cb_utils[HV_HEARTBEAT_MSG].channel != NULL) + hv_cb_utils[HV_HEARTBEAT_MSG].channel->onchannel_callback = + &chn_cb_negotiate; + hv_cb_utils[HV_HEARTBEAT_MSG].callback = NULL; - hv_cb_utils[HV_HEARTBEAT_MSG].channel->onchannel_callback = - &chn_cb_negotiate; - hv_cb_utils[HV_HEARTBEAT_MSG].callback = &chn_cb_negotiate; + if (hv_cb_utils[HV_KVP_MSG].channel != NULL) + hv_cb_utils[HV_KVP_MSG].channel->onchannel_callback = + &chn_cb_negotiate; + hv_cb_utils[HV_KVP_MSG].callback = NULL; - hv_cb_utils[HV_KVP_MSG].channel->onchannel_callback = - &chn_cb_negotiate; hv_kvp_deinit(); kfree(shut_txf_buf); diff --git a/drivers/staging/hv/hyperv.h b/drivers/staging/hv/hyperv.h new file mode 100644 index 000000000000..3310e9bdf562 --- /dev/null +++ b/drivers/staging/hv/hyperv.h @@ -0,0 +1,944 @@ +/* + * + * Copyright (c) 2011, Microsoft Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Authors: + * Haiyang Zhang <haiyangz@microsoft.com> + * Hank Janssen <hjanssen@microsoft.com> + * K. Y. Srinivasan <kys@microsoft.com> + * + */ + +#ifndef _HYPERV_H +#define _HYPERV_H + +#include <linux/scatterlist.h> +#include <linux/list.h> +#include <linux/timer.h> +#include <linux/workqueue.h> +#include <linux/completion.h> +#include <linux/device.h> + + +#include <asm/hyperv.h> + +struct hv_guid { + unsigned char data[16]; +}; + +#define MAX_PAGE_BUFFER_COUNT 16 +#define MAX_MULTIPAGE_BUFFER_COUNT 32 /* 128K */ + +#pragma pack(push, 1) + +/* Single-page buffer */ +struct hv_page_buffer { + u32 len; + u32 offset; + u64 pfn; +}; + +/* Multiple-page buffer */ +struct hv_multipage_buffer { + /* Length and Offset determines the # of pfns in the array */ + u32 len; + u32 offset; + u64 pfn_array[MAX_MULTIPAGE_BUFFER_COUNT]; +}; + +/* 0x18 includes the proprietary packet header */ +#define MAX_PAGE_BUFFER_PACKET (0x18 + \ + (sizeof(struct hv_page_buffer) * \ + MAX_PAGE_BUFFER_COUNT)) +#define MAX_MULTIPAGE_BUFFER_PACKET (0x18 + \ + sizeof(struct hv_multipage_buffer)) + + +#pragma pack(pop) + +struct hv_ring_buffer { + /* Offset in bytes from the start of ring data below */ + u32 write_index; + + /* Offset in bytes from the start of ring data below */ + u32 read_index; + + u32 interrupt_mask; + + /* Pad it to PAGE_SIZE so that data starts on page boundary */ + u8 reserved[4084]; + + /* NOTE: + * The interrupt_mask field is used only for channels but since our + * vmbus connection also uses this data structure and its data starts + * here, we commented out this field. + */ + + /* + * Ring data starts here + RingDataStartOffset + * !!! DO NOT place any fields below this !!! + */ + u8 buffer[0]; +} __packed; + +struct hv_ring_buffer_info { + struct hv_ring_buffer *ring_buffer; + u32 ring_size; /* Include the shared header */ + spinlock_t ring_lock; + + u32 ring_datasize; /* < ring_size */ + u32 ring_data_startoffset; +}; + +struct hv_ring_buffer_debug_info { + u32 current_interrupt_mask; + u32 current_read_index; + u32 current_write_index; + u32 bytes_avail_toread; + u32 bytes_avail_towrite; +}; + +/* + * We use the same version numbering for all Hyper-V modules. + * + * Definition of versioning is as follows; + * + * Major Number Changes for these scenarios; + * 1. When a new version of Windows Hyper-V + * is released. + * 2. A Major change has occurred in the + * Linux IC's. + * (For example the merge for the first time + * into the kernel) Every time the Major Number + * changes, the Revision number is reset to 0. + * Minor Number Changes when new functionality is added + * to the Linux IC's that is not a bug fix. + * + * 3.1 - Added completed hv_utils driver. Shutdown/Heartbeat/Timesync + */ +#define HV_DRV_VERSION "3.1" + + +/* + * A revision number of vmbus that is used for ensuring both ends on a + * partition are using compatible versions. + */ +#define VMBUS_REVISION_NUMBER 13 + +/* Make maximum size of pipe payload of 16K */ +#define MAX_PIPE_DATA_PAYLOAD (sizeof(u8) * 16384) + +/* Define PipeMode values. */ +#define VMBUS_PIPE_TYPE_BYTE 0x00000000 +#define VMBUS_PIPE_TYPE_MESSAGE 0x00000004 + +/* The size of the user defined data buffer for non-pipe offers. */ +#define MAX_USER_DEFINED_BYTES 120 + +/* The size of the user defined data buffer for pipe offers. */ +#define MAX_PIPE_USER_DEFINED_BYTES 116 + +/* + * At the center of the Channel Management library is the Channel Offer. This + * struct contains the fundamental information about an offer. + */ +struct vmbus_channel_offer { + struct hv_guid if_type; + struct hv_guid if_instance; + u64 int_latency; /* in 100ns units */ + u32 if_revision; + u32 server_ctx_size; /* in bytes */ + u16 chn_flags; + u16 mmio_megabytes; /* in bytes * 1024 * 1024 */ + + union { + /* Non-pipes: The user has MAX_USER_DEFINED_BYTES bytes. */ + struct { + unsigned char user_def[MAX_USER_DEFINED_BYTES]; + } std; + + /* + * Pipes: + * The following sructure is an integrated pipe protocol, which + * is implemented on top of standard user-defined data. Pipe + * clients have MAX_PIPE_USER_DEFINED_BYTES left for their own + * use. + */ + struct { + u32 pipe_mode; + unsigned char user_def[MAX_PIPE_USER_DEFINED_BYTES]; + } pipe; + } u; + u32 padding; +} __packed; + +/* Server Flags */ +#define VMBUS_CHANNEL_ENUMERATE_DEVICE_INTERFACE 1 +#define VMBUS_CHANNEL_SERVER_SUPPORTS_TRANSFER_PAGES 2 +#define VMBUS_CHANNEL_SERVER_SUPPORTS_GPADLS 4 +#define VMBUS_CHANNEL_NAMED_PIPE_MODE 0x10 +#define VMBUS_CHANNEL_LOOPBACK_OFFER 0x100 +#define VMBUS_CHANNEL_PARENT_OFFER 0x200 +#define VMBUS_CHANNEL_REQUEST_MONITORED_NOTIFICATION 0x400 + +struct vmpacket_descriptor { + u16 type; + u16 offset8; + u16 len8; + u16 flags; + u64 trans_id; +} __packed; + +struct vmpacket_header { + u32 prev_pkt_start_offset; + struct vmpacket_descriptor descriptor; +} __packed; + +struct vmtransfer_page_range { + u32 byte_count; + u32 byte_offset; +} __packed; + +struct vmtransfer_page_packet_header { + struct vmpacket_descriptor d; + u16 xfer_pageset_id; + bool sender_owns_set; + u8 reserved; + u32 range_cnt; + struct vmtransfer_page_range ranges[1]; +} __packed; + +struct vmgpadl_packet_header { + struct vmpacket_descriptor d; + u32 gpadl; + u32 reserved; +} __packed; + +struct vmadd_remove_transfer_page_set { + struct vmpacket_descriptor d; + u32 gpadl; + u16 xfer_pageset_id; + u16 reserved; +} __packed; + +/* + * This structure defines a range in guest physical space that can be made to + * look virtually contiguous. + */ +struct gpa_range { + u32 byte_count; + u32 byte_offset; + u64 pfn_array[0]; +}; + +/* + * This is the format for an Establish Gpadl packet, which contains a handle by + * which this GPADL will be known and a set of GPA ranges associated with it. + * This can be converted to a MDL by the guest OS. If there are multiple GPA + * ranges, then the resulting MDL will be "chained," representing multiple VA + * ranges. + */ +struct vmestablish_gpadl { + struct vmpacket_descriptor d; + u32 gpadl; + u32 range_cnt; + struct gpa_range range[1]; +} __packed; + +/* + * This is the format for a Teardown Gpadl packet, which indicates that the + * GPADL handle in the Establish Gpadl packet will never be referenced again. + */ +struct vmteardown_gpadl { + struct vmpacket_descriptor d; + u32 gpadl; + u32 reserved; /* for alignment to a 8-byte boundary */ +} __packed; + +/* + * This is the format for a GPA-Direct packet, which contains a set of GPA + * ranges, in addition to commands and/or data. + */ +struct vmdata_gpa_direct { + struct vmpacket_descriptor d; + u32 reserved; + u32 range_cnt; + struct gpa_range range[1]; +} __packed; + +/* This is the format for a Additional Data Packet. */ +struct vmadditional_data { + struct vmpacket_descriptor d; + u64 total_bytes; + u32 offset; + u32 byte_cnt; + unsigned char data[1]; +} __packed; + +union vmpacket_largest_possible_header { + struct vmpacket_descriptor simple_hdr; + struct vmtransfer_page_packet_header xfer_page_hdr; + struct vmgpadl_packet_header gpadl_hdr; + struct vmadd_remove_transfer_page_set add_rm_xfer_page_hdr; + struct vmestablish_gpadl establish_gpadl_hdr; + struct vmteardown_gpadl teardown_gpadl_hdr; + struct vmdata_gpa_direct data_gpa_direct_hdr; +}; + +#define VMPACKET_DATA_START_ADDRESS(__packet) \ + (void *)(((unsigned char *)__packet) + \ + ((struct vmpacket_descriptor)__packet)->offset8 * 8) + +#define VMPACKET_DATA_LENGTH(__packet) \ + ((((struct vmpacket_descriptor)__packet)->len8 - \ + ((struct vmpacket_descriptor)__packet)->offset8) * 8) + +#define VMPACKET_TRANSFER_MODE(__packet) \ + (((struct IMPACT)__packet)->type) + +enum vmbus_packet_type { + VM_PKT_INVALID = 0x0, + VM_PKT_SYNCH = 0x1, + VM_PKT_ADD_XFER_PAGESET = 0x2, + VM_PKT_RM_XFER_PAGESET = 0x3, + VM_PKT_ESTABLISH_GPADL = 0x4, + VM_PKT_TEARDOWN_GPADL = 0x5, + VM_PKT_DATA_INBAND = 0x6, + VM_PKT_DATA_USING_XFER_PAGES = 0x7, + VM_PKT_DATA_USING_GPADL = 0x8, + VM_PKT_DATA_USING_GPA_DIRECT = 0x9, + VM_PKT_CANCEL_REQUEST = 0xa, + VM_PKT_COMP = 0xb, + VM_PKT_DATA_USING_ADDITIONAL_PKT = 0xc, + VM_PKT_ADDITIONAL_DATA = 0xd +}; + +#define VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED 1 + + +/* Version 1 messages */ +enum vmbus_channel_message_type { + CHANNELMSG_INVALID = 0, + CHANNELMSG_OFFERCHANNEL = 1, + CHANNELMSG_RESCIND_CHANNELOFFER = 2, + CHANNELMSG_REQUESTOFFERS = 3, + CHANNELMSG_ALLOFFERS_DELIVERED = 4, + CHANNELMSG_OPENCHANNEL = 5, + CHANNELMSG_OPENCHANNEL_RESULT = 6, + CHANNELMSG_CLOSECHANNEL = 7, + CHANNELMSG_GPADL_HEADER = 8, + CHANNELMSG_GPADL_BODY = 9, + CHANNELMSG_GPADL_CREATED = 10, + CHANNELMSG_GPADL_TEARDOWN = 11, + CHANNELMSG_GPADL_TORNDOWN = 12, + CHANNELMSG_RELID_RELEASED = 13, + CHANNELMSG_INITIATE_CONTACT = 14, + CHANNELMSG_VERSION_RESPONSE = 15, + CHANNELMSG_UNLOAD = 16, +#ifdef VMBUS_FEATURE_PARENT_OR_PEER_MEMORY_MAPPED_INTO_A_CHILD + CHANNELMSG_VIEWRANGE_ADD = 17, + CHANNELMSG_VIEWRANGE_REMOVE = 18, +#endif + CHANNELMSG_COUNT +}; + +struct vmbus_channel_message_header { + enum vmbus_channel_message_type msgtype; + u32 padding; +} __packed; + +/* Query VMBus Version parameters */ +struct vmbus_channel_query_vmbus_version { + struct vmbus_channel_message_header header; + u32 version; +} __packed; + +/* VMBus Version Supported parameters */ +struct vmbus_channel_version_supported { + struct vmbus_channel_message_header header; + bool version_supported; +} __packed; + +/* Offer Channel parameters */ +struct vmbus_channel_offer_channel { + struct vmbus_channel_message_header header; + struct vmbus_channel_offer offer; + u32 child_relid; + u8 monitorid; + bool monitor_allocated; +} __packed; + +/* Rescind Offer parameters */ +struct vmbus_channel_rescind_offer { + struct vmbus_channel_message_header header; + u32 child_relid; +} __packed; + +/* + * Request Offer -- no parameters, SynIC message contains the partition ID + * Set Snoop -- no parameters, SynIC message contains the partition ID + * Clear Snoop -- no parameters, SynIC message contains the partition ID + * All Offers Delivered -- no parameters, SynIC message contains the partition + * ID + * Flush Client -- no parameters, SynIC message contains the partition ID + */ + +/* Open Channel parameters */ +struct vmbus_channel_open_channel { + struct vmbus_channel_message_header header; + + /* Identifies the specific VMBus channel that is being opened. */ + u32 child_relid; + + /* ID making a particular open request at a channel offer unique. */ + u32 openid; + + /* GPADL for the channel's ring buffer. */ + u32 ringbuffer_gpadlhandle; + + /* GPADL for the channel's server context save area. */ + u32 server_contextarea_gpadlhandle; + + /* + * The upstream ring buffer begins at offset zero in the memory + * described by RingBufferGpadlHandle. The downstream ring buffer + * follows it at this offset (in pages). + */ + u32 downstream_ringbuffer_pageoffset; + + /* User-specific data to be passed along to the server endpoint. */ + unsigned char userdata[MAX_USER_DEFINED_BYTES]; +} __packed; + +/* Open Channel Result parameters */ +struct vmbus_channel_open_result { + struct vmbus_channel_message_header header; + u32 child_relid; + u32 openid; + u32 status; +} __packed; + +/* Close channel parameters; */ +struct vmbus_channel_close_channel { + struct vmbus_channel_message_header header; + u32 child_relid; +} __packed; + +/* Channel Message GPADL */ +#define GPADL_TYPE_RING_BUFFER 1 +#define GPADL_TYPE_SERVER_SAVE_AREA 2 +#define GPADL_TYPE_TRANSACTION 8 + +/* + * The number of PFNs in a GPADL message is defined by the number of + * pages that would be spanned by ByteCount and ByteOffset. If the + * implied number of PFNs won't fit in this packet, there will be a + * follow-up packet that contains more. + */ +struct vmbus_channel_gpadl_header { + struct vmbus_channel_message_header header; + u32 child_relid; + u32 gpadl; + u16 range_buflen; + u16 rangecount; + struct gpa_range range[0]; +} __packed; + +/* This is the followup packet that contains more PFNs. */ +struct vmbus_channel_gpadl_body { + struct vmbus_channel_message_header header; + u32 msgnumber; + u32 gpadl; + u64 pfn[0]; +} __packed; + +struct vmbus_channel_gpadl_created { + struct vmbus_channel_message_header header; + u32 child_relid; + u32 gpadl; + u32 creation_status; +} __packed; + +struct vmbus_channel_gpadl_teardown { + struct vmbus_channel_message_header header; + u32 child_relid; + u32 gpadl; +} __packed; + +struct vmbus_channel_gpadl_torndown { + struct vmbus_channel_message_header header; + u32 gpadl; +} __packed; + +#ifdef VMBUS_FEATURE_PARENT_OR_PEER_MEMORY_MAPPED_INTO_A_CHILD +struct vmbus_channel_view_range_add { + struct vmbus_channel_message_header header; + PHYSICAL_ADDRESS viewrange_base; + u64 viewrange_length; + u32 child_relid; +} __packed; + +struct vmbus_channel_view_range_remove { + struct vmbus_channel_message_header header; + PHYSICAL_ADDRESS viewrange_base; + u32 child_relid; +} __packed; +#endif + +struct vmbus_channel_relid_released { + struct vmbus_channel_message_header header; + u32 child_relid; +} __packed; + +struct vmbus_channel_initiate_contact { + struct vmbus_channel_message_header header; + u32 vmbus_version_requested; + u32 padding2; + u64 interrupt_page; + u64 monitor_page1; + u64 monitor_page2; +} __packed; + +struct vmbus_channel_version_response { + struct vmbus_channel_message_header header; + bool version_supported; +} __packed; + +enum vmbus_channel_state { + CHANNEL_OFFER_STATE, + CHANNEL_OPENING_STATE, + CHANNEL_OPEN_STATE, +}; + +struct vmbus_channel { + struct list_head listentry; + + struct hv_device *device_obj; + + struct timer_list poll_timer; /* SA-111 workaround */ + struct work_struct work; + + enum vmbus_channel_state state; + /* + * For util channels, stash the + * the service index for easy access. + */ + s8 util_index; + + struct vmbus_channel_offer_channel offermsg; + /* + * These are based on the OfferMsg.MonitorId. + * Save it here for easy access. + */ + u8 monitor_grp; + u8 monitor_bit; + + u32 ringbuffer_gpadlhandle; + + /* Allocated memory for ring buffer */ + void *ringbuffer_pages; + u32 ringbuffer_pagecount; + struct hv_ring_buffer_info outbound; /* send to parent */ + struct hv_ring_buffer_info inbound; /* receive from parent */ + spinlock_t inbound_lock; + struct workqueue_struct *controlwq; + + /* Channel callback are invoked in this workqueue context */ + /* HANDLE dataWorkQueue; */ + + void (*onchannel_callback)(void *context); + void *channel_callback_context; +}; + +struct vmbus_channel_debug_info { + u32 relid; + enum vmbus_channel_state state; + struct hv_guid interfacetype; + struct hv_guid interface_instance; + u32 monitorid; + u32 servermonitor_pending; + u32 servermonitor_latency; + u32 servermonitor_connectionid; + u32 clientmonitor_pending; + u32 clientmonitor_latency; + u32 clientmonitor_connectionid; + + struct hv_ring_buffer_debug_info inbound; + struct hv_ring_buffer_debug_info outbound; +}; + +/* + * Represents each channel msg on the vmbus connection This is a + * variable-size data structure depending on the msg type itself + */ +struct vmbus_channel_msginfo { + /* Bookkeeping stuff */ + struct list_head msglistentry; + + /* So far, this is only used to handle gpadl body message */ + struct list_head submsglist; + + /* Synchronize the request/response if needed */ + struct completion waitevent; + union { + struct vmbus_channel_version_supported version_supported; + struct vmbus_channel_open_result open_result; + struct vmbus_channel_gpadl_torndown gpadl_torndown; + struct vmbus_channel_gpadl_created gpadl_created; + struct vmbus_channel_version_response version_response; + } response; + + u32 msgsize; + /* + * The channel message that goes out on the "wire". + * It will contain at minimum the VMBUS_CHANNEL_MESSAGE_HEADER header + */ + unsigned char msg[0]; +}; + + +void free_channel(struct vmbus_channel *channel); + +void vmbus_onmessage(void *context); + +int vmbus_request_offers(void); + +/* The format must be the same as struct vmdata_gpa_direct */ +struct vmbus_channel_packet_page_buffer { + u16 type; + u16 dataoffset8; + u16 length8; + u16 flags; + u64 transactionid; + u32 reserved; + u32 rangecount; + struct hv_page_buffer range[MAX_PAGE_BUFFER_COUNT]; +} __packed; + +/* The format must be the same as struct vmdata_gpa_direct */ +struct vmbus_channel_packet_multipage_buffer { + u16 type; + u16 dataoffset8; + u16 length8; + u16 flags; + u64 transactionid; + u32 reserved; + u32 rangecount; /* Always 1 in this case */ + struct hv_multipage_buffer range; +} __packed; + + +extern int vmbus_open(struct vmbus_channel *channel, + u32 send_ringbuffersize, + u32 recv_ringbuffersize, + void *userdata, + u32 userdatalen, + void(*onchannel_callback)(void *context), + void *context); + +extern void vmbus_close(struct vmbus_channel *channel); + +extern int vmbus_sendpacket(struct vmbus_channel *channel, + const void *buffer, + u32 bufferLen, + u64 requestid, + enum vmbus_packet_type type, + u32 flags); + +extern int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, + struct hv_page_buffer pagebuffers[], + u32 pagecount, + void *buffer, + u32 bufferlen, + u64 requestid); + +extern int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, + struct hv_multipage_buffer *mpb, + void *buffer, + u32 bufferlen, + u64 requestid); + +extern int vmbus_establish_gpadl(struct vmbus_channel *channel, + void *kbuffer, + u32 size, + u32 *gpadl_handle); + +extern int vmbus_teardown_gpadl(struct vmbus_channel *channel, + u32 gpadl_handle); + +extern int vmbus_recvpacket(struct vmbus_channel *channel, + void *buffer, + u32 bufferlen, + u32 *buffer_actual_len, + u64 *requestid); + +extern int vmbus_recvpacket_raw(struct vmbus_channel *channel, + void *buffer, + u32 bufferlen, + u32 *buffer_actual_len, + u64 *requestid); + +extern void vmbus_onchannel_event(struct vmbus_channel *channel); + +extern void vmbus_get_debug_info(struct vmbus_channel *channel, + struct vmbus_channel_debug_info *debug); + +extern void vmbus_ontimer(unsigned long data); + + +#define LOWORD(dw) ((unsigned short)(dw)) +#define HIWORD(dw) ((unsigned short)(((unsigned int) (dw) >> 16) & 0xFFFF)) + + +#define VMBUS 0x0001 +#define STORVSC 0x0002 +#define NETVSC 0x0004 +#define INPUTVSC 0x0008 +#define BLKVSC 0x0010 +#define VMBUS_DRV 0x0100 +#define STORVSC_DRV 0x0200 +#define NETVSC_DRV 0x0400 +#define INPUTVSC_DRV 0x0800 +#define BLKVSC_DRV 0x1000 + +#define ALL_MODULES (VMBUS |\ + STORVSC |\ + NETVSC |\ + INPUTVSC |\ + BLKVSC |\ + VMBUS_DRV |\ + STORVSC_DRV |\ + NETVSC_DRV |\ + INPUTVSC_DRV|\ + BLKVSC_DRV) + +/* Logging Level */ +#define ERROR_LVL 3 +#define WARNING_LVL 4 +#define INFO_LVL 6 +#define DEBUG_LVL 7 +#define DEBUG_LVL_ENTEREXIT 8 +#define DEBUG_RING_LVL 9 + +extern unsigned int vmbus_loglevel; + +#define DPRINT(mod, lvl, fmt, args...) do {\ + if ((mod & (HIWORD(vmbus_loglevel))) && \ + (lvl <= LOWORD(vmbus_loglevel))) \ + printk(KERN_DEBUG #mod": %s() " fmt "\n", __func__, ## args);\ + } while (0) + +#define DPRINT_DBG(mod, fmt, args...) do {\ + if ((mod & (HIWORD(vmbus_loglevel))) && \ + (DEBUG_LVL <= LOWORD(vmbus_loglevel))) \ + printk(KERN_DEBUG #mod": %s() " fmt "\n", __func__, ## args);\ + } while (0) + +#define DPRINT_INFO(mod, fmt, args...) do {\ + if ((mod & (HIWORD(vmbus_loglevel))) && \ + (INFO_LVL <= LOWORD(vmbus_loglevel))) \ + printk(KERN_INFO #mod": " fmt "\n", ## args);\ + } while (0) + +#define DPRINT_WARN(mod, fmt, args...) do {\ + if ((mod & (HIWORD(vmbus_loglevel))) && \ + (WARNING_LVL <= LOWORD(vmbus_loglevel))) \ + printk(KERN_WARNING #mod": WARNING! " fmt "\n", ## args);\ + } while (0) + +#define DPRINT_ERR(mod, fmt, args...) do {\ + if ((mod & (HIWORD(vmbus_loglevel))) && \ + (ERROR_LVL <= LOWORD(vmbus_loglevel))) \ + printk(KERN_ERR #mod": %s() ERROR!! " fmt "\n", \ + __func__, ## args);\ + } while (0) + + + +struct hv_driver; +struct hv_device; + +struct hv_dev_port_info { + u32 int_mask; + u32 read_idx; + u32 write_idx; + u32 bytes_avail_toread; + u32 bytes_avail_towrite; +}; + +struct hv_device_info { + u32 chn_id; + u32 chn_state; + struct hv_guid chn_type; + struct hv_guid chn_instance; + + u32 monitor_id; + u32 server_monitor_pending; + u32 server_monitor_latency; + u32 server_monitor_conn_id; + u32 client_monitor_pending; + u32 client_monitor_latency; + u32 client_monitor_conn_id; + + struct hv_dev_port_info inbound; + struct hv_dev_port_info outbound; +}; + +/* Base driver object */ +struct hv_driver { + const char *name; + + /* the device type supported by this driver */ + struct hv_guid dev_type; + + struct device_driver driver; + + int (*probe)(struct hv_device *); + int (*remove)(struct hv_device *); + void (*shutdown)(struct hv_device *); + +}; + +/* Base device object */ +struct hv_device { + /* the device type id of this device */ + struct hv_guid dev_type; + + /* the device instance id of this device */ + struct hv_guid dev_instance; + + struct device device; + + struct vmbus_channel *channel; + + /* Device extension; */ + void *ext; +}; + + +static inline struct hv_device *device_to_hv_device(struct device *d) +{ + return container_of(d, struct hv_device, device); +} + +static inline struct hv_driver *drv_to_hv_drv(struct device_driver *d) +{ + return container_of(d, struct hv_driver, driver); +} + + +/* Vmbus interface */ +int vmbus_child_driver_register(struct device_driver *drv); +void vmbus_child_driver_unregister(struct device_driver *drv); + +/* + * Common header for Hyper-V ICs + */ + +#define ICMSGTYPE_NEGOTIATE 0 +#define ICMSGTYPE_HEARTBEAT 1 +#define ICMSGTYPE_KVPEXCHANGE 2 +#define ICMSGTYPE_SHUTDOWN 3 +#define ICMSGTYPE_TIMESYNC 4 +#define ICMSGTYPE_VSS 5 + +#define ICMSGHDRFLAG_TRANSACTION 1 +#define ICMSGHDRFLAG_REQUEST 2 +#define ICMSGHDRFLAG_RESPONSE 4 + +#define HV_S_OK 0x00000000 +#define HV_E_FAIL 0x80004005 +#define HV_ERROR_NOT_SUPPORTED 0x80070032 +#define HV_ERROR_MACHINE_LOCKED 0x800704F7 + +struct vmbuspipe_hdr { + u32 flags; + u32 msgsize; +} __packed; + +struct ic_version { + u16 major; + u16 minor; +} __packed; + +struct icmsg_hdr { + struct ic_version icverframe; + u16 icmsgtype; + struct ic_version icvermsg; + u16 icmsgsize; + u32 status; + u8 ictransaction_id; + u8 icflags; + u8 reserved[2]; +} __packed; + +struct icmsg_negotiate { + u16 icframe_vercnt; + u16 icmsg_vercnt; + u32 reserved; + struct ic_version icversion_data[1]; /* any size array */ +} __packed; + +struct shutdown_msg_data { + u32 reason_code; + u32 timeout_seconds; + u32 flags; + u8 display_message[2048]; +} __packed; + +struct heartbeat_msg_data { + u64 seq_num; + u32 reserved[8]; +} __packed; + +/* Time Sync IC defs */ +#define ICTIMESYNCFLAG_PROBE 0 +#define ICTIMESYNCFLAG_SYNC 1 +#define ICTIMESYNCFLAG_SAMPLE 2 + +#ifdef __x86_64__ +#define WLTIMEDELTA 116444736000000000L /* in 100ns unit */ +#else +#define WLTIMEDELTA 116444736000000000LL +#endif + +struct ictimesync_data { + u64 parenttime; + u64 childtime; + u64 roundtriptime; + u8 flags; +} __packed; + +/* Index for each IC struct in array hv_cb_utils[] */ +#define HV_SHUTDOWN_MSG 0 +#define HV_TIMESYNC_MSG 1 +#define HV_HEARTBEAT_MSG 2 +#define HV_KVP_MSG 3 + +struct hyperv_service_callback { + u8 msg_type; + char *log_msg; + unsigned char data[16]; + struct vmbus_channel *channel; + void (*callback) (void *context); +}; + +extern void prep_negotiate_resp(struct icmsg_hdr *, + struct icmsg_negotiate *, u8 *); +extern void chn_cb_negotiate(void *); +extern struct hyperv_service_callback hv_cb_utils[]; + +#endif /* _HYPERV_H */ diff --git a/drivers/staging/hv/rndis.h b/drivers/staging/hv/hyperv_net.h index 014de047b86d..315097df799a 100644 --- a/drivers/staging/hv/rndis.h +++ b/drivers/staging/hv/hyperv_net.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2009, Microsoft Corporation. + * Copyright (c) 2011, Microsoft Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -18,11 +18,395 @@ * Authors: * Haiyang Zhang <haiyangz@microsoft.com> * Hank Janssen <hjanssen@microsoft.com> + * K. Y. Srinivasan <kys@microsoft.com> * */ -#ifndef _RNDIS_H_ -#define _RNDIS_H_ +#ifndef _HYPERV_NET_H +#define _HYPERV_NET_H + +#include <linux/list.h> +#include "hyperv.h" + +/* Fwd declaration */ +struct hv_netvsc_packet; + +/* Represent the xfer page packet which contains 1 or more netvsc packet */ +struct xferpage_packet { + struct list_head list_ent; + + /* # of netvsc packets this xfer packet contains */ + u32 count; +}; + +/* The number of pages which are enough to cover jumbo frame buffer. */ +#define NETVSC_PACKET_MAXPAGE 4 + +/* + * Represent netvsc packet which contains 1 RNDIS and 1 ethernet frame + * within the RNDIS + */ +struct hv_netvsc_packet { + /* Bookkeeping stuff */ + struct list_head list_ent; + + struct hv_device *device; + bool is_data_pkt; + + /* + * Valid only for receives when we break a xfer page packet + * into multiple netvsc packets + */ + struct xferpage_packet *xfer_page_pkt; + + union { + struct { + u64 recv_completion_tid; + void *recv_completion_ctx; + void (*recv_completion)(void *context); + } recv; + struct { + u64 send_completion_tid; + void *send_completion_ctx; + void (*send_completion)(void *context); + } send; + } completion; + + /* This points to the memory after page_buf */ + void *extension; + + u32 total_data_buflen; + /* Points to the send/receive buffer where the ethernet frame is */ + u32 page_buf_cnt; + struct hv_page_buffer page_buf[NETVSC_PACKET_MAXPAGE]; +}; + +struct netvsc_device_info { + unsigned char mac_adr[6]; + bool link_state; /* 0 - link up, 1 - link down */ + int ring_size; +}; + +/* Interface */ +int netvsc_device_add(struct hv_device *device, void *additional_info); +int netvsc_device_remove(struct hv_device *device); +int netvsc_send(struct hv_device *device, + struct hv_netvsc_packet *packet); +void netvsc_linkstatus_callback(struct hv_device *device_obj, + unsigned int status); +int netvsc_recv_callback(struct hv_device *device_obj, + struct hv_netvsc_packet *packet); +int netvsc_initialize(struct hv_driver *drv); +int rndis_filter_open(struct hv_device *dev); +int rndis_filter_close(struct hv_device *dev); +int rndis_filte_device_add(struct hv_device *dev, + void *additional_info); +int rndis_filter_device_remove(struct hv_device *dev); +int rndis_filter_receive(struct hv_device *dev, + struct hv_netvsc_packet *pkt); + + + +int rndis_filter_send(struct hv_device *dev, + struct hv_netvsc_packet *pkt); + +#define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF) + +#define NVSP_PROTOCOL_VERSION_1 2 +#define NVSP_MIN_PROTOCOL_VERSION NVSP_PROTOCOL_VERSION_1 +#define NVSP_MAX_PROTOCOL_VERSION NVSP_PROTOCOL_VERSION_1 + +enum { + NVSP_MSG_TYPE_NONE = 0, + + /* Init Messages */ + NVSP_MSG_TYPE_INIT = 1, + NVSP_MSG_TYPE_INIT_COMPLETE = 2, + + NVSP_VERSION_MSG_START = 100, + + /* Version 1 Messages */ + NVSP_MSG1_TYPE_SEND_NDIS_VER = NVSP_VERSION_MSG_START, + + NVSP_MSG1_TYPE_SEND_RECV_BUF, + NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE, + NVSP_MSG1_TYPE_REVOKE_RECV_BUF, + + NVSP_MSG1_TYPE_SEND_SEND_BUF, + NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE, + NVSP_MSG1_TYPE_REVOKE_SEND_BUF, + + NVSP_MSG1_TYPE_SEND_RNDIS_PKT, + NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE, + + /* + * This should be set to the number of messages for the version with + * the maximum number of messages. + */ + NVSP_NUM_MSG_PER_VERSION = 9, +}; + +enum { + NVSP_STAT_NONE = 0, + NVSP_STAT_SUCCESS, + NVSP_STAT_FAIL, + NVSP_STAT_PROTOCOL_TOO_NEW, + NVSP_STAT_PROTOCOL_TOO_OLD, + NVSP_STAT_INVALID_RNDIS_PKT, + NVSP_STAT_BUSY, + NVSP_STAT_MAX, +}; + +struct nvsp_message_header { + u32 msg_type; +}; + +/* Init Messages */ + +/* + * This message is used by the VSC to initialize the channel after the channels + * has been opened. This message should never include anything other then + * versioning (i.e. this message will be the same for ever). + */ +struct nvsp_message_init { + u32 min_protocol_ver; + u32 max_protocol_ver; +} __packed; + +/* + * This message is used by the VSP to complete the initialization of the + * channel. This message should never include anything other then versioning + * (i.e. this message will be the same for ever). + */ +struct nvsp_message_init_complete { + u32 negotiated_protocol_ver; + u32 max_mdl_chain_len; + u32 status; +} __packed; + +union nvsp_message_init_uber { + struct nvsp_message_init init; + struct nvsp_message_init_complete init_complete; +} __packed; + +/* Version 1 Messages */ + +/* + * This message is used by the VSC to send the NDIS version to the VSP. The VSP + * can use this information when handling OIDs sent by the VSC. + */ +struct nvsp_1_message_send_ndis_version { + u32 ndis_major_ver; + u32 ndis_minor_ver; +} __packed; + +/* + * This message is used by the VSC to send a receive buffer to the VSP. The VSP + * can then use the receive buffer to send data to the VSC. + */ +struct nvsp_1_message_send_receive_buffer { + u32 gpadl_handle; + u16 id; +} __packed; + +struct nvsp_1_receive_buffer_section { + u32 offset; + u32 sub_alloc_size; + u32 num_sub_allocs; + u32 end_offset; +} __packed; + +/* + * This message is used by the VSP to acknowledge a receive buffer send by the + * VSC. This message must be sent by the VSP before the VSP uses the receive + * buffer. + */ +struct nvsp_1_message_send_receive_buffer_complete { + u32 status; + u32 num_sections; + + /* + * The receive buffer is split into two parts, a large suballocation + * section and a small suballocation section. These sections are then + * suballocated by a certain size. + */ + + /* + * For example, the following break up of the receive buffer has 6 + * large suballocations and 10 small suballocations. + */ + + /* + * | Large Section | | Small Section | + * ------------------------------------------------------------ + * | | | | | | | | | | | | | | | | | | + * | | + * LargeOffset SmallOffset + */ + + struct nvsp_1_receive_buffer_section sections[1]; +} __packed; + +/* + * This message is sent by the VSC to revoke the receive buffer. After the VSP + * completes this transaction, the vsp should never use the receive buffer + * again. + */ +struct nvsp_1_message_revoke_receive_buffer { + u16 id; +}; + +/* + * This message is used by the VSC to send a send buffer to the VSP. The VSC + * can then use the send buffer to send data to the VSP. + */ +struct nvsp_1_message_send_send_buffer { + u32 gpadl_handle; + u16 id; +} __packed; + +/* + * This message is used by the VSP to acknowledge a send buffer sent by the + * VSC. This message must be sent by the VSP before the VSP uses the sent + * buffer. + */ +struct nvsp_1_message_send_send_buffer_complete { + u32 status; + + /* + * The VSC gets to choose the size of the send buffer and the VSP gets + * to choose the sections size of the buffer. This was done to enable + * dynamic reconfigurations when the cost of GPA-direct buffers + * decreases. + */ + u32 section_size; +} __packed; + +/* + * This message is sent by the VSC to revoke the send buffer. After the VSP + * completes this transaction, the vsp should never use the send buffer again. + */ +struct nvsp_1_message_revoke_send_buffer { + u16 id; +}; + +/* + * This message is used by both the VSP and the VSC to send a RNDIS message to + * the opposite channel endpoint. + */ +struct nvsp_1_message_send_rndis_packet { + /* + * This field is specified by RNIDS. They assume there's two different + * channels of communication. However, the Network VSP only has one. + * Therefore, the channel travels with the RNDIS packet. + */ + u32 channel_type; + + /* + * This field is used to send part or all of the data through a send + * buffer. This values specifies an index into the send buffer. If the + * index is 0xFFFFFFFF, then the send buffer is not being used and all + * of the data was sent through other VMBus mechanisms. + */ + u32 send_buf_section_index; + u32 send_buf_section_size; +} __packed; + +/* + * This message is used by both the VSP and the VSC to complete a RNDIS message + * to the opposite channel endpoint. At this point, the initiator of this + * message cannot use any resources associated with the original RNDIS packet. + */ +struct nvsp_1_message_send_rndis_packet_complete { + u32 status; +}; + +union nvsp_1_message_uber { + struct nvsp_1_message_send_ndis_version send_ndis_ver; + + struct nvsp_1_message_send_receive_buffer send_recv_buf; + struct nvsp_1_message_send_receive_buffer_complete + send_recv_buf_complete; + struct nvsp_1_message_revoke_receive_buffer revoke_recv_buf; + + struct nvsp_1_message_send_send_buffer send_send_buf; + struct nvsp_1_message_send_send_buffer_complete send_send_buf_complete; + struct nvsp_1_message_revoke_send_buffer revoke_send_buf; + + struct nvsp_1_message_send_rndis_packet send_rndis_pkt; + struct nvsp_1_message_send_rndis_packet_complete + send_rndis_pkt_complete; +} __packed; + +union nvsp_all_messages { + union nvsp_message_init_uber init_msg; + union nvsp_1_message_uber v1_msg; +} __packed; + +/* ALL Messages */ +struct nvsp_message { + struct nvsp_message_header hdr; + union nvsp_all_messages msg; +} __packed; + + + + +/* #define NVSC_MIN_PROTOCOL_VERSION 1 */ +/* #define NVSC_MAX_PROTOCOL_VERSION 1 */ + +#define NETVSC_SEND_BUFFER_SIZE (64*1024) /* 64K */ +#define NETVSC_SEND_BUFFER_ID 0xface + + +#define NETVSC_RECEIVE_BUFFER_SIZE (1024*1024) /* 1MB */ + +#define NETVSC_RECEIVE_BUFFER_ID 0xcafe + +#define NETVSC_RECEIVE_SG_COUNT 1 + +/* Preallocated receive packets */ +#define NETVSC_RECEIVE_PACKETLIST_COUNT 256 + +#define NETVSC_PACKET_SIZE 2048 + +/* Per netvsc channel-specific */ +struct netvsc_device { + struct hv_device *dev; + + atomic_t refcnt; + atomic_t num_outstanding_sends; + /* + * List of free preallocated hv_netvsc_packet to represent receive + * packet + */ + struct list_head recv_pkt_list; + spinlock_t recv_pkt_list_lock; + + /* Send buffer allocated by us but manages by NetVSP */ + void *send_buf; + u32 send_buf_size; + u32 send_buf_gpadl_handle; + u32 send_section_size; + + /* Receive buffer allocated by us but manages by NetVSP */ + void *recv_buf; + u32 recv_buf_size; + u32 recv_buf_gpadl_handle; + u32 recv_section_cnt; + struct nvsp_1_receive_buffer_section *recv_section; + + /* Used for NetVSP initialization protocol */ + struct completion channel_init_wait; + struct nvsp_message channel_init_pkt; + + struct nvsp_message revoke_packet; + /* unsigned char HwMacAddr[HW_MACADDR_LEN]; */ + + /* Holds rndis device info */ + void *extension; +}; + /* Status codes */ @@ -618,6 +1002,13 @@ struct rndis_message { union rndis_message_container msg; }; + +struct rndis_filter_packet { + void *completion_ctx; + void (*completion)(void *context); + struct rndis_message msg; +}; + /* Handy macros */ /* get the size of an RNDIS message. Pass in the message type, */ @@ -650,4 +1041,27 @@ struct rndis_message { #define RNDIS_MESSAGE_RAW_PTR_TO_MESSAGE_PTR(rndis_msg) \ ((void *) rndis_msg) -#endif /* _RNDIS_H_ */ + +#define __struct_bcount(x) + + + +#define RNDIS_HEADER_SIZE (sizeof(struct rndis_message) - \ + sizeof(union rndis_message_container)) + +#define NDIS_PACKET_TYPE_DIRECTED 0x00000001 +#define NDIS_PACKET_TYPE_MULTICAST 0x00000002 +#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 +#define NDIS_PACKET_TYPE_BROADCAST 0x00000008 +#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010 +#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020 +#define NDIS_PACKET_TYPE_SMT 0x00000040 +#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080 +#define NDIS_PACKET_TYPE_GROUP 0x00000100 +#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200 +#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 +#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 + + + +#endif /* _HYPERV_NET_H */ diff --git a/drivers/staging/hv/vstorage.h b/drivers/staging/hv/hyperv_storage.h index ebb4d671c424..a01f9a07c988 100644 --- a/drivers/staging/hv/vstorage.h +++ b/drivers/staging/hv/hyperv_storage.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2009, Microsoft Corporation. + * Copyright (c) 2011, Microsoft Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -18,13 +18,19 @@ * Authors: * Haiyang Zhang <haiyangz@microsoft.com> * Hank Janssen <hjanssen@microsoft.com> + * K. Y. Srinivasan <kys@microsoft.com> * */ +#ifndef _HYPERV_STORAGE_H +#define _HYPERV_STORAGE_H + + /* vstorage.w revision number. This is used in the case of a version match, */ /* to alert the user that structure sizes may be mismatched even though the */ /* protocol versions match. */ + #define REVISION_STRING(REVISION_) #REVISION_ #define FILL_VMSTOR_REVISION(RESULT_LVALUE_) \ do { \ @@ -190,3 +196,139 @@ struct vstor_packet { /* This is the set of flags that the vsc can set in any packets it sends */ #define VSC_LEGAL_FLAGS (REQUEST_COMPLETION_FLAG) + + +#include <linux/kernel.h> +#include <linux/wait.h> +#include "hyperv_storage.h" +#include "hyperv.h" + +/* Defines */ +#define STORVSC_RING_BUFFER_SIZE (20*PAGE_SIZE) +#define BLKVSC_RING_BUFFER_SIZE (20*PAGE_SIZE) + +#define STORVSC_MAX_IO_REQUESTS 128 + +/* + * In Hyper-V, each port/path/target maps to 1 scsi host adapter. In + * reality, the path/target is not used (ie always set to 0) so our + * scsi host adapter essentially has 1 bus with 1 target that contains + * up to 256 luns. + */ +#define STORVSC_MAX_LUNS_PER_TARGET 64 +#define STORVSC_MAX_TARGETS 1 +#define STORVSC_MAX_CHANNELS 1 + +struct hv_storvsc_request; + +/* Matches Windows-end */ +enum storvsc_request_type { + WRITE_TYPE, + READ_TYPE, + UNKNOWN_TYPE, +}; + + +struct hv_storvsc_request { + struct hv_storvsc_request *request; + struct hv_device *device; + + /* Synchronize the request/response if needed */ + struct completion wait_event; + + unsigned char *sense_buffer; + void *context; + void (*on_io_completion)(struct hv_storvsc_request *request); + struct hv_multipage_buffer data_buffer; + + struct vstor_packet vstor_packet; +}; + + +struct storvsc_device_info { + u32 ring_buffer_size; + unsigned int port_number; + unsigned char path_id; + unsigned char target_id; +}; + +struct storvsc_major_info { + int major; + int index; + bool do_register; + char *devname; + char *diskname; +}; + +/* A storvsc device is a device object that contains a vmbus channel */ +struct storvsc_device { + struct hv_device *device; + + /* 0 indicates the device is being destroyed */ + atomic_t ref_count; + + bool drain_notify; + atomic_t num_outstanding_req; + + wait_queue_head_t waiting_to_drain; + + /* + * Each unique Port/Path/Target represents 1 channel ie scsi + * controller. In reality, the pathid, targetid is always 0 + * and the port is set by us + */ + unsigned int port_number; + unsigned char path_id; + unsigned char target_id; + + /* Used for vsc/vsp channel reset process */ + struct hv_storvsc_request init_request; + struct hv_storvsc_request reset_request; +}; + + +/* Get the stordevice object iff exists and its refcount > 1 */ +static inline struct storvsc_device *get_stor_device(struct hv_device *device) +{ + struct storvsc_device *stor_device; + + stor_device = (struct storvsc_device *)device->ext; + if (stor_device && atomic_read(&stor_device->ref_count) > 1) + atomic_inc(&stor_device->ref_count); + else + stor_device = NULL; + + return stor_device; +} + + +static inline void put_stor_device(struct hv_device *device) +{ + struct storvsc_device *stor_device; + + stor_device = (struct storvsc_device *)device->ext; + + atomic_dec(&stor_device->ref_count); +} + +static inline void storvsc_wait_to_drain(struct storvsc_device *dev) +{ + dev->drain_notify = true; + wait_event(dev->waiting_to_drain, + atomic_read(&dev->num_outstanding_req) == 0); + dev->drain_notify = false; +} + +/* Interface */ + +int storvsc_dev_add(struct hv_device *device, + void *additional_info); +int storvsc_dev_remove(struct hv_device *device); + +int storvsc_do_io(struct hv_device *device, + struct hv_storvsc_request *request); + +int storvsc_get_major_info(struct storvsc_device_info *device_info, + struct storvsc_major_info *major_info); + +#endif /* _HYPERV_STORAGE_H */ diff --git a/drivers/staging/hv/hyperv_vmbus.h b/drivers/staging/hv/hyperv_vmbus.h new file mode 100644 index 000000000000..bf30a425b643 --- /dev/null +++ b/drivers/staging/hv/hyperv_vmbus.h @@ -0,0 +1,631 @@ +/* + * + * Copyright (c) 2011, Microsoft Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Authors: + * Haiyang Zhang <haiyangz@microsoft.com> + * Hank Janssen <hjanssen@microsoft.com> + * K. Y. Srinivasan <kys@microsoft.com> + * + */ + +#ifndef _HYPERV_VMBUS_H +#define _HYPERV_VMBUS_H + +#include <linux/list.h> +#include <asm/sync_bitops.h> +#include <linux/atomic.h> + +#include "hyperv.h" + +/* + * The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent + * is set by CPUID(HVCPUID_VERSION_FEATURES). + */ +enum hv_cpuid_function { + HVCPUID_VERSION_FEATURES = 0x00000001, + HVCPUID_VENDOR_MAXFUNCTION = 0x40000000, + HVCPUID_INTERFACE = 0x40000001, + + /* + * The remaining functions depend on the value of + * HVCPUID_INTERFACE + */ + HVCPUID_VERSION = 0x40000002, + HVCPUID_FEATURES = 0x40000003, + HVCPUID_ENLIGHTENMENT_INFO = 0x40000004, + HVCPUID_IMPLEMENTATION_LIMITS = 0x40000005, +}; + +/* Define version of the synthetic interrupt controller. */ +#define HV_SYNIC_VERSION (1) + +/* Define the expected SynIC version. */ +#define HV_SYNIC_VERSION_1 (0x1) + +/* Define synthetic interrupt controller message constants. */ +#define HV_MESSAGE_SIZE (256) +#define HV_MESSAGE_PAYLOAD_BYTE_COUNT (240) +#define HV_MESSAGE_PAYLOAD_QWORD_COUNT (30) +#define HV_ANY_VP (0xFFFFFFFF) + +/* Define synthetic interrupt controller flag constants. */ +#define HV_EVENT_FLAGS_COUNT (256 * 8) +#define HV_EVENT_FLAGS_BYTE_COUNT (256) +#define HV_EVENT_FLAGS_DWORD_COUNT (256 / sizeof(u32)) + +/* Define hypervisor message types. */ +enum hv_message_type { + HVMSG_NONE = 0x00000000, + + /* Memory access messages. */ + HVMSG_UNMAPPED_GPA = 0x80000000, + HVMSG_GPA_INTERCEPT = 0x80000001, + + /* Timer notification messages. */ + HVMSG_TIMER_EXPIRED = 0x80000010, + + /* Error messages. */ + HVMSG_INVALID_VP_REGISTER_VALUE = 0x80000020, + HVMSG_UNRECOVERABLE_EXCEPTION = 0x80000021, + HVMSG_UNSUPPORTED_FEATURE = 0x80000022, + + /* Trace buffer complete messages. */ + HVMSG_EVENTLOG_BUFFERCOMPLETE = 0x80000040, + + /* Platform-specific processor intercept messages. */ + HVMSG_X64_IOPORT_INTERCEPT = 0x80010000, + HVMSG_X64_MSR_INTERCEPT = 0x80010001, + HVMSG_X64_CPUID_INTERCEPT = 0x80010002, + HVMSG_X64_EXCEPTION_INTERCEPT = 0x80010003, + HVMSG_X64_APIC_EOI = 0x80010004, + HVMSG_X64_LEGACY_FP_ERROR = 0x80010005 +}; + +/* Define the number of synthetic interrupt sources. */ +#define HV_SYNIC_SINT_COUNT (16) +#define HV_SYNIC_STIMER_COUNT (4) + +/* Define invalid partition identifier. */ +#define HV_PARTITION_ID_INVALID ((u64)0x0) + +/* Define connection identifier type. */ +union hv_connection_id { + u32 asu32; + struct { + u32 id:24; + u32 reserved:8; + } u; +}; + +/* Define port identifier type. */ +union hv_port_id { + u32 asu32; + struct { + u32 id:24; + u32 reserved:8; + } u ; +}; + +/* Define port type. */ +enum hv_port_type { + HVPORT_MSG = 1, + HVPORT_EVENT = 2, + HVPORT_MONITOR = 3 +}; + +/* Define port information structure. */ +struct hv_port_info { + enum hv_port_type port_type; + u32 padding; + union { + struct { + u32 target_sint; + u32 target_vp; + u64 rsvdz; + } message_port_info; + struct { + u32 target_sint; + u32 target_vp; + u16 base_flag_bumber; + u16 flag_count; + u32 rsvdz; + } event_port_info; + struct { + u64 monitor_address; + u64 rsvdz; + } monitor_port_info; + }; +}; + +struct hv_connection_info { + enum hv_port_type port_type; + u32 padding; + union { + struct { + u64 rsvdz; + } message_connection_info; + struct { + u64 rsvdz; + } event_connection_info; + struct { + u64 monitor_address; + } monitor_connection_info; + }; +}; + +/* Define synthetic interrupt controller message flags. */ +union hv_message_flags { + u8 asu8; + struct { + u8 msg_pending:1; + u8 reserved:7; + }; +}; + +/* Define synthetic interrupt controller message header. */ +struct hv_message_header { + enum hv_message_type message_type; + u8 payload_size; + union hv_message_flags message_flags; + u8 reserved[2]; + union { + u64 sender; + union hv_port_id port; + }; +}; + +/* Define timer message payload structure. */ +struct hv_timer_message_payload { + u32 timer_index; + u32 reserved; + u64 expiration_time; /* When the timer expired */ + u64 delivery_time; /* When the message was delivered */ +}; + +/* Define synthetic interrupt controller message format. */ +struct hv_message { + struct hv_message_header header; + union { + u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT]; + } u ; +}; + +/* Define the number of message buffers associated with each port. */ +#define HV_PORT_MESSAGE_BUFFER_COUNT (16) + +/* Define the synthetic interrupt message page layout. */ +struct hv_message_page { + struct hv_message sint_message[HV_SYNIC_SINT_COUNT]; +}; + +/* Define the synthetic interrupt controller event flags format. */ +union hv_synic_event_flags { + u8 flags8[HV_EVENT_FLAGS_BYTE_COUNT]; + u32 flags32[HV_EVENT_FLAGS_DWORD_COUNT]; +}; + +/* Define the synthetic interrupt flags page layout. */ +struct hv_synic_event_flags_page { + union hv_synic_event_flags sintevent_flags[HV_SYNIC_SINT_COUNT]; +}; + +/* Define SynIC control register. */ +union hv_synic_scontrol { + u64 as_uint64; + struct { + u64 enable:1; + u64 reserved:63; + }; +}; + +/* Define synthetic interrupt source. */ +union hv_synic_sint { + u64 as_uint64; + struct { + u64 vector:8; + u64 reserved1:8; + u64 masked:1; + u64 auto_eoi:1; + u64 reserved2:46; + }; +}; + +/* Define the format of the SIMP register */ +union hv_synic_simp { + u64 as_uint64; + struct { + u64 simp_enabled:1; + u64 preserved:11; + u64 base_simp_gpa:52; + }; +}; + +/* Define the format of the SIEFP register */ +union hv_synic_siefp { + u64 as_uint64; + struct { + u64 siefp_enabled:1; + u64 preserved:11; + u64 base_siefp_gpa:52; + }; +}; + +/* Definitions for the monitored notification facility */ +union hv_monitor_trigger_group { + u64 as_uint64; + struct { + u32 pending; + u32 armed; + }; +}; + +struct hv_monitor_parameter { + union hv_connection_id connectionid; + u16 flagnumber; + u16 rsvdz; +}; + +union hv_monitor_trigger_state { + u32 asu32; + + struct { + u32 group_enable:4; + u32 rsvdz:28; + }; +}; + +/* struct hv_monitor_page Layout */ +/* ------------------------------------------------------ */ +/* | 0 | TriggerState (4 bytes) | Rsvd1 (4 bytes) | */ +/* | 8 | TriggerGroup[0] | */ +/* | 10 | TriggerGroup[1] | */ +/* | 18 | TriggerGroup[2] | */ +/* | 20 | TriggerGroup[3] | */ +/* | 28 | Rsvd2[0] | */ +/* | 30 | Rsvd2[1] | */ +/* | 38 | Rsvd2[2] | */ +/* | 40 | NextCheckTime[0][0] | NextCheckTime[0][1] | */ +/* | ... | */ +/* | 240 | Latency[0][0..3] | */ +/* | 340 | Rsvz3[0] | */ +/* | 440 | Parameter[0][0] | */ +/* | 448 | Parameter[0][1] | */ +/* | ... | */ +/* | 840 | Rsvd4[0] | */ +/* ------------------------------------------------------ */ +struct hv_monitor_page { + union hv_monitor_trigger_state trigger_state; + u32 rsvdz1; + + union hv_monitor_trigger_group trigger_group[4]; + u64 rsvdz2[3]; + + s32 next_checktime[4][32]; + + u16 latency[4][32]; + u64 rsvdz3[32]; + + struct hv_monitor_parameter parameter[4][32]; + + u8 rsvdz4[1984]; +}; + +/* Declare the various hypercall operations. */ +enum hv_call_code { + HVCALL_POST_MESSAGE = 0x005c, + HVCALL_SIGNAL_EVENT = 0x005d, +}; + +/* Definition of the hv_post_message hypercall input structure. */ +struct hv_input_post_message { + union hv_connection_id connectionid; + u32 reserved; + enum hv_message_type message_type; + u32 payload_size; + u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT]; +}; + +/* Definition of the hv_signal_event hypercall input structure. */ +struct hv_input_signal_event { + union hv_connection_id connectionid; + u16 flag_number; + u16 rsvdz; +}; + +/* + * Versioning definitions used for guests reporting themselves to the + * hypervisor, and visa versa. + */ + +/* Version info reported by guest OS's */ +enum hv_guest_os_vendor { + HVGUESTOS_VENDOR_MICROSOFT = 0x0001 +}; + +enum hv_guest_os_microsoft_ids { + HVGUESTOS_MICROSOFT_UNDEFINED = 0x00, + HVGUESTOS_MICROSOFT_MSDOS = 0x01, + HVGUESTOS_MICROSOFT_WINDOWS3X = 0x02, + HVGUESTOS_MICROSOFT_WINDOWS9X = 0x03, + HVGUESTOS_MICROSOFT_WINDOWSNT = 0x04, + HVGUESTOS_MICROSOFT_WINDOWSCE = 0x05 +}; + +/* + * Declare the MSR used to identify the guest OS. + */ +#define HV_X64_MSR_GUEST_OS_ID 0x40000000 + +union hv_x64_msr_guest_os_id_contents { + u64 as_uint64; + struct { + u64 build_number:16; + u64 service_version:8; /* Service Pack, etc. */ + u64 minor_version:8; + u64 major_version:8; + u64 os_id:8; /* enum hv_guest_os_microsoft_ids (if Vendor=MS) */ + u64 vendor_id:16; /* enum hv_guest_os_vendor */ + }; +}; + +/* + * Declare the MSR used to setup pages used to communicate with the hypervisor. + */ +#define HV_X64_MSR_HYPERCALL 0x40000001 + +union hv_x64_msr_hypercall_contents { + u64 as_uint64; + struct { + u64 enable:1; + u64 reserved:11; + u64 guest_physical_address:52; + }; +}; + + +enum { + VMBUS_MESSAGE_CONNECTION_ID = 1, + VMBUS_MESSAGE_PORT_ID = 1, + VMBUS_EVENT_CONNECTION_ID = 2, + VMBUS_EVENT_PORT_ID = 2, + VMBUS_MONITOR_CONNECTION_ID = 3, + VMBUS_MONITOR_PORT_ID = 3, + VMBUS_MESSAGE_SINT = 2, +}; + +/* #defines */ + +#define HV_PRESENT_BIT 0x80000000 + +#define HV_LINUX_GUEST_ID_LO 0x00000000 +#define HV_LINUX_GUEST_ID_HI 0xB16B00B5 +#define HV_LINUX_GUEST_ID (((u64)HV_LINUX_GUEST_ID_HI << 32) | \ + HV_LINUX_GUEST_ID_LO) + +#define HV_CPU_POWER_MANAGEMENT (1 << 0) +#define HV_RECOMMENDATIONS_MAX 4 + +#define HV_X64_MAX 5 +#define HV_CAPS_MAX 8 + + +#define HV_HYPERCALL_PARAM_ALIGN sizeof(u64) + + +/* Service definitions */ + +#define HV_SERVICE_PARENT_PORT (0) +#define HV_SERVICE_PARENT_CONNECTION (0) + +#define HV_SERVICE_CONNECT_RESPONSE_SUCCESS (0) +#define HV_SERVICE_CONNECT_RESPONSE_INVALID_PARAMETER (1) +#define HV_SERVICE_CONNECT_RESPONSE_UNKNOWN_SERVICE (2) +#define HV_SERVICE_CONNECT_RESPONSE_CONNECTION_REJECTED (3) + +#define HV_SERVICE_CONNECT_REQUEST_MESSAGE_ID (1) +#define HV_SERVICE_CONNECT_RESPONSE_MESSAGE_ID (2) +#define HV_SERVICE_DISCONNECT_REQUEST_MESSAGE_ID (3) +#define HV_SERVICE_DISCONNECT_RESPONSE_MESSAGE_ID (4) +#define HV_SERVICE_MAX_MESSAGE_ID (4) + +#define HV_SERVICE_PROTOCOL_VERSION (0x0010) +#define HV_CONNECT_PAYLOAD_BYTE_COUNT 64 + +/* #define VMBUS_REVISION_NUMBER 6 */ + +/* Our local vmbus's port and connection id. Anything >0 is fine */ +/* #define VMBUS_PORT_ID 11 */ + +/* 628180B8-308D-4c5e-B7DB-1BEB62E62EF4 */ +static const struct hv_guid VMBUS_SERVICE_ID = { + .data = { + 0xb8, 0x80, 0x81, 0x62, 0x8d, 0x30, 0x5e, 0x4c, + 0xb7, 0xdb, 0x1b, 0xeb, 0x62, 0xe6, 0x2e, 0xf4 + }, +}; + +#define MAX_NUM_CPUS 32 + + +struct hv_input_signal_event_buffer { + u64 align8; + struct hv_input_signal_event event; +}; + +struct hv_context { + /* We only support running on top of Hyper-V + * So at this point this really can only contain the Hyper-V ID + */ + u64 guestid; + + void *hypercall_page; + + bool synic_initialized; + + /* + * This is used as an input param to HvCallSignalEvent hypercall. The + * input param is immutable in our usage and must be dynamic mem (vs + * stack or global). */ + struct hv_input_signal_event_buffer *signal_event_buffer; + /* 8-bytes aligned of the buffer above */ + struct hv_input_signal_event *signal_event_param; + + void *synic_message_page[MAX_NUM_CPUS]; + void *synic_event_page[MAX_NUM_CPUS]; +}; + +extern struct hv_context hv_context; + + +/* Hv Interface */ + +extern int hv_init(void); + +extern void hv_cleanup(void); + +extern u16 hv_post_message(union hv_connection_id connection_id, + enum hv_message_type message_type, + void *payload, size_t payload_size); + +extern u16 hv_signal_event(void); + +extern void hv_synic_init(void *irqarg); + +extern void hv_synic_cleanup(void *arg); + + +/* Interface */ + + +int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, void *buffer, + u32 buflen); + +void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info); + +int hv_ringbuffer_write(struct hv_ring_buffer_info *ring_info, + struct scatterlist *sglist, + u32 sgcount); + +int hv_ringbuffer_peek(struct hv_ring_buffer_info *ring_info, void *buffer, + u32 buflen); + +int hv_ringbuffer_read(struct hv_ring_buffer_info *ring_info, + void *buffer, + u32 buflen, + u32 offset); + +u32 hv_get_ringbuffer_interrupt_mask(struct hv_ring_buffer_info *ring_info); + +void hv_dump_ring_info(struct hv_ring_buffer_info *ring_info, char *prefix); + +void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info, + struct hv_ring_buffer_debug_info *debug_info); + +/* + * Maximum channels is determined by the size of the interrupt page + * which is PAGE_SIZE. 1/2 of PAGE_SIZE is for send endpoint interrupt + * and the other is receive endpoint interrupt + */ +#define MAX_NUM_CHANNELS ((PAGE_SIZE >> 1) << 3) /* 16348 channels */ + +/* The value here must be in multiple of 32 */ +/* TODO: Need to make this configurable */ +#define MAX_NUM_CHANNELS_SUPPORTED 256 + + +enum vmbus_connect_state { + DISCONNECTED, + CONNECTING, + CONNECTED, + DISCONNECTING +}; + +#define MAX_SIZE_CHANNEL_MESSAGE HV_MESSAGE_PAYLOAD_BYTE_COUNT + +struct vmbus_connection { + enum vmbus_connect_state conn_state; + + atomic_t next_gpadl_handle; + + /* + * Represents channel interrupts. Each bit position represents a + * channel. When a channel sends an interrupt via VMBUS, it finds its + * bit in the sendInterruptPage, set it and calls Hv to generate a port + * event. The other end receives the port event and parse the + * recvInterruptPage to see which bit is set + */ + void *int_page; + void *send_int_page; + void *recv_int_page; + + /* + * 2 pages - 1st page for parent->child notification and 2nd + * is child->parent notification + */ + void *monitor_pages; + struct list_head chn_msg_list; + spinlock_t channelmsg_lock; + + /* List of channels */ + struct list_head chn_list; + spinlock_t channel_lock; + + struct workqueue_struct *work_queue; +}; + + +struct vmbus_msginfo { + /* Bookkeeping stuff */ + struct list_head msglist_entry; + + /* The message itself */ + unsigned char msg[0]; +}; + + +extern struct vmbus_connection vmbus_connection; + +/* General vmbus interface */ + +struct hv_device *vmbus_child_device_create(struct hv_guid *type, + struct hv_guid *instance, + struct vmbus_channel *channel); + +int vmbus_child_device_register(struct hv_device *child_device_obj); +void vmbus_child_device_unregister(struct hv_device *device_obj); + +/* static void */ +/* VmbusChildDeviceDestroy( */ +/* struct hv_device *); */ + +struct vmbus_channel *relid2channel(u32 relid); + + +/* Connection interface */ + +int vmbus_connect(void); + +int vmbus_disconnect(void); + +int vmbus_post_msg(void *buffer, size_t buflen); + +int vmbus_set_event(u32 child_relid); + +void vmbus_on_event(unsigned long data); + + +#endif /* _HYPERV_VMBUS_H */ diff --git a/drivers/staging/hv/logging.h b/drivers/staging/hv/logging.h deleted file mode 100644 index 17999515ce08..000000000000 --- a/drivers/staging/hv/logging.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang <haiyangz@microsoft.com> - * Hank Janssen <hjanssen@microsoft.com> - * - */ - - -#ifndef _LOGGING_H_ -#define _LOGGING_H_ - -#define LOWORD(dw) ((unsigned short)(dw)) -#define HIWORD(dw) ((unsigned short)(((unsigned int) (dw) >> 16) & 0xFFFF)) - -/* #include <linux/init.h> */ -/* #include <linux/module.h> */ - - -#define VMBUS 0x0001 -#define STORVSC 0x0002 -#define NETVSC 0x0004 -#define INPUTVSC 0x0008 -#define BLKVSC 0x0010 -#define VMBUS_DRV 0x0100 -#define STORVSC_DRV 0x0200 -#define NETVSC_DRV 0x0400 -#define INPUTVSC_DRV 0x0800 -#define BLKVSC_DRV 0x1000 - -#define ALL_MODULES (VMBUS |\ - STORVSC |\ - NETVSC |\ - INPUTVSC |\ - BLKVSC |\ - VMBUS_DRV |\ - STORVSC_DRV |\ - NETVSC_DRV |\ - INPUTVSC_DRV|\ - BLKVSC_DRV) - -/* Logging Level */ -#define ERROR_LVL 3 -#define WARNING_LVL 4 -#define INFO_LVL 6 -#define DEBUG_LVL 7 -#define DEBUG_LVL_ENTEREXIT 8 -#define DEBUG_RING_LVL 9 - -extern unsigned int vmbus_loglevel; - -#define DPRINT(mod, lvl, fmt, args...) do {\ - if ((mod & (HIWORD(vmbus_loglevel))) && \ - (lvl <= LOWORD(vmbus_loglevel))) \ - printk(KERN_DEBUG #mod": %s() " fmt "\n", __func__, ## args);\ - } while (0) - -#define DPRINT_DBG(mod, fmt, args...) do {\ - if ((mod & (HIWORD(vmbus_loglevel))) && \ - (DEBUG_LVL <= LOWORD(vmbus_loglevel))) \ - printk(KERN_DEBUG #mod": %s() " fmt "\n", __func__, ## args);\ - } while (0) - -#define DPRINT_INFO(mod, fmt, args...) do {\ - if ((mod & (HIWORD(vmbus_loglevel))) && \ - (INFO_LVL <= LOWORD(vmbus_loglevel))) \ - printk(KERN_INFO #mod": " fmt "\n", ## args);\ - } while (0) - -#define DPRINT_WARN(mod, fmt, args...) do {\ - if ((mod & (HIWORD(vmbus_loglevel))) && \ - (WARNING_LVL <= LOWORD(vmbus_loglevel))) \ - printk(KERN_WARNING #mod": WARNING! " fmt "\n", ## args);\ - } while (0) - -#define DPRINT_ERR(mod, fmt, args...) do {\ - if ((mod & (HIWORD(vmbus_loglevel))) && \ - (ERROR_LVL <= LOWORD(vmbus_loglevel))) \ - printk(KERN_ERR #mod": %s() ERROR!! " fmt "\n", \ - __func__, ## args);\ - } while (0) - -#endif /* _LOGGING_H_ */ diff --git a/drivers/staging/hv/netvsc.c b/drivers/staging/hv/netvsc.c index 20b159775e88..41cbb26eccbf 100644 --- a/drivers/staging/hv/netvsc.c +++ b/drivers/staging/hv/netvsc.c @@ -18,6 +18,8 @@ * Haiyang Zhang <haiyangz@microsoft.com> * Hank Janssen <hjanssen@microsoft.com> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/sched.h> #include <linux/wait.h> @@ -25,11 +27,9 @@ #include <linux/delay.h> #include <linux/io.h> #include <linux/slab.h> -#include "hv_api.h" -#include "logging.h" -#include "netvsc.h" -#include "rndis_filter.h" -#include "channel.h" + +#include "hyperv.h" +#include "hyperv_net.h" /* Globals */ @@ -43,38 +43,6 @@ static const struct hv_guid netvsc_device_type = { } }; -static int netvsc_device_add(struct hv_device *device, void *additional_info); - -static int netvsc_device_remove(struct hv_device *device); - -static void netvsc_cleanup(struct hv_driver *driver); - -static void netvsc_channel_cb(void *context); - -static int netvsc_init_send_buf(struct hv_device *device); - -static int netvsc_init_recv_buf(struct hv_device *device); - -static int netvsc_destroy_send_buf(struct netvsc_device *net_device); - -static int netvsc_destroy_recv_buf(struct netvsc_device *net_device); - -static int netvsc_connect_vsp(struct hv_device *device); - -static void netvsc_send_completion(struct hv_device *device, - struct vmpacket_descriptor *packet); - -static int netvsc_send(struct hv_device *device, - struct hv_netvsc_packet *packet); - -static void netvsc_receive(struct hv_device *device, - struct vmpacket_descriptor *packet); - -static void netvsc_receive_completion(void *context); - -static void netvsc_send_recv_completion(struct hv_device *device, - u64 transaction_id); - static struct netvsc_device *alloc_net_device(struct hv_device *device) { @@ -171,43 +139,85 @@ static struct netvsc_device *release_inbound_net_device( return net_device; } -/* - * netvsc_initialize - Main entry point - */ -int netvsc_initialize(struct hv_driver *drv) +static int netvsc_destroy_recv_buf(struct netvsc_device *net_device) { - struct netvsc_driver *driver = (struct netvsc_driver *)drv; + struct nvsp_message *revoke_packet; + int ret = 0; - DPRINT_DBG(NETVSC, "sizeof(struct hv_netvsc_packet)=%zd, " - "sizeof(struct nvsp_message)=%zd, " - "sizeof(struct vmtransfer_page_packet_header)=%zd", - sizeof(struct hv_netvsc_packet), - sizeof(struct nvsp_message), - sizeof(struct vmtransfer_page_packet_header)); + /* + * If we got a section count, it means we received a + * SendReceiveBufferComplete msg (ie sent + * NvspMessage1TypeSendReceiveBuffer msg) therefore, we need + * to send a revoke msg here + */ + if (net_device->recv_section_cnt) { + /* Send the revoke receive buffer */ + revoke_packet = &net_device->revoke_packet; + memset(revoke_packet, 0, sizeof(struct nvsp_message)); - drv->name = driver_name; - memcpy(&drv->dev_type, &netvsc_device_type, sizeof(struct hv_guid)); + revoke_packet->hdr.msg_type = + NVSP_MSG1_TYPE_REVOKE_RECV_BUF; + revoke_packet->msg.v1_msg. + revoke_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID; - /* Setup the dispatch table */ - driver->base.dev_add = netvsc_device_add; - driver->base.dev_rm = netvsc_device_remove; - driver->base.cleanup = netvsc_cleanup; + ret = vmbus_sendpacket(net_device->dev->channel, + revoke_packet, + sizeof(struct nvsp_message), + (unsigned long)revoke_packet, + VM_PKT_DATA_INBAND, 0); + /* + * If we failed here, we might as well return and + * have a leak rather than continue and a bugchk + */ + if (ret != 0) { + dev_err(&net_device->dev->device, "unable to send " + "revoke receive buffer to netvsp"); + return -1; + } + } - driver->send = netvsc_send; + /* Teardown the gpadl on the vsp end */ + if (net_device->recv_buf_gpadl_handle) { + ret = vmbus_teardown_gpadl(net_device->dev->channel, + net_device->recv_buf_gpadl_handle); - rndis_filter_init(driver); - return 0; + /* If we failed here, we might as well return and have a leak + * rather than continue and a bugchk + */ + if (ret != 0) { + dev_err(&net_device->dev->device, + "unable to teardown receive buffer's gpadl"); + return -1; + } + net_device->recv_buf_gpadl_handle = 0; + } + + if (net_device->recv_buf) { + /* Free up the receive buffer */ + free_pages((unsigned long)net_device->recv_buf, + get_order(net_device->recv_buf_size)); + net_device->recv_buf = NULL; + } + + if (net_device->recv_section) { + net_device->recv_section_cnt = 0; + kfree(net_device->recv_section); + net_device->recv_section = NULL; + } + + return ret; } static int netvsc_init_recv_buf(struct hv_device *device) { int ret = 0; + int t; struct netvsc_device *net_device; struct nvsp_message *init_packet; net_device = get_outbound_net_device(device); if (!net_device) { - DPRINT_ERR(NETVSC, "unable to get net device..." + dev_err(&device->device, "unable to get net device..." "device being destroyed?"); return -1; } @@ -216,15 +226,12 @@ static int netvsc_init_recv_buf(struct hv_device *device) (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, get_order(net_device->recv_buf_size)); if (!net_device->recv_buf) { - DPRINT_ERR(NETVSC, - "unable to allocate receive buffer of size %d", - net_device->recv_buf_size); + dev_err(&device->device, "unable to allocate receive " + "buffer of size %d", net_device->recv_buf_size); ret = -1; goto cleanup; } - DPRINT_INFO(NETVSC, "Establishing receive buffer's GPADL..."); - /* * Establish the gpadl handle for this buffer on this * channel. Note: This call uses the vmbus connection rather @@ -234,15 +241,13 @@ static int netvsc_init_recv_buf(struct hv_device *device) net_device->recv_buf_size, &net_device->recv_buf_gpadl_handle); if (ret != 0) { - DPRINT_ERR(NETVSC, - "unable to establish receive buffer's gpadl"); + dev_err(&device->device, + "unable to establish receive buffer's gpadl"); goto cleanup; } /* Notify the NetVsp of the gpadl handle */ - DPRINT_INFO(NETVSC, "Sending NvspMessage1TypeSendReceiveBuffer..."); - init_packet = &net_device->channel_init_pkt; memset(init_packet, 0, sizeof(struct nvsp_message)); @@ -254,28 +259,25 @@ static int netvsc_init_recv_buf(struct hv_device *device) send_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID; /* Send the gpadl notification request */ - net_device->wait_condition = 0; ret = vmbus_sendpacket(device->channel, init_packet, sizeof(struct nvsp_message), (unsigned long)init_packet, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); if (ret != 0) { - DPRINT_ERR(NETVSC, - "unable to send receive buffer's gpadl to netvsp"); + dev_err(&device->device, + "unable to send receive buffer's gpadl to netvsp"); goto cleanup; } - wait_event_timeout(net_device->channel_init_wait, - net_device->wait_condition, - msecs_to_jiffies(1000)); - BUG_ON(net_device->wait_condition == 0); + t = wait_for_completion_timeout(&net_device->channel_init_wait, HZ); + BUG_ON(t == 0); /* Check the response */ if (init_packet->msg.v1_msg. send_recv_buf_complete.status != NVSP_STAT_SUCCESS) { - DPRINT_ERR(NETVSC, "Unable to complete receive buffer " + dev_err(&device->device, "Unable to complete receive buffer " "initialzation with NetVsp - status %d", init_packet->msg.v1_msg. send_recv_buf_complete.status); @@ -301,14 +303,6 @@ static int netvsc_init_recv_buf(struct hv_device *device) net_device->recv_section_cnt * sizeof(struct nvsp_1_receive_buffer_section)); - DPRINT_INFO(NETVSC, "Receive sections info (count %d, offset %d, " - "endoffset %d, suballoc size %d, num suballocs %d)", - net_device->recv_section_cnt, - net_device->recv_section[0].offset, - net_device->recv_section[0].end_offset, - net_device->recv_section[0].sub_alloc_size, - net_device->recv_section[0].num_sub_allocs); - /* * For 1st release, there should only be 1 section that represents the * entire receive buffer @@ -329,15 +323,80 @@ exit: return ret; } +static int netvsc_destroy_send_buf(struct netvsc_device *net_device) +{ + struct nvsp_message *revoke_packet; + int ret = 0; + + /* + * If we got a section count, it means we received a + * SendReceiveBufferComplete msg (ie sent + * NvspMessage1TypeSendReceiveBuffer msg) therefore, we need + * to send a revoke msg here + */ + if (net_device->send_section_size) { + /* Send the revoke send buffer */ + revoke_packet = &net_device->revoke_packet; + memset(revoke_packet, 0, sizeof(struct nvsp_message)); + + revoke_packet->hdr.msg_type = + NVSP_MSG1_TYPE_REVOKE_SEND_BUF; + revoke_packet->msg.v1_msg. + revoke_send_buf.id = NETVSC_SEND_BUFFER_ID; + + ret = vmbus_sendpacket(net_device->dev->channel, + revoke_packet, + sizeof(struct nvsp_message), + (unsigned long)revoke_packet, + VM_PKT_DATA_INBAND, 0); + /* + * If we failed here, we might as well return and have a leak + * rather than continue and a bugchk + */ + if (ret != 0) { + dev_err(&net_device->dev->device, "unable to send " + "revoke send buffer to netvsp"); + return -1; + } + } + + /* Teardown the gpadl on the vsp end */ + if (net_device->send_buf_gpadl_handle) { + ret = vmbus_teardown_gpadl(net_device->dev->channel, + net_device->send_buf_gpadl_handle); + + /* + * If we failed here, we might as well return and have a leak + * rather than continue and a bugchk + */ + if (ret != 0) { + dev_err(&net_device->dev->device, + "unable to teardown send buffer's gpadl"); + return -1; + } + net_device->send_buf_gpadl_handle = 0; + } + + if (net_device->send_buf) { + /* Free up the receive buffer */ + free_pages((unsigned long)net_device->send_buf, + get_order(net_device->send_buf_size)); + net_device->send_buf = NULL; + } + + return ret; +} + static int netvsc_init_send_buf(struct hv_device *device) { int ret = 0; + int t; struct netvsc_device *net_device; struct nvsp_message *init_packet; net_device = get_outbound_net_device(device); if (!net_device) { - DPRINT_ERR(NETVSC, "unable to get net device..." + dev_err(&device->device, "unable to get net device..." "device being destroyed?"); return -1; } @@ -350,14 +409,12 @@ static int netvsc_init_send_buf(struct hv_device *device) (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, get_order(net_device->send_buf_size)); if (!net_device->send_buf) { - DPRINT_ERR(NETVSC, "unable to allocate send buffer of size %d", - net_device->send_buf_size); + dev_err(&device->device, "unable to allocate send " + "buffer of size %d", net_device->send_buf_size); ret = -1; goto cleanup; } - DPRINT_INFO(NETVSC, "Establishing send buffer's GPADL..."); - /* * Establish the gpadl handle for this buffer on this * channel. Note: This call uses the vmbus connection rather @@ -367,13 +424,11 @@ static int netvsc_init_send_buf(struct hv_device *device) net_device->send_buf_size, &net_device->send_buf_gpadl_handle); if (ret != 0) { - DPRINT_ERR(NETVSC, "unable to establish send buffer's gpadl"); + dev_err(&device->device, "unable to establish send buffer's gpadl"); goto cleanup; } /* Notify the NetVsp of the gpadl handle */ - DPRINT_INFO(NETVSC, "Sending NvspMessage1TypeSendSendBuffer..."); - init_packet = &net_device->channel_init_pkt; memset(init_packet, 0, sizeof(struct nvsp_message)); @@ -385,27 +440,25 @@ static int netvsc_init_send_buf(struct hv_device *device) NETVSC_SEND_BUFFER_ID; /* Send the gpadl notification request */ - net_device->wait_condition = 0; ret = vmbus_sendpacket(device->channel, init_packet, sizeof(struct nvsp_message), (unsigned long)init_packet, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); if (ret != 0) { - DPRINT_ERR(NETVSC, + dev_err(&device->device, "unable to send receive buffer's gpadl to netvsp"); goto cleanup; } - wait_event_timeout(net_device->channel_init_wait, - net_device->wait_condition, - msecs_to_jiffies(1000)); - BUG_ON(net_device->wait_condition == 0); + t = wait_for_completion_timeout(&net_device->channel_init_wait, HZ); + + BUG_ON(t == 0); /* Check the response */ if (init_packet->msg.v1_msg. send_send_buf_complete.status != NVSP_STAT_SUCCESS) { - DPRINT_ERR(NETVSC, "Unable to complete send buffer " + dev_err(&device->device, "Unable to complete send buffer " "initialzation with NetVsp - status %d", init_packet->msg.v1_msg. send_send_buf_complete.status); @@ -426,161 +479,17 @@ exit: return ret; } -static int netvsc_destroy_recv_buf(struct netvsc_device *net_device) -{ - struct nvsp_message *revoke_packet; - int ret = 0; - - /* - * If we got a section count, it means we received a - * SendReceiveBufferComplete msg (ie sent - * NvspMessage1TypeSendReceiveBuffer msg) therefore, we need - * to send a revoke msg here - */ - if (net_device->recv_section_cnt) { - DPRINT_INFO(NETVSC, - "Sending NvspMessage1TypeRevokeReceiveBuffer..."); - - /* Send the revoke receive buffer */ - revoke_packet = &net_device->revoke_packet; - memset(revoke_packet, 0, sizeof(struct nvsp_message)); - - revoke_packet->hdr.msg_type = - NVSP_MSG1_TYPE_REVOKE_RECV_BUF; - revoke_packet->msg.v1_msg. - revoke_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID; - - ret = vmbus_sendpacket(net_device->dev->channel, - revoke_packet, - sizeof(struct nvsp_message), - (unsigned long)revoke_packet, - VM_PKT_DATA_INBAND, 0); - /* - * If we failed here, we might as well return and - * have a leak rather than continue and a bugchk - */ - if (ret != 0) { - DPRINT_ERR(NETVSC, "unable to send revoke receive " - "buffer to netvsp"); - return -1; - } - } - - /* Teardown the gpadl on the vsp end */ - if (net_device->recv_buf_gpadl_handle) { - DPRINT_INFO(NETVSC, "Tearing down receive buffer's GPADL..."); - - ret = vmbus_teardown_gpadl(net_device->dev->channel, - net_device->recv_buf_gpadl_handle); - - /* If we failed here, we might as well return and have a leak rather than continue and a bugchk */ - if (ret != 0) { - DPRINT_ERR(NETVSC, - "unable to teardown receive buffer's gpadl"); - return -1; - } - net_device->recv_buf_gpadl_handle = 0; - } - - if (net_device->recv_buf) { - DPRINT_INFO(NETVSC, "Freeing up receive buffer..."); - - /* Free up the receive buffer */ - free_pages((unsigned long)net_device->recv_buf, - get_order(net_device->recv_buf_size)); - net_device->recv_buf = NULL; - } - - if (net_device->recv_section) { - net_device->recv_section_cnt = 0; - kfree(net_device->recv_section); - net_device->recv_section = NULL; - } - - return ret; -} - -static int netvsc_destroy_send_buf(struct netvsc_device *net_device) -{ - struct nvsp_message *revoke_packet; - int ret = 0; - - /* - * If we got a section count, it means we received a - * SendReceiveBufferComplete msg (ie sent - * NvspMessage1TypeSendReceiveBuffer msg) therefore, we need - * to send a revoke msg here - */ - if (net_device->send_section_size) { - DPRINT_INFO(NETVSC, - "Sending NvspMessage1TypeRevokeSendBuffer..."); - - /* Send the revoke send buffer */ - revoke_packet = &net_device->revoke_packet; - memset(revoke_packet, 0, sizeof(struct nvsp_message)); - - revoke_packet->hdr.msg_type = - NVSP_MSG1_TYPE_REVOKE_SEND_BUF; - revoke_packet->msg.v1_msg. - revoke_send_buf.id = NETVSC_SEND_BUFFER_ID; - - ret = vmbus_sendpacket(net_device->dev->channel, - revoke_packet, - sizeof(struct nvsp_message), - (unsigned long)revoke_packet, - VM_PKT_DATA_INBAND, 0); - /* - * If we failed here, we might as well return and have a leak - * rather than continue and a bugchk - */ - if (ret != 0) { - DPRINT_ERR(NETVSC, "unable to send revoke send buffer " - "to netvsp"); - return -1; - } - } - - /* Teardown the gpadl on the vsp end */ - if (net_device->send_buf_gpadl_handle) { - DPRINT_INFO(NETVSC, "Tearing down send buffer's GPADL..."); - ret = vmbus_teardown_gpadl(net_device->dev->channel, - net_device->send_buf_gpadl_handle); - - /* - * If we failed here, we might as well return and have a leak - * rather than continue and a bugchk - */ - if (ret != 0) { - DPRINT_ERR(NETVSC, "unable to teardown send buffer's " - "gpadl"); - return -1; - } - net_device->send_buf_gpadl_handle = 0; - } - - if (net_device->send_buf) { - DPRINT_INFO(NETVSC, "Freeing up send buffer..."); - - /* Free up the receive buffer */ - free_pages((unsigned long)net_device->send_buf, - get_order(net_device->send_buf_size)); - net_device->send_buf = NULL; - } - - return ret; -} - static int netvsc_connect_vsp(struct hv_device *device) { - int ret; + int ret, t; struct netvsc_device *net_device; struct nvsp_message *init_packet; int ndis_version; net_device = get_outbound_net_device(device); if (!net_device) { - DPRINT_ERR(NETVSC, "unable to get net device..." + dev_err(&device->device, "unable to get net device..." "device being destroyed?"); return -1; } @@ -594,54 +503,34 @@ static int netvsc_connect_vsp(struct hv_device *device) init_packet->msg.init_msg.init.max_protocol_ver = NVSP_MAX_PROTOCOL_VERSION; - DPRINT_INFO(NETVSC, "Sending NvspMessageTypeInit..."); - /* Send the init request */ - net_device->wait_condition = 0; ret = vmbus_sendpacket(device->channel, init_packet, sizeof(struct nvsp_message), (unsigned long)init_packet, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (ret != 0) { - DPRINT_ERR(NETVSC, "unable to send NvspMessageTypeInit"); + if (ret != 0) goto cleanup; - } - wait_event_timeout(net_device->channel_init_wait, - net_device->wait_condition, - msecs_to_jiffies(1000)); - if (net_device->wait_condition == 0) { + t = wait_for_completion_timeout(&net_device->channel_init_wait, HZ); + + if (t == 0) { ret = -ETIMEDOUT; goto cleanup; } - DPRINT_INFO(NETVSC, "NvspMessageTypeInit status(%d) max mdl chain (%d)", - init_packet->msg.init_msg.init_complete.status, - init_packet->msg.init_msg. - init_complete.max_mdl_chain_len); - if (init_packet->msg.init_msg.init_complete.status != NVSP_STAT_SUCCESS) { - DPRINT_ERR(NETVSC, - "unable to initialize with netvsp (status 0x%x)", - init_packet->msg.init_msg.init_complete.status); ret = -1; goto cleanup; } if (init_packet->msg.init_msg.init_complete. negotiated_protocol_ver != NVSP_PROTOCOL_VERSION_1) { - DPRINT_ERR(NETVSC, "unable to initialize with netvsp " - "(version expected 1 got %d)", - init_packet->msg.init_msg. - init_complete.negotiated_protocol_ver); ret = -1; goto cleanup; } - DPRINT_INFO(NETVSC, "Sending NvspMessage1TypeSendNdisVersion..."); - /* Send the ndis version */ memset(init_packet, 0, sizeof(struct nvsp_message)); @@ -661,8 +550,6 @@ static int netvsc_connect_vsp(struct hv_device *device) (unsigned long)init_packet, VM_PKT_DATA_INBAND, 0); if (ret != 0) { - DPRINT_ERR(NETVSC, - "unable to send NvspMessage1TypeSendNdisVersion"); ret = -1; goto cleanup; } @@ -677,143 +564,42 @@ cleanup: return ret; } -static void NetVscDisconnectFromVsp(struct netvsc_device *net_device) +static void netvsc_disconnect_vsp(struct netvsc_device *net_device) { netvsc_destroy_recv_buf(net_device); netvsc_destroy_send_buf(net_device); } /* - * netvsc_device_add - Callback when the device belonging to this - * driver is added - */ -static int netvsc_device_add(struct hv_device *device, void *additional_info) -{ - int ret = 0; - int i; - struct netvsc_device *net_device; - struct hv_netvsc_packet *packet, *pos; - struct netvsc_driver *net_driver = - (struct netvsc_driver *)device->drv; - - net_device = alloc_net_device(device); - if (!net_device) { - ret = -1; - goto cleanup; - } - - DPRINT_DBG(NETVSC, "netvsc channel object allocated - %p", net_device); - - /* Initialize the NetVSC channel extension */ - net_device->recv_buf_size = NETVSC_RECEIVE_BUFFER_SIZE; - spin_lock_init(&net_device->recv_pkt_list_lock); - - net_device->send_buf_size = NETVSC_SEND_BUFFER_SIZE; - - INIT_LIST_HEAD(&net_device->recv_pkt_list); - - for (i = 0; i < NETVSC_RECEIVE_PACKETLIST_COUNT; i++) { - packet = kzalloc(sizeof(struct hv_netvsc_packet) + - (NETVSC_RECEIVE_SG_COUNT * - sizeof(struct hv_page_buffer)), GFP_KERNEL); - if (!packet) { - DPRINT_DBG(NETVSC, "unable to allocate netvsc pkts " - "for receive pool (wanted %d got %d)", - NETVSC_RECEIVE_PACKETLIST_COUNT, i); - break; - } - list_add_tail(&packet->list_ent, - &net_device->recv_pkt_list); - } - init_waitqueue_head(&net_device->channel_init_wait); - - /* Open the channel */ - ret = vmbus_open(device->channel, net_driver->ring_buf_size, - net_driver->ring_buf_size, NULL, 0, - netvsc_channel_cb, device); - - if (ret != 0) { - DPRINT_ERR(NETVSC, "unable to open channel: %d", ret); - ret = -1; - goto cleanup; - } - - /* Channel is opened */ - DPRINT_INFO(NETVSC, "*** NetVSC channel opened successfully! ***"); - - /* Connect with the NetVsp */ - ret = netvsc_connect_vsp(device); - if (ret != 0) { - DPRINT_ERR(NETVSC, "unable to connect to NetVSP - %d", ret); - ret = -1; - goto close; - } - - DPRINT_INFO(NETVSC, "*** NetVSC channel handshake result - %d ***", - ret); - - return ret; - -close: - /* Now, we can close the channel safely */ - vmbus_close(device->channel); - -cleanup: - - if (net_device) { - list_for_each_entry_safe(packet, pos, - &net_device->recv_pkt_list, - list_ent) { - list_del(&packet->list_ent); - kfree(packet); - } - - release_outbound_net_device(device); - release_inbound_net_device(device); - - free_net_device(net_device); - } - - return ret; -} - -/* * netvsc_device_remove - Callback when the root bus device is removed */ -static int netvsc_device_remove(struct hv_device *device) +int netvsc_device_remove(struct hv_device *device) { struct netvsc_device *net_device; struct hv_netvsc_packet *netvsc_packet, *pos; - DPRINT_INFO(NETVSC, "Disabling outbound traffic on net device (%p)...", - device->ext); - /* Stop outbound traffic ie sends and receives completions */ net_device = release_outbound_net_device(device); if (!net_device) { - DPRINT_ERR(NETVSC, "No net device present!!"); + dev_err(&device->device, "No net device present!!"); return -1; } /* Wait for all send completions */ while (atomic_read(&net_device->num_outstanding_sends)) { - DPRINT_INFO(NETVSC, "waiting for %d requests to complete...", - atomic_read(&net_device->num_outstanding_sends)); + dev_err(&device->device, + "waiting for %d requests to complete...", + atomic_read(&net_device->num_outstanding_sends)); udelay(100); } - DPRINT_INFO(NETVSC, "Disconnecting from netvsp..."); - - NetVscDisconnectFromVsp(net_device); - - DPRINT_INFO(NETVSC, "Disabling inbound traffic on net device (%p)...", - device->ext); + netvsc_disconnect_vsp(net_device); /* Stop inbound traffic ie receives and sends completions */ net_device = release_inbound_net_device(device); /* At this point, no one should be accessing netDevice except in here */ - DPRINT_INFO(NETVSC, "net device (%p) safe to remove", net_device); + dev_notice(&device->device, "net device safe to remove"); /* Now, we can close the channel safely */ vmbus_close(device->channel); @@ -829,13 +615,6 @@ static int netvsc_device_remove(struct hv_device *device) return 0; } -/* - * netvsc_cleanup - Perform any cleanup when the driver is removed - */ -static void netvsc_cleanup(struct hv_driver *drv) -{ -} - static void netvsc_send_completion(struct hv_device *device, struct vmpacket_descriptor *packet) { @@ -845,7 +624,7 @@ static void netvsc_send_completion(struct hv_device *device, net_device = get_inbound_net_device(device); if (!net_device) { - DPRINT_ERR(NETVSC, "unable to get net device..." + dev_err(&device->device, "unable to get net device..." "device being destroyed?"); return; } @@ -853,9 +632,6 @@ static void netvsc_send_completion(struct hv_device *device, nvsp_packet = (struct nvsp_message *)((unsigned long)packet + (packet->offset8 << 3)); - DPRINT_DBG(NETVSC, "send completion packet - type %d", - nvsp_packet->hdr.msg_type); - if ((nvsp_packet->hdr.msg_type == NVSP_MSG_TYPE_INIT_COMPLETE) || (nvsp_packet->hdr.msg_type == NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE) || @@ -864,8 +640,7 @@ static void netvsc_send_completion(struct hv_device *device, /* Copy the response back */ memcpy(&net_device->channel_init_pkt, nvsp_packet, sizeof(struct nvsp_message)); - net_device->wait_condition = 1; - wake_up(&net_device->channel_init_wait); + complete(&net_device->channel_init_wait); } else if (nvsp_packet->hdr.msg_type == NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE) { /* Get the send context */ @@ -878,14 +653,14 @@ static void netvsc_send_completion(struct hv_device *device, atomic_dec(&net_device->num_outstanding_sends); } else { - DPRINT_ERR(NETVSC, "Unknown send completion packet type - " + dev_err(&device->device, "Unknown send completion packet type- " "%d received!!", nvsp_packet->hdr.msg_type); } put_net_device(device); } -static int netvsc_send(struct hv_device *device, +int netvsc_send(struct hv_device *device, struct hv_netvsc_packet *packet) { struct netvsc_device *net_device; @@ -895,7 +670,7 @@ static int netvsc_send(struct hv_device *device, net_device = get_outbound_net_device(device); if (!net_device) { - DPRINT_ERR(NETVSC, "net device (%p) shutting down..." + dev_err(&device->device, "net device (%p) shutting down..." "ignoring outbound packets", net_device); return -2; } @@ -931,7 +706,7 @@ static int netvsc_send(struct hv_device *device, } if (ret != 0) - DPRINT_ERR(NETVSC, "Unable to send packet %p ret %d", + dev_err(&device->device, "Unable to send packet %p ret %d", packet, ret); atomic_inc(&net_device->num_outstanding_sends); @@ -939,6 +714,98 @@ static int netvsc_send(struct hv_device *device, return ret; } +static void netvsc_send_recv_completion(struct hv_device *device, + u64 transaction_id) +{ + struct nvsp_message recvcompMessage; + int retries = 0; + int ret; + + recvcompMessage.hdr.msg_type = + NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE; + + /* FIXME: Pass in the status */ + recvcompMessage.msg.v1_msg.send_rndis_pkt_complete.status = + NVSP_STAT_SUCCESS; + +retry_send_cmplt: + /* Send the completion */ + ret = vmbus_sendpacket(device->channel, &recvcompMessage, + sizeof(struct nvsp_message), transaction_id, + VM_PKT_COMP, 0); + if (ret == 0) { + /* success */ + /* no-op */ + } else if (ret == -1) { + /* no more room...wait a bit and attempt to retry 3 times */ + retries++; + dev_err(&device->device, "unable to send receive completion pkt" + " (tid %llx)...retrying %d", transaction_id, retries); + + if (retries < 4) { + udelay(100); + goto retry_send_cmplt; + } else { + dev_err(&device->device, "unable to send receive " + "completion pkt (tid %llx)...give up retrying", + transaction_id); + } + } else { + dev_err(&device->device, "unable to send receive " + "completion pkt - %llx", transaction_id); + } +} + +/* Send a receive completion packet to RNDIS device (ie NetVsp) */ +static void netvsc_receive_completion(void *context) +{ + struct hv_netvsc_packet *packet = context; + struct hv_device *device = (struct hv_device *)packet->device; + struct netvsc_device *net_device; + u64 transaction_id = 0; + bool fsend_receive_comp = false; + unsigned long flags; + + /* + * Even though it seems logical to do a GetOutboundNetDevice() here to + * send out receive completion, we are using GetInboundNetDevice() + * since we may have disable outbound traffic already. + */ + net_device = get_inbound_net_device(device); + if (!net_device) { + dev_err(&device->device, "unable to get net device..." + "device being destroyed?"); + return; + } + + /* Overloading use of the lock. */ + spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags); + + packet->xfer_page_pkt->count--; + + /* + * Last one in the line that represent 1 xfer page packet. + * Return the xfer page packet itself to the freelist + */ + if (packet->xfer_page_pkt->count == 0) { + fsend_receive_comp = true; + transaction_id = packet->completion.recv.recv_completion_tid; + list_add_tail(&packet->xfer_page_pkt->list_ent, + &net_device->recv_pkt_list); + + } + + /* Put the packet back */ + list_add_tail(&packet->list_ent, &net_device->recv_pkt_list); + spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, flags); + + /* Send a receive completion for the xfer page packet */ + if (fsend_receive_comp) + netvsc_send_recv_completion(device, transaction_id); + + put_net_device(device); +} + static void netvsc_receive(struct hv_device *device, struct vmpacket_descriptor *packet) { @@ -953,11 +820,12 @@ static void netvsc_receive(struct hv_device *device, int i, j; int count = 0, bytes_remain = 0; unsigned long flags; + LIST_HEAD(listHead); net_device = get_inbound_net_device(device); if (!net_device) { - DPRINT_ERR(NETVSC, "unable to get net device..." + dev_err(&device->device, "unable to get net device..." "device being destroyed?"); return; } @@ -967,7 +835,7 @@ static void netvsc_receive(struct hv_device *device, * packet */ if (packet->type != VM_PKT_DATA_USING_XFER_PAGES) { - DPRINT_ERR(NETVSC, "Unknown packet type received - %d", + dev_err(&device->device, "Unknown packet type received - %d", packet->type); put_net_device(device); return; @@ -979,28 +847,22 @@ static void netvsc_receive(struct hv_device *device, /* Make sure this is a valid nvsp packet */ if (nvsp_packet->hdr.msg_type != NVSP_MSG1_TYPE_SEND_RNDIS_PKT) { - DPRINT_ERR(NETVSC, "Unknown nvsp packet type received - %d", - nvsp_packet->hdr.msg_type); + dev_err(&device->device, "Unknown nvsp packet type received-" + " %d", nvsp_packet->hdr.msg_type); put_net_device(device); return; } - DPRINT_DBG(NETVSC, "NVSP packet received - type %d", - nvsp_packet->hdr.msg_type); - vmxferpage_packet = (struct vmtransfer_page_packet_header *)packet; if (vmxferpage_packet->xfer_pageset_id != NETVSC_RECEIVE_BUFFER_ID) { - DPRINT_ERR(NETVSC, "Invalid xfer page set id - " + dev_err(&device->device, "Invalid xfer page set id - " "expecting %x got %x", NETVSC_RECEIVE_BUFFER_ID, vmxferpage_packet->xfer_pageset_id); put_net_device(device); return; } - DPRINT_DBG(NETVSC, "xfer page - range count %d", - vmxferpage_packet->range_cnt); - /* * Grab free packets (range count + 1) to represent this xfer * page packet. +1 to represent the xfer page packet itself. @@ -1021,9 +883,9 @@ static void netvsc_receive(struct hv_device *device, * some of the xfer page packet ranges... */ if (count < 2) { - DPRINT_ERR(NETVSC, "Got only %d netvsc pkt...needed %d pkts. " - "Dropping this xfer page packet completely!", - count, vmxferpage_packet->range_cnt + 1); + dev_err(&device->device, "Got only %d netvsc pkt...needed " + "%d pkts. Dropping this xfer page packet completely!", + count, vmxferpage_packet->range_cnt + 1); /* Return it to the freelist */ spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags); @@ -1049,9 +911,9 @@ static void netvsc_receive(struct hv_device *device, xferpage_packet->count = count - 1; if (xferpage_packet->count != vmxferpage_packet->range_cnt) { - DPRINT_INFO(NETVSC, "Needed %d netvsc pkts to satisy this xfer " - "page...got %d", vmxferpage_packet->range_cnt, - xferpage_packet->count); + dev_err(&device->device, "Needed %d netvsc pkts to satisy " + "this xfer page...got %d", + vmxferpage_packet->range_cnt, xferpage_packet->count); } /* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */ @@ -1117,17 +979,9 @@ static void netvsc_receive(struct hv_device *device, break; } } - DPRINT_DBG(NETVSC, "[%d] - (abs offset %u len %u) => " - "(pfn %llx, offset %u, len %u)", i, - vmxferpage_packet->ranges[i].byte_offset, - vmxferpage_packet->ranges[i].byte_count, - netvsc_packet->page_buf[0].pfn, - netvsc_packet->page_buf[0].offset, - netvsc_packet->page_buf[0].len); /* Pass it to the upper layer */ - ((struct netvsc_driver *)device->drv)-> - recv_cb(device, netvsc_packet); + rndis_filter_receive(device, netvsc_packet); netvsc_receive_completion(netvsc_packet-> completion.recv.recv_completion_ctx); @@ -1136,101 +990,6 @@ static void netvsc_receive(struct hv_device *device, put_net_device(device); } -static void netvsc_send_recv_completion(struct hv_device *device, - u64 transaction_id) -{ - struct nvsp_message recvcompMessage; - int retries = 0; - int ret; - - DPRINT_DBG(NETVSC, "Sending receive completion pkt - %llx", - transaction_id); - - recvcompMessage.hdr.msg_type = - NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE; - - /* FIXME: Pass in the status */ - recvcompMessage.msg.v1_msg.send_rndis_pkt_complete.status = - NVSP_STAT_SUCCESS; - -retry_send_cmplt: - /* Send the completion */ - ret = vmbus_sendpacket(device->channel, &recvcompMessage, - sizeof(struct nvsp_message), transaction_id, - VM_PKT_COMP, 0); - if (ret == 0) { - /* success */ - /* no-op */ - } else if (ret == -1) { - /* no more room...wait a bit and attempt to retry 3 times */ - retries++; - DPRINT_ERR(NETVSC, "unable to send receive completion pkt " - "(tid %llx)...retrying %d", transaction_id, retries); - - if (retries < 4) { - udelay(100); - goto retry_send_cmplt; - } else { - DPRINT_ERR(NETVSC, "unable to send receive completion " - "pkt (tid %llx)...give up retrying", - transaction_id); - } - } else { - DPRINT_ERR(NETVSC, "unable to send receive completion pkt - " - "%llx", transaction_id); - } -} - -/* Send a receive completion packet to RNDIS device (ie NetVsp) */ -static void netvsc_receive_completion(void *context) -{ - struct hv_netvsc_packet *packet = context; - struct hv_device *device = (struct hv_device *)packet->device; - struct netvsc_device *net_device; - u64 transaction_id = 0; - bool fsend_receive_comp = false; - unsigned long flags; - - /* - * Even though it seems logical to do a GetOutboundNetDevice() here to - * send out receive completion, we are using GetInboundNetDevice() - * since we may have disable outbound traffic already. - */ - net_device = get_inbound_net_device(device); - if (!net_device) { - DPRINT_ERR(NETVSC, "unable to get net device..." - "device being destroyed?"); - return; - } - - /* Overloading use of the lock. */ - spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags); - - packet->xfer_page_pkt->count--; - - /* - * Last one in the line that represent 1 xfer page packet. - * Return the xfer page packet itself to the freelist - */ - if (packet->xfer_page_pkt->count == 0) { - fsend_receive_comp = true; - transaction_id = packet->completion.recv.recv_completion_tid; - list_add_tail(&packet->xfer_page_pkt->list_ent, - &net_device->recv_pkt_list); - - } - - /* Put the packet back */ - list_add_tail(&packet->list_ent, &net_device->recv_pkt_list); - spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, flags); - - /* Send a receive completion for the xfer page packet */ - if (fsend_receive_comp) - netvsc_send_recv_completion(device, transaction_id); - - put_net_device(device); -} - static void netvsc_channel_cb(void *context) { int ret; @@ -1251,7 +1010,7 @@ static void netvsc_channel_cb(void *context) net_device = get_inbound_net_device(device); if (!net_device) { - DPRINT_ERR(NETVSC, "net device (%p) shutting down..." + dev_err(&device->device, "net device (%p) shutting down..." "ignoring inbound packets", net_device); goto out; } @@ -1261,9 +1020,6 @@ static void netvsc_channel_cb(void *context) &bytes_recvd, &request_id); if (ret == 0) { if (bytes_recvd > 0) { - DPRINT_DBG(NETVSC, "receive %d bytes, tid %llx", - bytes_recvd, request_id); - desc = (struct vmpacket_descriptor *)buffer; switch (desc->type) { case VM_PKT_COMP: @@ -1275,7 +1031,7 @@ static void netvsc_channel_cb(void *context) break; default: - DPRINT_ERR(NETVSC, + dev_err(&device->device, "unhandled packet type %d, " "tid %llx len %d\n", desc->type, request_id, @@ -1304,7 +1060,7 @@ static void netvsc_channel_cb(void *context) buffer = kmalloc(bytes_recvd, GFP_ATOMIC); if (buffer == NULL) { /* Try again next time around */ - DPRINT_ERR(NETVSC, + dev_err(&device->device, "unable to allocate buffer of size " "(%d)!!", bytes_recvd); break; @@ -1319,3 +1075,102 @@ out: kfree(buffer); return; } + +/* + * netvsc_device_add - Callback when the device belonging to this + * driver is added + */ +int netvsc_device_add(struct hv_device *device, void *additional_info) +{ + int ret = 0; + int i; + int ring_size = + ((struct netvsc_device_info *)additional_info)->ring_size; + struct netvsc_device *net_device; + struct hv_netvsc_packet *packet, *pos; + + net_device = alloc_net_device(device); + if (!net_device) { + ret = -1; + goto cleanup; + } + + /* Initialize the NetVSC channel extension */ + net_device->recv_buf_size = NETVSC_RECEIVE_BUFFER_SIZE; + spin_lock_init(&net_device->recv_pkt_list_lock); + + net_device->send_buf_size = NETVSC_SEND_BUFFER_SIZE; + + INIT_LIST_HEAD(&net_device->recv_pkt_list); + + for (i = 0; i < NETVSC_RECEIVE_PACKETLIST_COUNT; i++) { + packet = kzalloc(sizeof(struct hv_netvsc_packet) + + (NETVSC_RECEIVE_SG_COUNT * + sizeof(struct hv_page_buffer)), GFP_KERNEL); + if (!packet) + break; + + list_add_tail(&packet->list_ent, + &net_device->recv_pkt_list); + } + init_completion(&net_device->channel_init_wait); + + /* Open the channel */ + ret = vmbus_open(device->channel, ring_size * PAGE_SIZE, + ring_size * PAGE_SIZE, NULL, 0, + netvsc_channel_cb, device); + + if (ret != 0) { + dev_err(&device->device, "unable to open channel: %d", ret); + ret = -1; + goto cleanup; + } + + /* Channel is opened */ + pr_info("hv_netvsc channel opened successfully"); + + /* Connect with the NetVsp */ + ret = netvsc_connect_vsp(device); + if (ret != 0) { + dev_err(&device->device, + "unable to connect to NetVSP - %d", ret); + ret = -1; + goto close; + } + + return ret; + +close: + /* Now, we can close the channel safely */ + vmbus_close(device->channel); + +cleanup: + + if (net_device) { + list_for_each_entry_safe(packet, pos, + &net_device->recv_pkt_list, + list_ent) { + list_del(&packet->list_ent); + kfree(packet); + } + + release_outbound_net_device(device); + release_inbound_net_device(device); + + free_net_device(net_device); + } + + return ret; +} + +/* + * netvsc_initialize - Main entry point + */ +int netvsc_initialize(struct hv_driver *drv) +{ + + drv->name = driver_name; + memcpy(&drv->dev_type, &netvsc_device_type, sizeof(struct hv_guid)); + + return 0; +} diff --git a/drivers/staging/hv/netvsc.h b/drivers/staging/hv/netvsc.h deleted file mode 100644 index 45d24b9d91a1..000000000000 --- a/drivers/staging/hv/netvsc.h +++ /dev/null @@ -1,332 +0,0 @@ -/* - * - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang <haiyangz@microsoft.com> - * Hank Janssen <hjanssen@microsoft.com> - * - */ - - -#ifndef _NETVSC_H_ -#define _NETVSC_H_ - -#include <linux/list.h> -#include "vmbus_packet_format.h" -#include "vmbus_channel_interface.h" -#include "netvsc_api.h" - - -#define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF) - -#define NVSP_PROTOCOL_VERSION_1 2 -#define NVSP_MIN_PROTOCOL_VERSION NVSP_PROTOCOL_VERSION_1 -#define NVSP_MAX_PROTOCOL_VERSION NVSP_PROTOCOL_VERSION_1 - -enum { - NVSP_MSG_TYPE_NONE = 0, - - /* Init Messages */ - NVSP_MSG_TYPE_INIT = 1, - NVSP_MSG_TYPE_INIT_COMPLETE = 2, - - NVSP_VERSION_MSG_START = 100, - - /* Version 1 Messages */ - NVSP_MSG1_TYPE_SEND_NDIS_VER = NVSP_VERSION_MSG_START, - - NVSP_MSG1_TYPE_SEND_RECV_BUF, - NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE, - NVSP_MSG1_TYPE_REVOKE_RECV_BUF, - - NVSP_MSG1_TYPE_SEND_SEND_BUF, - NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE, - NVSP_MSG1_TYPE_REVOKE_SEND_BUF, - - NVSP_MSG1_TYPE_SEND_RNDIS_PKT, - NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE, - - /* - * This should be set to the number of messages for the version with - * the maximum number of messages. - */ - NVSP_NUM_MSG_PER_VERSION = 9, -}; - -enum { - NVSP_STAT_NONE = 0, - NVSP_STAT_SUCCESS, - NVSP_STAT_FAIL, - NVSP_STAT_PROTOCOL_TOO_NEW, - NVSP_STAT_PROTOCOL_TOO_OLD, - NVSP_STAT_INVALID_RNDIS_PKT, - NVSP_STAT_BUSY, - NVSP_STAT_MAX, -}; - -struct nvsp_message_header { - u32 msg_type; -}; - -/* Init Messages */ - -/* - * This message is used by the VSC to initialize the channel after the channels - * has been opened. This message should never include anything other then - * versioning (i.e. this message will be the same for ever). - */ -struct nvsp_message_init { - u32 min_protocol_ver; - u32 max_protocol_ver; -} __packed; - -/* - * This message is used by the VSP to complete the initialization of the - * channel. This message should never include anything other then versioning - * (i.e. this message will be the same for ever). - */ -struct nvsp_message_init_complete { - u32 negotiated_protocol_ver; - u32 max_mdl_chain_len; - u32 status; -} __packed; - -union nvsp_message_init_uber { - struct nvsp_message_init init; - struct nvsp_message_init_complete init_complete; -} __packed; - -/* Version 1 Messages */ - -/* - * This message is used by the VSC to send the NDIS version to the VSP. The VSP - * can use this information when handling OIDs sent by the VSC. - */ -struct nvsp_1_message_send_ndis_version { - u32 ndis_major_ver; - u32 ndis_minor_ver; -} __packed; - -/* - * This message is used by the VSC to send a receive buffer to the VSP. The VSP - * can then use the receive buffer to send data to the VSC. - */ -struct nvsp_1_message_send_receive_buffer { - u32 gpadl_handle; - u16 id; -} __packed; - -struct nvsp_1_receive_buffer_section { - u32 offset; - u32 sub_alloc_size; - u32 num_sub_allocs; - u32 end_offset; -} __packed; - -/* - * This message is used by the VSP to acknowledge a receive buffer send by the - * VSC. This message must be sent by the VSP before the VSP uses the receive - * buffer. - */ -struct nvsp_1_message_send_receive_buffer_complete { - u32 status; - u32 num_sections; - - /* - * The receive buffer is split into two parts, a large suballocation - * section and a small suballocation section. These sections are then - * suballocated by a certain size. - */ - - /* - * For example, the following break up of the receive buffer has 6 - * large suballocations and 10 small suballocations. - */ - - /* - * | Large Section | | Small Section | - * ------------------------------------------------------------ - * | | | | | | | | | | | | | | | | | | - * | | - * LargeOffset SmallOffset - */ - - struct nvsp_1_receive_buffer_section sections[1]; -} __packed; - -/* - * This message is sent by the VSC to revoke the receive buffer. After the VSP - * completes this transaction, the vsp should never use the receive buffer - * again. - */ -struct nvsp_1_message_revoke_receive_buffer { - u16 id; -}; - -/* - * This message is used by the VSC to send a send buffer to the VSP. The VSC - * can then use the send buffer to send data to the VSP. - */ -struct nvsp_1_message_send_send_buffer { - u32 gpadl_handle; - u16 id; -} __packed; - -/* - * This message is used by the VSP to acknowledge a send buffer sent by the - * VSC. This message must be sent by the VSP before the VSP uses the sent - * buffer. - */ -struct nvsp_1_message_send_send_buffer_complete { - u32 status; - - /* - * The VSC gets to choose the size of the send buffer and the VSP gets - * to choose the sections size of the buffer. This was done to enable - * dynamic reconfigurations when the cost of GPA-direct buffers - * decreases. - */ - u32 section_size; -} __packed; - -/* - * This message is sent by the VSC to revoke the send buffer. After the VSP - * completes this transaction, the vsp should never use the send buffer again. - */ -struct nvsp_1_message_revoke_send_buffer { - u16 id; -}; - -/* - * This message is used by both the VSP and the VSC to send a RNDIS message to - * the opposite channel endpoint. - */ -struct nvsp_1_message_send_rndis_packet { - /* - * This field is specified by RNIDS. They assume there's two different - * channels of communication. However, the Network VSP only has one. - * Therefore, the channel travels with the RNDIS packet. - */ - u32 channel_type; - - /* - * This field is used to send part or all of the data through a send - * buffer. This values specifies an index into the send buffer. If the - * index is 0xFFFFFFFF, then the send buffer is not being used and all - * of the data was sent through other VMBus mechanisms. - */ - u32 send_buf_section_index; - u32 send_buf_section_size; -} __packed; - -/* - * This message is used by both the VSP and the VSC to complete a RNDIS message - * to the opposite channel endpoint. At this point, the initiator of this - * message cannot use any resources associated with the original RNDIS packet. - */ -struct nvsp_1_message_send_rndis_packet_complete { - u32 status; -}; - -union nvsp_1_message_uber { - struct nvsp_1_message_send_ndis_version send_ndis_ver; - - struct nvsp_1_message_send_receive_buffer send_recv_buf; - struct nvsp_1_message_send_receive_buffer_complete - send_recv_buf_complete; - struct nvsp_1_message_revoke_receive_buffer revoke_recv_buf; - - struct nvsp_1_message_send_send_buffer send_send_buf; - struct nvsp_1_message_send_send_buffer_complete send_send_buf_complete; - struct nvsp_1_message_revoke_send_buffer revoke_send_buf; - - struct nvsp_1_message_send_rndis_packet send_rndis_pkt; - struct nvsp_1_message_send_rndis_packet_complete - send_rndis_pkt_complete; -} __packed; - -union nvsp_all_messages { - union nvsp_message_init_uber init_msg; - union nvsp_1_message_uber v1_msg; -} __packed; - -/* ALL Messages */ -struct nvsp_message { - struct nvsp_message_header hdr; - union nvsp_all_messages msg; -} __packed; - - - - -/* #define NVSC_MIN_PROTOCOL_VERSION 1 */ -/* #define NVSC_MAX_PROTOCOL_VERSION 1 */ - -#define NETVSC_SEND_BUFFER_SIZE (64*1024) /* 64K */ -#define NETVSC_SEND_BUFFER_ID 0xface - - -#define NETVSC_RECEIVE_BUFFER_SIZE (1024*1024) /* 1MB */ - -#define NETVSC_RECEIVE_BUFFER_ID 0xcafe - -#define NETVSC_RECEIVE_SG_COUNT 1 - -/* Preallocated receive packets */ -#define NETVSC_RECEIVE_PACKETLIST_COUNT 256 - -#define NETVSC_PACKET_SIZE 2048 - -/* Per netvsc channel-specific */ -struct netvsc_device { - struct hv_device *dev; - - atomic_t refcnt; - atomic_t num_outstanding_sends; - /* - * List of free preallocated hv_netvsc_packet to represent receive - * packet - */ - struct list_head recv_pkt_list; - spinlock_t recv_pkt_list_lock; - - /* Send buffer allocated by us but manages by NetVSP */ - void *send_buf; - u32 send_buf_size; - u32 send_buf_gpadl_handle; - u32 send_section_size; - - /* Receive buffer allocated by us but manages by NetVSP */ - void *recv_buf; - u32 recv_buf_size; - u32 recv_buf_gpadl_handle; - u32 recv_section_cnt; - struct nvsp_1_receive_buffer_section *recv_section; - - /* Used for NetVSP initialization protocol */ - int wait_condition; - wait_queue_head_t channel_init_wait; - struct nvsp_message channel_init_pkt; - - struct nvsp_message revoke_packet; - /* unsigned char HwMacAddr[HW_MACADDR_LEN]; */ - - /* Holds rndis device info */ - void *extension; -}; - -#endif /* _NETVSC_H_ */ diff --git a/drivers/staging/hv/netvsc_api.h b/drivers/staging/hv/netvsc_api.h deleted file mode 100644 index b4bed3636594..000000000000 --- a/drivers/staging/hv/netvsc_api.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang <haiyangz@microsoft.com> - * Hank Janssen <hjanssen@microsoft.com> - * - */ - - -#ifndef _NETVSC_API_H_ -#define _NETVSC_API_H_ - -#include "vmbus_api.h" - -/* Fwd declaration */ -struct hv_netvsc_packet; - -/* Represent the xfer page packet which contains 1 or more netvsc packet */ -struct xferpage_packet { - struct list_head list_ent; - - /* # of netvsc packets this xfer packet contains */ - u32 count; -}; - -/* The number of pages which are enough to cover jumbo frame buffer. */ -#define NETVSC_PACKET_MAXPAGE 4 - -/* - * Represent netvsc packet which contains 1 RNDIS and 1 ethernet frame - * within the RNDIS - */ -struct hv_netvsc_packet { - /* Bookkeeping stuff */ - struct list_head list_ent; - - struct hv_device *device; - bool is_data_pkt; - - /* - * Valid only for receives when we break a xfer page packet - * into multiple netvsc packets - */ - struct xferpage_packet *xfer_page_pkt; - - union { - struct{ - u64 recv_completion_tid; - void *recv_completion_ctx; - void (*recv_completion)(void *context); - } recv; - struct{ - u64 send_completion_tid; - void *send_completion_ctx; - void (*send_completion)(void *context); - } send; - } completion; - - /* This points to the memory after page_buf */ - void *extension; - - u32 total_data_buflen; - /* Points to the send/receive buffer where the ethernet frame is */ - u32 page_buf_cnt; - struct hv_page_buffer page_buf[NETVSC_PACKET_MAXPAGE]; -}; - -/* Represents the net vsc driver */ -struct netvsc_driver { - /* Must be the first field */ - /* Which is a bug FIXME! */ - struct hv_driver base; - - u32 ring_buf_size; - u32 req_ext_size; - - /* - * This is set by the caller to allow us to callback when we - * receive a packet from the "wire" - */ - int (*recv_cb)(struct hv_device *dev, - struct hv_netvsc_packet *packet); - void (*link_status_change)(struct hv_device *dev, u32 status); - - /* Specific to this driver */ - int (*send)(struct hv_device *dev, struct hv_netvsc_packet *packet); - - void *ctx; -}; - -struct netvsc_device_info { - unsigned char mac_adr[6]; - bool link_state; /* 0 - link up, 1 - link down */ -}; - -/* Interface */ -int netvsc_initialize(struct hv_driver *drv); -int rndis_filter_open(struct hv_device *dev); -int rndis_filter_close(struct hv_device *dev); - -#endif /* _NETVSC_API_H_ */ diff --git a/drivers/staging/hv/netvsc_drv.c b/drivers/staging/hv/netvsc_drv.c index aaa81883f0ad..7b9c229f7295 100644 --- a/drivers/staging/hv/netvsc_drv.c +++ b/drivers/staging/hv/netvsc_drv.c @@ -18,6 +18,8 @@ * Haiyang Zhang <haiyangz@microsoft.com> * Hank Janssen <hjanssen@microsoft.com> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/init.h> #include <linux/module.h> #include <linux/highmem.h> @@ -36,11 +38,9 @@ #include <net/route.h> #include <net/sock.h> #include <net/pkt_sched.h> -#include "hv_api.h" -#include "logging.h" -#include "version_info.h" -#include "vmbus.h" -#include "netvsc_api.h" + +#include "hyperv.h" +#include "hyperv_net.h" struct net_device_context { /* point back to our device context */ @@ -58,9 +58,6 @@ static int ring_size = 128; module_param(ring_size, int, S_IRUGO); MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)"); -/* The one and only one */ -static struct netvsc_driver g_netvsc_drv; - /* no-op so the netdev core doesn't return -EINVAL when modifying the the * multicast address list in SIOCADDMULTI. hv is setup to get all multicast * when it calls RndisFilterOnOpen() */ @@ -78,14 +75,14 @@ static int netvsc_open(struct net_device *net) /* Open up the device */ ret = rndis_filter_open(device_obj); if (ret != 0) { - DPRINT_ERR(NETVSC_DRV, - "unable to open device (ret %d).", ret); + netdev_err(net, "unable to open device (ret %d).\n", + ret); return ret; } netif_start_queue(net); } else { - DPRINT_ERR(NETVSC_DRV, "unable to open device...link is down."); + netdev_err(net, "unable to open device...link is down.\n"); } return ret; @@ -101,7 +98,7 @@ static int netvsc_close(struct net_device *net) ret = rndis_filter_close(device_obj); if (ret != 0) - DPRINT_ERR(NETVSC_DRV, "unable to close device (ret %d).", ret); + netdev_err(net, "unable to close device (ret %d).\n", ret); return ret; } @@ -130,16 +127,10 @@ static void netvsc_xmit_completion(void *context) static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) { struct net_device_context *net_device_ctx = netdev_priv(net); - struct hv_driver *drv = - drv_to_hv_drv(net_device_ctx->device_ctx->device.driver); - struct netvsc_driver *net_drv_obj = drv->priv; struct hv_netvsc_packet *packet; int ret; unsigned int i, num_pages; - DPRINT_DBG(NETVSC_DRV, "xmit packet - len %d data_len %d", - skb->len, skb->data_len); - /* Add 1 for skb->data and additional one for RNDIS */ num_pages = skb_shinfo(skb)->nr_frags + 1 + 1; if (num_pages > net_device_ctx->avail) @@ -148,10 +139,10 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) /* Allocate a netvsc packet based on # of frags. */ packet = kzalloc(sizeof(struct hv_netvsc_packet) + (num_pages * sizeof(struct hv_page_buffer)) + - net_drv_obj->req_ext_size, GFP_ATOMIC); + sizeof(struct rndis_filter_packet), GFP_ATOMIC); if (!packet) { /* out of memory, silently drop packet */ - DPRINT_ERR(NETVSC_DRV, "unable to allocate hv_netvsc_packet"); + netdev_err(net, "unable to allocate hv_netvsc_packet\n"); dev_kfree_skb(skb); net->stats.tx_dropped++; @@ -191,16 +182,12 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) packet->completion.send.send_completion_ctx = packet; packet->completion.send.send_completion_tid = (unsigned long)skb; - ret = net_drv_obj->send(net_device_ctx->device_ctx, + ret = rndis_filter_send(net_device_ctx->device_ctx, packet); if (ret == 0) { net->stats.tx_bytes += skb->len; net->stats.tx_packets++; - DPRINT_DBG(NETVSC_DRV, "# of xmits %lu total size %lu", - net->stats.tx_packets, - net->stats.tx_bytes); - net_device_ctx->avail -= num_pages; if (net_device_ctx->avail < PACKET_PAGES_LOWATER) netif_stop_queue(net); @@ -216,15 +203,15 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) /* * netvsc_linkstatus_callback - Link up/down notification */ -static void netvsc_linkstatus_callback(struct hv_device *device_obj, +void netvsc_linkstatus_callback(struct hv_device *device_obj, unsigned int status) { struct net_device *net = dev_get_drvdata(&device_obj->device); struct net_device_context *ndev_ctx; if (!net) { - DPRINT_ERR(NETVSC_DRV, "got link status but net device " - "not initialized yet"); + netdev_err(net, "got link status but net device " + "not initialized yet\n"); return; } @@ -244,7 +231,7 @@ static void netvsc_linkstatus_callback(struct hv_device *device_obj, * netvsc_recv_callback - Callback when we receive a packet from the * "wire" on the specified device. */ -static int netvsc_recv_callback(struct hv_device *device_obj, +int netvsc_recv_callback(struct hv_device *device_obj, struct hv_netvsc_packet *packet) { struct net_device *net = dev_get_drvdata(&device_obj->device); @@ -254,8 +241,8 @@ static int netvsc_recv_callback(struct hv_device *device_obj, unsigned long flags; if (!net) { - DPRINT_ERR(NETVSC_DRV, "got receive callback but net device " - "not initialized yet"); + netdev_err(net, "got receive callback but net device" + " not initialized yet\n"); return 0; } @@ -301,9 +288,6 @@ static int netvsc_recv_callback(struct hv_device *device_obj, */ netif_rx(skb); - DPRINT_DBG(NETVSC_DRV, "# of recvs %lu total size %lu", - net->stats.rx_packets, net->stats.rx_bytes); - return 0; } @@ -349,20 +333,13 @@ static void netvsc_send_garp(struct work_struct *w) } -static int netvsc_probe(struct device *device) +static int netvsc_probe(struct hv_device *dev) { - struct hv_driver *drv = - drv_to_hv_drv(device->driver); - struct netvsc_driver *net_drv_obj = drv->priv; - struct hv_device *device_obj = device_to_hv_device(device); struct net_device *net = NULL; struct net_device_context *net_device_ctx; struct netvsc_device_info device_info; int ret; - if (!net_drv_obj->base.dev_add) - return -1; - net = alloc_etherdev(sizeof(struct net_device_context)); if (!net) return -1; @@ -371,19 +348,19 @@ static int netvsc_probe(struct device *device) netif_carrier_off(net); net_device_ctx = netdev_priv(net); - net_device_ctx->device_ctx = device_obj; + net_device_ctx->device_ctx = dev; net_device_ctx->avail = ring_size; - dev_set_drvdata(device, net); + dev_set_drvdata(&dev->device, net); INIT_WORK(&net_device_ctx->work, netvsc_send_garp); /* Notify the netvsc driver of the new device */ - ret = net_drv_obj->base.dev_add(device_obj, &device_info); + device_info.ring_size = ring_size; + ret = rndis_filte_device_add(dev, &device_info); if (ret != 0) { free_netdev(net); - dev_set_drvdata(device, NULL); + dev_set_drvdata(&dev->device, NULL); - DPRINT_ERR(NETVSC_DRV, "unable to add netvsc device (ret %d)", - ret); + netdev_err(net, "unable to add netvsc device (ret %d)\n", ret); return ret; } @@ -408,35 +385,28 @@ static int netvsc_probe(struct device *device) net->features = NETIF_F_SG; SET_ETHTOOL_OPS(net, ðtool_ops); - SET_NETDEV_DEV(net, device); + SET_NETDEV_DEV(net, &dev->device); ret = register_netdev(net); if (ret != 0) { /* Remove the device and release the resource */ - net_drv_obj->base.dev_rm(device_obj); + rndis_filter_device_remove(dev); free_netdev(net); } return ret; } -static int netvsc_remove(struct device *device) +static int netvsc_remove(struct hv_device *dev) { - struct hv_driver *drv = - drv_to_hv_drv(device->driver); - struct netvsc_driver *net_drv_obj = drv->priv; - struct hv_device *device_obj = device_to_hv_device(device); - struct net_device *net = dev_get_drvdata(&device_obj->device); + struct net_device *net = dev_get_drvdata(&dev->device); int ret; if (net == NULL) { - DPRINT_INFO(NETVSC, "no net device to remove"); + dev_err(&dev->device, "No net device to remove\n"); return 0; } - if (!net_drv_obj->base.dev_rm) - return -1; - /* Stop outbound asap */ netif_stop_queue(net); /* netif_carrier_off(net); */ @@ -447,84 +417,27 @@ static int netvsc_remove(struct device *device) * Call to the vsc driver to let it know that the device is being * removed */ - ret = net_drv_obj->base.dev_rm(device_obj); + ret = rndis_filter_device_remove(dev); if (ret != 0) { /* TODO: */ - DPRINT_ERR(NETVSC, "unable to remove vsc device (ret %d)", ret); + netdev_err(net, "unable to remove vsc device (ret %d)\n", ret); } free_netdev(net); return ret; } -static int netvsc_drv_exit_cb(struct device *dev, void *data) -{ - struct device **curr = (struct device **)data; - - *curr = dev; - /* stop iterating */ - return 1; -} +/* The one and only one */ +static struct hv_driver netvsc_drv = { + .probe = netvsc_probe, + .remove = netvsc_remove, +}; -static void netvsc_drv_exit(void) +static void __exit netvsc_drv_exit(void) { - struct netvsc_driver *netvsc_drv_obj = &g_netvsc_drv; - struct hv_driver *drv = &g_netvsc_drv.base; - struct device *current_dev; - int ret; - - while (1) { - current_dev = NULL; - - /* Get the device */ - ret = driver_for_each_device(&drv->driver, NULL, - ¤t_dev, netvsc_drv_exit_cb); - if (ret) - DPRINT_WARN(NETVSC_DRV, - "driver_for_each_device returned %d", ret); - - if (current_dev == NULL) - break; - - /* Initiate removal from the top-down */ - DPRINT_INFO(NETVSC_DRV, "unregistering device (%p)...", - current_dev); - - device_unregister(current_dev); - } - - if (netvsc_drv_obj->base.cleanup) - netvsc_drv_obj->base.cleanup(&netvsc_drv_obj->base); - - vmbus_child_driver_unregister(&drv->driver); - - return; + vmbus_child_driver_unregister(&netvsc_drv.driver); } -static int netvsc_drv_init(int (*drv_init)(struct hv_driver *drv)) -{ - struct netvsc_driver *net_drv_obj = &g_netvsc_drv; - struct hv_driver *drv = &g_netvsc_drv.base; - int ret; - - net_drv_obj->ring_buf_size = ring_size * PAGE_SIZE; - net_drv_obj->recv_cb = netvsc_recv_callback; - net_drv_obj->link_status_change = netvsc_linkstatus_callback; - drv->priv = net_drv_obj; - - /* Callback to client driver to complete the initialization */ - drv_init(&net_drv_obj->base); - - drv->driver.name = net_drv_obj->base.name; - - drv->driver.probe = netvsc_probe; - drv->driver.remove = netvsc_remove; - - /* The driver belongs to vmbus */ - ret = vmbus_child_driver_register(&drv->driver); - - return ret; -} static const struct dmi_system_id __initconst hv_netvsc_dmi_table[] __maybe_unused = { @@ -540,19 +453,26 @@ hv_netvsc_dmi_table[] __maybe_unused = { }; MODULE_DEVICE_TABLE(dmi, hv_netvsc_dmi_table); -static int __init netvsc_init(void) +static int __init netvsc_drv_init(void) { - DPRINT_INFO(NETVSC_DRV, "Netvsc initializing...."); + struct hv_driver *drv = &netvsc_drv; + int ret; + + pr_info("initializing...."); if (!dmi_check_system(hv_netvsc_dmi_table)) return -ENODEV; - return netvsc_drv_init(netvsc_initialize); -} -static void __exit netvsc_exit(void) -{ - netvsc_drv_exit(); + /* Callback to client driver to complete the initialization */ + netvsc_initialize(drv); + + drv->driver.name = drv->name; + + /* The driver belongs to vmbus */ + ret = vmbus_child_driver_register(&drv->driver); + + return ret; } static const struct pci_device_id __initconst @@ -566,5 +486,5 @@ MODULE_LICENSE("GPL"); MODULE_VERSION(HV_DRV_VERSION); MODULE_DESCRIPTION("Microsoft Hyper-V network driver"); -module_init(netvsc_init); -module_exit(netvsc_exit); +module_init(netvsc_drv_init); +module_exit(netvsc_drv_exit); diff --git a/drivers/staging/hv/ring_buffer.c b/drivers/staging/hv/ring_buffer.c index 66688fb69741..3da333018b5a 100644 --- a/drivers/staging/hv/ring_buffer.c +++ b/drivers/staging/hv/ring_buffer.c @@ -18,13 +18,16 @@ * Authors: * Haiyang Zhang <haiyangz@microsoft.com> * Hank Janssen <hjanssen@microsoft.com> + * K. Y. Srinivasan <kys@microsoft.com> * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> #include <linux/mm.h> -#include "logging.h" -#include "ring_buffer.h" + +#include "hyperv.h" +#include "hyperv_vmbus.h" /* #defines */ @@ -34,18 +37,15 @@ #define BYTES_AVAIL_TO_WRITE(r, w, z) ((w) >= (r)) ? ((z) - ((w) - (r))) : ((r) - (w)) -/*++ - -Name: - get_ringbuffer_availbytes() - -Description: - Get number of bytes available to read and to write to - for the specified ring buffer - ---*/ +/* + * + * hv_get_ringbuffer_availbytes() + * + * Get number of bytes available to read and to write to + * for the specified ring buffer + */ static inline void -get_ringbuffer_availbytes(struct hv_ring_buffer_info *rbi, +hv_get_ringbuffer_availbytes(struct hv_ring_buffer_info *rbi, u32 *read, u32 *write) { u32 read_loc, write_loc; @@ -58,162 +58,131 @@ get_ringbuffer_availbytes(struct hv_ring_buffer_info *rbi, *read = rbi->ring_datasize - *write; } -/*++ - -Name: - get_next_write_location() - -Description: - Get the next write location for the specified ring buffer - ---*/ +/* + * hv_get_next_write_location() + * + * Get the next write location for the specified ring buffer + * + */ static inline u32 -get_next_write_location(struct hv_ring_buffer_info *ring_info) +hv_get_next_write_location(struct hv_ring_buffer_info *ring_info) { u32 next = ring_info->ring_buffer->write_index; - /* ASSERT(next < ring_info->RingDataSize); */ - return next; } -/*++ - -Name: - set_next_write_location() - -Description: - Set the next write location for the specified ring buffer - ---*/ +/* + * hv_set_next_write_location() + * + * Set the next write location for the specified ring buffer + * + */ static inline void -set_next_write_location(struct hv_ring_buffer_info *ring_info, +hv_set_next_write_location(struct hv_ring_buffer_info *ring_info, u32 next_write_location) { ring_info->ring_buffer->write_index = next_write_location; } -/*++ - -Name: - get_next_read_location() - -Description: - Get the next read location for the specified ring buffer - ---*/ +/* + * hv_get_next_read_location() + * + * Get the next read location for the specified ring buffer + */ static inline u32 -get_next_read_location(struct hv_ring_buffer_info *ring_info) +hv_get_next_read_location(struct hv_ring_buffer_info *ring_info) { u32 next = ring_info->ring_buffer->read_index; - /* ASSERT(next < ring_info->RingDataSize); */ - return next; } -/*++ - -Name: - get_next_readlocation_withoffset() - -Description: - Get the next read location + offset for the specified ring buffer. - This allows the caller to skip - ---*/ +/* + * hv_get_next_readlocation_withoffset() + * + * Get the next read location + offset for the specified ring buffer. + * This allows the caller to skip + */ static inline u32 -get_next_readlocation_withoffset(struct hv_ring_buffer_info *ring_info, +hv_get_next_readlocation_withoffset(struct hv_ring_buffer_info *ring_info, u32 offset) { u32 next = ring_info->ring_buffer->read_index; - /* ASSERT(next < ring_info->RingDataSize); */ next += offset; next %= ring_info->ring_datasize; return next; } -/*++ - -Name: - set_next_read_location() - -Description: - Set the next read location for the specified ring buffer - ---*/ +/* + * + * hv_set_next_read_location() + * + * Set the next read location for the specified ring buffer + * + */ static inline void -set_next_read_location(struct hv_ring_buffer_info *ring_info, +hv_set_next_read_location(struct hv_ring_buffer_info *ring_info, u32 next_read_location) { ring_info->ring_buffer->read_index = next_read_location; } -/*++ - -Name: - get_ring_buffer() - -Description: - Get the start of the ring buffer - ---*/ +/* + * + * hv_get_ring_buffer() + * + * Get the start of the ring buffer + */ static inline void * -get_ring_buffer(struct hv_ring_buffer_info *ring_info) +hv_get_ring_buffer(struct hv_ring_buffer_info *ring_info) { return (void *)ring_info->ring_buffer->buffer; } -/*++ - -Name: - get_ring_buffersize() - -Description: - Get the size of the ring buffer - ---*/ +/* + * + * hv_get_ring_buffersize() + * + * Get the size of the ring buffer + */ static inline u32 -get_ring_buffersize(struct hv_ring_buffer_info *ring_info) +hv_get_ring_buffersize(struct hv_ring_buffer_info *ring_info) { return ring_info->ring_datasize; } -/*++ - -Name: - get_ring_bufferindices() - -Description: - Get the read and write indices as u64 of the specified ring buffer - ---*/ +/* + * + * hv_get_ring_bufferindices() + * + * Get the read and write indices as u64 of the specified ring buffer + * + */ static inline u64 -get_ring_bufferindices(struct hv_ring_buffer_info *ring_info) +hv_get_ring_bufferindices(struct hv_ring_buffer_info *ring_info) { return (u64)ring_info->ring_buffer->write_index << 32; } -/*++ - -Name: - dump_ring_info() - -Description: - Dump out to console the ring buffer info - ---*/ -void dump_ring_info(struct hv_ring_buffer_info *ring_info, char *prefix) +/* + * + * hv_dump_ring_info() + * + * Dump out to console the ring buffer info + * + */ +void hv_dump_ring_info(struct hv_ring_buffer_info *ring_info, char *prefix) { u32 bytes_avail_towrite; u32 bytes_avail_toread; - get_ringbuffer_availbytes(ring_info, + hv_get_ringbuffer_availbytes(ring_info, &bytes_avail_toread, &bytes_avail_towrite); @@ -231,41 +200,90 @@ void dump_ring_info(struct hv_ring_buffer_info *ring_info, char *prefix) } -/* Internal routines */ - -static u32 -copyto_ringbuffer( - struct hv_ring_buffer_info *ring_info, - u32 start_write_offset, - void *src, - u32 srclen); - -static u32 -copyfrom_ringbuffer( +/* + * + * hv_copyfrom_ringbuffer() + * + * Helper routine to copy to source from ring buffer. + * Assume there is enough room. Handles wrap-around in src case only!! + * + */ +static u32 hv_copyfrom_ringbuffer( struct hv_ring_buffer_info *ring_info, void *dest, u32 destlen, - u32 start_read_offset); + u32 start_read_offset) +{ + void *ring_buffer = hv_get_ring_buffer(ring_info); + u32 ring_buffer_size = hv_get_ring_buffersize(ring_info); + u32 frag_len; + /* wrap-around detected at the src */ + if (destlen > ring_buffer_size - start_read_offset) { + frag_len = ring_buffer_size - start_read_offset; -/*++ + memcpy(dest, ring_buffer + start_read_offset, frag_len); + memcpy(dest + frag_len, ring_buffer, destlen - frag_len); + } else + + memcpy(dest, ring_buffer + start_read_offset, destlen); + + + start_read_offset += destlen; + start_read_offset %= ring_buffer_size; + + return start_read_offset; +} + + +/* + * + * hv_copyto_ringbuffer() + * + * Helper routine to copy from source to ring buffer. + * Assume there is enough room. Handles wrap-around in dest case only!! + * + */ +static u32 hv_copyto_ringbuffer( + struct hv_ring_buffer_info *ring_info, + u32 start_write_offset, + void *src, + u32 srclen) +{ + void *ring_buffer = hv_get_ring_buffer(ring_info); + u32 ring_buffer_size = hv_get_ring_buffersize(ring_info); + u32 frag_len; + + /* wrap-around detected! */ + if (srclen > ring_buffer_size - start_write_offset) { + frag_len = ring_buffer_size - start_write_offset; + memcpy(ring_buffer + start_write_offset, src, frag_len); + memcpy(ring_buffer, src + frag_len, srclen - frag_len); + } else + memcpy(ring_buffer + start_write_offset, src, srclen); -Name: - ringbuffer_get_debuginfo() + start_write_offset += srclen; + start_write_offset %= ring_buffer_size; -Description: - Get various debug metrics for the specified ring buffer + return start_write_offset; +} ---*/ -void ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info, +/* + * + * hv_ringbuffer_get_debuginfo() + * + * Get various debug metrics for the specified ring buffer + * + */ +void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info, struct hv_ring_buffer_debug_info *debug_info) { u32 bytes_avail_towrite; u32 bytes_avail_toread; if (ring_info->ring_buffer) { - get_ringbuffer_availbytes(ring_info, + hv_get_ringbuffer_availbytes(ring_info, &bytes_avail_toread, &bytes_avail_towrite); @@ -281,30 +299,26 @@ void ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info, } -/*++ - -Name: - get_ringbuffer_interrupt_mask() - -Description: - Get the interrupt mask for the specified ring buffer - ---*/ -u32 get_ringbuffer_interrupt_mask(struct hv_ring_buffer_info *rbi) +/* + * + * hv_get_ringbuffer_interrupt_mask() + * + * Get the interrupt mask for the specified ring buffer + * + */ +u32 hv_get_ringbuffer_interrupt_mask(struct hv_ring_buffer_info *rbi) { return rbi->ring_buffer->interrupt_mask; } -/*++ - -Name: - ringbuffer_init() - -Description: - Initialize the ring buffer - ---*/ -int ringbuffer_init(struct hv_ring_buffer_info *ring_info, +/* + * + * hv_ringbuffer_init() + * + *Initialize the ring buffer + * + */ +int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, void *buffer, u32 buflen) { if (sizeof(struct hv_ring_buffer) != PAGE_SIZE) @@ -324,29 +338,25 @@ int ringbuffer_init(struct hv_ring_buffer_info *ring_info, return 0; } -/*++ - -Name: - ringbuffer_cleanup() - -Description: - Cleanup the ring buffer - ---*/ -void ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info) +/* + * + * hv_ringbuffer_cleanup() + * + * Cleanup the ring buffer + * + */ +void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info) { } -/*++ - -Name: - ringbuffer_write() - -Description: - Write to the ring buffer - ---*/ -int ringbuffer_write(struct hv_ring_buffer_info *outring_info, +/* + * + * hv_ringbuffer_write() + * + * Write to the ring buffer + * + */ +int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info, struct scatterlist *sglist, u32 sgcount) { int i = 0; @@ -355,7 +365,7 @@ int ringbuffer_write(struct hv_ring_buffer_info *outring_info, u32 totalbytes_towrite = 0; struct scatterlist *sg; - volatile u32 next_write_location; + u32 next_write_location; u64 prev_indices = 0; unsigned long flags; @@ -368,43 +378,34 @@ int ringbuffer_write(struct hv_ring_buffer_info *outring_info, spin_lock_irqsave(&outring_info->ring_lock, flags); - get_ringbuffer_availbytes(outring_info, + hv_get_ringbuffer_availbytes(outring_info, &bytes_avail_toread, &bytes_avail_towrite); - DPRINT_DBG(VMBUS, "Writing %u bytes...", totalbytes_towrite); - - /* Dumpring_info(Outring_info, "BEFORE "); */ /* If there is only room for the packet, assume it is full. */ /* Otherwise, the next time around, we think the ring buffer */ /* is empty since the read index == write index */ if (bytes_avail_towrite <= totalbytes_towrite) { - DPRINT_DBG(VMBUS, - "No more space left on outbound ring buffer " - "(needed %u, avail %u)", - totalbytes_towrite, - bytes_avail_towrite); - spin_unlock_irqrestore(&outring_info->ring_lock, flags); return -1; } /* Write to the ring buffer */ - next_write_location = get_next_write_location(outring_info); + next_write_location = hv_get_next_write_location(outring_info); for_each_sg(sglist, sg, sgcount, i) { - next_write_location = copyto_ringbuffer(outring_info, + next_write_location = hv_copyto_ringbuffer(outring_info, next_write_location, sg_virt(sg), sg->length); } /* Set previous packet start */ - prev_indices = get_ring_bufferindices(outring_info); + prev_indices = hv_get_ring_bufferindices(outring_info); - next_write_location = copyto_ringbuffer(outring_info, + next_write_location = hv_copyto_ringbuffer(outring_info, next_write_location, &prev_indices, sizeof(u64)); @@ -413,25 +414,22 @@ int ringbuffer_write(struct hv_ring_buffer_info *outring_info, mb(); /* Now, update the write location */ - set_next_write_location(outring_info, next_write_location); + hv_set_next_write_location(outring_info, next_write_location); - /* Dumpring_info(Outring_info, "AFTER "); */ spin_unlock_irqrestore(&outring_info->ring_lock, flags); return 0; } -/*++ - -Name: - ringbuffer_peek() - -Description: - Read without advancing the read index - ---*/ -int ringbuffer_peek(struct hv_ring_buffer_info *Inring_info, +/* + * + * hv_ringbuffer_peek() + * + * Read without advancing the read index + * + */ +int hv_ringbuffer_peek(struct hv_ring_buffer_info *Inring_info, void *Buffer, u32 buflen) { u32 bytes_avail_towrite; @@ -441,17 +439,12 @@ int ringbuffer_peek(struct hv_ring_buffer_info *Inring_info, spin_lock_irqsave(&Inring_info->ring_lock, flags); - get_ringbuffer_availbytes(Inring_info, + hv_get_ringbuffer_availbytes(Inring_info, &bytes_avail_toread, &bytes_avail_towrite); /* Make sure there is something to read */ if (bytes_avail_toread < buflen) { - /* DPRINT_DBG(VMBUS, - "got callback but not enough to read " - "<avail to read %d read size %d>!!", - bytes_avail_toread, - BufferLen); */ spin_unlock_irqrestore(&Inring_info->ring_lock, flags); @@ -459,9 +452,9 @@ int ringbuffer_peek(struct hv_ring_buffer_info *Inring_info, } /* Convert to byte offset */ - next_read_location = get_next_read_location(Inring_info); + next_read_location = hv_get_next_read_location(Inring_info); - next_read_location = copyfrom_ringbuffer(Inring_info, + next_read_location = hv_copyfrom_ringbuffer(Inring_info, Buffer, buflen, next_read_location); @@ -472,16 +465,14 @@ int ringbuffer_peek(struct hv_ring_buffer_info *Inring_info, } -/*++ - -Name: - ringbuffer_read() - -Description: - Read and advance the read index - ---*/ -int ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer, +/* + * + * hv_ringbuffer_read() + * + * Read and advance the read index + * + */ +int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer, u32 buflen, u32 offset) { u32 bytes_avail_towrite; @@ -495,36 +486,26 @@ int ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer, spin_lock_irqsave(&inring_info->ring_lock, flags); - get_ringbuffer_availbytes(inring_info, + hv_get_ringbuffer_availbytes(inring_info, &bytes_avail_toread, &bytes_avail_towrite); - DPRINT_DBG(VMBUS, "Reading %u bytes...", buflen); - - /* Dumpring_info(Inring_info, "BEFORE "); */ - /* Make sure there is something to read */ if (bytes_avail_toread < buflen) { - DPRINT_DBG(VMBUS, - "got callback but not enough to read " - "<avail to read %d read size %d>!!", - bytes_avail_toread, - buflen); - spin_unlock_irqrestore(&inring_info->ring_lock, flags); return -1; } next_read_location = - get_next_readlocation_withoffset(inring_info, offset); + hv_get_next_readlocation_withoffset(inring_info, offset); - next_read_location = copyfrom_ringbuffer(inring_info, + next_read_location = hv_copyfrom_ringbuffer(inring_info, buffer, buflen, next_read_location); - next_read_location = copyfrom_ringbuffer(inring_info, + next_read_location = hv_copyfrom_ringbuffer(inring_info, &prev_indices, sizeof(u64), next_read_location); @@ -535,94 +516,9 @@ int ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer, mb(); /* Update the read index */ - set_next_read_location(inring_info, next_read_location); - - /* Dumpring_info(Inring_info, "AFTER "); */ + hv_set_next_read_location(inring_info, next_read_location); spin_unlock_irqrestore(&inring_info->ring_lock, flags); return 0; } - - -/*++ - -Name: - copyto_ringbuffer() - -Description: - Helper routine to copy from source to ring buffer. - Assume there is enough room. Handles wrap-around in dest case only!! - ---*/ -static u32 -copyto_ringbuffer( - struct hv_ring_buffer_info *ring_info, - u32 start_write_offset, - void *src, - u32 srclen) -{ - void *ring_buffer = get_ring_buffer(ring_info); - u32 ring_buffer_size = get_ring_buffersize(ring_info); - u32 frag_len; - - /* wrap-around detected! */ - if (srclen > ring_buffer_size - start_write_offset) { - DPRINT_DBG(VMBUS, "wrap-around detected!"); - - frag_len = ring_buffer_size - start_write_offset; - memcpy(ring_buffer + start_write_offset, src, frag_len); - memcpy(ring_buffer, src + frag_len, srclen - frag_len); - } else - memcpy(ring_buffer + start_write_offset, src, srclen); - - start_write_offset += srclen; - start_write_offset %= ring_buffer_size; - - return start_write_offset; -} - - -/*++ - -Name: - copyfrom_ringbuffer() - -Description: - Helper routine to copy to source from ring buffer. - Assume there is enough room. Handles wrap-around in src case only!! - ---*/ -static u32 -copyfrom_ringbuffer( - struct hv_ring_buffer_info *ring_info, - void *dest, - u32 destlen, - u32 start_read_offset) -{ - void *ring_buffer = get_ring_buffer(ring_info); - u32 ring_buffer_size = get_ring_buffersize(ring_info); - - u32 frag_len; - - /* wrap-around detected at the src */ - if (destlen > ring_buffer_size - start_read_offset) { - DPRINT_DBG(VMBUS, "src wrap-around detected!"); - - frag_len = ring_buffer_size - start_read_offset; - - memcpy(dest, ring_buffer + start_read_offset, frag_len); - memcpy(dest + frag_len, ring_buffer, destlen - frag_len); - } else - - memcpy(dest, ring_buffer + start_read_offset, destlen); - - - start_read_offset += destlen; - start_read_offset %= ring_buffer_size; - - return start_read_offset; -} - - -/* eof */ diff --git a/drivers/staging/hv/ring_buffer.h b/drivers/staging/hv/ring_buffer.h deleted file mode 100644 index 7bf20d67187b..000000000000 --- a/drivers/staging/hv/ring_buffer.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang <haiyangz@microsoft.com> - * Hank Janssen <hjanssen@microsoft.com> - * - */ - - -#ifndef _RING_BUFFER_H_ -#define _RING_BUFFER_H_ - -#include <linux/scatterlist.h> - -struct hv_ring_buffer { - /* Offset in bytes from the start of ring data below */ - volatile u32 write_index; - - /* Offset in bytes from the start of ring data below */ - volatile u32 read_index; - - volatile u32 interrupt_mask; - - /* Pad it to PAGE_SIZE so that data starts on page boundary */ - u8 reserved[4084]; - - /* NOTE: - * The interrupt_mask field is used only for channels but since our - * vmbus connection also uses this data structure and its data starts - * here, we commented out this field. - */ - /* volatile u32 InterruptMask; */ - - /* - * Ring data starts here + RingDataStartOffset - * !!! DO NOT place any fields below this !!! - */ - u8 buffer[0]; -} __packed; - -struct hv_ring_buffer_info { - struct hv_ring_buffer *ring_buffer; - u32 ring_size; /* Include the shared header */ - spinlock_t ring_lock; - - u32 ring_datasize; /* < ring_size */ - u32 ring_data_startoffset; -}; - -struct hv_ring_buffer_debug_info { - u32 current_interrupt_mask; - u32 current_read_index; - u32 current_write_index; - u32 bytes_avail_toread; - u32 bytes_avail_towrite; -}; - - - -/* Interface */ - - -int ringbuffer_init(struct hv_ring_buffer_info *ring_info, void *buffer, - u32 buflen); - -void ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info); - -int ringbuffer_write(struct hv_ring_buffer_info *ring_info, - struct scatterlist *sglist, - u32 sgcount); - -int ringbuffer_peek(struct hv_ring_buffer_info *ring_info, void *buffer, - u32 buflen); - -int ringbuffer_read(struct hv_ring_buffer_info *ring_info, - void *buffer, - u32 buflen, - u32 offset); - -u32 get_ringbuffer_interrupt_mask(struct hv_ring_buffer_info *ring_info); - -void dump_ring_info(struct hv_ring_buffer_info *ring_info, char *prefix); - -void ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info, - struct hv_ring_buffer_debug_info *debug_info); - -#endif /* _RING_BUFFER_H_ */ diff --git a/drivers/staging/hv/rndis_filter.c b/drivers/staging/hv/rndis_filter.c index 048376b2b676..60ebdb1b6082 100644 --- a/drivers/staging/hv/rndis_filter.c +++ b/drivers/staging/hv/rndis_filter.c @@ -25,17 +25,11 @@ #include <linux/slab.h> #include <linux/io.h> #include <linux/if_ether.h> +#include <linux/netdevice.h> -#include "logging.h" -#include "hv_api.h" -#include "netvsc_api.h" -#include "rndis_filter.h" +#include "hyperv.h" +#include "hyperv_net.h" -/* Data types */ -struct rndis_filter_driver_object { - /* The original driver */ - struct netvsc_driver inner_drv; -}; enum rndis_device_state { RNDIS_DEV_UNINITIALIZED = 0, @@ -59,8 +53,7 @@ struct rndis_device { struct rndis_request { struct list_head list_ent; - int wait_condition; - wait_queue_head_t wait_event; + struct completion wait_event; /* * FIXME: We assumed a fixed size response here. If we do ever need to @@ -76,31 +69,11 @@ struct rndis_request { struct rndis_message request_msg; }; - -struct rndis_filter_packet { - void *completion_ctx; - void (*completion)(void *context); - struct rndis_message msg; -}; - - -static int rndis_filte_device_add(struct hv_device *dev, - void *additional_info); - -static int rndis_filter_device_remove(struct hv_device *dev); - -static void rndis_filter_cleanup(struct hv_driver *drv); - -static int rndis_filter_send(struct hv_device *dev, - struct hv_netvsc_packet *pkt); - static void rndis_filter_send_completion(void *ctx); static void rndis_filter_send_request_completion(void *ctx); -/* The one and only */ -static struct rndis_filter_driver_object rndis_filter; static struct rndis_device *get_rndis_device(void) { @@ -132,7 +105,7 @@ static struct rndis_request *get_rndis_request(struct rndis_device *dev, if (!request) return NULL; - init_waitqueue_head(&request->wait_event); + init_completion(&request->wait_event); rndis_msg = &request->request_msg; rndis_msg->ndis_msg_type = msg_type; @@ -264,7 +237,7 @@ static int rndis_filter_send_request(struct rndis_device *dev, rndis_filter_send_request_completion; packet->completion.send.send_completion_tid = (unsigned long)dev; - ret = rndis_filter.inner_drv.send(dev->net_dev->dev, packet); + ret = netvsc_send(dev->net_dev->dev, packet); return ret; } @@ -283,14 +256,6 @@ static void rndis_filter_receive_response(struct rndis_device *dev, */ if (request->request_msg.msg.init_req.req_id == resp->msg.init_complete.req_id) { - DPRINT_DBG(NETVSC, "found rndis request for " - "this response (id 0x%x req type 0x%x res " - "type 0x%x)", - request->request_msg.msg. - init_req.req_id, - request->request_msg.ndis_msg_type, - resp->ndis_msg_type); - found = true; break; } @@ -302,10 +267,11 @@ static void rndis_filter_receive_response(struct rndis_device *dev, memcpy(&request->response_msg, resp, resp->msg_len); } else { - DPRINT_ERR(NETVSC, "rndis response buffer overflow " - "detected (size %u max %zu)", - resp->msg_len, - sizeof(struct rndis_filter_packet)); + dev_err(&dev->net_dev->dev->device, + "rndis response buffer overflow " + "detected (size %u max %zu)\n", + resp->msg_len, + sizeof(struct rndis_filter_packet)); if (resp->ndis_msg_type == REMOTE_NDIS_RESET_CMPLT) { @@ -319,13 +285,13 @@ static void rndis_filter_receive_response(struct rndis_device *dev, } } - request->wait_condition = 1; - wake_up(&request->wait_event); + complete(&request->wait_event); } else { - DPRINT_ERR(NETVSC, "no rndis request found for this response " - "(id 0x%x res type 0x%x)", - resp->msg.init_complete.req_id, - resp->ndis_msg_type); + dev_err(&dev->net_dev->dev->device, + "no rndis request found for this response " + "(id 0x%x res type 0x%x)\n", + resp->msg.init_complete.req_id, + resp->ndis_msg_type); } } @@ -336,10 +302,10 @@ static void rndis_filter_receive_indicate_status(struct rndis_device *dev, &resp->msg.indicate_status; if (indicate->status == RNDIS_STATUS_MEDIA_CONNECT) { - rndis_filter.inner_drv.link_status_change( + netvsc_linkstatus_callback( dev->net_dev->dev, 1); } else if (indicate->status == RNDIS_STATUS_MEDIA_DISCONNECT) { - rndis_filter.inner_drv.link_status_change( + netvsc_linkstatus_callback( dev->net_dev->dev, 0); } else { /* @@ -371,11 +337,10 @@ static void rndis_filter_receive_data(struct rndis_device *dev, pkt->is_data_pkt = true; - rndis_filter.inner_drv.recv_cb(dev->net_dev->dev, - pkt); + netvsc_recv_callback(dev->net_dev->dev, pkt); } -static int rndis_filter_receive(struct hv_device *dev, +int rndis_filter_receive(struct hv_device *dev, struct hv_netvsc_packet *pkt) { struct netvsc_device *net_dev = dev->ext; @@ -388,15 +353,15 @@ static int rndis_filter_receive(struct hv_device *dev, /* Make sure the rndis device state is initialized */ if (!net_dev->extension) { - DPRINT_ERR(NETVSC, "got rndis message but no rndis device..." - "dropping this message!"); + dev_err(&dev->device, "got rndis message but no rndis device - " + "dropping this message!\n"); return -1; } rndis_dev = (struct rndis_device *)net_dev->extension; if (rndis_dev->state == RNDIS_DEV_UNINITIALIZED) { - DPRINT_ERR(NETVSC, "got rndis message but rndis device " - "uninitialized...dropping this message!"); + dev_err(&dev->device, "got rndis message but rndis device " + "uninitialized...dropping this message!\n"); return -1; } @@ -417,8 +382,8 @@ static int rndis_filter_receive(struct hv_device *dev, kunmap_atomic(rndis_hdr - pkt->page_buf[0].offset, KM_IRQ0); - DPRINT_ERR(NETVSC, "invalid rndis message? (expected %u " - "bytes got %u)...dropping this message!", + dev_err(&dev->device, "invalid rndis message? (expected %u " + "bytes got %u)...dropping this message!\n", rndis_hdr->msg_len, pkt->total_data_buflen); return -1; @@ -427,8 +392,8 @@ static int rndis_filter_receive(struct hv_device *dev, if ((rndis_hdr->ndis_msg_type != REMOTE_NDIS_PACKET_MSG) && (rndis_hdr->msg_len > sizeof(struct rndis_message))) { - DPRINT_ERR(NETVSC, "incoming rndis message buffer overflow " - "detected (got %u, max %zu)...marking it an error!", + dev_err(&dev->device, "incoming rndis message buffer overflow " + "detected (got %u, max %zu)..marking it an error!\n", rndis_hdr->msg_len, sizeof(struct rndis_message)); } @@ -460,7 +425,8 @@ static int rndis_filter_receive(struct hv_device *dev, rndis_filter_receive_indicate_status(rndis_dev, &rndis_msg); break; default: - DPRINT_ERR(NETVSC, "unhandled rndis message (type %u len %u)", + dev_err(&dev->device, + "unhandled rndis message (type %u len %u)\n", rndis_msg.ndis_msg_type, rndis_msg.msg_len); break; @@ -477,6 +443,7 @@ static int rndis_filter_query_device(struct rndis_device *dev, u32 oid, struct rndis_query_request *query; struct rndis_query_complete *query_complete; int ret = 0; + int t; if (!result) return -EINVAL; @@ -496,14 +463,12 @@ static int rndis_filter_query_device(struct rndis_device *dev, u32 oid, query->info_buflen = 0; query->dev_vc_handle = 0; - request->wait_condition = 0; ret = rndis_filter_send_request(dev, request); if (ret != 0) goto Cleanup; - wait_event_timeout(request->wait_event, request->wait_condition, - msecs_to_jiffies(1000)); - if (request->wait_condition == 0) { + t = wait_for_completion_timeout(&request->wait_event, HZ); + if (t == 0) { ret = -ETIMEDOUT; goto Cleanup; } @@ -555,7 +520,7 @@ static int rndis_filter_set_packet_filter(struct rndis_device *dev, struct rndis_set_request *set; struct rndis_set_complete *set_complete; u32 status; - int ret; + int ret, t; request = get_rndis_request(dev, REMOTE_NDIS_SET_MSG, RNDIS_MESSAGE_SIZE(struct rndis_set_request) + @@ -574,16 +539,16 @@ static int rndis_filter_set_packet_filter(struct rndis_device *dev, memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request), &new_filter, sizeof(u32)); - request->wait_condition = 0; ret = rndis_filter_send_request(dev, request); if (ret != 0) goto Cleanup; - wait_event_timeout(request->wait_event, request->wait_condition, - msecs_to_jiffies(2000)); - if (request->wait_condition == 0) { + t = wait_for_completion_timeout(&request->wait_event, HZ); + + if (t == 0) { ret = -1; - DPRINT_ERR(NETVSC, "timeout before we got a set response..."); + dev_err(&dev->net_dev->dev->device, + "timeout before we got a set response...\n"); /* * We can't deallocate the request since we may still receive a * send completion for it. @@ -603,42 +568,6 @@ Exit: return ret; } -int rndis_filter_init(struct netvsc_driver *drv) -{ - DPRINT_DBG(NETVSC, "sizeof(struct rndis_filter_packet) == %zd", - sizeof(struct rndis_filter_packet)); - - drv->req_ext_size = sizeof(struct rndis_filter_packet); - - /* Driver->Context = rndisDriver; */ - - memset(&rndis_filter, 0, sizeof(struct rndis_filter_driver_object)); - - /*rndisDriver->Driver = Driver; - - ASSERT(Driver->OnLinkStatusChanged); - rndisDriver->OnLinkStatusChanged = Driver->OnLinkStatusChanged;*/ - - /* Save the original dispatch handlers before we override it */ - rndis_filter.inner_drv.base.dev_add = drv->base.dev_add; - rndis_filter.inner_drv.base.dev_rm = - drv->base.dev_rm; - rndis_filter.inner_drv.base.cleanup = drv->base.cleanup; - - rndis_filter.inner_drv.send = drv->send; - rndis_filter.inner_drv.recv_cb = drv->recv_cb; - rndis_filter.inner_drv.link_status_change = - drv->link_status_change; - - /* Override */ - drv->base.dev_add = rndis_filte_device_add; - drv->base.dev_rm = rndis_filter_device_remove; - drv->base.cleanup = rndis_filter_cleanup; - drv->send = rndis_filter_send; - drv->recv_cb = rndis_filter_receive; - - return 0; -} static int rndis_filter_init_device(struct rndis_device *dev) { @@ -646,7 +575,7 @@ static int rndis_filter_init_device(struct rndis_device *dev) struct rndis_initialize_request *init; struct rndis_initialize_complete *init_complete; u32 status; - int ret; + int ret, t; request = get_rndis_request(dev, REMOTE_NDIS_INITIALIZE_MSG, RNDIS_MESSAGE_SIZE(struct rndis_initialize_request)); @@ -664,7 +593,6 @@ static int rndis_filter_init_device(struct rndis_device *dev) dev->state = RNDIS_DEV_INITIALIZING; - request->wait_condition = 0; ret = rndis_filter_send_request(dev, request); if (ret != 0) { dev->state = RNDIS_DEV_UNINITIALIZED; @@ -672,9 +600,9 @@ static int rndis_filter_init_device(struct rndis_device *dev) } - wait_event_timeout(request->wait_event, request->wait_condition, - msecs_to_jiffies(1000)); - if (request->wait_condition == 0) { + t = wait_for_completion_timeout(&request->wait_event, HZ); + + if (t == 0) { ret = -ETIMEDOUT; goto Cleanup; } @@ -753,7 +681,7 @@ static int rndis_filter_close_device(struct rndis_device *dev) return ret; } -static int rndis_filte_device_add(struct hv_device *dev, +int rndis_filte_device_add(struct hv_device *dev, void *additional_info) { int ret; @@ -765,14 +693,12 @@ static int rndis_filte_device_add(struct hv_device *dev, if (!rndisDevice) return -1; - DPRINT_DBG(NETVSC, "rndis device object allocated - %p", rndisDevice); - /* * Let the inner driver handle this first to create the netvsc channel * NOTE! Once the channel is created, we may get a receive callback * (RndisFilterOnReceive()) before this call is completed */ - ret = rndis_filter.inner_drv.base.dev_add(dev, additional_info); + ret = netvsc_device_add(dev, additional_info); if (ret != 0) { kfree(rndisDevice); return ret; @@ -802,21 +728,20 @@ static int rndis_filte_device_add(struct hv_device *dev, */ } - DPRINT_INFO(NETVSC, "Device 0x%p mac addr %pM", - rndisDevice, rndisDevice->hw_mac_adr); - memcpy(deviceInfo->mac_adr, rndisDevice->hw_mac_adr, ETH_ALEN); rndis_filter_query_device_link_status(rndisDevice); deviceInfo->link_state = rndisDevice->link_stat; - DPRINT_INFO(NETVSC, "Device 0x%p link state %s", rndisDevice, - ((deviceInfo->link_state) ? ("down") : ("up"))); + + dev_info(&dev->device, "Device MAC %pM link state %s", + rndisDevice->hw_mac_adr, + ((deviceInfo->link_state) ? ("down\n") : ("up\n"))); return ret; } -static int rndis_filter_device_remove(struct hv_device *dev) +int rndis_filter_device_remove(struct hv_device *dev) { struct netvsc_device *net_dev = dev->ext; struct rndis_device *rndis_dev = net_dev->extension; @@ -827,15 +752,11 @@ static int rndis_filter_device_remove(struct hv_device *dev) kfree(rndis_dev); net_dev->extension = NULL; - /* Pass control to inner driver to remove the device */ - rndis_filter.inner_drv.base.dev_rm(dev); + netvsc_device_remove(dev); return 0; } -static void rndis_filter_cleanup(struct hv_driver *drv) -{ -} int rndis_filter_open(struct hv_device *dev) { @@ -857,7 +778,7 @@ int rndis_filter_close(struct hv_device *dev) return rndis_filter_close_device(netDevice->extension); } -static int rndis_filter_send(struct hv_device *dev, +int rndis_filter_send(struct hv_device *dev, struct hv_netvsc_packet *pkt) { int ret; @@ -897,7 +818,7 @@ static int rndis_filter_send(struct hv_device *dev, pkt->completion.send.send_completion = rndis_filter_send_completion; pkt->completion.send.send_completion_ctx = filterPacket; - ret = rndis_filter.inner_drv.send(dev, pkt); + ret = netvsc_send(dev, pkt); if (ret != 0) { /* * Reset the completion to originals to allow retries from diff --git a/drivers/staging/hv/rndis_filter.h b/drivers/staging/hv/rndis_filter.h deleted file mode 100644 index 4da18f3cbade..000000000000 --- a/drivers/staging/hv/rndis_filter.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang <haiyangz@microsoft.com> - * Hank Janssen <hjanssen@microsoft.com> - * - */ - - -#ifndef _RNDISFILTER_H_ -#define _RNDISFILTER_H_ - -#define __struct_bcount(x) - -#include "netvsc.h" - -#include "rndis.h" - -#define RNDIS_HEADER_SIZE (sizeof(struct rndis_message) - \ - sizeof(union rndis_message_container)) - -#define NDIS_PACKET_TYPE_DIRECTED 0x00000001 -#define NDIS_PACKET_TYPE_MULTICAST 0x00000002 -#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 -#define NDIS_PACKET_TYPE_BROADCAST 0x00000008 -#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010 -#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020 -#define NDIS_PACKET_TYPE_SMT 0x00000040 -#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080 -#define NDIS_PACKET_TYPE_GROUP 0x00000100 -#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200 -#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 -#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 - - -/* Interface */ - -extern int rndis_filter_init(struct netvsc_driver *driver); - -#endif /* _RNDISFILTER_H_ */ diff --git a/drivers/staging/hv/storvsc.c b/drivers/staging/hv/storvsc.c index e2ad72924184..06cd3276813c 100644 --- a/drivers/staging/hv/storvsc.c +++ b/drivers/staging/hv/storvsc.c @@ -17,71 +17,19 @@ * Authors: * Haiyang Zhang <haiyangz@microsoft.com> * Hank Janssen <hjanssen@microsoft.com> + * K. Y. Srinivasan <kys@microsoft.com> + * */ #include <linux/kernel.h> #include <linux/sched.h> -#include <linux/wait.h> +#include <linux/completion.h> #include <linux/string.h> #include <linux/slab.h> #include <linux/mm.h> #include <linux/delay.h> -#include "hv_api.h" -#include "logging.h" -#include "storvsc_api.h" -#include "vmbus_packet_format.h" -#include "vstorage.h" -#include "channel.h" - - -struct storvsc_request_extension { - /* LIST_ENTRY ListEntry; */ - - struct hv_storvsc_request *request; - struct hv_device *device; - - /* Synchronize the request/response if needed */ - int wait_condition; - wait_queue_head_t wait_event; - - struct vstor_packet vstor_packet; -}; - -/* A storvsc device is a device object that contains a vmbus channel */ -struct storvsc_device { - struct hv_device *device; - - /* 0 indicates the device is being destroyed */ - atomic_t ref_count; - atomic_t num_outstanding_req; - - /* - * Each unique Port/Path/Target represents 1 channel ie scsi - * controller. In reality, the pathid, targetid is always 0 - * and the port is set by us - */ - unsigned int port_number; - unsigned char path_id; - unsigned char target_id; - - /* LIST_ENTRY OutstandingRequestList; */ - /* HANDLE OutstandingRequestLock; */ - - /* Used for vsc/vsp channel reset process */ - struct storvsc_request_extension init_request; - struct storvsc_request_extension reset_request; -}; - - -static const char *g_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 - } -}; +#include "hyperv.h" +#include "hyperv_storage.h" static inline struct storvsc_device *alloc_stor_device(struct hv_device *device) @@ -96,6 +44,7 @@ static inline struct storvsc_device *alloc_stor_device(struct hv_device *device) /* (ie get_stor_device() and must_get_stor_device()) to proceed. */ atomic_cmpxchg(&stor_device->ref_count, 0, 2); + init_waitqueue_head(&stor_device->waiting_to_drain); stor_device->device = device; device->ext = stor_device; @@ -104,24 +53,9 @@ static inline struct storvsc_device *alloc_stor_device(struct hv_device *device) static inline void free_stor_device(struct storvsc_device *device) { - /* ASSERT(atomic_read(&device->ref_count) == 0); */ kfree(device); } -/* Get the stordevice object iff exists and its refcount > 1 */ -static inline struct storvsc_device *get_stor_device(struct hv_device *device) -{ - struct storvsc_device *stor_device; - - stor_device = (struct storvsc_device *)device->ext; - if (stor_device && atomic_read(&stor_device->ref_count) > 1) - atomic_inc(&stor_device->ref_count); - else - stor_device = NULL; - - return stor_device; -} - /* Get the stordevice object iff exists and its refcount > 0 */ static inline struct storvsc_device *must_get_stor_device( struct hv_device *device) @@ -137,17 +71,6 @@ static inline struct storvsc_device *must_get_stor_device( return stor_device; } -static inline void put_stor_device(struct hv_device *device) -{ - struct storvsc_device *stor_device; - - stor_device = (struct storvsc_device *)device->ext; - /* ASSERT(stor_device); */ - - atomic_dec(&stor_device->ref_count); - /* ASSERT(atomic_read(&stor_device->ref_count)); */ -} - /* Drop ref count to 1 to effectively disable get_stor_device() */ static inline struct storvsc_device *release_stor_device( struct hv_device *device) @@ -155,7 +78,6 @@ static inline struct storvsc_device *release_stor_device( struct storvsc_device *stor_device; stor_device = (struct storvsc_device *)device->ext; - /* ASSERT(stor_device); */ /* Busy wait until the ref drop to 2, then set it to 1 */ while (atomic_cmpxchg(&stor_device->ref_count, 2, 1) != 2) @@ -171,7 +93,6 @@ static inline struct storvsc_device *final_release_stor_device( struct storvsc_device *stor_device; stor_device = (struct storvsc_device *)device->ext; - /* ASSERT(stor_device); */ /* Busy wait until the ref drop to 1, then set it to 0 */ while (atomic_cmpxchg(&stor_device->ref_count, 1, 0) != 1) @@ -181,19 +102,16 @@ static inline struct storvsc_device *final_release_stor_device( return stor_device; } -static int stor_vsc_channel_init(struct hv_device *device) +static int storvsc_channel_init(struct hv_device *device) { struct storvsc_device *stor_device; - struct storvsc_request_extension *request; + struct hv_storvsc_request *request; struct vstor_packet *vstor_packet; - int ret; + int ret, t; stor_device = get_stor_device(device); - if (!stor_device) { - DPRINT_ERR(STORVSC, "unable to get stor device..." - "device being destroyed?"); + if (!stor_device) return -1; - } request = &stor_device->init_request; vstor_packet = &request->vstor_packet; @@ -202,40 +120,30 @@ static int stor_vsc_channel_init(struct hv_device *device) * Now, initiate the vsc/vsp initialization protocol on the open * channel */ - memset(request, 0, sizeof(struct storvsc_request_extension)); - init_waitqueue_head(&request->wait_event); + memset(request, 0, sizeof(struct hv_storvsc_request)); + init_completion(&request->wait_event); vstor_packet->operation = VSTOR_OPERATION_BEGIN_INITIALIZATION; vstor_packet->flags = REQUEST_COMPLETION_FLAG; DPRINT_INFO(STORVSC, "BEGIN_INITIALIZATION_OPERATION..."); - request->wait_condition = 0; ret = vmbus_sendpacket(device->channel, vstor_packet, sizeof(struct vstor_packet), (unsigned long)request, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (ret != 0) { - DPRINT_ERR(STORVSC, - "unable to send BEGIN_INITIALIZATION_OPERATION"); + if (ret != 0) goto cleanup; - } - wait_event_timeout(request->wait_event, request->wait_condition, - msecs_to_jiffies(1000)); - if (request->wait_condition == 0) { + t = wait_for_completion_timeout(&request->wait_event, HZ); + if (t == 0) { ret = -ETIMEDOUT; goto cleanup; } - if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || - vstor_packet->status != 0) { - DPRINT_ERR(STORVSC, "BEGIN_INITIALIZATION_OPERATION failed " - "(op %d status 0x%x)", - vstor_packet->operation, vstor_packet->status); + vstor_packet->status != 0) goto cleanup; - } DPRINT_INFO(STORVSC, "QUERY_PROTOCOL_VERSION_OPERATION..."); @@ -247,33 +155,24 @@ static int stor_vsc_channel_init(struct hv_device *device) vstor_packet->version.major_minor = VMSTOR_PROTOCOL_VERSION_CURRENT; FILL_VMSTOR_REVISION(vstor_packet->version.revision); - request->wait_condition = 0; ret = vmbus_sendpacket(device->channel, vstor_packet, sizeof(struct vstor_packet), (unsigned long)request, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (ret != 0) { - DPRINT_ERR(STORVSC, - "unable to send BEGIN_INITIALIZATION_OPERATION"); + if (ret != 0) goto cleanup; - } - wait_event_timeout(request->wait_event, request->wait_condition, - msecs_to_jiffies(1000)); - if (request->wait_condition == 0) { + t = wait_for_completion_timeout(&request->wait_event, HZ); + if (t == 0) { ret = -ETIMEDOUT; goto cleanup; } /* TODO: Check returned version */ if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || - vstor_packet->status != 0) { - DPRINT_ERR(STORVSC, "QUERY_PROTOCOL_VERSION_OPERATION failed " - "(op %d status 0x%x)", - vstor_packet->operation, vstor_packet->status); + vstor_packet->status != 0) goto cleanup; - } /* Query channel properties */ DPRINT_INFO(STORVSC, "QUERY_PROPERTIES_OPERATION..."); @@ -284,76 +183,54 @@ static int stor_vsc_channel_init(struct hv_device *device) vstor_packet->storage_channel_properties.port_number = stor_device->port_number; - request->wait_condition = 0; ret = vmbus_sendpacket(device->channel, vstor_packet, sizeof(struct vstor_packet), (unsigned long)request, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (ret != 0) { - DPRINT_ERR(STORVSC, - "unable to send QUERY_PROPERTIES_OPERATION"); + if (ret != 0) goto cleanup; - } - wait_event_timeout(request->wait_event, request->wait_condition, - msecs_to_jiffies(1000)); - if (request->wait_condition == 0) { + t = wait_for_completion_timeout(&request->wait_event, HZ); + if (t == 0) { ret = -ETIMEDOUT; goto cleanup; } /* TODO: Check returned version */ if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || - vstor_packet->status != 0) { - DPRINT_ERR(STORVSC, "QUERY_PROPERTIES_OPERATION failed " - "(op %d status 0x%x)", - vstor_packet->operation, vstor_packet->status); + vstor_packet->status != 0) goto cleanup; - } stor_device->path_id = vstor_packet->storage_channel_properties.path_id; stor_device->target_id = vstor_packet->storage_channel_properties.target_id; - DPRINT_DBG(STORVSC, "channel flag 0x%x, max xfer len 0x%x", - vstor_packet->storage_channel_properties.flags, - vstor_packet->storage_channel_properties.max_transfer_bytes); - DPRINT_INFO(STORVSC, "END_INITIALIZATION_OPERATION..."); memset(vstor_packet, 0, sizeof(struct vstor_packet)); vstor_packet->operation = VSTOR_OPERATION_END_INITIALIZATION; vstor_packet->flags = REQUEST_COMPLETION_FLAG; - request->wait_condition = 0; ret = vmbus_sendpacket(device->channel, vstor_packet, sizeof(struct vstor_packet), (unsigned long)request, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (ret != 0) { - DPRINT_ERR(STORVSC, - "unable to send END_INITIALIZATION_OPERATION"); + if (ret != 0) goto cleanup; - } - wait_event_timeout(request->wait_event, request->wait_condition, - msecs_to_jiffies(1000)); - if (request->wait_condition == 0) { + t = wait_for_completion_timeout(&request->wait_event, HZ); + if (t == 0) { ret = -ETIMEDOUT; goto cleanup; } if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || - vstor_packet->status != 0) { - DPRINT_ERR(STORVSC, "END_INITIALIZATION_OPERATION failed " - "(op %d status 0x%x)", - vstor_packet->operation, vstor_packet->status); + vstor_packet->status != 0) goto cleanup; - } DPRINT_INFO(STORVSC, "**** storage channel up and running!! ****"); @@ -362,78 +239,70 @@ cleanup: return ret; } -static void stor_vsc_on_io_completion(struct hv_device *device, +static void storvsc_on_io_completion(struct hv_device *device, struct vstor_packet *vstor_packet, - struct storvsc_request_extension *request_ext) + struct hv_storvsc_request *request) { - struct hv_storvsc_request *request; struct storvsc_device *stor_device; + struct vstor_packet *stor_pkt; stor_device = must_get_stor_device(device); - if (!stor_device) { - DPRINT_ERR(STORVSC, "unable to get stor device..." - "device being destroyed?"); + if (!stor_device) return; - } - - DPRINT_DBG(STORVSC, "IO_COMPLETE_OPERATION - request extension %p " - "completed bytes xfer %u", request_ext, - vstor_packet->vm_srb.data_transfer_length); - /* ASSERT(request_ext != NULL); */ - /* ASSERT(request_ext->request != NULL); */ + stor_pkt = &request->vstor_packet; - request = request_ext->request; - - /* ASSERT(request->OnIOCompletion != NULL); */ /* Copy over the status...etc */ - request->status = vstor_packet->vm_srb.scsi_status; + stor_pkt->vm_srb.scsi_status = vstor_packet->vm_srb.scsi_status; + stor_pkt->vm_srb.srb_status = vstor_packet->vm_srb.srb_status; + stor_pkt->vm_srb.sense_info_length = + vstor_packet->vm_srb.sense_info_length; - if (request->status != 0 || vstor_packet->vm_srb.srb_status != 1) { + if (vstor_packet->vm_srb.scsi_status != 0 || + vstor_packet->vm_srb.srb_status != 1){ DPRINT_WARN(STORVSC, "cmd 0x%x scsi status 0x%x srb status 0x%x\n", - request->cdb[0], vstor_packet->vm_srb.scsi_status, + stor_pkt->vm_srb.cdb[0], + vstor_packet->vm_srb.scsi_status, vstor_packet->vm_srb.srb_status); } - if ((request->status & 0xFF) == 0x02) { + if ((vstor_packet->vm_srb.scsi_status & 0xFF) == 0x02) { /* CHECK_CONDITION */ if (vstor_packet->vm_srb.srb_status & 0x80) { /* autosense data available */ DPRINT_WARN(STORVSC, "storvsc pkt %p autosense data " - "valid - len %d\n", request_ext, + "valid - len %d\n", request, vstor_packet->vm_srb.sense_info_length); - /* ASSERT(vstor_packet->vm_srb.sense_info_length <= */ - /* request->SenseBufferSize); */ memcpy(request->sense_buffer, vstor_packet->vm_srb.sense_data, vstor_packet->vm_srb.sense_info_length); - request->sense_buffer_size = - vstor_packet->vm_srb.sense_info_length; } } - /* TODO: */ - request->bytes_xfer = vstor_packet->vm_srb.data_transfer_length; + stor_pkt->vm_srb.data_transfer_length = + vstor_packet->vm_srb.data_transfer_length; request->on_io_completion(request); - atomic_dec(&stor_device->num_outstanding_req); + if (atomic_dec_and_test(&stor_device->num_outstanding_req) && + stor_device->drain_notify) + wake_up(&stor_device->waiting_to_drain); + put_stor_device(device); } -static void stor_vsc_on_receive(struct hv_device *device, +static void storvsc_on_receive(struct hv_device *device, struct vstor_packet *vstor_packet, - struct storvsc_request_extension *request_ext) + struct hv_storvsc_request *request) { switch (vstor_packet->operation) { case VSTOR_OPERATION_COMPLETE_IO: - DPRINT_DBG(STORVSC, "IO_COMPLETE_OPERATION"); - stor_vsc_on_io_completion(device, vstor_packet, request_ext); + storvsc_on_io_completion(device, vstor_packet, request); break; case VSTOR_OPERATION_REMOVE_DEVICE: DPRINT_INFO(STORVSC, "REMOVE_DEVICE_OPERATION"); @@ -447,60 +316,42 @@ static void stor_vsc_on_receive(struct hv_device *device, } } -static void stor_vsc_on_channel_callback(void *context) +static void storvsc_on_channel_callback(void *context) { struct hv_device *device = (struct hv_device *)context; struct storvsc_device *stor_device; u32 bytes_recvd; u64 request_id; unsigned char packet[ALIGN(sizeof(struct vstor_packet), 8)]; - struct storvsc_request_extension *request; + struct hv_storvsc_request *request; int ret; - /* ASSERT(device); */ stor_device = must_get_stor_device(device); - if (!stor_device) { - DPRINT_ERR(STORVSC, "unable to get stor device..." - "device being destroyed?"); + if (!stor_device) return; - } do { ret = vmbus_recvpacket(device->channel, packet, ALIGN(sizeof(struct vstor_packet), 8), &bytes_recvd, &request_id); if (ret == 0 && bytes_recvd > 0) { - DPRINT_DBG(STORVSC, "receive %d bytes - tid %llx", - bytes_recvd, request_id); - /* ASSERT(bytes_recvd == - sizeof(struct vstor_packet)); */ - - request = (struct storvsc_request_extension *) + request = (struct hv_storvsc_request *) (unsigned long)request_id; - /* ASSERT(request);c */ - /* if (vstor_packet.Flags & SYNTHETIC_FLAG) */ if ((request == &stor_device->init_request) || (request == &stor_device->reset_request)) { - /* DPRINT_INFO(STORVSC, - * "reset completion - operation " - * "%u status %u", - * vstor_packet.Operation, - * vstor_packet.Status); */ memcpy(&request->vstor_packet, packet, sizeof(struct vstor_packet)); - request->wait_condition = 1; - wake_up(&request->wait_event); + complete(&request->wait_event); } else { - stor_vsc_on_receive(device, + storvsc_on_receive(device, (struct vstor_packet *)packet, request); } } else { - /* DPRINT_DBG(STORVSC, "nothing else to read..."); */ break; } } while (1); @@ -509,45 +360,33 @@ static void stor_vsc_on_channel_callback(void *context) return; } -static int stor_vsc_connect_to_vsp(struct hv_device *device) +static int storvsc_connect_to_vsp(struct hv_device *device, u32 ring_size) { struct vmstorage_channel_properties props; - struct storvsc_driver_object *stor_driver; int ret; - stor_driver = (struct storvsc_driver_object *)device->drv; memset(&props, 0, sizeof(struct vmstorage_channel_properties)); /* Open the channel */ ret = vmbus_open(device->channel, - stor_driver->ring_buffer_size, - stor_driver->ring_buffer_size, + ring_size, + ring_size, (void *)&props, sizeof(struct vmstorage_channel_properties), - stor_vsc_on_channel_callback, device); - - DPRINT_DBG(STORVSC, "storage props: path id %d, tgt id %d, max xfer %d", - props.path_id, props.target_id, props.max_transfer_bytes); + storvsc_on_channel_callback, device); - if (ret != 0) { - DPRINT_ERR(STORVSC, "unable to open channel: %d", ret); + if (ret != 0) return -1; - } - ret = stor_vsc_channel_init(device); + ret = storvsc_channel_init(device); return ret; } -/* - * stor_vsc_on_device_add - Callback when the device belonging to this driver - * is added - */ -static int stor_vsc_on_device_add(struct hv_device *device, +int storvsc_dev_add(struct hv_device *device, void *additional_info) { struct storvsc_device *stor_device; - /* struct vmstorage_channel_properties *props; */ struct storvsc_device_info *device_info; int ret = 0; @@ -559,8 +398,6 @@ static int stor_vsc_on_device_add(struct hv_device *device, } /* Save the channel properties to our storvsc channel */ - /* props = (struct vmstorage_channel_properties *) - * channel->offerMsg.Offer.u.Standard.UserDefined; */ /* FIXME: */ /* @@ -569,30 +406,18 @@ static int stor_vsc_on_device_add(struct hv_device *device, * scsi channel prior to the bus scan */ - /* storChannel->PortNumber = 0; - storChannel->PathId = props->PathId; - storChannel->TargetId = props->TargetId; */ - stor_device->port_number = device_info->port_number; /* Send it back up */ - ret = stor_vsc_connect_to_vsp(device); + ret = storvsc_connect_to_vsp(device, device_info->ring_buffer_size); - /* device_info->PortNumber = stor_device->PortNumber; */ device_info->path_id = stor_device->path_id; device_info->target_id = stor_device->target_id; - DPRINT_DBG(STORVSC, "assigned port %u, path %u target %u\n", - stor_device->port_number, stor_device->path_id, - stor_device->target_id); - cleanup: return ret; } -/* - * stor_vsc_on_device_remove - Callback when the our device is being removed - */ -static int stor_vsc_on_device_remove(struct hv_device *device) +int storvsc_dev_remove(struct hv_device *device) { struct storvsc_device *stor_device; @@ -606,19 +431,11 @@ static int stor_vsc_on_device_remove(struct hv_device *device) * only allow inbound traffic (responses) to proceed so that * outstanding requests can be completed. */ - while (atomic_read(&stor_device->num_outstanding_req)) { - DPRINT_INFO(STORVSC, "waiting for %d requests to complete...", - atomic_read(&stor_device->num_outstanding_req)); - udelay(100); - } - DPRINT_INFO(STORVSC, "removing storage device (%p)...", - device->ext); + storvsc_wait_to_drain(stor_device); stor_device = final_release_stor_device(device); - DPRINT_INFO(STORVSC, "storage device (%p) safe to remove", stor_device); - /* Close the channel */ vmbus_close(device->channel); @@ -626,148 +443,52 @@ static int stor_vsc_on_device_remove(struct hv_device *device) return 0; } -int stor_vsc_on_host_reset(struct hv_device *device) -{ - struct storvsc_device *stor_device; - struct storvsc_request_extension *request; - struct vstor_packet *vstor_packet; - int ret; - - 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_waitqueue_head(&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; - - request->wait_condition = 0; - 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; - } - - wait_event_timeout(request->wait_event, request->wait_condition, - msecs_to_jiffies(1000)); - if (request->wait_condition == 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; -} - -/* - * stor_vsc_on_io_request - Callback to initiate an I/O request - */ -static int stor_vsc_on_io_request(struct hv_device *device, +int storvsc_do_io(struct hv_device *device, struct hv_storvsc_request *request) { struct storvsc_device *stor_device; - struct storvsc_request_extension *request_extension; struct vstor_packet *vstor_packet; int ret = 0; - request_extension = - (struct storvsc_request_extension *)request->extension; - vstor_packet = &request_extension->vstor_packet; + vstor_packet = &request->vstor_packet; stor_device = get_stor_device(device); - DPRINT_DBG(STORVSC, "enter - Device %p, DeviceExt %p, Request %p, " - "Extension %p", device, stor_device, request, - request_extension); - - DPRINT_DBG(STORVSC, "req %p len %d bus %d, target %d, lun %d cdblen %d", - request, request->data_buffer.len, request->bus, - request->target_id, request->lun_id, request->cdb_len); - - if (!stor_device) { - DPRINT_ERR(STORVSC, "unable to get stor device..." - "device being destroyed?"); + if (!stor_device) return -2; - } - /* print_hex_dump_bytes("", DUMP_PREFIX_NONE, request->Cdb, - * request->CdbLen); */ - request_extension->request = request; - request_extension->device = device; + request->device = device; - memset(vstor_packet, 0 , sizeof(struct vstor_packet)); vstor_packet->flags |= REQUEST_COMPLETION_FLAG; vstor_packet->vm_srb.length = sizeof(struct vmscsi_request); - vstor_packet->vm_srb.port_number = request->host; - vstor_packet->vm_srb.path_id = request->bus; - vstor_packet->vm_srb.target_id = request->target_id; - vstor_packet->vm_srb.lun = request->lun_id; vstor_packet->vm_srb.sense_info_length = SENSE_BUFFER_SIZE; - /* Copy over the scsi command descriptor block */ - vstor_packet->vm_srb.cdb_length = request->cdb_len; - memcpy(&vstor_packet->vm_srb.cdb, request->cdb, request->cdb_len); - vstor_packet->vm_srb.data_in = request->type; - vstor_packet->vm_srb.data_transfer_length = request->data_buffer.len; + vstor_packet->vm_srb.data_transfer_length = + request->data_buffer.len; vstor_packet->operation = VSTOR_OPERATION_EXECUTE_SRB; - DPRINT_DBG(STORVSC, "srb - len %d port %d, path %d, target %d, " - "lun %d senselen %d cdblen %d", - vstor_packet->vm_srb.length, - vstor_packet->vm_srb.port_number, - vstor_packet->vm_srb.path_id, - vstor_packet->vm_srb.target_id, - vstor_packet->vm_srb.lun, - vstor_packet->vm_srb.sense_info_length, - vstor_packet->vm_srb.cdb_length); - - if (request_extension->request->data_buffer.len) { + if (request->data_buffer.len) { ret = vmbus_sendpacket_multipagebuffer(device->channel, - &request_extension->request->data_buffer, + &request->data_buffer, vstor_packet, sizeof(struct vstor_packet), - (unsigned long)request_extension); + (unsigned long)request); } else { ret = vmbus_sendpacket(device->channel, vstor_packet, sizeof(struct vstor_packet), - (unsigned long)request_extension, + (unsigned long)request, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); } - if (ret != 0) { - DPRINT_DBG(STORVSC, "Unable to send packet %p ret %d", - vstor_packet, ret); - } + if (ret != 0) + return ret; atomic_inc(&stor_device->num_outstanding_req); @@ -776,62 +497,68 @@ static int stor_vsc_on_io_request(struct hv_device *device, } /* - * stor_vsc_on_cleanup - Perform any cleanup when the driver is removed + * The channel properties uniquely specify how the device is to be + * presented to the guest. Map this information for use by the block + * driver. For Linux guests on Hyper-V, we emulate a scsi HBA in the guest + * (storvsc_drv) and so scsi devices in the guest are handled by + * native upper level Linux drivers. Consequently, Hyper-V + * block driver, while being a generic block driver, presently does not + * deal with anything other than devices that would need to be presented + * to the guest as an IDE disk. + * + * This function maps the channel properties as embedded in the input + * parameter device_info onto information necessary to register the + * corresponding block device. + * + * Currently, there is no way to stop the emulation of the block device + * on the host side. And so, to prevent the native IDE drivers in Linux + * from taking over these devices (to be managedby Hyper-V block + * driver), we will take over if need be the major of the IDE controllers. + * */ -static void stor_vsc_on_cleanup(struct hv_driver *driver) -{ -} -/* - * stor_vsc_initialize - Main entry point - */ -int stor_vsc_initialize(struct hv_driver *driver) +int storvsc_get_major_info(struct storvsc_device_info *device_info, + struct storvsc_major_info *major_info) { - struct storvsc_driver_object *stor_driver; - - stor_driver = (struct storvsc_driver_object *)driver; - - DPRINT_DBG(STORVSC, "sizeof(STORVSC_REQUEST)=%zd " - "sizeof(struct storvsc_request_extension)=%zd " - "sizeof(struct vstor_packet)=%zd, " - "sizeof(struct vmscsi_request)=%zd", - sizeof(struct hv_storvsc_request), - sizeof(struct storvsc_request_extension), - sizeof(struct vstor_packet), - sizeof(struct vmscsi_request)); - - /* Make sure we are at least 2 pages since 1 page is used for control */ - /* ASSERT(stor_driver->RingBufferSize >= (PAGE_SIZE << 1)); */ - - driver->name = g_driver_name; - memcpy(&driver->dev_type, &gStorVscDeviceType, - sizeof(struct hv_guid)); - - stor_driver->request_ext_size = - sizeof(struct storvsc_request_extension); + static bool ide0_registered; + static bool ide1_registered; /* - * 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) + * For now we only support IDE disks. */ - stor_driver->max_outstanding_req_per_channel = - ((stor_driver->ring_buffer_size - PAGE_SIZE) / - ALIGN(MAX_MULTIPAGE_BUFFER_PACKET + - 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; + major_info->devname = "ide"; + major_info->diskname = "hd"; + + if (device_info->path_id) { + major_info->major = 22; + if (!ide1_registered) { + major_info->do_register = true; + ide1_registered = true; + } else + major_info->do_register = false; + + if (device_info->target_id) + major_info->index = 3; + else + major_info->index = 2; + + return 0; + } else { + major_info->major = 3; + if (!ide0_registered) { + major_info->do_register = true; + ide0_registered = true; + } else + major_info->do_register = false; + + if (device_info->target_id) + major_info->index = 1; + else + major_info->index = 0; + + return 0; + } - return 0; + return -ENODEV; } + diff --git a/drivers/staging/hv/storvsc_api.h b/drivers/staging/hv/storvsc_api.h deleted file mode 100644 index fbf57556d890..000000000000 --- a/drivers/staging/hv/storvsc_api.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang <haiyangz@microsoft.com> - * Hank Janssen <hjanssen@microsoft.com> - * - */ - - -#ifndef _STORVSC_API_H_ -#define _STORVSC_API_H_ - -#include "vmbus_api.h" - -/* Defines */ -#define STORVSC_RING_BUFFER_SIZE (20*PAGE_SIZE) -#define BLKVSC_RING_BUFFER_SIZE (20*PAGE_SIZE) - -#define STORVSC_MAX_IO_REQUESTS 128 - -/* - * In Hyper-V, each port/path/target maps to 1 scsi host adapter. In - * reality, the path/target is not used (ie always set to 0) so our - * scsi host adapter essentially has 1 bus with 1 target that contains - * up to 256 luns. - */ -#define STORVSC_MAX_LUNS_PER_TARGET 64 -#define STORVSC_MAX_TARGETS 1 -#define STORVSC_MAX_CHANNELS 1 - -struct hv_storvsc_request; - -/* Matches Windows-end */ -enum storvsc_request_type{ - WRITE_TYPE, - READ_TYPE, - UNKNOWN_TYPE, -}; - -struct hv_storvsc_request { - enum storvsc_request_type type; - u32 host; - u32 bus; - u32 target_id; - u32 lun_id; - u8 *cdb; - u32 cdb_len; - u32 status; - u32 bytes_xfer; - - unsigned char *sense_buffer; - u32 sense_buffer_size; - - void *context; - - void (*on_io_completion)(struct hv_storvsc_request *request); - - /* This points to the memory after DataBuffer */ - void *extension; - - struct hv_multipage_buffer data_buffer; -}; - -/* Represents the block vsc driver */ -struct storvsc_driver_object { - /* Must be the first field */ - /* Which is a bug FIXME! */ - struct hv_driver base; - - /* Set by caller (in bytes) */ - u32 ring_buffer_size; - - /* Allocate this much private extension for each I/O request */ - u32 request_ext_size; - - /* Maximum # of requests in flight per channel/device */ - u32 max_outstanding_req_per_channel; - - /* Specific to this driver */ - int (*on_io_request)(struct hv_device *device, - struct hv_storvsc_request *request); -}; - -struct storvsc_device_info { - unsigned int port_number; - unsigned char path_id; - unsigned char target_id; -}; - -/* Interface */ -int stor_vsc_initialize(struct hv_driver *driver); -int stor_vsc_on_host_reset(struct hv_device *device); -int blk_vsc_initialize(struct hv_driver *driver); - -#endif /* _STORVSC_API_H_ */ 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; } diff --git a/drivers/staging/hv/utils.h b/drivers/staging/hv/utils.h deleted file mode 100644 index acebbbf888b0..000000000000 --- a/drivers/staging/hv/utils.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang <haiyangz@microsoft.com> - * Hank Janssen <hjanssen@microsoft.com> - */ -#ifndef __HV_UTILS_H_ -#define __HV_UTILS_H_ - -/* - * Common header for Hyper-V ICs - */ -#define ICMSGTYPE_NEGOTIATE 0 -#define ICMSGTYPE_HEARTBEAT 1 -#define ICMSGTYPE_KVPEXCHANGE 2 -#define ICMSGTYPE_SHUTDOWN 3 -#define ICMSGTYPE_TIMESYNC 4 -#define ICMSGTYPE_VSS 5 - -#define ICMSGHDRFLAG_TRANSACTION 1 -#define ICMSGHDRFLAG_REQUEST 2 -#define ICMSGHDRFLAG_RESPONSE 4 - -#define HV_S_OK 0x00000000 -#define HV_E_FAIL 0x80004005 -#define HV_ERROR_NOT_SUPPORTED 0x80070032 -#define HV_ERROR_MACHINE_LOCKED 0x800704F7 - -struct vmbuspipe_hdr { - u32 flags; - u32 msgsize; -} __packed; - -struct ic_version { - u16 major; - u16 minor; -} __packed; - -struct icmsg_hdr { - struct ic_version icverframe; - u16 icmsgtype; - struct ic_version icvermsg; - u16 icmsgsize; - u32 status; - u8 ictransaction_id; - u8 icflags; - u8 reserved[2]; -} __packed; - -struct icmsg_negotiate { - u16 icframe_vercnt; - u16 icmsg_vercnt; - u32 reserved; - struct ic_version icversion_data[1]; /* any size array */ -} __packed; - -struct shutdown_msg_data { - u32 reason_code; - u32 timeout_seconds; - u32 flags; - u8 display_message[2048]; -} __packed; - -struct heartbeat_msg_data { - u64 seq_num; - u32 reserved[8]; -} __packed; - -/* Time Sync IC defs */ -#define ICTIMESYNCFLAG_PROBE 0 -#define ICTIMESYNCFLAG_SYNC 1 -#define ICTIMESYNCFLAG_SAMPLE 2 - -#ifdef __x86_64__ -#define WLTIMEDELTA 116444736000000000L /* in 100ns unit */ -#else -#define WLTIMEDELTA 116444736000000000LL -#endif - -struct ictimesync_data{ - u64 parenttime; - u64 childtime; - u64 roundtriptime; - u8 flags; -} __packed; - -/* Index for each IC struct in array hv_cb_utils[] */ -#define HV_SHUTDOWN_MSG 0 -#define HV_TIMESYNC_MSG 1 -#define HV_HEARTBEAT_MSG 2 -#define HV_KVP_MSG 3 - -struct hyperv_service_callback { - u8 msg_type; - char *log_msg; - unsigned char data[16]; - struct vmbus_channel *channel; - void (*callback) (void *context); -}; - -extern void prep_negotiate_resp(struct icmsg_hdr *, - struct icmsg_negotiate *, u8 *); -extern void chn_cb_negotiate(void *); -extern struct hyperv_service_callback hv_cb_utils[]; - -#endif /* __HV_UTILS_H_ */ diff --git a/drivers/staging/hv/version_info.h b/drivers/staging/hv/version_info.h deleted file mode 100644 index 35178f2c7967..000000000000 --- a/drivers/staging/hv/version_info.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang <haiyangz@microsoft.com> - * Hank Janssen <hjanssen@microsoft.com> - * - */ - -#ifndef __HV_VERSION_INFO -#define __HV_VERSION_INFO - -/* - * We use the same version numbering for all Hyper-V modules. - * - * Definition of versioning is as follows; - * - * Major Number Changes for these scenarios; - * 1. When a new version of Windows Hyper-V - * is released. - * 2. A Major change has occurred in the - * Linux IC's. - * (For example the merge for the first time - * into the kernel) Every time the Major Number - * changes, the Revision number is reset to 0. - * Minor Number Changes when new functionality is added - * to the Linux IC's that is not a bug fix. - * - * 3.1 - Added completed hv_utils driver. Shutdown/Heartbeat/Timesync - */ -#define HV_DRV_VERSION "3.1" - - -#endif diff --git a/drivers/staging/hv/vmbus.h b/drivers/staging/hv/vmbus.h deleted file mode 100644 index 73087f26bec2..000000000000 --- a/drivers/staging/hv/vmbus.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang <haiyangz@microsoft.com> - * Hank Janssen <hjanssen@microsoft.com> - * - */ - - -#ifndef _VMBUS_H_ -#define _VMBUS_H_ - -#include <linux/device.h> -#include "vmbus_api.h" - - - - -static inline struct hv_device *device_to_hv_device(struct device *d) -{ - return container_of(d, struct hv_device, device); -} - -static inline struct hv_driver *drv_to_hv_drv(struct device_driver *d) -{ - return container_of(d, struct hv_driver, driver); -} - - -/* Vmbus interface */ -int vmbus_child_driver_register(struct device_driver *drv); -void vmbus_child_driver_unregister(struct device_driver *drv); - -extern struct completion hv_channel_ready; - -#endif /* _VMBUS_H_ */ diff --git a/drivers/staging/hv/vmbus_api.h b/drivers/staging/hv/vmbus_api.h deleted file mode 100644 index f0d96eba7013..000000000000 --- a/drivers/staging/hv/vmbus_api.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang <haiyangz@microsoft.com> - * Hank Janssen <hjanssen@microsoft.com> - * - */ - - -#ifndef _VMBUS_API_H_ -#define _VMBUS_API_H_ - -#include <linux/device.h> -#include <linux/workqueue.h> - -#define MAX_PAGE_BUFFER_COUNT 16 -#define MAX_MULTIPAGE_BUFFER_COUNT 32 /* 128K */ - -#pragma pack(push, 1) - -/* Single-page buffer */ -struct hv_page_buffer { - u32 len; - u32 offset; - u64 pfn; -}; - -/* Multiple-page buffer */ -struct hv_multipage_buffer { - /* Length and Offset determines the # of pfns in the array */ - u32 len; - u32 offset; - u64 pfn_array[MAX_MULTIPAGE_BUFFER_COUNT]; -}; - -/* 0x18 includes the proprietary packet header */ -#define MAX_PAGE_BUFFER_PACKET (0x18 + \ - (sizeof(struct hv_page_buffer) * \ - MAX_PAGE_BUFFER_COUNT)) -#define MAX_MULTIPAGE_BUFFER_PACKET (0x18 + \ - sizeof(struct hv_multipage_buffer)) - - -#pragma pack(pop) - -struct hv_driver; -struct hv_device; - -struct hv_dev_port_info { - u32 int_mask; - u32 read_idx; - u32 write_idx; - u32 bytes_avail_toread; - u32 bytes_avail_towrite; -}; - -struct hv_device_info { - u32 chn_id; - u32 chn_state; - struct hv_guid chn_type; - struct hv_guid chn_instance; - - u32 monitor_id; - u32 server_monitor_pending; - u32 server_monitor_latency; - u32 server_monitor_conn_id; - u32 client_monitor_pending; - u32 client_monitor_latency; - u32 client_monitor_conn_id; - - struct hv_dev_port_info inbound; - struct hv_dev_port_info outbound; -}; - -/* Base driver object */ -struct hv_driver { - const char *name; - - /* the device type supported by this driver */ - struct hv_guid dev_type; - - /* - * Device type specific drivers (net, blk etc.) - * need a mechanism to get a pointer to - * device type specific driver structure given - * a pointer to the base hyperv driver structure. - * The current code solves this problem using - * a hack. Support this need explicitly - */ - void *priv; - - struct device_driver driver; - - int (*dev_add)(struct hv_device *device, void *data); - int (*dev_rm)(struct hv_device *device); - void (*cleanup)(struct hv_driver *driver); -}; - -/* Base device object */ -struct hv_device { - /* the driver for this device */ - struct hv_driver *drv; - - char name[64]; - - struct work_struct probe_failed_work_item; - - int probe_error; - - /* the device type id of this device */ - struct hv_guid dev_type; - - /* the device instance id of this device */ - struct hv_guid dev_instance; - - struct device device; - - struct vmbus_channel *channel; - - /* Device extension; */ - void *ext; -}; - -#endif /* _VMBUS_API_H_ */ diff --git a/drivers/staging/hv/vmbus_channel_interface.h b/drivers/staging/hv/vmbus_channel_interface.h deleted file mode 100644 index 20ae258e5f9c..000000000000 --- a/drivers/staging/hv/vmbus_channel_interface.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang <haiyangz@microsoft.com> - * Hank Janssen <hjanssen@microsoft.com> - * - */ - -#ifndef __VMBUSCHANNELINTERFACE_H -#define __VMBUSCHANNELINTERFACE_H - -/* - * A revision number of vmbus that is used for ensuring both ends on a - * partition are using compatible versions. - */ -#define VMBUS_REVISION_NUMBER 13 - -/* Make maximum size of pipe payload of 16K */ -#define MAX_PIPE_DATA_PAYLOAD (sizeof(u8) * 16384) - -/* Define PipeMode values. */ -#define VMBUS_PIPE_TYPE_BYTE 0x00000000 -#define VMBUS_PIPE_TYPE_MESSAGE 0x00000004 - -/* The size of the user defined data buffer for non-pipe offers. */ -#define MAX_USER_DEFINED_BYTES 120 - -/* The size of the user defined data buffer for pipe offers. */ -#define MAX_PIPE_USER_DEFINED_BYTES 116 - -/* - * At the center of the Channel Management library is the Channel Offer. This - * struct contains the fundamental information about an offer. - */ -struct vmbus_channel_offer { - struct hv_guid if_type; - struct hv_guid if_instance; - u64 int_latency; /* in 100ns units */ - u32 if_revision; - u32 server_ctx_size; /* in bytes */ - u16 chn_flags; - u16 mmio_megabytes; /* in bytes * 1024 * 1024 */ - - union { - /* Non-pipes: The user has MAX_USER_DEFINED_BYTES bytes. */ - struct { - unsigned char user_def[MAX_USER_DEFINED_BYTES]; - } std; - - /* - * Pipes: - * The following sructure is an integrated pipe protocol, which - * is implemented on top of standard user-defined data. Pipe - * clients have MAX_PIPE_USER_DEFINED_BYTES left for their own - * use. - */ - struct { - u32 pipe_mode; - unsigned char user_def[MAX_PIPE_USER_DEFINED_BYTES]; - } pipe; - } u; - u32 padding; -} __packed; - -/* Server Flags */ -#define VMBUS_CHANNEL_ENUMERATE_DEVICE_INTERFACE 1 -#define VMBUS_CHANNEL_SERVER_SUPPORTS_TRANSFER_PAGES 2 -#define VMBUS_CHANNEL_SERVER_SUPPORTS_GPADLS 4 -#define VMBUS_CHANNEL_NAMED_PIPE_MODE 0x10 -#define VMBUS_CHANNEL_LOOPBACK_OFFER 0x100 -#define VMBUS_CHANNEL_PARENT_OFFER 0x200 -#define VMBUS_CHANNEL_REQUEST_MONITORED_NOTIFICATION 0x400 - -#endif diff --git a/drivers/staging/hv/vmbus_drv.c b/drivers/staging/hv/vmbus_drv.c index 79089f85d903..ec1d38cd481c 100644 --- a/drivers/staging/hv/vmbus_drv.c +++ b/drivers/staging/hv/vmbus_drv.c @@ -17,7 +17,11 @@ * Authors: * Haiyang Zhang <haiyangz@microsoft.com> * Hank Janssen <hjanssen@microsoft.com> + * K. Y. Srinivasan <kys@microsoft.com> + * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/init.h> #include <linux/module.h> #include <linux/device.h> @@ -27,52 +31,176 @@ #include <linux/pci.h> #include <linux/dmi.h> #include <linux/slab.h> +#include <linux/acpi.h> +#include <acpi/acpi_bus.h> #include <linux/completion.h> -#include "version_info.h" -#include "hv_api.h" -#include "logging.h" -#include "vmbus.h" -#include "channel.h" -#include "vmbus_private.h" +#include "hyperv.h" +#include "hyperv_vmbus.h" -/* FIXME! We need to do this dynamically for PIC and APIC system */ -#define VMBUS_IRQ 0x5 -#define VMBUS_IRQ_VECTOR IRQ5_VECTOR -/* Main vmbus driver data structure */ -struct vmbus_driver_context { +static struct pci_dev *hv_pci_dev; - struct bus_type bus; - struct tasklet_struct msg_dpc; - struct tasklet_struct event_dpc; +static struct tasklet_struct msg_dpc; +static struct tasklet_struct event_dpc; - /* The bus root device */ - struct hv_device device_ctx; -}; +unsigned int vmbus_loglevel = (ALL_MODULES << 16 | INFO_LVL); +EXPORT_SYMBOL(vmbus_loglevel); + /* (ALL_MODULES << 16 | DEBUG_LVL_ENTEREXIT); */ + /* (((VMBUS | VMBUS_DRV)<<16) | DEBUG_LVL_ENTEREXIT); */ + +static int pci_probe_error; +static struct completion probe_event; +static int irq; + +static void get_channel_info(struct hv_device *device, + struct hv_device_info *info) +{ + struct vmbus_channel_debug_info debug_info; -static int vmbus_match(struct device *device, struct device_driver *driver); -static int vmbus_probe(struct device *device); -static int vmbus_remove(struct device *device); -static void vmbus_shutdown(struct device *device); -static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env); + if (!device->channel) + return; -static irqreturn_t vmbus_isr(int irq, void *dev_id); + vmbus_get_debug_info(device->channel, &debug_info); -static void vmbus_device_release(struct device *device); -static void vmbus_bus_release(struct device *device); + info->chn_id = debug_info.relid; + info->chn_state = debug_info.state; + memcpy(&info->chn_type, &debug_info.interfacetype, + sizeof(struct hv_guid)); + memcpy(&info->chn_instance, &debug_info.interface_instance, + sizeof(struct hv_guid)); + + info->monitor_id = debug_info.monitorid; + + info->server_monitor_pending = debug_info.servermonitor_pending; + info->server_monitor_latency = debug_info.servermonitor_latency; + info->server_monitor_conn_id = debug_info.servermonitor_connectionid; + + info->client_monitor_pending = debug_info.clientmonitor_pending; + info->client_monitor_latency = debug_info.clientmonitor_latency; + info->client_monitor_conn_id = debug_info.clientmonitor_connectionid; + + info->inbound.int_mask = debug_info.inbound.current_interrupt_mask; + info->inbound.read_idx = debug_info.inbound.current_read_index; + info->inbound.write_idx = debug_info.inbound.current_write_index; + info->inbound.bytes_avail_toread = + debug_info.inbound.bytes_avail_toread; + info->inbound.bytes_avail_towrite = + debug_info.inbound.bytes_avail_towrite; + + info->outbound.int_mask = + debug_info.outbound.current_interrupt_mask; + info->outbound.read_idx = debug_info.outbound.current_read_index; + info->outbound.write_idx = debug_info.outbound.current_write_index; + info->outbound.bytes_avail_toread = + debug_info.outbound.bytes_avail_toread; + info->outbound.bytes_avail_towrite = + debug_info.outbound.bytes_avail_towrite; +} +/* + * vmbus_show_device_attr - Show the device attribute in sysfs. + * + * This is invoked when user does a + * "cat /sys/bus/vmbus/devices/<busdevice>/<attr name>" + */ static ssize_t vmbus_show_device_attr(struct device *dev, struct device_attribute *dev_attr, - char *buf); + char *buf) +{ + struct hv_device *device_ctx = device_to_hv_device(dev); + struct hv_device_info device_info; + memset(&device_info, 0, sizeof(struct hv_device_info)); -unsigned int vmbus_loglevel = (ALL_MODULES << 16 | INFO_LVL); -EXPORT_SYMBOL(vmbus_loglevel); - /* (ALL_MODULES << 16 | DEBUG_LVL_ENTEREXIT); */ - /* (((VMBUS | VMBUS_DRV)<<16) | DEBUG_LVL_ENTEREXIT); */ + get_channel_info(device_ctx, &device_info); -static int vmbus_irq = VMBUS_IRQ; + if (!strcmp(dev_attr->attr.name, "class_id")) { + return sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x%02x%02x%02x%02x%02x%02x}\n", + device_info.chn_type.data[3], + device_info.chn_type.data[2], + device_info.chn_type.data[1], + device_info.chn_type.data[0], + device_info.chn_type.data[5], + device_info.chn_type.data[4], + device_info.chn_type.data[7], + device_info.chn_type.data[6], + device_info.chn_type.data[8], + device_info.chn_type.data[9], + device_info.chn_type.data[10], + device_info.chn_type.data[11], + device_info.chn_type.data[12], + device_info.chn_type.data[13], + device_info.chn_type.data[14], + device_info.chn_type.data[15]); + } else if (!strcmp(dev_attr->attr.name, "device_id")) { + return sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x%02x%02x%02x%02x%02x%02x}\n", + device_info.chn_instance.data[3], + device_info.chn_instance.data[2], + device_info.chn_instance.data[1], + device_info.chn_instance.data[0], + device_info.chn_instance.data[5], + device_info.chn_instance.data[4], + device_info.chn_instance.data[7], + device_info.chn_instance.data[6], + device_info.chn_instance.data[8], + device_info.chn_instance.data[9], + device_info.chn_instance.data[10], + device_info.chn_instance.data[11], + device_info.chn_instance.data[12], + device_info.chn_instance.data[13], + device_info.chn_instance.data[14], + device_info.chn_instance.data[15]); + } else if (!strcmp(dev_attr->attr.name, "state")) { + return sprintf(buf, "%d\n", device_info.chn_state); + } else if (!strcmp(dev_attr->attr.name, "id")) { + return sprintf(buf, "%d\n", device_info.chn_id); + } else if (!strcmp(dev_attr->attr.name, "out_intr_mask")) { + return sprintf(buf, "%d\n", device_info.outbound.int_mask); + } else if (!strcmp(dev_attr->attr.name, "out_read_index")) { + return sprintf(buf, "%d\n", device_info.outbound.read_idx); + } else if (!strcmp(dev_attr->attr.name, "out_write_index")) { + return sprintf(buf, "%d\n", device_info.outbound.write_idx); + } else if (!strcmp(dev_attr->attr.name, "out_read_bytes_avail")) { + return sprintf(buf, "%d\n", + device_info.outbound.bytes_avail_toread); + } else if (!strcmp(dev_attr->attr.name, "out_write_bytes_avail")) { + return sprintf(buf, "%d\n", + device_info.outbound.bytes_avail_towrite); + } else if (!strcmp(dev_attr->attr.name, "in_intr_mask")) { + return sprintf(buf, "%d\n", device_info.inbound.int_mask); + } else if (!strcmp(dev_attr->attr.name, "in_read_index")) { + return sprintf(buf, "%d\n", device_info.inbound.read_idx); + } else if (!strcmp(dev_attr->attr.name, "in_write_index")) { + return sprintf(buf, "%d\n", device_info.inbound.write_idx); + } else if (!strcmp(dev_attr->attr.name, "in_read_bytes_avail")) { + return sprintf(buf, "%d\n", + device_info.inbound.bytes_avail_toread); + } else if (!strcmp(dev_attr->attr.name, "in_write_bytes_avail")) { + return sprintf(buf, "%d\n", + device_info.inbound.bytes_avail_towrite); + } else if (!strcmp(dev_attr->attr.name, "monitor_id")) { + return sprintf(buf, "%d\n", device_info.monitor_id); + } else if (!strcmp(dev_attr->attr.name, "server_monitor_pending")) { + return sprintf(buf, "%d\n", device_info.server_monitor_pending); + } else if (!strcmp(dev_attr->attr.name, "server_monitor_latency")) { + return sprintf(buf, "%d\n", device_info.server_monitor_latency); + } else if (!strcmp(dev_attr->attr.name, "server_monitor_conn_id")) { + return sprintf(buf, "%d\n", + device_info.server_monitor_conn_id); + } else if (!strcmp(dev_attr->attr.name, "client_monitor_pending")) { + return sprintf(buf, "%d\n", device_info.client_monitor_pending); + } else if (!strcmp(dev_attr->attr.name, "client_monitor_latency")) { + return sprintf(buf, "%d\n", device_info.client_monitor_latency); + } else if (!strcmp(dev_attr->attr.name, "client_monitor_conn_id")) { + return sprintf(buf, "%d\n", + device_info.client_monitor_conn_id); + } else { + return 0; + } +} /* Set up per device attributes in /sys/bus/vmbus/devices/<bus device> */ static struct device_attribute vmbus_device_attrs[] = { @@ -104,68 +232,182 @@ static struct device_attribute vmbus_device_attrs[] = { __ATTR_NULL }; -/* The one and only one */ -static struct vmbus_driver_context vmbus_drv = { - .bus.name = "vmbus", - .bus.match = vmbus_match, - .bus.shutdown = vmbus_shutdown, - .bus.remove = vmbus_remove, - .bus.probe = vmbus_probe, - .bus.uevent = vmbus_uevent, - .bus.dev_attrs = vmbus_device_attrs, -}; -static const char *driver_name = "hyperv"; +/* + * vmbus_uevent - add uevent for our device + * + * This routine is invoked when a device is added or removed on the vmbus to + * generate a uevent to udev in the userspace. The udev will then look at its + * rule and the uevent generated here to load the appropriate driver + */ +static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env) +{ + struct hv_device *dev = device_to_hv_device(device); + int ret; + + ret = add_uevent_var(env, "VMBUS_DEVICE_CLASS_GUID={" + "%02x%02x%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x%02x%02x%02x%02x%02x%02x}", + dev->dev_type.data[3], + dev->dev_type.data[2], + dev->dev_type.data[1], + dev->dev_type.data[0], + dev->dev_type.data[5], + dev->dev_type.data[4], + dev->dev_type.data[7], + dev->dev_type.data[6], + dev->dev_type.data[8], + dev->dev_type.data[9], + dev->dev_type.data[10], + dev->dev_type.data[11], + dev->dev_type.data[12], + dev->dev_type.data[13], + dev->dev_type.data[14], + dev->dev_type.data[15]); + + if (ret) + return ret; + + ret = add_uevent_var(env, "VMBUS_DEVICE_DEVICE_GUID={" + "%02x%02x%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x%02x%02x%02x%02x%02x%02x}", + dev->dev_instance.data[3], + dev->dev_instance.data[2], + dev->dev_instance.data[1], + dev->dev_instance.data[0], + dev->dev_instance.data[5], + dev->dev_instance.data[4], + dev->dev_instance.data[7], + dev->dev_instance.data[6], + dev->dev_instance.data[8], + dev->dev_instance.data[9], + dev->dev_instance.data[10], + dev->dev_instance.data[11], + dev->dev_instance.data[12], + dev->dev_instance.data[13], + dev->dev_instance.data[14], + dev->dev_instance.data[15]); + if (ret) + return ret; + + return 0; +} + /* - * Windows vmbus does not defined this. - * We defined this to be consistent with other devices + * vmbus_match - Attempt to match the specified device to the specified driver + */ +static int vmbus_match(struct device *device, struct device_driver *driver) +{ + int match = 0; + struct hv_driver *drv = drv_to_hv_drv(driver); + struct hv_device *device_ctx = device_to_hv_device(device); + + /* We found our driver ? */ + if (memcmp(&device_ctx->dev_type, &drv->dev_type, + sizeof(struct hv_guid)) == 0) + match = 1; + + return match; +} + +/* + * vmbus_probe - Add the new vmbus's child device */ -/* {c5295816-f63a-4d5f-8d1a-4daf999ca185} */ -static const struct hv_guid device_type = { - .data = { - 0x16, 0x58, 0x29, 0xc5, 0x3a, 0xf6, 0x5f, 0x4d, - 0x8d, 0x1a, 0x4d, 0xaf, 0x99, 0x9c, 0xa1, 0x85 +static int vmbus_probe(struct device *child_device) +{ + int ret = 0; + struct hv_driver *drv = + drv_to_hv_drv(child_device->driver); + struct hv_device *dev = device_to_hv_device(child_device); + + if (drv->probe) { + ret = drv->probe(dev); + if (ret != 0) + pr_err("probe failed for device %s (%d)\n", + dev_name(child_device), ret); + + } else { + pr_err("probe not set for driver %s\n", + dev_name(child_device)); + ret = -1; } -}; + return ret; +} + +/* + * vmbus_remove - Remove a vmbus device + */ +static int vmbus_remove(struct device *child_device) +{ + int ret; + struct hv_driver *drv; + + struct hv_device *dev = device_to_hv_device(child_device); + + if (child_device->driver) { + drv = drv_to_hv_drv(child_device->driver); -/* {ac3760fc-9adf-40aa-9427-a70ed6de95c5} */ -static const struct hv_guid device_id = { - .data = { - 0xfc, 0x60, 0x37, 0xac, 0xdf, 0x9a, 0xaa, 0x40, - 0x94, 0x27, 0xa7, 0x0e, 0xd6, 0xde, 0x95, 0xc5 + if (drv->remove) { + ret = drv->remove(dev); + } else { + pr_err("remove not set for driver %s\n", + dev_name(child_device)); + ret = -1; + } } -}; -static struct hv_device *vmbus_device; /* vmbus root device */ + return 0; +} /* - * vmbus_dev_add - Callback when the root bus device is added + * vmbus_shutdown - Shutdown a vmbus device */ -static int vmbus_dev_add(struct hv_device *dev, void *info) +static void vmbus_shutdown(struct device *child_device) { - u32 *irqvector = info; - int ret; + struct hv_driver *drv; + struct hv_device *dev = device_to_hv_device(child_device); - vmbus_device = dev; - memcpy(&vmbus_device->dev_type, &device_type, sizeof(struct hv_guid)); - memcpy(&vmbus_device->dev_instance, &device_id, - sizeof(struct hv_guid)); + /* The device may not be attached yet */ + if (!child_device->driver) + return; - /* strcpy(dev->name, "vmbus"); */ - /* SynIC setup... */ - on_each_cpu(hv_synic_init, (void *)irqvector, 1); + drv = drv_to_hv_drv(child_device->driver); - /* Connect to VMBus in the root partition */ - ret = vmbus_connect(); + if (drv->shutdown) + drv->shutdown(dev); - /* VmbusSendEvent(device->localPortId+1); */ - return ret; + return; } +/* + * vmbus_device_release - Final callback release of the vmbus child device + */ +static void vmbus_device_release(struct device *device) +{ + struct hv_device *device_ctx = device_to_hv_device(device); + + kfree(device_ctx); + +} + +/* The one and only one */ +static struct bus_type hv_bus = { + .name = "vmbus", + .match = vmbus_match, + .shutdown = vmbus_shutdown, + .remove = vmbus_remove, + .probe = vmbus_probe, + .uevent = vmbus_uevent, + .dev_attrs = vmbus_device_attrs, +}; + +static const char *driver_name = "hyperv"; + + struct onmessage_work_context { struct work_struct work; struct hv_message msg; @@ -242,172 +484,38 @@ static int vmbus_on_isr(void) msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; /* Check if there are actual msgs to be process */ - if (msg->header.message_type != HVMSG_NONE) { - DPRINT_DBG(VMBUS, "received msg type %d size %d", - msg->header.message_type, - msg->header.payload_size); + if (msg->header.message_type != HVMSG_NONE) ret |= 0x1; - } /* TODO: Check if there are events to be process */ page_addr = hv_context.synic_event_page[cpu]; event = (union hv_synic_event_flags *)page_addr + VMBUS_MESSAGE_SINT; /* Since we are a child, we only need to check bit 0 */ - if (sync_test_and_clear_bit(0, (unsigned long *) &event->flags32[0])) { - DPRINT_DBG(VMBUS, "received event %d", event->flags32[0]); + if (sync_test_and_clear_bit(0, (unsigned long *) &event->flags32[0])) ret |= 0x2; - } return ret; } -static void get_channel_info(struct hv_device *device, - struct hv_device_info *info) -{ - struct vmbus_channel_debug_info debug_info; - - if (!device->channel) - return; - - vmbus_get_debug_info(device->channel, &debug_info); - - info->chn_id = debug_info.relid; - info->chn_state = debug_info.state; - memcpy(&info->chn_type, &debug_info.interfacetype, - sizeof(struct hv_guid)); - memcpy(&info->chn_instance, &debug_info.interface_instance, - sizeof(struct hv_guid)); - info->monitor_id = debug_info.monitorid; - - info->server_monitor_pending = debug_info.servermonitor_pending; - info->server_monitor_latency = debug_info.servermonitor_latency; - info->server_monitor_conn_id = debug_info.servermonitor_connectionid; - - info->client_monitor_pending = debug_info.clientmonitor_pending; - info->client_monitor_latency = debug_info.clientmonitor_latency; - info->client_monitor_conn_id = debug_info.clientmonitor_connectionid; - - info->inbound.int_mask = debug_info.inbound.current_interrupt_mask; - info->inbound.read_idx = debug_info.inbound.current_read_index; - info->inbound.write_idx = debug_info.inbound.current_write_index; - info->inbound.bytes_avail_toread = - debug_info.inbound.bytes_avail_toread; - info->inbound.bytes_avail_towrite = - debug_info.inbound.bytes_avail_towrite; - - info->outbound.int_mask = - debug_info.outbound.current_interrupt_mask; - info->outbound.read_idx = debug_info.outbound.current_read_index; - info->outbound.write_idx = debug_info.outbound.current_write_index; - info->outbound.bytes_avail_toread = - debug_info.outbound.bytes_avail_toread; - info->outbound.bytes_avail_towrite = - debug_info.outbound.bytes_avail_towrite; -} - -/* - * vmbus_show_device_attr - Show the device attribute in sysfs. - * - * This is invoked when user does a - * "cat /sys/bus/vmbus/devices/<busdevice>/<attr name>" - */ -static ssize_t vmbus_show_device_attr(struct device *dev, - struct device_attribute *dev_attr, - char *buf) +static irqreturn_t vmbus_isr(int irq, void *dev_id) { - struct hv_device *device_ctx = device_to_hv_device(dev); - struct hv_device_info device_info; + int ret; - memset(&device_info, 0, sizeof(struct hv_device_info)); + ret = vmbus_on_isr(); - get_channel_info(device_ctx, &device_info); + /* Schedules a dpc if necessary */ + if (ret > 0) { + if (test_bit(0, (unsigned long *)&ret)) + tasklet_schedule(&msg_dpc); - if (!strcmp(dev_attr->attr.name, "class_id")) { - return sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-" - "%02x%02x%02x%02x%02x%02x%02x%02x}\n", - device_info.chn_type.data[3], - device_info.chn_type.data[2], - device_info.chn_type.data[1], - device_info.chn_type.data[0], - device_info.chn_type.data[5], - device_info.chn_type.data[4], - device_info.chn_type.data[7], - device_info.chn_type.data[6], - device_info.chn_type.data[8], - device_info.chn_type.data[9], - device_info.chn_type.data[10], - device_info.chn_type.data[11], - device_info.chn_type.data[12], - device_info.chn_type.data[13], - device_info.chn_type.data[14], - device_info.chn_type.data[15]); - } else if (!strcmp(dev_attr->attr.name, "device_id")) { - return sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-" - "%02x%02x%02x%02x%02x%02x%02x%02x}\n", - device_info.chn_instance.data[3], - device_info.chn_instance.data[2], - device_info.chn_instance.data[1], - device_info.chn_instance.data[0], - device_info.chn_instance.data[5], - device_info.chn_instance.data[4], - device_info.chn_instance.data[7], - device_info.chn_instance.data[6], - device_info.chn_instance.data[8], - device_info.chn_instance.data[9], - device_info.chn_instance.data[10], - device_info.chn_instance.data[11], - device_info.chn_instance.data[12], - device_info.chn_instance.data[13], - device_info.chn_instance.data[14], - device_info.chn_instance.data[15]); - } else if (!strcmp(dev_attr->attr.name, "state")) { - return sprintf(buf, "%d\n", device_info.chn_state); - } else if (!strcmp(dev_attr->attr.name, "id")) { - return sprintf(buf, "%d\n", device_info.chn_id); - } else if (!strcmp(dev_attr->attr.name, "out_intr_mask")) { - return sprintf(buf, "%d\n", device_info.outbound.int_mask); - } else if (!strcmp(dev_attr->attr.name, "out_read_index")) { - return sprintf(buf, "%d\n", device_info.outbound.read_idx); - } else if (!strcmp(dev_attr->attr.name, "out_write_index")) { - return sprintf(buf, "%d\n", device_info.outbound.write_idx); - } else if (!strcmp(dev_attr->attr.name, "out_read_bytes_avail")) { - return sprintf(buf, "%d\n", - device_info.outbound.bytes_avail_toread); - } else if (!strcmp(dev_attr->attr.name, "out_write_bytes_avail")) { - return sprintf(buf, "%d\n", - device_info.outbound.bytes_avail_towrite); - } else if (!strcmp(dev_attr->attr.name, "in_intr_mask")) { - return sprintf(buf, "%d\n", device_info.inbound.int_mask); - } else if (!strcmp(dev_attr->attr.name, "in_read_index")) { - return sprintf(buf, "%d\n", device_info.inbound.read_idx); - } else if (!strcmp(dev_attr->attr.name, "in_write_index")) { - return sprintf(buf, "%d\n", device_info.inbound.write_idx); - } else if (!strcmp(dev_attr->attr.name, "in_read_bytes_avail")) { - return sprintf(buf, "%d\n", - device_info.inbound.bytes_avail_toread); - } else if (!strcmp(dev_attr->attr.name, "in_write_bytes_avail")) { - return sprintf(buf, "%d\n", - device_info.inbound.bytes_avail_towrite); - } else if (!strcmp(dev_attr->attr.name, "monitor_id")) { - return sprintf(buf, "%d\n", device_info.monitor_id); - } else if (!strcmp(dev_attr->attr.name, "server_monitor_pending")) { - return sprintf(buf, "%d\n", device_info.server_monitor_pending); - } else if (!strcmp(dev_attr->attr.name, "server_monitor_latency")) { - return sprintf(buf, "%d\n", device_info.server_monitor_latency); - } else if (!strcmp(dev_attr->attr.name, "server_monitor_conn_id")) { - return sprintf(buf, "%d\n", - device_info.server_monitor_conn_id); - } else if (!strcmp(dev_attr->attr.name, "client_monitor_pending")) { - return sprintf(buf, "%d\n", device_info.client_monitor_pending); - } else if (!strcmp(dev_attr->attr.name, "client_monitor_latency")) { - return sprintf(buf, "%d\n", device_info.client_monitor_latency); - } else if (!strcmp(dev_attr->attr.name, "client_monitor_conn_id")) { - return sprintf(buf, "%d\n", - device_info.client_monitor_conn_id); + if (test_bit(1, (unsigned long *)&ret)) + tasklet_schedule(&event_dpc); + + return IRQ_HANDLED; } else { - return 0; + return IRQ_NONE; } } @@ -416,148 +524,69 @@ static ssize_t vmbus_show_device_attr(struct device *dev, * * Here, we * - initialize the vmbus driver context - * - setup various driver entry points * - invoke the vmbus hv main init routine * - get the irq resource - * - invoke the vmbus to add the vmbus root device - * - setup the vmbus root device * - retrieve the channel offers */ -static int vmbus_bus_init(void) +static int vmbus_bus_init(struct pci_dev *pdev) { - struct vmbus_driver_context *vmbus_drv_ctx = &vmbus_drv; - struct hv_device *dev_ctx = &vmbus_drv.device_ctx; int ret; unsigned int vector; - DPRINT_INFO(VMBUS, "+++++++ HV Driver version = %s +++++++", - HV_DRV_VERSION); - DPRINT_INFO(VMBUS, "+++++++ Vmbus supported version = %d +++++++", - VMBUS_REVISION_NUMBER); - DPRINT_INFO(VMBUS, "+++++++ Vmbus using SINT %d +++++++", - VMBUS_MESSAGE_SINT); - DPRINT_DBG(VMBUS, "sizeof(vmbus_channel_packet_page_buffer)=%zd, " - "sizeof(VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER)=%zd", - sizeof(struct vmbus_channel_packet_page_buffer), - sizeof(struct vmbus_channel_packet_multipage_buffer)); - - /* Hypervisor initialization...setup hypercall page..etc */ ret = hv_init(); if (ret != 0) { - DPRINT_ERR(VMBUS, "Unable to initialize the hypervisor - 0x%x", - ret); + pr_err("Unable to initialize the hypervisor - 0x%x\n", ret); goto cleanup; } - - vmbus_drv_ctx->bus.name = driver_name; - /* Initialize the bus context */ - tasklet_init(&vmbus_drv_ctx->msg_dpc, vmbus_on_msg_dpc, - (unsigned long)NULL); - tasklet_init(&vmbus_drv_ctx->event_dpc, vmbus_on_event, - (unsigned long)NULL); + tasklet_init(&msg_dpc, vmbus_on_msg_dpc, 0); + tasklet_init(&event_dpc, vmbus_on_event, 0); /* Now, register the bus with LDM */ - ret = bus_register(&vmbus_drv_ctx->bus); + ret = bus_register(&hv_bus); if (ret) { ret = -1; goto cleanup; } /* Get the interrupt resource */ - ret = request_irq(vmbus_irq, vmbus_isr, IRQF_SAMPLE_RANDOM, - driver_name, NULL); + ret = request_irq(pdev->irq, vmbus_isr, + IRQF_SHARED | IRQF_SAMPLE_RANDOM, + driver_name, pdev); if (ret != 0) { - DPRINT_ERR(VMBUS_DRV, "ERROR - Unable to request IRQ %d", - vmbus_irq); + pr_err("Unable to request IRQ %d\n", + pdev->irq); - bus_unregister(&vmbus_drv_ctx->bus); + bus_unregister(&hv_bus); ret = -1; goto cleanup; } - vector = VMBUS_IRQ_VECTOR; - DPRINT_INFO(VMBUS_DRV, "irq 0x%x vector 0x%x", vmbus_irq, vector); + vector = IRQ0_VECTOR + pdev->irq; - /* Add the root device */ - memset(dev_ctx, 0, sizeof(struct hv_device)); - - ret = vmbus_dev_add(dev_ctx, &vector); - if (ret != 0) { - DPRINT_ERR(VMBUS_DRV, - "ERROR - Unable to add vmbus root device"); - - free_irq(vmbus_irq, NULL); - - bus_unregister(&vmbus_drv_ctx->bus); - - ret = -1; - goto cleanup; - } - /* strcpy(dev_ctx->device.bus_id, dev_ctx->device_obj.name); */ - dev_set_name(&dev_ctx->device, "vmbus_0_0"); - - /* No need to bind a driver to the root device. */ - dev_ctx->device.parent = NULL; - /* NULL; vmbus_remove() does not get invoked */ - dev_ctx->device.bus = &vmbus_drv_ctx->bus; - - /* Setup the device dispatch table */ - dev_ctx->device.release = vmbus_bus_release; - - /* register the root device */ - ret = device_register(&dev_ctx->device); + /* + * Notify the hypervisor of our irq and + * connect to the host. + */ + on_each_cpu(hv_synic_init, (void *)&vector, 1); + ret = vmbus_connect(); if (ret) { - DPRINT_ERR(VMBUS_DRV, - "ERROR - Unable to register vmbus root device"); - - free_irq(vmbus_irq, NULL); - bus_unregister(&vmbus_drv_ctx->bus); - - ret = -1; + free_irq(pdev->irq, pdev); + bus_unregister(&hv_bus); goto cleanup; } + vmbus_request_offers(); - wait_for_completion(&hv_channel_ready); cleanup: return ret; } -/* - * vmbus_bus_exit - Terminate the vmbus driver. - * - * This routine is opposite of vmbus_bus_init() - */ -static void vmbus_bus_exit(void) -{ - struct vmbus_driver_context *vmbus_drv_ctx = &vmbus_drv; - - struct hv_device *dev_ctx = &vmbus_drv.device_ctx; - - vmbus_release_unattached_channels(); - vmbus_disconnect(); - on_each_cpu(hv_synic_cleanup, NULL, 1); - - hv_cleanup(); - - /* Unregister the root bus device */ - device_unregister(&dev_ctx->device); - - bus_unregister(&vmbus_drv_ctx->bus); - - free_irq(vmbus_irq, NULL); - - tasklet_kill(&vmbus_drv_ctx->msg_dpc); - tasklet_kill(&vmbus_drv_ctx->event_dpc); -} - - /** * vmbus_child_driver_register() - Register a vmbus's child driver * @drv: Pointer to driver structure you want to register @@ -573,11 +602,10 @@ int vmbus_child_driver_register(struct device_driver *drv) { int ret; - DPRINT_INFO(VMBUS_DRV, "child driver (%p) registering - name %s", - drv, drv->name); + pr_info("child driver registering - name %s\n", drv->name); /* The child driver on this vmbus */ - drv->bus = &vmbus_drv.bus; + drv->bus = &hv_bus; ret = driver_register(drv); @@ -599,8 +627,7 @@ EXPORT_SYMBOL(vmbus_child_driver_register); */ void vmbus_child_driver_unregister(struct device_driver *drv) { - DPRINT_INFO(VMBUS_DRV, "child driver (%p) unregistering - name %s", - drv, drv->name); + pr_info("child driver unregistering - name %s\n", drv->name); driver_unregister(drv); @@ -621,30 +648,10 @@ struct hv_device *vmbus_child_device_create(struct hv_guid *type, /* Allocate the new child device */ child_device_obj = kzalloc(sizeof(struct hv_device), GFP_KERNEL); if (!child_device_obj) { - DPRINT_ERR(VMBUS_DRV, - "unable to allocate device_context for child device"); + pr_err("Unable to allocate device object for child device\n"); return NULL; } - DPRINT_DBG(VMBUS_DRV, "child device (%p) allocated - " - "type {%02x%02x%02x%02x-%02x%02x-%02x%02x-" - "%02x%02x%02x%02x%02x%02x%02x%02x}," - "id {%02x%02x%02x%02x-%02x%02x-%02x%02x-" - "%02x%02x%02x%02x%02x%02x%02x%02x}", - &child_device_obj->device, - type->data[3], type->data[2], type->data[1], type->data[0], - type->data[5], type->data[4], type->data[7], type->data[6], - type->data[8], type->data[9], type->data[10], type->data[11], - type->data[12], type->data[13], type->data[14], type->data[15], - instance->data[3], instance->data[2], - instance->data[1], instance->data[0], - instance->data[5], instance->data[4], - instance->data[7], instance->data[6], - instance->data[8], instance->data[9], - instance->data[10], instance->data[11], - instance->data[12], instance->data[13], - instance->data[14], instance->data[15]); - child_device_obj->channel = channel; memcpy(&child_device_obj->dev_type, type, sizeof(struct hv_guid)); memcpy(&child_device_obj->dev_instance, instance, @@ -663,16 +670,13 @@ int vmbus_child_device_register(struct hv_device *child_device_obj) static atomic_t device_num = ATOMIC_INIT(0); - DPRINT_DBG(VMBUS_DRV, "child device (%p) registering", - child_device_obj); - /* Set the device name. Otherwise, device_register() will fail. */ dev_set_name(&child_device_obj->device, "vmbus_0_%d", atomic_inc_return(&device_num)); /* The new device belongs to this bus */ - child_device_obj->device.bus = &vmbus_drv.bus; /* device->dev.bus; */ - child_device_obj->device.parent = &vmbus_device->device; + child_device_obj->device.bus = &hv_bus; /* device->dev.bus; */ + child_device_obj->device.parent = &hv_pci_dev->dev; child_device_obj->device.release = vmbus_device_release; /* @@ -681,15 +685,11 @@ int vmbus_child_device_register(struct hv_device *child_device_obj) */ ret = device_register(&child_device_obj->device); - /* vmbus_probe() error does not get propergate to device_register(). */ - ret = child_device_obj->probe_error; - if (ret) - DPRINT_ERR(VMBUS_DRV, "unable to register child device (%p)", - &child_device_obj->device); + pr_err("Unable to register child device\n"); else - DPRINT_INFO(VMBUS_DRV, "child device (%p) registered", - &child_device_obj->device); + pr_info("child device %s registered\n", + dev_name(&child_device_obj->device)); return ret; } @@ -700,313 +700,110 @@ int vmbus_child_device_register(struct hv_device *child_device_obj) */ void vmbus_child_device_unregister(struct hv_device *device_obj) { - - DPRINT_INFO(VMBUS_DRV, "unregistering child device (%p)", - &device_obj->device); - /* * Kick off the process of unregistering the device. * This will call vmbus_remove() and eventually vmbus_device_release() */ device_unregister(&device_obj->device); - DPRINT_INFO(VMBUS_DRV, "child device (%p) unregistered", - &device_obj->device); + pr_info("child device %s unregistered\n", + dev_name(&device_obj->device)); } -/* - * vmbus_uevent - add uevent for our device - * - * This routine is invoked when a device is added or removed on the vmbus to - * generate a uevent to udev in the userspace. The udev will then look at its - * rule and the uevent generated here to load the appropriate driver - */ -static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env) -{ - struct hv_device *dev = device_to_hv_device(device); - int ret; - - DPRINT_INFO(VMBUS_DRV, "generating uevent - VMBUS_DEVICE_CLASS_GUID={" - "%02x%02x%02x%02x-%02x%02x-%02x%02x-" - "%02x%02x%02x%02x%02x%02x%02x%02x}", - dev->dev_type.data[3], dev->dev_type.data[2], - dev->dev_type.data[1], dev->dev_type.data[0], - dev->dev_type.data[5], dev->dev_type.data[4], - dev->dev_type.data[7], dev->dev_type.data[6], - dev->dev_type.data[8], dev->dev_type.data[9], - dev->dev_type.data[10], - dev->dev_type.data[11], - dev->dev_type.data[12], - dev->dev_type.data[13], - dev->dev_type.data[14], - dev->dev_type.data[15]); - - ret = add_uevent_var(env, "VMBUS_DEVICE_CLASS_GUID={" - "%02x%02x%02x%02x-%02x%02x-%02x%02x-" - "%02x%02x%02x%02x%02x%02x%02x%02x}", - dev->dev_type.data[3], - dev->dev_type.data[2], - dev->dev_type.data[1], - dev->dev_type.data[0], - dev->dev_type.data[5], - dev->dev_type.data[4], - dev->dev_type.data[7], - dev->dev_type.data[6], - dev->dev_type.data[8], - dev->dev_type.data[9], - dev->dev_type.data[10], - dev->dev_type.data[11], - dev->dev_type.data[12], - dev->dev_type.data[13], - dev->dev_type.data[14], - dev->dev_type.data[15]); - - if (ret) - return ret; - - ret = add_uevent_var(env, "VMBUS_DEVICE_DEVICE_GUID={" - "%02x%02x%02x%02x-%02x%02x-%02x%02x-" - "%02x%02x%02x%02x%02x%02x%02x%02x}", - dev->dev_instance.data[3], - dev->dev_instance.data[2], - dev->dev_instance.data[1], - dev->dev_instance.data[0], - dev->dev_instance.data[5], - dev->dev_instance.data[4], - dev->dev_instance.data[7], - dev->dev_instance.data[6], - dev->dev_instance.data[8], - dev->dev_instance.data[9], - dev->dev_instance.data[10], - dev->dev_instance.data[11], - dev->dev_instance.data[12], - dev->dev_instance.data[13], - dev->dev_instance.data[14], - dev->dev_instance.data[15]); - if (ret) - return ret; - - return 0; -} /* - * vmbus_match - Attempt to match the specified device to the specified driver + * VMBUS is an acpi enumerated device. Get the the IRQ information + * from DSDT. */ -static int vmbus_match(struct device *device, struct device_driver *driver) -{ - int match = 0; - struct hv_driver *drv = drv_to_hv_drv(driver); - struct hv_device *device_ctx = device_to_hv_device(device); - - /* We found our driver ? */ - if (memcmp(&device_ctx->dev_type, &drv->dev_type, - sizeof(struct hv_guid)) == 0) { - - device_ctx->drv = drv->priv; - DPRINT_INFO(VMBUS_DRV, - "device object (%p) set to driver object (%p)", - &device_ctx, - device_ctx->drv); - - match = 1; - } - return match; -} -/* - * vmbus_probe_failed_cb - Callback when a driver probe failed in vmbus_probe() - * - * We need a callback because we cannot invoked device_unregister() inside - * vmbus_probe() since vmbus_probe() may be invoked inside device_register() - * i.e. we cannot call device_unregister() inside device_register() - */ -static void vmbus_probe_failed_cb(struct work_struct *context) +static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *irq) { - struct hv_device *device_ctx = (struct hv_device *)context; - - /* - * Kick off the process of unregistering the device. - * This will call vmbus_remove() and eventually vmbus_device_release() - */ - device_unregister(&device_ctx->device); - /* put_device(&device_ctx->device); */ -} - -/* - * vmbus_probe - Add the new vmbus's child device - */ -static int vmbus_probe(struct device *child_device) -{ - int ret = 0; - struct hv_driver *drv = - drv_to_hv_drv(child_device->driver); - struct hv_device *dev = device_to_hv_device(child_device); + if (res->type == ACPI_RESOURCE_TYPE_IRQ) { + struct acpi_resource_irq *irqp; + irqp = &res->data.irq; - /* Let the specific open-source driver handles the probe if it can */ - if (drv->driver.probe) { - ret = dev->probe_error = - drv->driver.probe(child_device); - if (ret != 0) { - DPRINT_ERR(VMBUS_DRV, "probe() failed for device %s " - "(%p) on driver %s (%d)...", - dev_name(child_device), child_device, - child_device->driver->name, ret); - - INIT_WORK(&dev->probe_failed_work_item, - vmbus_probe_failed_cb); - schedule_work(&dev->probe_failed_work_item); - } - } else { - DPRINT_ERR(VMBUS_DRV, "probe() method not set for driver - %s", - child_device->driver->name); - ret = -1; + *((unsigned int *)irq) = irqp->interrupts[0]; } - return ret; + + return AE_OK; } -/* - * vmbus_remove - Remove a vmbus device - */ -static int vmbus_remove(struct device *child_device) +static int vmbus_acpi_add(struct acpi_device *device) { - int ret; - struct hv_driver *drv; + acpi_status result; - /* Special case root bus device */ - if (child_device->parent == NULL) { - /* - * No-op since it is statically defined and handle in - * vmbus_bus_exit() - */ - return 0; - } - - if (child_device->driver) { - drv = drv_to_hv_drv(child_device->driver); + result = + acpi_walk_resources(device->handle, METHOD_NAME__CRS, + vmbus_walk_resources, &irq); - /* - * Let the specific open-source driver handles the removal if - * it can - */ - if (drv->driver.remove) { - ret = drv->driver.remove(child_device); - } else { - DPRINT_ERR(VMBUS_DRV, - "remove() method not set for driver - %s", - child_device->driver->name); - ret = -1; - } + if (ACPI_FAILURE(result)) { + complete(&probe_event); + return -ENODEV; } - + complete(&probe_event); return 0; } -/* - * vmbus_shutdown - Shutdown a vmbus device - */ -static void vmbus_shutdown(struct device *child_device) -{ - struct hv_driver *drv; - - /* Special case root bus device */ - if (child_device->parent == NULL) { - /* - * No-op since it is statically defined and handle in - * vmbus_bus_exit() - */ - return; - } +static const struct acpi_device_id vmbus_acpi_device_ids[] = { + {"VMBUS", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, vmbus_acpi_device_ids); - /* The device may not be attached yet */ - if (!child_device->driver) - return; +static struct acpi_driver vmbus_acpi_driver = { + .name = "vmbus", + .ids = vmbus_acpi_device_ids, + .ops = { + .add = vmbus_acpi_add, + }, +}; - drv = drv_to_hv_drv(child_device->driver); +static int vmbus_acpi_init(void) +{ + int result; - /* Let the specific open-source driver handles the removal if it can */ - if (drv->driver.shutdown) - drv->driver.shutdown(child_device); - return; -} + result = acpi_bus_register_driver(&vmbus_acpi_driver); + if (result < 0) + return result; -/* - * vmbus_bus_release - Final callback release of the vmbus root device - */ -static void vmbus_bus_release(struct device *device) -{ - /* FIXME */ - /* Empty release functions are a bug, or a major sign - * of a problem design, this MUST BE FIXED! */ - dev_err(device, "%s needs to be fixed!\n", __func__); - WARN_ON(1); + return 0; } -/* - * vmbus_device_release - Final callback release of the vmbus child device - */ -static void vmbus_device_release(struct device *device) +static void vmbus_acpi_exit(void) { - struct hv_device *device_ctx = device_to_hv_device(device); - - kfree(device_ctx); + acpi_bus_unregister_driver(&vmbus_acpi_driver); - /* !!DO NOT REFERENCE device_ctx anymore at this point!! */ + return; } - -static irqreturn_t vmbus_isr(int irq, void *dev_id) +static int __devinit hv_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) { - int ret; + hv_pci_dev = pdev; - ret = vmbus_on_isr(); + pci_probe_error = pci_enable_device(pdev); + if (pci_probe_error) + goto probe_cleanup; - /* Schedules a dpc if necessary */ - if (ret > 0) { - if (test_bit(0, (unsigned long *)&ret)) - tasklet_schedule(&vmbus_drv.msg_dpc); - - if (test_bit(1, (unsigned long *)&ret)) - tasklet_schedule(&vmbus_drv.event_dpc); - - return IRQ_HANDLED; - } else { - return IRQ_NONE; - } -} - -static struct dmi_system_id __initdata microsoft_hv_dmi_table[] = { - { - .ident = "Hyper-V", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"), - DMI_MATCH(DMI_BOARD_NAME, "Virtual Machine"), - }, - }, - { }, -}; -MODULE_DEVICE_TABLE(dmi, microsoft_hv_dmi_table); + /* + * If the PCI sub-sytem did not assign us an + * irq, use the bios provided one. + */ -static int __init vmbus_init(void) -{ - DPRINT_INFO(VMBUS_DRV, - "Vmbus initializing.... current log level 0x%x (%x,%x)", - vmbus_loglevel, HIWORD(vmbus_loglevel), LOWORD(vmbus_loglevel)); - /* Todo: it is used for loglevel, to be ported to new kernel. */ + if (pdev->irq == 0) + pdev->irq = irq; - if (!dmi_check_system(microsoft_hv_dmi_table)) - return -ENODEV; + pci_probe_error = vmbus_bus_init(pdev); - return vmbus_bus_init(); -} + if (pci_probe_error) + pci_disable_device(pdev); -static void __exit vmbus_exit(void) -{ - vmbus_bus_exit(); - /* Todo: it is used for loglevel, to be ported to new kernel. */ +probe_cleanup: + complete(&probe_event); + return pci_probe_error; } /* @@ -1021,10 +818,53 @@ static const struct pci_device_id microsoft_hv_pci_table[] = { }; MODULE_DEVICE_TABLE(pci, microsoft_hv_pci_table); +static struct pci_driver hv_bus_driver = { + .name = "hv_bus", + .probe = hv_pci_probe, + .id_table = microsoft_hv_pci_table, +}; + +static int __init hv_pci_init(void) +{ + int ret; + + init_completion(&probe_event); + + /* + * Get irq resources first. + */ + + ret = vmbus_acpi_init(); + if (ret) + return ret; + + wait_for_completion(&probe_event); + + if (irq <= 0) { + vmbus_acpi_exit(); + return -ENODEV; + } + + vmbus_acpi_exit(); + init_completion(&probe_event); + ret = pci_register_driver(&hv_bus_driver); + if (ret) + return ret; + /* + * All the vmbus initialization occurs within the + * hv_pci_probe() function. Wait for hv_pci_probe() + * to complete. + */ + wait_for_completion(&probe_event); + + if (pci_probe_error) + pci_unregister_driver(&hv_bus_driver); + return pci_probe_error; +} + + MODULE_LICENSE("GPL"); MODULE_VERSION(HV_DRV_VERSION); -module_param(vmbus_irq, int, S_IRUGO); -module_param(vmbus_loglevel, int, S_IRUGO); +module_param(vmbus_loglevel, int, S_IRUGO|S_IWUSR); -module_init(vmbus_init); -module_exit(vmbus_exit); +module_init(hv_pci_init); diff --git a/drivers/staging/hv/vmbus_packet_format.h b/drivers/staging/hv/vmbus_packet_format.h deleted file mode 100644 index c0b2c2b11646..000000000000 --- a/drivers/staging/hv/vmbus_packet_format.h +++ /dev/null @@ -1,161 +0,0 @@ -/* - * - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang <haiyangz@microsoft.com> - * Hank Janssen <hjanssen@microsoft.com> - * - */ - -#ifndef _VMBUSPACKETFORMAT_H_ -#define _VMBUSPACKETFORMAT_H_ - -struct vmpacket_descriptor { - u16 type; - u16 offset8; - u16 len8; - u16 flags; - u64 trans_id; -} __packed; - -struct vmpacket_header { - u32 prev_pkt_start_offset; - struct vmpacket_descriptor descriptor; -} __packed; - -struct vmtransfer_page_range { - u32 byte_count; - u32 byte_offset; -} __packed; - -struct vmtransfer_page_packet_header { - struct vmpacket_descriptor d; - u16 xfer_pageset_id; - bool sender_owns_set; - u8 reserved; - u32 range_cnt; - struct vmtransfer_page_range ranges[1]; -} __packed; - -struct vmgpadl_packet_header { - struct vmpacket_descriptor d; - u32 gpadl; - u32 reserved; -} __packed; - -struct vmadd_remove_transfer_page_set { - struct vmpacket_descriptor d; - u32 gpadl; - u16 xfer_pageset_id; - u16 reserved; -} __packed; - -/* - * This structure defines a range in guest physical space that can be made to - * look virtually contiguous. - */ -struct gpa_range { - u32 byte_count; - u32 byte_offset; - u64 pfn_array[0]; -}; - -/* - * This is the format for an Establish Gpadl packet, which contains a handle by - * which this GPADL will be known and a set of GPA ranges associated with it. - * This can be converted to a MDL by the guest OS. If there are multiple GPA - * ranges, then the resulting MDL will be "chained," representing multiple VA - * ranges. - */ -struct vmestablish_gpadl { - struct vmpacket_descriptor d; - u32 gpadl; - u32 range_cnt; - struct gpa_range range[1]; -} __packed; - -/* - * This is the format for a Teardown Gpadl packet, which indicates that the - * GPADL handle in the Establish Gpadl packet will never be referenced again. - */ -struct vmteardown_gpadl { - struct vmpacket_descriptor d; - u32 gpadl; - u32 reserved; /* for alignment to a 8-byte boundary */ -} __packed; - -/* - * This is the format for a GPA-Direct packet, which contains a set of GPA - * ranges, in addition to commands and/or data. - */ -struct vmdata_gpa_direct { - struct vmpacket_descriptor d; - u32 reserved; - u32 range_cnt; - struct gpa_range range[1]; -} __packed; - -/* This is the format for a Additional Data Packet. */ -struct vmadditional_data { - struct vmpacket_descriptor d; - u64 total_bytes; - u32 offset; - u32 byte_cnt; - unsigned char data[1]; -} __packed; - -union vmpacket_largest_possible_header { - struct vmpacket_descriptor simple_hdr; - struct vmtransfer_page_packet_header xfer_page_hdr; - struct vmgpadl_packet_header gpadl_hdr; - struct vmadd_remove_transfer_page_set add_rm_xfer_page_hdr; - struct vmestablish_gpadl establish_gpadl_hdr; - struct vmteardown_gpadl teardown_gpadl_hdr; - struct vmdata_gpa_direct data_gpa_direct_hdr; -}; - -#define VMPACKET_DATA_START_ADDRESS(__packet) \ - (void *)(((unsigned char *)__packet) + \ - ((struct vmpacket_descriptor)__packet)->offset8 * 8) - -#define VMPACKET_DATA_LENGTH(__packet) \ - ((((struct vmpacket_descriptor)__packet)->len8 - \ - ((struct vmpacket_descriptor)__packet)->offset8) * 8) - -#define VMPACKET_TRANSFER_MODE(__packet) \ - (((struct IMPACT)__packet)->type) - -enum vmbus_packet_type { - VM_PKT_INVALID = 0x0, - VM_PKT_SYNCH = 0x1, - VM_PKT_ADD_XFER_PAGESET = 0x2, - VM_PKT_RM_XFER_PAGESET = 0x3, - VM_PKT_ESTABLISH_GPADL = 0x4, - VM_PKT_TEARDOWN_GPADL = 0x5, - VM_PKT_DATA_INBAND = 0x6, - VM_PKT_DATA_USING_XFER_PAGES = 0x7, - VM_PKT_DATA_USING_GPADL = 0x8, - VM_PKT_DATA_USING_GPA_DIRECT = 0x9, - VM_PKT_CANCEL_REQUEST = 0xa, - VM_PKT_COMP = 0xb, - VM_PKT_DATA_USING_ADDITIONAL_PKT = 0xc, - VM_PKT_ADDITIONAL_DATA = 0xd -}; - -#define VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED 1 - -#endif diff --git a/drivers/staging/hv/vmbus_private.h b/drivers/staging/hv/vmbus_private.h deleted file mode 100644 index 6f0d8df5e178..000000000000 --- a/drivers/staging/hv/vmbus_private.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang <haiyangz@microsoft.com> - * Hank Janssen <hjanssen@microsoft.com> - * - */ - - -#ifndef _VMBUS_PRIVATE_H_ -#define _VMBUS_PRIVATE_H_ - -#include "hv.h" -#include "vmbus_api.h" -#include "channel.h" -#include "channel_mgmt.h" -#include "ring_buffer.h" -#include <linux/list.h> -#include <asm/sync_bitops.h> - - -/* - * Maximum channels is determined by the size of the interrupt page - * which is PAGE_SIZE. 1/2 of PAGE_SIZE is for send endpoint interrupt - * and the other is receive endpoint interrupt - */ -#define MAX_NUM_CHANNELS ((PAGE_SIZE >> 1) << 3) /* 16348 channels */ - -/* The value here must be in multiple of 32 */ -/* TODO: Need to make this configurable */ -#define MAX_NUM_CHANNELS_SUPPORTED 256 - - -enum vmbus_connect_state { - DISCONNECTED, - CONNECTING, - CONNECTED, - DISCONNECTING -}; - -#define MAX_SIZE_CHANNEL_MESSAGE HV_MESSAGE_PAYLOAD_BYTE_COUNT - -struct vmbus_connection { - enum vmbus_connect_state conn_state; - - atomic_t next_gpadl_handle; - - /* - * Represents channel interrupts. Each bit position represents a - * channel. When a channel sends an interrupt via VMBUS, it finds its - * bit in the sendInterruptPage, set it and calls Hv to generate a port - * event. The other end receives the port event and parse the - * recvInterruptPage to see which bit is set - */ - void *int_page; - void *send_int_page; - void *recv_int_page; - - /* - * 2 pages - 1st page for parent->child notification and 2nd - * is child->parent notification - */ - void *monitor_pages; - struct list_head chn_msg_list; - spinlock_t channelmsg_lock; - - /* List of channels */ - struct list_head chn_list; - spinlock_t channel_lock; - - struct workqueue_struct *work_queue; -}; - - -struct vmbus_msginfo { - /* Bookkeeping stuff */ - struct list_head msglist_entry; - - /* Synchronize the request/response if needed */ - int wait_condition; - wait_queue_head_t wait_event; - - /* The message itself */ - unsigned char msg[0]; -}; - - -extern struct vmbus_connection vmbus_connection; - -/* General vmbus interface */ - -struct hv_device *vmbus_child_device_create(struct hv_guid *type, - struct hv_guid *instance, - struct vmbus_channel *channel); - -int vmbus_child_device_register(struct hv_device *child_device_obj); -void vmbus_child_device_unregister(struct hv_device *device_obj); - -/* static void */ -/* VmbusChildDeviceDestroy( */ -/* struct hv_device *); */ - -struct vmbus_channel *relid2channel(u32 relid); - - -/* Connection interface */ - -int vmbus_connect(void); - -int vmbus_disconnect(void); - -int vmbus_post_msg(void *buffer, size_t buflen); - -int vmbus_set_event(u32 child_relid); - -void vmbus_on_event(unsigned long data); - - -#endif /* _VMBUS_PRIVATE_H_ */ |