diff options
Diffstat (limited to 'drivers/staging/hv')
24 files changed, 1249 insertions, 9580 deletions
diff --git a/drivers/staging/hv/Kconfig b/drivers/staging/hv/Kconfig index 5e0c9f6..072185e 100644 --- a/drivers/staging/hv/Kconfig +++ b/drivers/staging/hv/Kconfig @@ -1,46 +1,17 @@ -config HYPERV - tristate "Microsoft Hyper-V client drivers" - depends on X86 && ACPI && PCI && m - default n - help - Select this option to run Linux as a Hyper-V client operating - system. - -if HYPERV - config HYPERV_STORAGE tristate "Microsoft Hyper-V virtual storage driver" - depends on SCSI - default HYPERV + depends on HYPERV && SCSI help Select this option to enable the Hyper-V virtual storage driver. -config HYPERV_BLOCK - tristate "Microsoft Hyper-V virtual block driver" - depends on BLOCK && SCSI && (LBDAF || 64BIT) - default HYPERV - help - Select this option to enable the Hyper-V virtual block driver. - config HYPERV_NET tristate "Microsoft Hyper-V virtual network driver" - depends on NET - default HYPERV + depends on HYPERV && NET help Select this option to enable the Hyper-V virtual network driver. -config HYPERV_UTILS - tristate "Microsoft Hyper-V Utilities driver" - depends on CONNECTOR && NLS - default HYPERV - help - Select this option to enable the Hyper-V Utilities. - config HYPERV_MOUSE tristate "Microsoft Hyper-V mouse driver" - depends on HID - default HYPERV + depends on HYPERV && HID help Select this option to enable the Hyper-V mouse driver. - -endif diff --git a/drivers/staging/hv/Makefile b/drivers/staging/hv/Makefile index 3004674..e071c12 100644 --- a/drivers/staging/hv/Makefile +++ b/drivers/staging/hv/Makefile @@ -1,14 +1,7 @@ -obj-$(CONFIG_HYPERV) += hv_vmbus.o hv_timesource.o +obj-$(CONFIG_HYPERV) += hv_timesource.o obj-$(CONFIG_HYPERV_STORAGE) += hv_storvsc.o -obj-$(CONFIG_HYPERV_BLOCK) += hv_blkvsc.o obj-$(CONFIG_HYPERV_NET) += hv_netvsc.o -obj-$(CONFIG_HYPERV_UTILS) += hv_utils.o obj-$(CONFIG_HYPERV_MOUSE) += hv_mouse.o -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 storvsc.o +hv_storvsc-y := storvsc_drv.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/TODO b/drivers/staging/hv/TODO index 582fd4a..ed4d636 100644 --- a/drivers/staging/hv/TODO +++ b/drivers/staging/hv/TODO @@ -1,14 +1,7 @@ TODO: - - fix remaining checkpatch warnings and errors - - audit the vmbus to verify it is working properly with the - driver model - - see if the vmbus can be merged with the other virtual busses - in the kernel - audit the network driver - - checking for carrier inside open is wrong, network device API - confusion?? - - audit the block driver - audit the scsi driver Please send patches for this code to Greg Kroah-Hartman <gregkh@suse.de>, -Hank Janssen <hjanssen@microsoft.com>, and Haiyang Zhang <haiyangz@microsoft.com>. +Hank Janssen <hjanssen@microsoft.com>, Haiyang Zhang <haiyangz@microsoft.com>, +K. Y. Srinivasan <kys@microsoft.com> diff --git a/drivers/staging/hv/blkvsc_drv.c b/drivers/staging/hv/blkvsc_drv.c deleted file mode 100644 index d286b22..0000000 --- a/drivers/staging/hv/blkvsc_drv.c +++ /dev/null @@ -1,1026 +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> - * K. Y. Srinivasan <kys@microsoft.com> - */ -#include <linux/init.h> -#include <linux/module.h> -#include <linux/device.h> -#include <linux/blkdev.h> -#include <linux/major.h> -#include <linux/delay.h> -#include <linux/hdreg.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 "hyperv.h" -#include "hyperv_storage.h" - - -#define BLKVSC_MINORS 64 - -enum blkvsc_device_type { - UNKNOWN_DEV_TYPE, - HARDDISK_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 - * represented by 1 or more struct blkvsc_request - */ -struct blkvsc_request_group { - int outstanding; - int status; - struct list_head blkvsc_req_list; /* list of blkvsc_requests */ -}; - -struct blkvsc_request { - /* blkvsc_request_group.blkvsc_req_list */ - struct list_head req_entry; - - /* block_device_context.pending_list */ - struct list_head pend_entry; - - /* This may be null if we generate a request internally */ - struct request *req; - - struct block_device_context *dev; - - /* The group this request is part of. Maybe null */ - struct blkvsc_request_group *group; - - int write; - sector_t sector_start; - unsigned long sector_count; - - unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE]; - unsigned char cmd_len; - unsigned char cmnd[MAX_COMMAND_SIZE]; - - struct hv_storvsc_request request; -}; - -/* Per device structure */ -struct block_device_context { - /* point back to our device context */ - struct hv_device *device_ctx; - struct kmem_cache *request_pool; - spinlock_t lock; - struct gendisk *gd; - enum blkvsc_device_type device_type; - struct list_head pending_list; - - unsigned char device_id[64]; - unsigned int device_id_len; - int num_outstanding_reqs; - int shutting_down; - unsigned int sector_size; - sector_t capacity; - unsigned int port; - unsigned char path; - unsigned char target; - int users; -}; - -static const char *drv_name = "blkvsc"; - -/* {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 - } -}; - -/* - * 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_ringbuffer_size = BLKVSC_RING_BUFFER_SIZE; - -module_param(blkvsc_ringbuffer_size, int, S_IRUGO); -MODULE_PARM_DESC(ring_size, "Ring buffer size (in bytes)"); - -/* - * There is a circular dependency involving blkvsc_probe() - * and block_ops. - */ -static int blkvsc_probe(struct hv_device *dev); - -static int blkvsc_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; - - device_info->ring_buffer_size = blkvsc_ringbuffer_size; - - ret = storvsc_dev_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; -} - -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_storvsc_request *storvsc_req; - struct vmscsi_request *vm_srb; - int ret; - - - storvsc_req = &blkvsc_req->request; - vm_srb = &storvsc_req->vstor_packet.vm_srb; - - vm_srb->data_in = blkvsc_req->write ? WRITE_TYPE : READ_TYPE; - - storvsc_req->on_io_completion = request_completion; - storvsc_req->context = blkvsc_req; - - 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 */ - - vm_srb->cdb_length = blkvsc_req->cmd_len; - - memcpy(vm_srb->cdb, blkvsc_req->cmnd, vm_srb->cdb_length); - - storvsc_req->sense_buffer = blkvsc_req->sense_buffer; - - ret = storvsc_do_io(blkdev->device_ctx, - &blkvsc_req->request); - if (ret == 0) - blkdev->num_outstanding_reqs++; - - return ret; -} - - -static int blkvsc_open(struct block_device *bdev, fmode_t mode) -{ - struct block_device_context *blkdev = bdev->bd_disk->private_data; - unsigned long flags; - - spin_lock_irqsave(&blkdev->lock, flags); - - blkdev->users++; - - spin_unlock_irqrestore(&blkdev->lock, flags); - - return 0; -} - - -static int blkvsc_getgeo(struct block_device *bd, struct hd_geometry *hg) -{ - sector_t nsect = get_capacity(bd->bd_disk); - sector_t cylinders = nsect; - - /* - * 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; - -} - - -static void blkvsc_init_rw(struct blkvsc_request *blkvsc_req) -{ - - blkvsc_req->cmd_len = 16; - - 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); -} - - -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; - - 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_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; - struct vmscsi_request *vm_srb; - unsigned long flags; - - - vm_srb = &blkvsc_req->request.vstor_packet.vm_srb; - - spin_lock_irqsave(&blkdev->lock, flags); - blkdev->num_outstanding_reqs--; - spin_unlock_irqrestore(&blkdev->lock, flags); - - 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); - - complete(&blkvsc_req->request.wait_event); -} - - -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; - - int ret = 0; - - blkvsc_req = kmem_cache_zalloc(blkdev->request_pool, GFP_KERNEL); - if (!blkvsc_req) - return -ENOMEM; - - page_buf = alloc_page(GFP_KERNEL); - if (!page_buf) { - kmem_cache_free(blkdev->request_pool, blkvsc_req); - return -ENOMEM; - } - - 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.offset = 0; - - 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; - - case DO_CAPACITY: - blkdev->sector_size = 0; - blkdev->capacity = 0; - - blkvsc_req->cmnd[0] = READ_CAPACITY; - blkvsc_req->cmd_len = 16; - blkvsc_req->request.data_buffer.len = 8; - break; - - 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; - } - - spin_lock_irqsave(&blkdev->lock, flags); - blkvsc_submit_request(blkvsc_req, blkvsc_cmd_completion); - spin_unlock_irqrestore(&blkdev->lock, flags); - - wait_for_completion_interruptible(&blkvsc_req->request.wait_event); - - /* check error */ - if (vm_srb->scsi_status) { - scsi_normalize_sense(blkvsc_req->sense_buffer, - SCSI_SENSE_BUFFERSIZE, &sense_hdr); - - return 0; - } - - buf = kmap(page_buf); - - switch (op) { - case DO_INQUIRY: - device_type = buf[0] & 0x1F; - - if (device_type == 0x0) - blkdev->device_type = HARDDISK_TYPE; - else - blkdev->device_type = UNKNOWN_DEV_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); - break; - - case DO_CAPACITY: - /* 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]; - break; - default: - break; - - } - -cleanup: - - kunmap(page_buf); - - __free_page(page_buf); - - kmem_cache_free(blkdev->request_pool, blkvsc_req); - - return ret; -} - - -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; - - int ret = 0; - - - /* 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) { - - if (comp_req == pend_req) - break; - - list_del(&comp_req->req_entry); - - 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); - - /* FIXME: shouldn't this do more than return? */ - if (ret) - goto out; - } - - kmem_cache_free(blkdev->request_pool, comp_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 - */ - kmem_cache_free(blkdev->request_pool, - pend_req->group); - } - } - - kmem_cache_free(blkdev->request_pool, pend_req); - } - -out: - return ret; -} - - -/* - * blkvsc_remove() - Callback when our device is removed - */ -static int blkvsc_remove(struct hv_device *dev) -{ - struct block_device_context *blkdev = dev_get_drvdata(&dev->device); - unsigned long flags; - - - /* Get to a known state */ - spin_lock_irqsave(&blkdev->lock, flags); - - blkdev->shutting_down = 1; - - blk_stop_queue(blkdev->gd->queue); - - blkvsc_cancel_pending_reqs(blkdev); - - spin_unlock_irqrestore(&blkdev->lock, flags); - - blkvsc_do_operation(blkdev, DO_FLUSH); - - if (blkdev->users == 0) { - del_gendisk(blkdev->gd); - put_disk(blkdev->gd); - blk_cleanup_queue(blkdev->gd->queue); - - storvsc_dev_remove(blkdev->device_ctx); - - kmem_cache_destroy(blkdev->request_pool); - kfree(blkdev); - } - - return 0; -} - -static void blkvsc_shutdown(struct hv_device *dev) -{ - struct block_device_context *blkdev = dev_get_drvdata(&dev->device); - unsigned long flags; - - if (!blkdev) - return; - - spin_lock_irqsave(&blkdev->lock, flags); - - blkdev->shutting_down = 1; - - blk_stop_queue(blkdev->gd->queue); - - blkvsc_cancel_pending_reqs(blkdev); - - spin_unlock_irqrestore(&blkdev->lock, flags); - - blkvsc_do_operation(blkdev, DO_FLUSH); - - /* - * Now wait for all outgoing I/O to be drained. - */ - storvsc_wait_to_drain((struct storvsc_device *)dev->ext); - -} - -static int blkvsc_release(struct gendisk *disk, fmode_t mode) -{ - struct block_device_context *blkdev = disk->private_data; - unsigned long flags; - - spin_lock_irqsave(&blkdev->lock, flags); - - if ((--blkdev->users == 0) && (blkdev->shutting_down)) { - blk_stop_queue(blkdev->gd->queue); - spin_unlock_irqrestore(&blkdev->lock, flags); - - blkvsc_do_operation(blkdev, DO_FLUSH); - del_gendisk(blkdev->gd); - put_disk(blkdev->gd); - blk_cleanup_queue(blkdev->gd->queue); - - storvsc_dev_remove(blkdev->device_ctx); - - kmem_cache_destroy(blkdev->request_pool); - kfree(blkdev); - } else - spin_unlock_irqrestore(&blkdev->lock, flags); - - return 0; -} - - -/* - * We break the request into 1 or more blkvsc_requests and submit - * 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, - struct request *req) -{ - struct bio *bio = NULL; - struct bio_vec *bvec = NULL; - struct bio_vec *prev_bvec = NULL; - struct blkvsc_request *blkvsc_req = NULL; - struct blkvsc_request *tmp; - int databuf_idx = 0; - int seg_idx = 0; - sector_t start_sector; - unsigned long num_sectors = 0; - int ret = 0; - int pending = 0; - struct blkvsc_request_group *group = NULL; - - /* Create a group to tie req to list of blkvsc_reqs */ - group = kmem_cache_zalloc(blkdev->request_pool, GFP_ATOMIC); - if (!group) - return -ENOMEM; - - INIT_LIST_HEAD(&group->blkvsc_req_list); - group->outstanding = group->status = 0; - - start_sector = blk_rq_pos(req); - - /* foreach bio in the request */ - if (req->bio) { - for (bio = req->bio; bio; bio = bio->bi_next) { - /* - * Map this bio into an existing or new storvsc request - */ - bio_for_each_segment(bvec, bio, seg_idx) { - /* Get a new storvsc request */ - /* 1st-time */ - if ((!blkvsc_req) || - (databuf_idx >= MAX_MULTIPAGE_BUFFER_COUNT) - /* hole at the begin of page */ - || (bvec->bv_offset != 0) || - /* hold at the end of page */ - (prev_bvec && - (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_init_rw(blkvsc_req); - } - - /* - * Create new blkvsc_req to represent - * the current bvec - */ - 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); - } - - kmem_cache_free( - blkdev->request_pool, group); - return -ENOMEM; - } - - memset(blkvsc_req, 0, - sizeof(struct blkvsc_request)); - - blkvsc_req->dev = blkdev; - blkvsc_req->req = req; - 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); - - start_sector += num_sectors; - num_sectors = 0; - databuf_idx = 0; - } - - /* - * 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); - blkvsc_req->request.data_buffer.len - += bvec->bv_len; - - prev_bvec = bvec; - - databuf_idx++; - num_sectors += bvec->bv_len >> 9; - - } /* bio_for_each_segment */ - - } /* rq_for_each_bio */ - } - - /* Handle the last 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_init_rw(blkvsc_req); - } - - list_for_each_entry(blkvsc_req, &group->blkvsc_req_list, req_entry) { - if (pending) { - - list_add_tail(&blkvsc_req->pend_entry, - &blkdev->pending_list); - } else { - ret = blkvsc_submit_request(blkvsc_req, - blkvsc_request_completion); - if (ret == -1) { - pending = 1; - list_add_tail(&blkvsc_req->pend_entry, - &blkdev->pending_list); - } - - } - } - - return pending; -} - -static int blkvsc_do_pending_reqs(struct block_device_context *blkdev) -{ - struct blkvsc_request *pend_req, *tmp; - int ret = 0; - - /* Flush the pending list first */ - list_for_each_entry_safe(pend_req, tmp, &blkdev->pending_list, - pend_entry) { - - ret = blkvsc_submit_request(pend_req, - blkvsc_request_completion); - if (ret != 0) - break; - else - list_del(&pend_req->pend_entry); - } - - return ret; -} - - -static void blkvsc_request(struct request_queue *queue) -{ - struct block_device_context *blkdev = NULL; - struct request *req; - int ret = 0; - - while ((req = blk_peek_request(queue)) != NULL) { - - blkdev = req->rq_disk->private_data; - if (blkdev->shutting_down || req->cmd_type != REQ_TYPE_FS) { - __blk_end_request_cur(req, 0); - continue; - } - - ret = blkvsc_do_pending_reqs(blkdev); - - if (ret != 0) { - blk_stop_queue(queue); - break; - } - - blk_start_request(req); - - ret = blkvsc_do_request(blkdev, req); - if (ret > 0) { - blk_stop_queue(queue); - break; - } else if (ret < 0) { - blk_requeue_request(queue, req); - blk_stop_queue(queue); - break; - } - } -} - - - -/* The one and only one */ -static struct hv_driver blkvsc_drv = { - .probe = blkvsc_probe, - .remove = blkvsc_remove, - .shutdown = blkvsc_shutdown, -}; - -static const struct block_device_operations block_ops = { - .owner = THIS_MODULE, - .open = blkvsc_open, - .release = blkvsc_release, - .getgeo = blkvsc_getgeo, - .ioctl = blkvsc_ioctl, -}; - -/* - * blkvsc_drv_init - BlkVsc driver initialization. - */ -static int blkvsc_drv_init(void) -{ - struct hv_driver *drv = &blkvsc_drv; - int ret; - - BUILD_BUG_ON(sizeof(sector_t) != 8); - - memcpy(&drv->dev_type, &dev_type, sizeof(struct hv_guid)); - drv->driver.name = drv_name; - - /* The driver belongs to vmbus */ - ret = vmbus_child_driver_register(&drv->driver); - - return ret; -} - - -static void blkvsc_drv_exit(void) -{ - - vmbus_child_driver_unregister(&blkvsc_drv.driver); -} - -/* - * blkvsc_probe - Add a new device for this driver - */ -static int blkvsc_probe(struct hv_device *dev) -{ - struct block_device_context *blkdev = NULL; - struct storvsc_device_info device_info; - struct storvsc_major_info major_info; - int ret = 0; - - blkdev = kzalloc(sizeof(struct block_device_context), GFP_KERNEL); - if (!blkdev) { - ret = -ENOMEM; - goto cleanup; - } - - INIT_LIST_HEAD(&blkdev->pending_list); - - /* Initialize what we can here */ - spin_lock_init(&blkdev->lock); - - - 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; - } - - - ret = blkvsc_device_add(dev, &device_info); - if (ret != 0) - goto cleanup; - - 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; - - dev_set_drvdata(&dev->device, blkdev); - - ret = storvsc_get_major_info(&device_info, &major_info); - - if (ret) - goto cleanup; - - if (major_info.do_register) { - ret = register_blkdev(major_info.major, major_info.devname); - - if (ret != 0) { - DPRINT_ERR(BLKVSC_DRV, - "register_blkdev() failed! ret %d", ret); - goto remove; - } - } - - DPRINT_INFO(BLKVSC_DRV, "blkvsc registered for major %d!!", - major_info.major); - - blkdev->gd = alloc_disk(BLKVSC_MINORS); - if (!blkdev->gd) { - 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); - - 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->private_data = blkdev; - blkdev->gd->driverfs_dev = &(blkdev->device_ctx->device); - sprintf(blkdev->gd->disk_name, "hd%c", 'a' + major_info.index); - - blkvsc_do_operation(blkdev, DO_INQUIRY); - blkvsc_do_operation(blkdev, DO_CAPACITY); - - 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); - - 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 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; - struct vmscsi_request *vm_srb; - - - 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) { - - 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) -{ - blkvsc_drv_exit(); -} - -MODULE_LICENSE("GPL"); -MODULE_VERSION(HV_DRV_VERSION); -MODULE_DESCRIPTION("Microsoft Hyper-V virtual block driver"); -module_init(blkvsc_drv_init); -module_exit(blkvsc_exit); diff --git a/drivers/staging/hv/channel.c b/drivers/staging/hv/channel.c deleted file mode 100644 index 455f47a..0000000 --- a/drivers/staging/hv/channel.c +++ /dev/null @@ -1,877 +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> - */ -#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 "hyperv.h" -#include "hyperv_vmbus.h" - -#define NUM_PAGES_SPANNED(addr, len) \ -((PAGE_ALIGN(addr + len) >> PAGE_SHIFT) - (addr >> PAGE_SHIFT)) - -/* Internal routines */ -static int create_gpadl_header( - void *kbuffer, /* must be phys and virt contiguous */ - u32 size, /* page-size multiple */ - struct vmbus_channel_msginfo **msginfo, - u32 *messagecount); -static void vmbus_setevent(struct vmbus_channel *channel); - -/* - * vmbus_setevent- Trigger an event notification on the specified - * channel. - */ -static void vmbus_setevent(struct vmbus_channel *channel) -{ - struct hv_monitor_page *monitorpage; - - if (channel->offermsg.monitor_allocated) { - /* Each u32 represents 32 channels */ - sync_set_bit(channel->offermsg.child_relid & 31, - (unsigned long *) vmbus_connection.send_int_page + - (channel->offermsg.child_relid >> 5)); - - monitorpage = vmbus_connection.monitor_pages; - monitorpage++; /* Get the child to parent monitor page */ - - sync_set_bit(channel->monitor_bit, - (unsigned long *)&monitorpage->trigger_group - [channel->monitor_grp].pending); - - } else { - vmbus_set_event(channel->offermsg.child_relid); - } -} - -/* - * vmbus_get_debug_info -Retrieve various channel debug info - */ -void vmbus_get_debug_info(struct vmbus_channel *channel, - struct vmbus_channel_debug_info *debuginfo) -{ - struct hv_monitor_page *monitorpage; - u8 monitor_group = (u8)channel->offermsg.monitorid / 32; - u8 monitor_offset = (u8)channel->offermsg.monitorid % 32; - /* u32 monitorBit = 1 << monitorOffset; */ - - debuginfo->relid = channel->offermsg.child_relid; - debuginfo->state = channel->state; - memcpy(&debuginfo->interfacetype, - &channel->offermsg.offer.if_type, sizeof(struct hv_guid)); - memcpy(&debuginfo->interface_instance, - &channel->offermsg.offer.if_instance, - sizeof(struct hv_guid)); - - monitorpage = (struct hv_monitor_page *)vmbus_connection.monitor_pages; - - debuginfo->monitorid = channel->offermsg.monitorid; - - debuginfo->servermonitor_pending = - monitorpage->trigger_group[monitor_group].pending; - debuginfo->servermonitor_latency = - monitorpage->latency[monitor_group][monitor_offset]; - debuginfo->servermonitor_connectionid = - monitorpage->parameter[monitor_group] - [monitor_offset].connectionid.u.id; - - monitorpage++; - - debuginfo->clientmonitor_pending = - monitorpage->trigger_group[monitor_group].pending; - debuginfo->clientmonitor_latency = - monitorpage->latency[monitor_group][monitor_offset]; - debuginfo->clientmonitor_connectionid = - monitorpage->parameter[monitor_group] - [monitor_offset].connectionid.u.id; - - hv_ringbuffer_get_debuginfo(&channel->inbound, &debuginfo->inbound); - hv_ringbuffer_get_debuginfo(&channel->outbound, &debuginfo->outbound); -} - -/* - * vmbus_open - Open the specified channel. - */ -int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, - u32 recv_ringbuffer_size, void *userdata, u32 userdatalen, - void (*onchannelcallback)(void *context), void *context) -{ - struct vmbus_channel_open_channel *openMsg; - struct vmbus_channel_msginfo *openInfo = NULL; - void *in, *out; - unsigned long flags; - int ret, t, err = 0; - - newchannel->onchannel_callback = onchannelcallback; - newchannel->channel_callback_context = context; - - /* Allocate the ring buffer */ - out = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, - get_order(send_ringbuffer_size + recv_ringbuffer_size)); - - if (!out) - return -ENOMEM; - - - in = (void *)((unsigned long)out + send_ringbuffer_size); - - newchannel->ringbuffer_pages = out; - newchannel->ringbuffer_pagecount = (send_ringbuffer_size + - recv_ringbuffer_size) >> PAGE_SHIFT; - - ret = hv_ringbuffer_init( - &newchannel->outbound, out, send_ringbuffer_size); - - if (ret != 0) { - err = ret; - goto errorout; - } - - ret = hv_ringbuffer_init( - &newchannel->inbound, in, recv_ringbuffer_size); - if (ret != 0) { - err = ret; - goto errorout; - } - - - /* Establish the gpadl for the ring buffer */ - newchannel->ringbuffer_gpadlhandle = 0; - - ret = vmbus_establish_gpadl(newchannel, - newchannel->outbound.ring_buffer, - send_ringbuffer_size + - recv_ringbuffer_size, - &newchannel->ringbuffer_gpadlhandle); - - if (ret != 0) { - err = ret; - goto errorout; - } - - /* Create and init the channel open message */ - openInfo = kmalloc(sizeof(*openInfo) + - sizeof(struct vmbus_channel_open_channel), - GFP_KERNEL); - if (!openInfo) { - err = -ENOMEM; - goto errorout; - } - - init_completion(&openInfo->waitevent); - - openMsg = (struct vmbus_channel_open_channel *)openInfo->msg; - openMsg->header.msgtype = CHANNELMSG_OPENCHANNEL; - openMsg->openid = newchannel->offermsg.child_relid; - openMsg->child_relid = newchannel->offermsg.child_relid; - openMsg->ringbuffer_gpadlhandle = newchannel->ringbuffer_gpadlhandle; - openMsg->downstream_ringbuffer_pageoffset = send_ringbuffer_size >> - PAGE_SHIFT; - openMsg->server_contextarea_gpadlhandle = 0; - - if (userdatalen > MAX_USER_DEFINED_BYTES) { - err = -EINVAL; - goto errorout; - } - - if (userdatalen) - memcpy(openMsg->userdata, userdata, userdatalen); - - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - list_add_tail(&openInfo->msglistentry, - &vmbus_connection.chn_msg_list); - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - - ret = vmbus_post_msg(openMsg, - sizeof(struct vmbus_channel_open_channel)); - - if (ret != 0) - goto cleanup; - - t = wait_for_completion_timeout(&openInfo->waitevent, 5*HZ); - if (t == 0) { - err = -ETIMEDOUT; - goto errorout; - } - - - if (openInfo->response.open_result.status) - err = openInfo->response.open_result.status; - -cleanup: - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - list_del(&openInfo->msglistentry); - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - - kfree(openInfo); - return err; - -errorout: - 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); - return err; -} -EXPORT_SYMBOL_GPL(vmbus_open); - -/* - * dump_gpadl_body - Dump the gpadl body message to the console for - * debugging purposes. - */ -static void dump_gpadl_body(struct vmbus_channel_gpadl_body *gpadl, u32 len) -{ - int i; - int pfncount; - - 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++) - DPRINT_DBG(VMBUS, "gpadl body - %d) pfn %llu", - i, gpadl->pfn[i]); -} - -/* - * dump_gpadl_header - Dump the gpadl header message to the console for - * debugging purposes. - */ -static void dump_gpadl_header(struct vmbus_channel_gpadl_header *gpadl) -{ - int i, j; - int pagecount; - - DPRINT_DBG(VMBUS, - "gpadl header - relid %d, range count %d, range buflen %d", - gpadl->child_relid, gpadl->rangecount, gpadl->range_buflen); - for (i = 0; i < gpadl->rangecount; i++) { - pagecount = gpadl->range[i].byte_count >> PAGE_SHIFT; - pagecount = (pagecount > 26) ? 26 : pagecount; - - DPRINT_DBG(VMBUS, "gpadl range %d - len %d offset %d " - "page count %d", i, gpadl->range[i].byte_count, - gpadl->range[i].byte_offset, pagecount); - - for (j = 0; j < pagecount; j++) - DPRINT_DBG(VMBUS, "%d) pfn %llu", j, - gpadl->range[i].pfn_array[j]); - } -} - -/* - * create_gpadl_header - Creates a gpadl for the specified buffer - */ -static int create_gpadl_header(void *kbuffer, u32 size, - struct vmbus_channel_msginfo **msginfo, - u32 *messagecount) -{ - int i; - int pagecount; - unsigned long long pfn; - struct vmbus_channel_gpadl_header *gpadl_header; - struct vmbus_channel_gpadl_body *gpadl_body; - struct vmbus_channel_msginfo *msgheader; - struct vmbus_channel_msginfo *msgbody = NULL; - u32 msgsize; - - int pfnsum, pfncount, pfnleft, pfncurr, pfnsize; - - pagecount = size >> PAGE_SHIFT; - pfn = virt_to_phys(kbuffer) >> PAGE_SHIFT; - - /* do we need a gpadl body msg */ - pfnsize = MAX_SIZE_CHANNEL_MESSAGE - - sizeof(struct vmbus_channel_gpadl_header) - - sizeof(struct gpa_range); - pfncount = pfnsize / sizeof(u64); - - if (pagecount > pfncount) { - /* we need a gpadl body */ - /* fill in the header */ - msgsize = sizeof(struct vmbus_channel_msginfo) + - sizeof(struct vmbus_channel_gpadl_header) + - sizeof(struct gpa_range) + pfncount * sizeof(u64); - msgheader = kzalloc(msgsize, GFP_KERNEL); - if (!msgheader) - goto nomem; - - INIT_LIST_HEAD(&msgheader->submsglist); - msgheader->msgsize = msgsize; - - gpadl_header = (struct vmbus_channel_gpadl_header *) - msgheader->msg; - gpadl_header->rangecount = 1; - gpadl_header->range_buflen = sizeof(struct gpa_range) + - pagecount * sizeof(u64); - gpadl_header->range[0].byte_offset = 0; - gpadl_header->range[0].byte_count = size; - for (i = 0; i < pfncount; i++) - gpadl_header->range[0].pfn_array[i] = pfn+i; - *msginfo = msgheader; - *messagecount = 1; - - pfnsum = pfncount; - pfnleft = pagecount - pfncount; - - /* how many pfns can we fit */ - pfnsize = MAX_SIZE_CHANNEL_MESSAGE - - sizeof(struct vmbus_channel_gpadl_body); - pfncount = pfnsize / sizeof(u64); - - /* fill in the body */ - while (pfnleft) { - if (pfnleft > pfncount) - pfncurr = pfncount; - else - pfncurr = pfnleft; - - msgsize = sizeof(struct vmbus_channel_msginfo) + - sizeof(struct vmbus_channel_gpadl_body) + - pfncurr * sizeof(u64); - msgbody = kzalloc(msgsize, GFP_KERNEL); - - if (!msgbody) { - struct vmbus_channel_msginfo *pos = NULL; - struct vmbus_channel_msginfo *tmp = NULL; - /* - * Free up all the allocated messages. - */ - list_for_each_entry_safe(pos, tmp, - &msgheader->submsglist, - msglistentry) { - - list_del(&pos->msglistentry); - kfree(pos); - } - - goto nomem; - } - - msgbody->msgsize = msgsize; - (*messagecount)++; - gpadl_body = - (struct vmbus_channel_gpadl_body *)msgbody->msg; - - /* - * Gpadl is u32 and we are using a pointer which could - * be 64-bit - * This is governed by the guest/host protocol and - * so the hypervisor gurantees that this is ok. - */ - for (i = 0; i < pfncurr; i++) - gpadl_body->pfn[i] = pfn + pfnsum + i; - - /* add to msg header */ - list_add_tail(&msgbody->msglistentry, - &msgheader->submsglist); - pfnsum += pfncurr; - pfnleft -= pfncurr; - } - } else { - /* everything fits in a header */ - msgsize = sizeof(struct vmbus_channel_msginfo) + - sizeof(struct vmbus_channel_gpadl_header) + - sizeof(struct gpa_range) + pagecount * sizeof(u64); - msgheader = kzalloc(msgsize, GFP_KERNEL); - if (msgheader == NULL) - goto nomem; - msgheader->msgsize = msgsize; - - gpadl_header = (struct vmbus_channel_gpadl_header *) - msgheader->msg; - gpadl_header->rangecount = 1; - gpadl_header->range_buflen = sizeof(struct gpa_range) + - pagecount * sizeof(u64); - gpadl_header->range[0].byte_offset = 0; - gpadl_header->range[0].byte_count = size; - for (i = 0; i < pagecount; i++) - gpadl_header->range[0].pfn_array[i] = pfn+i; - - *msginfo = msgheader; - *messagecount = 1; - } - - return 0; -nomem: - kfree(msgheader); - kfree(msgbody); - return -ENOMEM; -} - -/* - * vmbus_establish_gpadl - Estabish a GPADL for the specified buffer - * - * @channel: a channel - * @kbuffer: from kmalloc - * @size: page-size multiple - * @gpadl_handle: some funky thing - */ -int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, - u32 size, u32 *gpadl_handle) -{ - struct vmbus_channel_gpadl_header *gpadlmsg; - struct vmbus_channel_gpadl_body *gpadl_body; - /* struct vmbus_channel_gpadl_created *gpadlCreated; */ - struct vmbus_channel_msginfo *msginfo = NULL; - struct vmbus_channel_msginfo *submsginfo; - u32 msgcount; - struct list_head *curr; - 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); - - ret = create_gpadl_header(kbuffer, size, &msginfo, &msgcount); - if (ret) - return ret; - - init_completion(&msginfo->waitevent); - - gpadlmsg = (struct vmbus_channel_gpadl_header *)msginfo->msg; - gpadlmsg->header.msgtype = CHANNELMSG_GPADL_HEADER; - gpadlmsg->child_relid = channel->offermsg.child_relid; - gpadlmsg->gpadl = next_gpadl_handle; - - dump_gpadl_header(gpadlmsg); - - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - list_add_tail(&msginfo->msglistentry, - &vmbus_connection.chn_msg_list); - - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - - ret = vmbus_post_msg(gpadlmsg, msginfo->msgsize - - sizeof(*msginfo)); - if (ret != 0) - goto cleanup; - - if (msgcount > 1) { - list_for_each(curr, &msginfo->submsglist) { - - submsginfo = (struct vmbus_channel_msginfo *)curr; - gpadl_body = - (struct vmbus_channel_gpadl_body *)submsginfo->msg; - - gpadl_body->header.msgtype = - CHANNELMSG_GPADL_BODY; - gpadl_body->gpadl = next_gpadl_handle; - - dump_gpadl_body(gpadl_body, submsginfo->msgsize - - sizeof(*submsginfo)); - ret = vmbus_post_msg(gpadl_body, - submsginfo->msgsize - - sizeof(*submsginfo)); - if (ret != 0) - goto cleanup; - - } - } - t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); - BUG_ON(t == 0); - - - /* At this point, we received the gpadl created msg */ - *gpadl_handle = gpadlmsg->gpadl; - -cleanup: - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - list_del(&msginfo->msglistentry); - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - - kfree(msginfo); - return ret; -} -EXPORT_SYMBOL_GPL(vmbus_establish_gpadl); - -/* - * vmbus_teardown_gpadl -Teardown the specified GPADL handle - */ -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, t; - - /* ASSERT(gpadl_handle != 0); */ - - info = kmalloc(sizeof(*info) + - sizeof(struct vmbus_channel_gpadl_teardown), GFP_KERNEL); - if (!info) - return -ENOMEM; - - init_completion(&info->waitevent); - - msg = (struct vmbus_channel_gpadl_teardown *)info->msg; - - msg->header.msgtype = CHANNELMSG_GPADL_TEARDOWN; - msg->child_relid = channel->offermsg.child_relid; - msg->gpadl = gpadl_handle; - - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - list_add_tail(&info->msglistentry, - &vmbus_connection.chn_msg_list); - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - ret = vmbus_post_msg(msg, - sizeof(struct vmbus_channel_gpadl_teardown)); - - BUG_ON(ret != 0); - t = wait_for_completion_timeout(&info->waitevent, 5*HZ); - BUG_ON(t == 0); - - /* Received a torndown response */ - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - list_del(&info->msglistentry); - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - - kfree(info); - return ret; -} -EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl); - -/* - * vmbus_close - Close the specified channel - */ -void vmbus_close(struct vmbus_channel *channel) -{ - struct vmbus_channel_close_channel *msg; - int ret; - - /* Stop callback and cancel the timer asap */ - channel->onchannel_callback = NULL; - - /* Send a closing message */ - - msg = &channel->close_msg.msg; - - msg->header.msgtype = CHANNELMSG_CLOSECHANNEL; - msg->child_relid = channel->offermsg.child_relid; - - ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_close_channel)); - - BUG_ON(ret != 0); - /* Tear down the gpadl for the channel's ring buffer */ - if (channel->ringbuffer_gpadlhandle) - vmbus_teardown_gpadl(channel, - channel->ringbuffer_gpadlhandle); - - /* Cleanup the ring buffers for this channel */ - hv_ringbuffer_cleanup(&channel->outbound); - hv_ringbuffer_cleanup(&channel->inbound); - - free_pages((unsigned long)channel->ringbuffer_pages, - get_order(channel->ringbuffer_pagecount * PAGE_SIZE)); - - -} -EXPORT_SYMBOL_GPL(vmbus_close); - -/** - * vmbus_sendpacket() - Send the specified buffer on the given channel - * @channel: Pointer to vmbus_channel structure. - * @buffer: Pointer to the buffer you want to receive the data into. - * @bufferlen: Maximum size of what the the buffer will hold - * @requestid: Identifier of the request - * @type: Type of packet that is being send e.g. negotiate, time - * packet etc. - * - * Sends data in @buffer directly to hyper-v via the vmbus - * This will send the data unparsed to hyper-v. - * - * Mainly used by Hyper-V drivers. - */ -int vmbus_sendpacket(struct vmbus_channel *channel, const void *buffer, - u32 bufferlen, u64 requestid, - enum vmbus_packet_type type, u32 flags) -{ - struct vmpacket_descriptor desc; - u32 packetlen = sizeof(struct vmpacket_descriptor) + bufferlen; - u32 packetlen_aligned = ALIGN(packetlen, sizeof(u64)); - struct scatterlist bufferlist[3]; - u64 aligned_data = 0; - int ret; - - - /* Setup the descriptor */ - desc.type = type; /* VmbusPacketTypeDataInBand; */ - desc.flags = flags; /* VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; */ - /* in 8-bytes granularity */ - desc.offset8 = sizeof(struct vmpacket_descriptor) >> 3; - desc.len8 = (u16)(packetlen_aligned >> 3); - desc.trans_id = requestid; - - sg_init_table(bufferlist, 3); - sg_set_buf(&bufferlist[0], &desc, sizeof(struct vmpacket_descriptor)); - sg_set_buf(&bufferlist[1], buffer, bufferlen); - sg_set_buf(&bufferlist[2], &aligned_data, - packetlen_aligned - packetlen); - - ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); - - if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) - vmbus_setevent(channel); - - return ret; -} -EXPORT_SYMBOL(vmbus_sendpacket); - -/* - * vmbus_sendpacket_pagebuffer - Send a range of single-page buffer - * packets using a GPADL Direct packet type. - */ -int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, - struct hv_page_buffer pagebuffers[], - u32 pagecount, void *buffer, u32 bufferlen, - u64 requestid) -{ - int ret; - int i; - struct vmbus_channel_packet_page_buffer desc; - u32 descsize; - u32 packetlen; - u32 packetlen_aligned; - struct scatterlist bufferlist[3]; - u64 aligned_data = 0; - - if (pagecount > MAX_PAGE_BUFFER_COUNT) - return -EINVAL; - - - /* - * Adjust the size down since vmbus_channel_packet_page_buffer is the - * largest size we support - */ - descsize = sizeof(struct vmbus_channel_packet_page_buffer) - - ((MAX_PAGE_BUFFER_COUNT - pagecount) * - sizeof(struct hv_page_buffer)); - packetlen = descsize + bufferlen; - packetlen_aligned = ALIGN(packetlen, sizeof(u64)); - - /* Setup the descriptor */ - desc.type = VM_PKT_DATA_USING_GPA_DIRECT; - desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; - desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */ - desc.length8 = (u16)(packetlen_aligned >> 3); - desc.transactionid = requestid; - desc.rangecount = pagecount; - - for (i = 0; i < pagecount; i++) { - desc.range[i].len = pagebuffers[i].len; - desc.range[i].offset = pagebuffers[i].offset; - desc.range[i].pfn = pagebuffers[i].pfn; - } - - sg_init_table(bufferlist, 3); - sg_set_buf(&bufferlist[0], &desc, descsize); - sg_set_buf(&bufferlist[1], buffer, bufferlen); - sg_set_buf(&bufferlist[2], &aligned_data, - packetlen_aligned - packetlen); - - ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); - - if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) - vmbus_setevent(channel); - - return ret; -} -EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer); - -/* - * vmbus_sendpacket_multipagebuffer - Send a multi-page buffer packet - * using a GPADL Direct packet type. - */ -int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, - struct hv_multipage_buffer *multi_pagebuffer, - void *buffer, u32 bufferlen, u64 requestid) -{ - int ret; - struct vmbus_channel_packet_multipage_buffer desc; - u32 descsize; - u32 packetlen; - u32 packetlen_aligned; - struct scatterlist bufferlist[3]; - u64 aligned_data = 0; - u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset, - multi_pagebuffer->len); - - - if ((pfncount < 0) || (pfncount > MAX_MULTIPAGE_BUFFER_COUNT)) - return -EINVAL; - - /* - * Adjust the size down since vmbus_channel_packet_multipage_buffer is - * the largest size we support - */ - descsize = sizeof(struct vmbus_channel_packet_multipage_buffer) - - ((MAX_MULTIPAGE_BUFFER_COUNT - pfncount) * - sizeof(u64)); - packetlen = descsize + bufferlen; - packetlen_aligned = ALIGN(packetlen, sizeof(u64)); - - - /* Setup the descriptor */ - desc.type = VM_PKT_DATA_USING_GPA_DIRECT; - desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; - desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */ - desc.length8 = (u16)(packetlen_aligned >> 3); - desc.transactionid = requestid; - desc.rangecount = 1; - - desc.range.len = multi_pagebuffer->len; - desc.range.offset = multi_pagebuffer->offset; - - memcpy(desc.range.pfn_array, multi_pagebuffer->pfn_array, - pfncount * sizeof(u64)); - - sg_init_table(bufferlist, 3); - sg_set_buf(&bufferlist[0], &desc, descsize); - sg_set_buf(&bufferlist[1], buffer, bufferlen); - sg_set_buf(&bufferlist[2], &aligned_data, - packetlen_aligned - packetlen); - - ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); - - if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) - vmbus_setevent(channel); - - return ret; -} -EXPORT_SYMBOL_GPL(vmbus_sendpacket_multipagebuffer); - -/** - * vmbus_recvpacket() - Retrieve the user packet on the specified channel - * @channel: Pointer to vmbus_channel structure. - * @buffer: Pointer to the buffer you want to receive the data into. - * @bufferlen: Maximum size of what the the buffer will hold - * @buffer_actual_len: The actual size of the data after it was received - * @requestid: Identifier of the request - * - * Receives directly from the hyper-v vmbus and puts the data it received - * into Buffer. This will receive the data unparsed from hyper-v. - * - * Mainly used by Hyper-V drivers. - */ -int vmbus_recvpacket(struct vmbus_channel *channel, void *buffer, - u32 bufferlen, u32 *buffer_actual_len, u64 *requestid) -{ - struct vmpacket_descriptor desc; - u32 packetlen; - u32 userlen; - int ret; - unsigned long flags; - - *buffer_actual_len = 0; - *requestid = 0; - - spin_lock_irqsave(&channel->inbound_lock, flags); - - ret = hv_ringbuffer_peek(&channel->inbound, &desc, - sizeof(struct vmpacket_descriptor)); - if (ret != 0) { - spin_unlock_irqrestore(&channel->inbound_lock, flags); - return 0; - } - - packetlen = desc.len8 << 3; - userlen = packetlen - (desc.offset8 << 3); - - *buffer_actual_len = userlen; - - if (userlen > bufferlen) { - spin_unlock_irqrestore(&channel->inbound_lock, flags); - - pr_err("Buffer too small - got %d needs %d\n", - bufferlen, userlen); - return -ETOOSMALL; - } - - *requestid = desc.trans_id; - - /* Copy over the packet to the user buffer */ - ret = hv_ringbuffer_read(&channel->inbound, buffer, userlen, - (desc.offset8 << 3)); - - spin_unlock_irqrestore(&channel->inbound_lock, flags); - - return 0; -} -EXPORT_SYMBOL(vmbus_recvpacket); - -/* - * vmbus_recvpacket_raw - Retrieve the raw packet on the specified channel - */ -int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer, - u32 bufferlen, u32 *buffer_actual_len, - u64 *requestid) -{ - struct vmpacket_descriptor desc; - u32 packetlen; - u32 userlen; - int ret; - unsigned long flags; - - *buffer_actual_len = 0; - *requestid = 0; - - spin_lock_irqsave(&channel->inbound_lock, flags); - - ret = hv_ringbuffer_peek(&channel->inbound, &desc, - sizeof(struct vmpacket_descriptor)); - if (ret != 0) { - spin_unlock_irqrestore(&channel->inbound_lock, flags); - return 0; - } - - - packetlen = desc.len8 << 3; - userlen = packetlen - (desc.offset8 << 3); - - *buffer_actual_len = packetlen; - - if (packetlen > bufferlen) { - spin_unlock_irqrestore(&channel->inbound_lock, flags); - - 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 = hv_ringbuffer_read(&channel->inbound, buffer, packetlen, 0); - - spin_unlock_irqrestore(&channel->inbound_lock, flags); - return 0; -} -EXPORT_SYMBOL_GPL(vmbus_recvpacket_raw); diff --git a/drivers/staging/hv/channel_mgmt.c b/drivers/staging/hv/channel_mgmt.c deleted file mode 100644 index bf011f3..0000000 --- a/drivers/staging/hv/channel_mgmt.c +++ /dev/null @@ -1,784 +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> - */ -#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/list.h> -#include <linux/module.h> -#include <linux/completion.h> - -#include "hyperv.h" -#include "hyperv_vmbus.h" - -struct vmbus_channel_message_table_entry { - 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 - supported_device_classes[MAX_NUM_DEVICE_CLASSES_SUPPORTED] = { - /* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */ - /* Storage - SCSI */ - { - .data = { - 0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, - 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f - } - }, - - /* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */ - /* Network */ - { - .data = { - 0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, - 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E - } - }, - - /* {CFA8B69E-5B4A-4cc0-B98B-8BA1A1F3F95A} */ - /* Input */ - { - .data = { - 0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c, - 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A - } - }, - - /* {32412632-86cb-44a2-9b5c-50d1417354f5} */ - /* IDE */ - { - .data = { - 0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, - 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5 - } - }, - /* 0E0B6031-5213-4934-818B-38D90CED39DB */ - /* Shutdown */ - { - .data = { - 0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49, - 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB - } - }, - /* {9527E630-D0AE-497b-ADCE-E80AB0175CAF} */ - /* TimeSync */ - { - .data = { - 0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, - 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf - } - }, - /* {57164f39-9115-4e78-ab55-382f3bd5422d} */ - /* Heartbeat */ - { - .data = { - 0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, - 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d - } - }, - /* {A9A0F4E7-5A45-4d96-B827-8A841E8C03E6} */ - /* KVP */ - { - .data = { - 0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, - 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6 - } - }, - -}; - - -/** - * prep_negotiate_resp() - Create default response for Hyper-V Negotiate message - * @icmsghdrp: Pointer to msg header structure - * @icmsg_negotiate: Pointer to negotiate message structure - * @buf: Raw buffer channel data - * - * @icmsghdrp is of type &struct icmsg_hdr. - * @negop is of type &struct icmsg_negotiate. - * Set up and fill in default negotiate response message. This response can - * come from both the vmbus driver and the hv_utils driver. The current api - * will respond properly to both Windows 2008 and Windows 2008-R2 operating - * systems. - * - * Mainly used by Hyper-V drivers. - */ -void prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, - struct icmsg_negotiate *negop, - u8 *buf) -{ - if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - icmsghdrp->icmsgsize = 0x10; - - negop = (struct icmsg_negotiate *)&buf[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; - - if (negop->icframe_vercnt == 2 && - negop->icversion_data[1].major == 3) { - negop->icversion_data[0].major = 3; - negop->icversion_data[0].minor = 0; - negop->icversion_data[1].major = 3; - negop->icversion_data[1].minor = 0; - } else { - negop->icversion_data[0].major = 1; - negop->icversion_data[0].minor = 0; - negop->icversion_data[1].major = 1; - negop->icversion_data[1].minor = 0; - } - - negop->icframe_vercnt = 1; - negop->icmsg_vercnt = 1; - } -} -EXPORT_SYMBOL(prep_negotiate_resp); - -/** - * chn_cb_negotiate() - Default handler for non IDE/SCSI/NETWORK - * Hyper-V requests - * @context: Pointer to argument structure. - * - * Set up the default handler for non device driver specific requests - * from Hyper-V. This stub responds to the default negotiate messages - * that come in for every non IDE/SCSI/Network request. - * This behavior is normally overwritten in the hv_utils driver. That - * driver handles requests like graceful shutdown, heartbeats etc. - * - * Mainly used by Hyper-V drivers. - */ -void chn_cb_negotiate(void *context) -{ - struct vmbus_channel *channel = context; - u8 *buf; - u32 buflen, recvlen; - u64 requestid; - - 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); - - vmbus_recvpacket(channel, buf, buflen, &recvlen, &requestid); - - if (recvlen > 0) { - icmsghdrp = (struct icmsg_hdr *)&buf[ - sizeof(struct vmbuspipe_hdr)]; - - prep_negotiate_resp(icmsghdrp, negop, buf); - - icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION - | ICMSGHDRFLAG_RESPONSE; - - vmbus_sendpacket(channel, buf, - recvlen, requestid, - VM_PKT_DATA_INBAND, 0); - } - - kfree(buf); -} -EXPORT_SYMBOL(chn_cb_negotiate); - -/* - * Function table used for message responses for non IDE/SCSI/Network type - * messages. (Such as KVP/Shutdown etc) - */ -struct hyperv_service_callback hv_cb_utils[MAX_MSG_TYPES] = { - /* 0E0B6031-5213-4934-818B-38D90CED39DB */ - /* Shutdown */ - { - .msg_type = HV_SHUTDOWN_MSG, - .data = { - 0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49, - 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB - }, - .log_msg = "Shutdown channel functionality initialized" - }, - - /* {9527E630-D0AE-497b-ADCE-E80AB0175CAF} */ - /* TimeSync */ - { - .msg_type = HV_TIMESYNC_MSG, - .data = { - 0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, - 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf - }, - .log_msg = "Timesync channel functionality initialized" - }, - /* {57164f39-9115-4e78-ab55-382f3bd5422d} */ - /* Heartbeat */ - { - .msg_type = HV_HEARTBEAT_MSG, - .data = { - 0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, - 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d - }, - .log_msg = "Heartbeat channel functionality initialized" - }, - /* {A9A0F4E7-5A45-4d96-B827-8A841E8C03E6} */ - /* KVP */ - { - .data = { - 0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, - 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6 - }, - .log_msg = "KVP channel functionality initialized" - }, -}; -EXPORT_SYMBOL(hv_cb_utils); - -/* - * alloc_channel - Allocate and initialize a vmbus channel object - */ -static struct vmbus_channel *alloc_channel(void) -{ - struct vmbus_channel *channel; - - channel = kzalloc(sizeof(*channel), GFP_ATOMIC); - if (!channel) - return NULL; - - spin_lock_init(&channel->inbound_lock); - - channel->controlwq = create_workqueue("hv_vmbus_ctl"); - if (!channel->controlwq) { - kfree(channel); - return NULL; - } - - return channel; -} - -/* - * release_hannel - Release the vmbus channel object itself - */ -static void release_channel(struct work_struct *work) -{ - struct vmbus_channel *channel = container_of(work, - struct vmbus_channel, - work); - - destroy_workqueue(channel->controlwq); - - kfree(channel); -} - -/* - * free_channel - Release the resources used by the vmbus channel object - */ -void free_channel(struct vmbus_channel *channel) -{ - - /* - * We have to release the channel's workqueue/thread in the vmbus's - * workqueue/thread context - * ie we can't destroy ourselves. - */ - INIT_WORK(&channel->work, release_channel); - queue_work(vmbus_connection.work_queue, &channel->work); -} - - - -/* - * vmbus_process_rescind_offer - - * Rescind the offer by initiating a device removal - */ -static void vmbus_process_rescind_offer(struct work_struct *work) -{ - struct vmbus_channel *channel = container_of(work, - struct vmbus_channel, - work); - - vmbus_child_device_unregister(channel->device_obj); -} - -/* - * vmbus_process_offer - Process the offer by creating a channel/device - * associated with this offer - */ -static void vmbus_process_offer(struct work_struct *work) -{ - struct vmbus_channel *newchannel = container_of(work, - struct vmbus_channel, - work); - struct vmbus_channel *channel; - bool fnew = true; - int ret; - int cnt; - unsigned long flags; - - /* The next possible work is rescind handling */ - INIT_WORK(&newchannel->work, vmbus_process_rescind_offer); - - /* Make sure this is a new offer */ - spin_lock_irqsave(&vmbus_connection.channel_lock, flags); - - list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { - if (!memcmp(&channel->offermsg.offer.if_type, - &newchannel->offermsg.offer.if_type, - sizeof(struct hv_guid)) && - !memcmp(&channel->offermsg.offer.if_instance, - &newchannel->offermsg.offer.if_instance, - sizeof(struct hv_guid))) { - fnew = false; - break; - } - } - - if (fnew) - list_add_tail(&newchannel->listentry, - &vmbus_connection.chn_list); - - spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); - - if (!fnew) { - free_channel(newchannel); - return; - } - - /* - * Start the process of binding this offer to the driver - * We need to set the DeviceObject field before calling - * vmbus_child_dev_add() - */ - newchannel->device_obj = vmbus_child_device_create( - &newchannel->offermsg.offer.if_type, - &newchannel->offermsg.offer.if_instance, - newchannel); - - /* - * Add the new device to the bus. This will kick off device-driver - * binding which eventually invokes the device driver's AddDevice() - * method. - */ - ret = vmbus_child_device_register(newchannel->device_obj); - if (ret != 0) { - pr_err("unable to add child device object (relid %d)\n", - newchannel->offermsg.child_relid); - - spin_lock_irqsave(&vmbus_connection.channel_lock, flags); - list_del(&newchannel->listentry); - spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); - - free_channel(newchannel); - } else { - /* - * This state is used to indicate a successful open - * so that when we do close the channel normally, we - * 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++) { - if (memcmp(&newchannel->offermsg.offer.if_type, - &hv_cb_utils[cnt].data, - sizeof(struct hv_guid)) == 0 && - vmbus_open(newchannel, 2 * PAGE_SIZE, - 2 * PAGE_SIZE, NULL, 0, - chn_cb_negotiate, - newchannel) == 0) { - hv_cb_utils[cnt].channel = newchannel; - newchannel->util_index = cnt; - - pr_info("%s\n", hv_cb_utils[cnt].log_msg); - - } - } - } -} - -/* - * vmbus_onoffer - Handler for channel offers from vmbus in parent partition. - * - * We ignore all offers except network and storage offers. For each network and - * storage offers, we create a channel object and queue a work item to the - * channel object to process the offer synchronously - */ -static void vmbus_onoffer(struct vmbus_channel_message_header *hdr) -{ - struct vmbus_channel_offer_channel *offer; - struct vmbus_channel *newchannel; - struct hv_guid *guidtype; - struct hv_guid *guidinstance; - int i; - int fsupported = 0; - - offer = (struct vmbus_channel_offer_channel *)hdr; - for (i = 0; i < MAX_NUM_DEVICE_CLASSES_SUPPORTED; i++) { - if (memcmp(&offer->offer.if_type, - &supported_device_classes[i], - sizeof(struct hv_guid)) == 0) { - fsupported = 1; - break; - } - } - - if (!fsupported) - return; - - guidtype = &offer->offer.if_type; - guidinstance = &offer->offer.if_instance; - - /* Allocate the channel object and save this offer. */ - newchannel = alloc_channel(); - if (!newchannel) { - pr_err("Unable to allocate channel object\n"); - return; - } - - memcpy(&newchannel->offermsg, offer, - sizeof(struct vmbus_channel_offer_channel)); - newchannel->monitor_grp = (u8)offer->monitorid / 32; - newchannel->monitor_bit = (u8)offer->monitorid % 32; - - INIT_WORK(&newchannel->work, vmbus_process_offer); - queue_work(newchannel->controlwq, &newchannel->work); -} - -/* - * vmbus_onoffer_rescind - Rescind offer handler. - * - * We queue a work item to process this offer synchronously - */ -static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) -{ - struct vmbus_channel_rescind_offer *rescind; - struct vmbus_channel *channel; - - rescind = (struct vmbus_channel_rescind_offer *)hdr; - channel = relid2channel(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 */ - queue_work(channel->controlwq, &channel->work); -} - -/* - * vmbus_onoffers_delivered - - * This is invoked when all offers have been delivered. - * - * Nothing to do here. - */ -static void vmbus_onoffers_delivered( - struct vmbus_channel_message_header *hdr) -{ -} - -/* - * vmbus_onopen_result - Open result handler. - * - * This is invoked when we received a response to our channel open request. - * Find the matching request, copy the response and signal the requesting - * thread. - */ -static void vmbus_onopen_result(struct vmbus_channel_message_header *hdr) -{ - struct vmbus_channel_open_result *result; - struct vmbus_channel_msginfo *msginfo; - struct vmbus_channel_message_header *requestheader; - struct vmbus_channel_open_channel *openmsg; - unsigned long flags; - - result = (struct vmbus_channel_open_result *)hdr; - - /* - * Find the open msg, copy the result and signal/unblock the wait event - */ - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - - list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, - msglistentry) { - requestheader = - (struct vmbus_channel_message_header *)msginfo->msg; - - if (requestheader->msgtype == CHANNELMSG_OPENCHANNEL) { - openmsg = - (struct vmbus_channel_open_channel *)msginfo->msg; - if (openmsg->child_relid == result->child_relid && - openmsg->openid == result->openid) { - memcpy(&msginfo->response.open_result, - result, - sizeof( - struct vmbus_channel_open_result)); - complete(&msginfo->waitevent); - break; - } - } - } - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); -} - -/* - * vmbus_ongpadl_created - GPADL created handler. - * - * This is invoked when we received a response to our gpadl create request. - * Find the matching request, copy the response and signal the requesting - * thread. - */ -static void vmbus_ongpadl_created(struct vmbus_channel_message_header *hdr) -{ - struct vmbus_channel_gpadl_created *gpadlcreated; - struct vmbus_channel_msginfo *msginfo; - struct vmbus_channel_message_header *requestheader; - struct vmbus_channel_gpadl_header *gpadlheader; - unsigned long flags; - - gpadlcreated = (struct vmbus_channel_gpadl_created *)hdr; - - /* - * Find the establish msg, copy the result and signal/unblock the wait - * event - */ - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - - list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, - msglistentry) { - requestheader = - (struct vmbus_channel_message_header *)msginfo->msg; - - if (requestheader->msgtype == CHANNELMSG_GPADL_HEADER) { - gpadlheader = - (struct vmbus_channel_gpadl_header *)requestheader; - - if ((gpadlcreated->child_relid == - gpadlheader->child_relid) && - (gpadlcreated->gpadl == gpadlheader->gpadl)) { - memcpy(&msginfo->response.gpadl_created, - gpadlcreated, - sizeof( - struct vmbus_channel_gpadl_created)); - complete(&msginfo->waitevent); - break; - } - } - } - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); -} - -/* - * vmbus_ongpadl_torndown - GPADL torndown handler. - * - * This is invoked when we received a response to our gpadl teardown request. - * Find the matching request, copy the response and signal the requesting - * thread. - */ -static void vmbus_ongpadl_torndown( - struct vmbus_channel_message_header *hdr) -{ - struct vmbus_channel_gpadl_torndown *gpadl_torndown; - struct vmbus_channel_msginfo *msginfo; - struct vmbus_channel_message_header *requestheader; - struct vmbus_channel_gpadl_teardown *gpadl_teardown; - unsigned long flags; - - gpadl_torndown = (struct vmbus_channel_gpadl_torndown *)hdr; - - /* - * Find the open msg, copy the result and signal/unblock the wait event - */ - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - - list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, - msglistentry) { - requestheader = - (struct vmbus_channel_message_header *)msginfo->msg; - - if (requestheader->msgtype == CHANNELMSG_GPADL_TEARDOWN) { - gpadl_teardown = - (struct vmbus_channel_gpadl_teardown *)requestheader; - - if (gpadl_torndown->gpadl == gpadl_teardown->gpadl) { - memcpy(&msginfo->response.gpadl_torndown, - gpadl_torndown, - sizeof( - struct vmbus_channel_gpadl_torndown)); - complete(&msginfo->waitevent); - break; - } - } - } - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); -} - -/* - * vmbus_onversion_response - Version response handler - * - * This is invoked when we received a response to our initiate contact request. - * Find the matching request, copy the response and signal the requesting - * thread. - */ -static void vmbus_onversion_response( - struct vmbus_channel_message_header *hdr) -{ - struct vmbus_channel_msginfo *msginfo; - struct vmbus_channel_message_header *requestheader; - struct vmbus_channel_initiate_contact *initiate; - struct vmbus_channel_version_response *version_response; - unsigned long flags; - - version_response = (struct vmbus_channel_version_response *)hdr; - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - - list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, - msglistentry) { - requestheader = - (struct vmbus_channel_message_header *)msginfo->msg; - - if (requestheader->msgtype == - CHANNELMSG_INITIATE_CONTACT) { - initiate = - (struct vmbus_channel_initiate_contact *)requestheader; - memcpy(&msginfo->response.version_response, - version_response, - sizeof(struct vmbus_channel_version_response)); - complete(&msginfo->waitevent); - } - } - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); -} - -/* Channel message dispatch table */ -static struct vmbus_channel_message_table_entry - channel_message_table[CHANNELMSG_COUNT] = { - {CHANNELMSG_INVALID, NULL}, - {CHANNELMSG_OFFERCHANNEL, vmbus_onoffer}, - {CHANNELMSG_RESCIND_CHANNELOFFER, vmbus_onoffer_rescind}, - {CHANNELMSG_REQUESTOFFERS, NULL}, - {CHANNELMSG_ALLOFFERS_DELIVERED, vmbus_onoffers_delivered}, - {CHANNELMSG_OPENCHANNEL, NULL}, - {CHANNELMSG_OPENCHANNEL_RESULT, vmbus_onopen_result}, - {CHANNELMSG_CLOSECHANNEL, NULL}, - {CHANNELMSG_GPADL_HEADER, NULL}, - {CHANNELMSG_GPADL_BODY, NULL}, - {CHANNELMSG_GPADL_CREATED, vmbus_ongpadl_created}, - {CHANNELMSG_GPADL_TEARDOWN, NULL}, - {CHANNELMSG_GPADL_TORNDOWN, vmbus_ongpadl_torndown}, - {CHANNELMSG_RELID_RELEASED, NULL}, - {CHANNELMSG_INITIATE_CONTACT, NULL}, - {CHANNELMSG_VERSION_RESPONSE, vmbus_onversion_response}, - {CHANNELMSG_UNLOAD, NULL}, -}; - -/* - * vmbus_onmessage - Handler for channel protocol messages. - * - * This is invoked in the vmbus worker thread context. - */ -void vmbus_onmessage(void *context) -{ - struct hv_message *msg = context; - struct vmbus_channel_message_header *hdr; - int size; - - hdr = (struct vmbus_channel_message_header *)msg->u.payload; - size = msg->header.payload_size; - - if (hdr->msgtype >= CHANNELMSG_COUNT) { - 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 (channel_message_table[hdr->msgtype].message_handler) - channel_message_table[hdr->msgtype].message_handler(hdr); - else - pr_err("Unhandled channel message type %d\n", hdr->msgtype); -} - -/* - * vmbus_request_offers - Send a request to get all our pending offers. - */ -int vmbus_request_offers(void) -{ - struct vmbus_channel_message_header *msg; - struct vmbus_channel_msginfo *msginfo; - int ret, t; - - msginfo = kmalloc(sizeof(*msginfo) + - sizeof(struct vmbus_channel_message_header), - GFP_KERNEL); - if (!msginfo) - return -ENOMEM; - - init_completion(&msginfo->waitevent); - - msg = (struct vmbus_channel_message_header *)msginfo->msg; - - msg->msgtype = CHANNELMSG_REQUESTOFFERS; - - - ret = vmbus_post_msg(msg, - sizeof(struct vmbus_channel_message_header)); - if (ret != 0) { - pr_err("Unable to request offers - %d\n", ret); - - goto cleanup; - } - - t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); - if (t == 0) { - ret = -ETIMEDOUT; - goto cleanup; - } - - - -cleanup: - kfree(msginfo); - - return ret; -} - -/* eof */ diff --git a/drivers/staging/hv/connection.c b/drivers/staging/hv/connection.c deleted file mode 100644 index e6b4039..0000000 --- a/drivers/staging/hv/connection.c +++ /dev/null @@ -1,290 +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> - * - */ -#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 "hyperv.h" -#include "hyperv_vmbus.h" - - -struct vmbus_connection vmbus_connection = { - .conn_state = DISCONNECTED, - .next_gpadl_handle = ATOMIC_INIT(0xE1E10), -}; - -/* - * vmbus_connect - Sends a connect request on the partition service 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; - - /* Make sure we are not connecting or connected */ - if (vmbus_connection.conn_state != DISCONNECTED) - return -EISCONN; - - /* Initialize the vmbus connection */ - vmbus_connection.conn_state = CONNECTING; - vmbus_connection.work_queue = create_workqueue("hv_vmbus_con"); - if (!vmbus_connection.work_queue) { - ret = -ENOMEM; - goto cleanup; - } - - INIT_LIST_HEAD(&vmbus_connection.chn_msg_list); - spin_lock_init(&vmbus_connection.channelmsg_lock); - - INIT_LIST_HEAD(&vmbus_connection.chn_list); - spin_lock_init(&vmbus_connection.channel_lock); - - /* - * Setup the vmbus event connection for channel interrupt - * abstraction stuff - */ - vmbus_connection.int_page = - (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, 0); - if (vmbus_connection.int_page == NULL) { - ret = -ENOMEM; - goto cleanup; - } - - vmbus_connection.recv_int_page = vmbus_connection.int_page; - vmbus_connection.send_int_page = - (void *)((unsigned long)vmbus_connection.int_page + - (PAGE_SIZE >> 1)); - - /* - * Setup the monitor notification facility. The 1st page for - * parent->child and the 2nd page for child->parent - */ - vmbus_connection.monitor_pages = - (void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 1); - if (vmbus_connection.monitor_pages == NULL) { - ret = -ENOMEM; - goto cleanup; - } - - msginfo = kzalloc(sizeof(*msginfo) + - sizeof(struct vmbus_channel_initiate_contact), - GFP_KERNEL); - if (msginfo == NULL) { - ret = -ENOMEM; - goto cleanup; - } - - init_completion(&msginfo->waitevent); - - msg = (struct vmbus_channel_initiate_contact *)msginfo->msg; - - msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT; - msg->vmbus_version_requested = VMBUS_REVISION_NUMBER; - msg->interrupt_page = virt_to_phys(vmbus_connection.int_page); - msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages); - msg->monitor_page2 = virt_to_phys( - (void *)((unsigned long)vmbus_connection.monitor_pages + - PAGE_SIZE)); - - /* - * Add to list before we send the request since we may - * receive the response before returning from this routine - */ - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - list_add_tail(&msginfo->msglistentry, - &vmbus_connection.chn_msg_list); - - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - - ret = vmbus_post_msg(msg, - sizeof(struct vmbus_channel_initiate_contact)); - if (ret != 0) { - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - list_del(&msginfo->msglistentry); - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, - flags); - goto cleanup; - } - - /* Wait for the connection response */ - t = wait_for_completion_timeout(&msginfo->waitevent, 5*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; - } - - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - list_del(&msginfo->msglistentry); - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - - /* Check if successful */ - if (msginfo->response.version_response.version_supported) { - vmbus_connection.conn_state = CONNECTED; - } else { - pr_err("Unable to connect, " - "Version %d not supported by Hyper-V\n", - VMBUS_REVISION_NUMBER); - ret = -ECONNREFUSED; - goto cleanup; - } - - kfree(msginfo); - return 0; - -cleanup: - vmbus_connection.conn_state = DISCONNECTED; - - if (vmbus_connection.work_queue) - destroy_workqueue(vmbus_connection.work_queue); - - if (vmbus_connection.int_page) { - free_pages((unsigned long)vmbus_connection.int_page, 0); - vmbus_connection.int_page = NULL; - } - - if (vmbus_connection.monitor_pages) { - free_pages((unsigned long)vmbus_connection.monitor_pages, 1); - vmbus_connection.monitor_pages = NULL; - } - - kfree(msginfo); - - return ret; -} - - -/* - * relid2channel - Get the channel object given its - * child relative id (ie channel id) - */ -struct vmbus_channel *relid2channel(u32 relid) -{ - struct vmbus_channel *channel; - struct vmbus_channel *found_channel = NULL; - unsigned long flags; - - spin_lock_irqsave(&vmbus_connection.channel_lock, flags); - list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { - if (channel->offermsg.child_relid == relid) { - found_channel = channel; - break; - } - } - spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); - - return found_channel; -} - -/* - * process_chn_event - Process a channel event notification - */ -static void process_chn_event(u32 relid) -{ - struct vmbus_channel *channel; - - /* ASSERT(relId > 0); */ - - /* - * Find the channel based on this relid and invokes the - * channel callback to process the event - */ - channel = relid2channel(relid); - - if (channel) { - channel->onchannel_callback(channel->channel_callback_context); - } else { - pr_err("channel not found for relid - %u\n", relid); - } -} - -/* - * vmbus_on_event - Handler for events - */ -void vmbus_on_event(unsigned long data) -{ - u32 dword; - u32 maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5; - int bit; - u32 relid; - u32 *recv_int_page = vmbus_connection.recv_int_page; - - /* Check events */ - 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); - } - } - } -} - -/* - * vmbus_post_msg - Send a msg on the vmbus's message connection - */ -int vmbus_post_msg(void *buffer, size_t buflen) -{ - union hv_connection_id conn_id; - - conn_id.asu32 = 0; - conn_id.u.id = VMBUS_MESSAGE_CONNECTION_ID; - return hv_post_message(conn_id, 1, buffer, buflen); -} - -/* - * vmbus_set_event - Send an event notification to the parent - */ -int vmbus_set_event(u32 child_relid) -{ - /* Each u32 represents 32 channels */ - sync_set_bit(child_relid & 31, - (unsigned long *)vmbus_connection.send_int_page + - (child_relid >> 5)); - - return hv_signal_event(); -} diff --git a/drivers/staging/hv/hv.c b/drivers/staging/hv/hv.c deleted file mode 100644 index 824f816..0000000 --- a/drivers/staging/hv/hv.c +++ /dev/null @@ -1,438 +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> - * - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/slab.h> -#include <linux/vmalloc.h> - -#include "hyperv.h" -#include "hyperv_vmbus.h" - -/* The one and only */ -struct hv_context hv_context = { - .synic_initialized = false, - .hypercall_page = NULL, - .signal_event_param = NULL, - .signal_event_buffer = NULL, -}; - -/* - * query_hypervisor_presence - * - Query the cpuid for presence of windows hypervisor - */ -static int query_hypervisor_presence(void) -{ - unsigned int eax; - unsigned int ebx; - unsigned int ecx; - unsigned int edx; - unsigned int op; - - eax = 0; - ebx = 0; - ecx = 0; - edx = 0; - op = HVCPUID_VERSION_FEATURES; - cpuid(op, &eax, &ebx, &ecx, &edx); - - return ecx & HV_PRESENT_BIT; -} - -/* - * query_hypervisor_info - Get version info of the windows hypervisor - */ -static int query_hypervisor_info(void) -{ - unsigned int eax; - unsigned int ebx; - unsigned int ecx; - unsigned int edx; - unsigned int max_leaf; - unsigned int op; - - /* - * Its assumed that this is called after confirming that Viridian - * is present. Query id and revision. - */ - eax = 0; - ebx = 0; - ecx = 0; - edx = 0; - op = HVCPUID_VENDOR_MAXFUNCTION; - cpuid(op, &eax, &ebx, &ecx, &edx); - - max_leaf = eax; - - if (max_leaf >= HVCPUID_VERSION) { - eax = 0; - ebx = 0; - ecx = 0; - edx = 0; - op = HVCPUID_VERSION; - cpuid(op, &eax, &ebx, &ecx, &edx); - pr_info("Hyper-V Host OS Build:%d-%d.%d-%d-%d.%d\n", - eax, - ebx >> 16, - ebx & 0xFFFF, - ecx, - edx >> 24, - edx & 0xFFFFFF); - } - return max_leaf; -} - -/* - * do_hypercall- Invoke the specified hypercall - */ -static u64 do_hypercall(u64 control, void *input, void *output) -{ -#ifdef CONFIG_X86_64 - u64 hv_status = 0; - u64 input_address = (input) ? virt_to_phys(input) : 0; - u64 output_address = (output) ? virt_to_phys(output) : 0; - volatile void *hypercall_page = hv_context.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)); - - return hv_status; - -#else - - u32 control_hi = control >> 32; - u32 control_lo = control & 0xFFFFFFFF; - u32 hv_status_hi = 1; - u32 hv_status_lo = 1; - u64 input_address = (input) ? virt_to_phys(input) : 0; - u32 input_address_hi = input_address >> 32; - u32 input_address_lo = input_address & 0xFFFFFFFF; - u64 output_address = (output) ? virt_to_phys(output) : 0; - u32 output_address_hi = output_address >> 32; - u32 output_address_lo = output_address & 0xFFFFFFFF; - volatile void *hypercall_page = hv_context.hypercall_page; - - __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)); - - return hv_status_lo | ((u64)hv_status_hi << 32); -#endif /* !x86_64 */ -} - -/* - * hv_init - Main initialization routine. - * - * This routine must be called before any other routines in here are called - */ -int hv_init(void) -{ - int ret = 0; - int max_leaf; - union hv_x64_msr_hypercall_contents hypercall_msr; - void *virtaddr = NULL; - - memset(hv_context.synic_event_page, 0, sizeof(void *) * MAX_NUM_CPUS); - memset(hv_context.synic_message_page, 0, - sizeof(void *) * MAX_NUM_CPUS); - - if (!query_hypervisor_presence()) - goto cleanup; - - max_leaf = query_hypervisor_info(); - /* HvQueryHypervisorFeatures(maxLeaf); */ - - /* - * We only support running on top of Hyper-V - */ - rdmsrl(HV_X64_MSR_GUEST_OS_ID, hv_context.guestid); - - if (hv_context.guestid != 0) - goto cleanup; - - /* Write our OS info */ - wrmsrl(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID); - hv_context.guestid = HV_LINUX_GUEST_ID; - - /* See if the hypercall page is already set */ - rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); - - /* - * Allocate the hypercall page memory - * virtaddr = osd_page_alloc(1); - */ - virtaddr = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_EXEC); - - if (!virtaddr) - goto cleanup; - - hypercall_msr.enable = 1; - - hypercall_msr.guest_physical_address = vmalloc_to_pfn(virtaddr); - wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); - - /* Confirm that hypercall page did get setup. */ - hypercall_msr.as_uint64 = 0; - rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); - - if (!hypercall_msr.enable) - goto cleanup; - - hv_context.hypercall_page = virtaddr; - - /* 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; - - hv_context.signal_event_param = - (struct hv_input_signal_event *) - (ALIGN((unsigned long) - hv_context.signal_event_buffer, - HV_HYPERCALL_PARAM_ALIGN)); - hv_context.signal_event_param->connectionid.asu32 = 0; - hv_context.signal_event_param->connectionid.u.id = - VMBUS_EVENT_CONNECTION_ID; - hv_context.signal_event_param->flag_number = 0; - hv_context.signal_event_param->rsvdz = 0; - - return ret; - -cleanup: - if (virtaddr) { - if (hypercall_msr.enable) { - hypercall_msr.as_uint64 = 0; - wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); - } - - vfree(virtaddr); - } - ret = -1; - return ret; -} - -/* - * hv_cleanup - Cleanup routine. - * - * This routine is called normally during driver unloading or exiting. - */ -void hv_cleanup(void) -{ - union hv_x64_msr_hypercall_contents hypercall_msr; - - kfree(hv_context.signal_event_buffer); - hv_context.signal_event_buffer = NULL; - hv_context.signal_event_param = NULL; - - if (hv_context.hypercall_page) { - hypercall_msr.as_uint64 = 0; - wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); - vfree(hv_context.hypercall_page); - hv_context.hypercall_page = NULL; - } -} - -/* - * hv_post_message - Post a message using the hypervisor message IPC. - * - * This involves a hypercall. - */ -u16 hv_post_message(union hv_connection_id connection_id, - enum hv_message_type message_type, - void *payload, size_t payload_size) -{ - struct aligned_input { - u64 alignment8; - struct hv_input_post_message msg; - }; - - struct hv_input_post_message *aligned_msg; - u16 status; - unsigned long addr; - - if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT) - return -EMSGSIZE; - - addr = (unsigned long)kmalloc(sizeof(struct aligned_input), GFP_ATOMIC); - if (!addr) - return -ENOMEM; - - aligned_msg = (struct hv_input_post_message *) - (ALIGN(addr, HV_HYPERCALL_PARAM_ALIGN)); - - aligned_msg->connectionid = connection_id; - aligned_msg->message_type = message_type; - aligned_msg->payload_size = payload_size; - memcpy((void *)aligned_msg->payload, payload, payload_size); - - status = do_hypercall(HVCALL_POST_MESSAGE, aligned_msg, NULL) - & 0xFFFF; - - kfree((void *)addr); - - return status; -} - - -/* - * hv_signal_event - - * Signal an event on the specified connection using the hypervisor event IPC. - * - * This involves a hypercall. - */ -u16 hv_signal_event(void) -{ - u16 status; - - status = do_hypercall(HVCALL_SIGNAL_EVENT, - hv_context.signal_event_param, - NULL) & 0xFFFF; - return status; -} - -/* - * hv_synic_init - Initialize the Synthethic Interrupt Controller. - * - * If it is already initialized by another entity (ie x2v shim), we need to - * retrieve the initialized message and event pages. Otherwise, we create and - * initialize the message and event pages. - */ -void hv_synic_init(void *irqarg) -{ - u64 version; - union hv_synic_simp simp; - union hv_synic_siefp siefp; - union hv_synic_sint shared_sint; - union hv_synic_scontrol sctrl; - - u32 irq_vector = *((u32 *)(irqarg)); - int cpu = smp_processor_id(); - - if (!hv_context.hypercall_page) - return; - - /* Check the version */ - rdmsrl(HV_X64_MSR_SVERSION, version); - - hv_context.synic_message_page[cpu] = - (void *)get_zeroed_page(GFP_ATOMIC); - - if (hv_context.synic_message_page[cpu] == NULL) { - 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) { - pr_err("Unable to allocate SYNIC event page\n"); - goto cleanup; - } - - /* Setup the Synic's message page */ - rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64); - simp.simp_enabled = 1; - simp.base_simp_gpa = virt_to_phys(hv_context.synic_message_page[cpu]) - >> PAGE_SHIFT; - - wrmsrl(HV_X64_MSR_SIMP, simp.as_uint64); - - /* Setup the Synic's event page */ - rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); - siefp.siefp_enabled = 1; - siefp.base_siefp_gpa = virt_to_phys(hv_context.synic_event_page[cpu]) - >> PAGE_SHIFT; - - wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); - - /* Setup the shared SINT. */ - rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); - - shared_sint.as_uint64 = 0; - shared_sint.vector = irq_vector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */ - shared_sint.masked = false; - shared_sint.auto_eoi = true; - - wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); - - /* Enable the global synic bit */ - rdmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); - sctrl.enable = 1; - - wrmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); - - hv_context.synic_initialized = true; - return; - -cleanup: - if (hv_context.synic_event_page[cpu]) - free_page((unsigned long)hv_context.synic_event_page[cpu]); - - if (hv_context.synic_message_page[cpu]) - free_page((unsigned long)hv_context.synic_message_page[cpu]); - return; -} - -/* - * hv_synic_cleanup - Cleanup routine for hv_synic_init(). - */ -void hv_synic_cleanup(void *arg) -{ - union hv_synic_sint shared_sint; - union hv_synic_simp simp; - union hv_synic_siefp siefp; - int cpu = smp_processor_id(); - - if (!hv_context.synic_initialized) - return; - - rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); - - shared_sint.masked = 1; - - /* Need to correctly cleanup in the case of SMP!!! */ - /* Disable the interrupt */ - wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); - - rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64); - simp.simp_enabled = 0; - simp.base_simp_gpa = 0; - - wrmsrl(HV_X64_MSR_SIMP, simp.as_uint64); - - rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); - siefp.siefp_enabled = 0; - siefp.base_siefp_gpa = 0; - - wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); - - free_page((unsigned long)hv_context.synic_message_page[cpu]); - free_page((unsigned long)hv_context.synic_event_page[cpu]); -} diff --git a/drivers/staging/hv/hv_kvp.c b/drivers/staging/hv/hv_kvp.c deleted file mode 100644 index 13b0ecf..0000000 --- a/drivers/staging/hv/hv_kvp.c +++ /dev/null @@ -1,334 +0,0 @@ -/* - * An implementation of key value pair (KVP) functionality for Linux. - * - * - * Copyright (C) 2010, Novell, Inc. - * Author : K. Y. Srinivasan <ksrinivasan@novell.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. 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., 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 "hyperv.h" -#include "hv_kvp.h" - - - -/* - * Global state maintained for transaction that is being processed. - * Note that only one transaction can be active at any point in time. - * - * This state is set when we receive a request from the host; we - * cleanup this state when the transaction is completed - when we respond - * to the host with the key value. - */ - -static struct { - bool active; /* transaction status - active or not */ - int recv_len; /* number of bytes received. */ - struct vmbus_channel *recv_channel; /* chn we got the request */ - u64 recv_req_id; /* request ID. */ -} kvp_transaction; - -static int kvp_send_key(int index); - -static void kvp_respond_to_host(char *key, char *value, int error); -static void kvp_work_func(struct work_struct *dummy); -static void kvp_register(void); - -static DECLARE_DELAYED_WORK(kvp_work, kvp_work_func); - -static struct cb_id kvp_id = { CN_KVP_IDX, CN_KVP_VAL }; -static const char kvp_name[] = "kvp_kernel_module"; -static int timeout_fired; -static u8 *recv_buffer; -/* - * Register the kernel component with the user-level daemon. - * As part of this registration, pass the LIC version number. - */ - -static void -kvp_register(void) -{ - - struct cn_msg *msg; - - msg = kzalloc(sizeof(*msg) + strlen(HV_DRV_VERSION) + 1 , GFP_ATOMIC); - - if (msg) { - msg->id.idx = CN_KVP_IDX; - msg->id.val = CN_KVP_VAL; - msg->seq = KVP_REGISTER; - strcpy(msg->data, HV_DRV_VERSION); - msg->len = strlen(HV_DRV_VERSION) + 1; - cn_netlink_send(msg, 0, GFP_ATOMIC); - kfree(msg); - } -} -static void -kvp_work_func(struct work_struct *dummy) -{ - /* - * If the timer fires, the user-mode component has not responded; - * process the pending transaction. - */ - kvp_respond_to_host("Unknown key", "Guest timed out", timeout_fired); - timeout_fired = 1; -} - -/* - * Callback when data is received from user mode. - */ - -static void -kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) -{ - struct hv_ku_msg *message; - - message = (struct hv_ku_msg *)msg->data; - if (msg->seq == KVP_REGISTER) { - pr_info("KVP: user-mode registering done.\n"); - kvp_register(); - } - - if (msg->seq == KVP_USER_SET) { - /* - * Complete the transaction by forwarding the key value - * to the host. But first, cancel the timeout. - */ - if (cancel_delayed_work_sync(&kvp_work)) - kvp_respond_to_host(message->kvp_key, - message->kvp_value, - !strlen(message->kvp_key)); - } -} - -static int -kvp_send_key(int index) -{ - struct cn_msg *msg; - - msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC); - - if (msg) { - msg->id.idx = CN_KVP_IDX; - msg->id.val = CN_KVP_VAL; - msg->seq = KVP_KERNEL_GET; - ((struct hv_ku_msg *)msg->data)->kvp_index = index; - msg->len = sizeof(struct hv_ku_msg); - cn_netlink_send(msg, 0, GFP_ATOMIC); - kfree(msg); - return 0; - } - return 1; -} - -/* - * Send a response back to the host. - */ - -static void -kvp_respond_to_host(char *key, char *value, int error) -{ - struct hv_kvp_msg *kvp_msg; - struct hv_kvp_msg_enumerate *kvp_data; - char *key_name; - struct icmsg_hdr *icmsghdrp; - int keylen, valuelen; - u32 buf_len; - struct vmbus_channel *channel; - u64 req_id; - - /* - * If a transaction is not active; log and return. - */ - - if (!kvp_transaction.active) { - /* - * This is a spurious call! - */ - pr_warn("KVP: Transaction not active\n"); - return; - } - /* - * Copy the global state for completing the transaction. Note that - * only one transaction can be active at a time. - */ - - buf_len = kvp_transaction.recv_len; - channel = kvp_transaction.recv_channel; - req_id = kvp_transaction.recv_req_id; - - icmsghdrp = (struct icmsg_hdr *) - &recv_buffer[sizeof(struct vmbuspipe_hdr)]; - kvp_msg = (struct hv_kvp_msg *) - &recv_buffer[sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; - kvp_data = &kvp_msg->kvp_data; - key_name = key; - - /* - * If the error parameter is set, terminate the host's enumeration. - */ - if (error) { - /* - * We don't support this index or the we have timedout; - * terminate the host-side iteration by returning an error. - */ - icmsghdrp->status = HV_E_FAIL; - goto response_done; - } - - /* - * The windows host expects the key/value pair to be encoded - * in utf16. - */ - keylen = utf8s_to_utf16s(key_name, strlen(key_name), - (wchar_t *)kvp_data->data.key); - kvp_data->data.key_size = 2*(keylen + 1); /* utf16 encoding */ - valuelen = utf8s_to_utf16s(value, strlen(value), - (wchar_t *)kvp_data->data.value); - kvp_data->data.value_size = 2*(valuelen + 1); /* utf16 encoding */ - - kvp_data->data.value_type = REG_SZ; /* all our values are strings */ - icmsghdrp->status = HV_S_OK; - -response_done: - icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; - - vmbus_sendpacket(channel, recv_buffer, buf_len, req_id, - VM_PKT_DATA_INBAND, 0); - - kvp_transaction.active = false; -} - -/* - * This callback is invoked when we get a KVP message from the host. - * The host ensures that only one KVP transaction can be active at a time. - * KVP implementation in Linux needs to forward the key to a user-mde - * component to retrive the corresponding value. Consequently, we cannot - * respond to the host in the conext of this callback. Since the host - * guarantees that at most only one transaction can be active at a time, - * we stash away the transaction state in a set of global variables. - */ - -void hv_kvp_onchannelcallback(void *context) -{ - struct vmbus_channel *channel = context; - u32 recvlen; - u64 requestid; - - struct hv_kvp_msg *kvp_msg; - struct hv_kvp_msg_enumerate *kvp_data; - - struct icmsg_hdr *icmsghdrp; - struct icmsg_negotiate *negop = NULL; - - - if (kvp_transaction.active) - return; - - - vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE, &recvlen, &requestid); - - if (recvlen > 0) { - icmsghdrp = (struct icmsg_hdr *)&recv_buffer[ - sizeof(struct vmbuspipe_hdr)]; - - if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - prep_negotiate_resp(icmsghdrp, negop, recv_buffer); - } else { - kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; - - kvp_data = &kvp_msg->kvp_data; - - /* - * We only support the "get" operation on - * "KVP_POOL_AUTO" pool. - */ - - if ((kvp_msg->kvp_hdr.pool != KVP_POOL_AUTO) || - (kvp_msg->kvp_hdr.operation != - KVP_OP_ENUMERATE)) { - icmsghdrp->status = HV_E_FAIL; - goto callback_done; - } - - /* - * Stash away this global state for completing the - * transaction; note transactions are serialized. - */ - kvp_transaction.recv_len = recvlen; - kvp_transaction.recv_channel = channel; - kvp_transaction.recv_req_id = requestid; - kvp_transaction.active = true; - - /* - * Get the information from the - * user-mode component. - * component. This transaction will be - * completed when we get the value from - * the user-mode component. - * Set a timeout to deal with - * user-mode not responding. - */ - kvp_send_key(kvp_data->index); - schedule_delayed_work(&kvp_work, 100); - - return; - - } - -callback_done: - - icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION - | ICMSGHDRFLAG_RESPONSE; - - vmbus_sendpacket(channel, recv_buffer, - recvlen, requestid, - VM_PKT_DATA_INBAND, 0); - } - -} - -int -hv_kvp_init(void) -{ - int err; - - err = cn_add_callback(&kvp_id, kvp_name, kvp_cn_callback); - if (err) - return err; - recv_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!recv_buffer) - return -ENOMEM; - - return 0; -} - -void hv_kvp_deinit(void) -{ - cn_del_callback(&kvp_id); - cancel_delayed_work_sync(&kvp_work); - kfree(recv_buffer); -} diff --git a/drivers/staging/hv/hv_kvp.h b/drivers/staging/hv/hv_kvp.h deleted file mode 100644 index 8c402f3..0000000 --- a/drivers/staging/hv/hv_kvp.h +++ /dev/null @@ -1,184 +0,0 @@ -/* - * An implementation of HyperV key value pair (KVP) functionality for Linux. - * - * - * Copyright (C) 2010, Novell, Inc. - * Author : K. Y. Srinivasan <ksrinivasan@novell.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ -#ifndef _KVP_H -#define _KVP_H_ - -/* - * Maximum value size - used for both key names and value data, and includes - * any applicable NULL terminators. - * - * Note: This limit is somewhat arbitrary, but falls easily within what is - * supported for all native guests (back to Win 2000) and what is reasonable - * for the IC KVP exchange functionality. Note that Windows Me/98/95 are - * limited to 255 character key names. - * - * MSDN recommends not storing data values larger than 2048 bytes in the - * registry. - * - * Note: This value is used in defining the KVP exchange message - this value - * cannot be modified without affecting the message size and compatibility. - */ - -/* - * bytes, including any null terminators - */ -#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE (2048) - - -/* - * Maximum key size - the registry limit for the length of an entry name - * is 256 characters, including the null terminator - */ - -#define HV_KVP_EXCHANGE_MAX_KEY_SIZE (512) - -/* - * In Linux, we implement the KVP functionality in two components: - * 1) The kernel component which is packaged as part of the hv_utils driver - * is responsible for communicating with the host and responsible for - * implementing the host/guest protocol. 2) A user level daemon that is - * responsible for data gathering. - * - * Host/Guest Protocol: The host iterates over an index and expects the guest - * to assign a key name to the index and also return the value corresponding to - * the key. The host will have atmost one KVP transaction outstanding at any - * given point in time. The host side iteration stops when the guest returns - * an error. Microsoft has specified the following mapping of key names to - * host specified index: - * - * Index Key Name - * 0 FullyQualifiedDomainName - * 1 IntegrationServicesVersion - * 2 NetworkAddressIPv4 - * 3 NetworkAddressIPv6 - * 4 OSBuildNumber - * 5 OSName - * 6 OSMajorVersion - * 7 OSMinorVersion - * 8 OSVersion - * 9 ProcessorArchitecture - * - * The Windows host expects the Key Name and Key Value to be encoded in utf16. - * - * Guest Kernel/KVP Daemon Protocol: As noted earlier, we implement all of the - * data gathering functionality in a user mode daemon. The user level daemon - * is also responsible for binding the key name to the index as well. The - * kernel and user-level daemon communicate using a connector channel. - * - * The user mode component first registers with the - * the kernel component. Subsequently, the kernel component requests, data - * for the specified keys. In response to this message the user mode component - * fills in the value corresponding to the specified key. We overload the - * sequence field in the cn_msg header to define our KVP message types. - * - * - * The kernel component simply acts as a conduit for communication between the - * Windows host and the user-level daemon. The kernel component passes up the - * index received from the Host to the user-level daemon. If the index is - * valid (supported), the corresponding key as well as its - * value (both are strings) is returned. If the index is invalid - * (not supported), a NULL key string is returned. - */ - -/* - * - * The following definitions are shared with the user-mode component; do not - * change any of this without making the corresponding changes in - * the KVP user-mode component. - */ - -#define CN_KVP_VAL 0x1 /* This supports queries from the kernel */ -#define CN_KVP_USER_VAL 0x2 /* This supports queries from the user */ - -enum hv_ku_op { - KVP_REGISTER = 0, /* Register the user mode component */ - KVP_KERNEL_GET, /* Kernel is requesting the value */ - KVP_KERNEL_SET, /* Kernel is providing the value */ - KVP_USER_GET, /* User is requesting the value */ - KVP_USER_SET /* User is providing the value */ -}; - -struct hv_ku_msg { - __u32 kvp_index; /* Key index */ - __u8 kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */ - __u8 kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key value */ -}; - - - - -#ifdef __KERNEL__ - -/* - * Registry value types. - */ - -#define REG_SZ 1 - -enum hv_kvp_exchg_op { - KVP_OP_GET = 0, - KVP_OP_SET, - KVP_OP_DELETE, - KVP_OP_ENUMERATE, - KVP_OP_COUNT /* Number of operations, must be last. */ -}; - -enum hv_kvp_exchg_pool { - KVP_POOL_EXTERNAL = 0, - KVP_POOL_GUEST, - KVP_POOL_AUTO, - KVP_POOL_AUTO_EXTERNAL, - KVP_POOL_AUTO_INTERNAL, - KVP_POOL_COUNT /* Number of pools, must be last. */ -}; - -struct hv_kvp_hdr { - u8 operation; - u8 pool; -}; - -struct hv_kvp_exchg_msg_value { - u32 value_type; - u32 key_size; - u32 value_size; - u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; - u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; -}; - -struct hv_kvp_msg_enumerate { - u32 index; - struct hv_kvp_exchg_msg_value data; -}; - -struct hv_kvp_msg { - struct hv_kvp_hdr kvp_hdr; - struct hv_kvp_msg_enumerate kvp_data; -}; - -int hv_kvp_init(void); -void hv_kvp_deinit(void); -void hv_kvp_onchannelcallback(void *); - -#endif /* __KERNEL__ */ -#endif /* _KVP_H */ - diff --git a/drivers/staging/hv/hv_mouse.c b/drivers/staging/hv/hv_mouse.c index d957fc2..ccd39c7 100644 --- a/drivers/staging/hv/hv_mouse.c +++ b/drivers/staging/hv/hv_mouse.c @@ -22,20 +22,15 @@ #include <linux/input.h> #include <linux/hid.h> #include <linux/hiddev.h> -#include <linux/pci.h> -#include <linux/dmi.h> +#include <linux/hyperv.h> -#include "hyperv.h" - -/* - * Data types - */ struct hv_input_dev_info { + unsigned int size; unsigned short vendor; unsigned short product; unsigned short version; - char name[128]; + unsigned short reserved[11]; }; /* The maximum size of a synthetic input message. */ @@ -54,17 +49,17 @@ struct hv_input_dev_info { (SYNTHHID_INPUT_VERSION_MAJOR << 16)) -#pragma pack(push,1) +#pragma pack(push, 1) /* * Message types in the synthetic input protocol */ enum synthhid_msg_type { - SynthHidProtocolRequest, - SynthHidProtocolResponse, - SynthHidInitialDeviceInfo, - SynthHidInitialDeviceInfoAck, - SynthHidInputReport, - SynthHidMax + SYNTH_HID_PROTOCOL_REQUEST, + SYNTH_HID_PROTOCOL_RESPONSE, + SYNTH_HID_INITIAL_DEVICE_INFO, + SYNTH_HID_INITIAL_DEVICE_INFO_ACK, + SYNTH_HID_INPUT_REPORT, + SYNTH_HID_MAX }; /* @@ -120,15 +115,15 @@ struct synthhid_input_report { #pragma pack(pop) -#define INPUTVSC_SEND_RING_BUFFER_SIZE 10*PAGE_SIZE -#define INPUTVSC_RECV_RING_BUFFER_SIZE 10*PAGE_SIZE +#define INPUTVSC_SEND_RING_BUFFER_SIZE (10*PAGE_SIZE) +#define INPUTVSC_RECV_RING_BUFFER_SIZE (10*PAGE_SIZE) #define NBITS(x) (((x)/BITS_PER_LONG)+1) enum pipe_prot_msg_type { - PipeMessageInvalid = 0, - PipeMessageData, - PipeMessageMaximum + PIPE_MESSAGE_INVALID, + PIPE_MESSAGE_DATA, + PIPE_MESSAGE_MAXIMUM }; @@ -138,9 +133,6 @@ struct pipe_prt_msg { char data[1]; }; -/* - * Data types - */ struct mousevsc_prt_msg { enum pipe_prot_msg_type type; u32 size; @@ -156,38 +148,22 @@ struct mousevsc_prt_msg { */ struct mousevsc_dev { struct hv_device *device; - /* 0 indicates the device is being destroyed */ - 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 protocol_wait_event; - wait_queue_head_t dev_info_wait_event; - int protocol_wait_condition; - int device_wait_condition; + struct completion wait_event; int dev_info_status; struct hid_descriptor *hid_desc; unsigned char *report_desc; u32 report_desc_size; struct hv_input_dev_info hid_dev_info; + int connected; + struct hid_device *hid_device; }; -static const char *driver_name = "mousevsc"; - -/* {CFA8B69E-5B4A-4cc0-B98B-8BA1A1F3F95A} */ -static const struct hv_guid mouse_guid = { - .data = {0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c, - 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A} -}; - -static void deviceinfo_callback(struct hv_device *dev, struct hv_input_dev_info *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 *alloc_input_device(struct hv_device *device) { struct mousevsc_dev *input_dev; @@ -197,130 +173,21 @@ static struct mousevsc_dev *alloc_input_device(struct hv_device *device) if (!input_dev) return NULL; - /* - * Set to 2 to allow both inbound and outbound traffics - * (ie get_input_device() and must_get_input_device()) to proceed. - */ - atomic_cmpxchg(&input_dev->ref_count, 0, 2); - input_dev->device = device; - device->ext = input_dev; + hv_set_drvdata(device, input_dev); + init_completion(&input_dev->wait_event); return input_dev; } static void free_input_device(struct mousevsc_dev *device) { - WARN_ON(atomic_read(&device->ref_count) == 0); + kfree(device->hid_desc); + kfree(device->report_desc); + hv_set_drvdata(device->device, NULL); kfree(device); } -/* - * Get the inputdevice object if exists and its refcount > 1 - */ -static struct mousevsc_dev *get_input_device(struct hv_device *device) -{ - struct mousevsc_dev *input_dev; - - input_dev = (struct mousevsc_dev *)device->ext; - -/* - * FIXME - * This sure isn't a valid thing to print for debugging, no matter - * what the intention is... - * - * printk(KERN_ERR "-------------------------> REFCOUNT = %d", - * input_dev->ref_count); - */ - - if (input_dev && atomic_read(&input_dev->ref_count) > 1) - atomic_inc(&input_dev->ref_count); - else - input_dev = NULL; - - return input_dev; -} - -/* - * Get the inputdevice object iff exists and its refcount > 0 - */ -static struct mousevsc_dev *must_get_input_device(struct hv_device *device) -{ - struct mousevsc_dev *input_dev; - - input_dev = (struct mousevsc_dev *)device->ext; - - if (input_dev && atomic_read(&input_dev->ref_count)) - atomic_inc(&input_dev->ref_count); - else - input_dev = NULL; - - return input_dev; -} - -static void put_input_device(struct hv_device *device) -{ - struct mousevsc_dev *input_dev; - - input_dev = (struct mousevsc_dev *)device->ext; - - atomic_dec(&input_dev->ref_count); -} - -/* - * Drop ref count to 1 to effectively disable get_input_device() - */ -static struct mousevsc_dev *release_input_device(struct hv_device *device) -{ - struct mousevsc_dev *input_dev; - - input_dev = (struct mousevsc_dev *)device->ext; - - /* Busy wait until the ref drop to 2, then set it to 1 */ - while (atomic_cmpxchg(&input_dev->ref_count, 2, 1) != 2) - udelay(100); - - return input_dev; -} - -/* - * Drop ref count to 0. No one can use input_device object. - */ -static struct mousevsc_dev *final_release_input_device(struct hv_device *device) -{ - struct mousevsc_dev *input_dev; - - input_dev = (struct mousevsc_dev *)device->ext; - - /* Busy wait until the ref drop to 1, then set it to 0 */ - while (atomic_cmpxchg(&input_dev->ref_count, 1, 0) != 1) - udelay(100); - - device->ext = NULL; - return input_dev; -} - -static void mousevsc_on_send_completion(struct hv_device *device, - struct vmpacket_descriptor *packet) -{ - struct mousevsc_dev *input_dev; - void *request; - - 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; - - if (request == &input_dev->protocol_req) { - /* FIXME */ - /* Shouldn't we be doing something here? */ - } - - put_input_device(device); -} static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, struct synthhid_device_info *device_info) @@ -332,33 +199,27 @@ static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, /* Assume success for now */ input_device->dev_info_status = 0; - /* Save the device attr */ memcpy(&input_device->hid_dev_info, &device_info->hid_dev_info, sizeof(struct hv_input_dev_info)); - /* Save the hid desc */ desc = &device_info->hid_descriptor; - WARN_ON(desc->bLength > 0); + WARN_ON(desc->bLength == 0); - input_device->hid_desc = kzalloc(desc->bLength, GFP_KERNEL); + input_device->hid_desc = kzalloc(desc->bLength, GFP_ATOMIC); - if (!input_device->hid_desc) { - pr_err("unable to allocate hid descriptor - size %d", desc->bLength); - goto Cleanup; - } + if (!input_device->hid_desc) + goto cleanup; memcpy(input_device->hid_desc, desc, desc->bLength); - /* Save the report desc */ input_device->report_desc_size = desc->desc[0].wDescriptorLength; + if (input_device->report_desc_size == 0) + goto cleanup; input_device->report_desc = kzalloc(input_device->report_desc_size, - GFP_KERNEL); + GFP_ATOMIC); - if (!input_device->report_desc) { - pr_err("unable to allocate report descriptor - size %d", - input_device->report_desc_size); - goto Cleanup; - } + if (!input_device->report_desc) + goto cleanup; memcpy(input_device->report_desc, ((unsigned char *)desc) + desc->bLength, @@ -367,10 +228,10 @@ static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, /* Send the ack */ memset(&ack, 0, sizeof(struct mousevsc_prt_msg)); - ack.type = PipeMessageData; + ack.type = PIPE_MESSAGE_DATA; ack.size = sizeof(struct synthhid_device_info_ack); - ack.ack.header.type = SynthHidInitialDeviceInfoAck; + ack.ack.header.type = SYNTH_HID_INITIAL_DEVICE_INFO_ACK; ack.ack.header.size = 1; ack.ack.reserved = 0; @@ -381,18 +242,14 @@ static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, (unsigned long)&ack, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (ret != 0) { - pr_err("unable to send synthhid device info ack - ret %d", - ret); - goto Cleanup; - } + if (ret != 0) + goto cleanup; - input_device->device_wait_condition = 1; - wake_up(&input_device->dev_info_wait_event); + complete(&input_device->wait_event); return; -Cleanup: +cleanup: kfree(input_device->hid_desc); input_device->hid_desc = NULL; @@ -400,25 +257,7 @@ Cleanup: input_device->report_desc = NULL; input_device->dev_info_status = -1; - input_device->device_wait_condition = 1; - wake_up(&input_device->dev_info_wait_event); -} - -static void mousevsc_on_receive_input_report(struct mousevsc_dev *input_device, - struct synthhid_input_report *input_report) -{ - struct hv_driver *input_drv; - - if (!input_device->init_complete) { - pr_info("Initialization incomplete...ignoring input_report msg"); - return; - } - - input_drv = drv_to_hv_drv(input_device->device->device.driver); - - inputreport_callback(input_device->device, - input_report->buffer, - input_report->header.size); + complete(&input_device->wait_event); } static void mousevsc_on_receive(struct hv_device *device, @@ -426,37 +265,27 @@ static void mousevsc_on_receive(struct hv_device *device, { struct pipe_prt_msg *pipe_msg; struct synthhid_msg *hid_msg; - struct mousevsc_dev *input_dev; - - input_dev = must_get_input_device(device); - if (!input_dev) { - pr_err("unable to get input device...device being destroyed?"); - return; - } + struct mousevsc_dev *input_dev = hv_get_drvdata(device); + struct synthhid_input_report *input_report; pipe_msg = (struct pipe_prt_msg *)((unsigned long)packet + (packet->offset8 << 3)); - if (pipe_msg->type != PipeMessageData) { - pr_err("unknown pipe msg type - type %d len %d", - pipe_msg->type, pipe_msg->size); - put_input_device(device); - return ; - } + if (pipe_msg->type != PIPE_MESSAGE_DATA) + return; hid_msg = (struct synthhid_msg *)&pipe_msg->data[0]; switch (hid_msg->header.type) { - case SynthHidProtocolResponse: + case SYNTH_HID_PROTOCOL_RESPONSE: memcpy(&input_dev->protocol_resp, pipe_msg, pipe_msg->size + sizeof(struct pipe_prt_msg) - sizeof(unsigned char)); - input_dev->protocol_wait_condition = 1; - wake_up(&input_dev->protocol_wait_event); + complete(&input_dev->wait_event); break; - case SynthHidInitialDeviceInfo: - WARN_ON(pipe_msg->size >= sizeof(struct hv_input_dev_info)); + case SYNTH_HID_INITIAL_DEVICE_INFO: + WARN_ON(pipe_msg->size < sizeof(struct hv_input_dev_info)); /* * Parse out the device info into device attr, @@ -465,10 +294,14 @@ static void mousevsc_on_receive(struct hv_device *device, mousevsc_on_receive_device_info(input_dev, (struct synthhid_device_info *)&pipe_msg->data[0]); break; - case SynthHidInputReport: - mousevsc_on_receive_input_report(input_dev, - (struct synthhid_input_report *)&pipe_msg->data[0]); - + case SYNTH_HID_INPUT_REPORT: + input_report = + (struct synthhid_input_report *)&pipe_msg->data[0]; + if (!input_dev->init_complete) + break; + hid_input_report(input_dev->hid_device, + HID_INPUT_REPORT, input_report->buffer, + input_report->header.size, 1); break; default: pr_err("unsupported hid msg type - type %d len %d", @@ -476,7 +309,6 @@ static void mousevsc_on_receive(struct hv_device *device, break; } - put_input_device(device); } static void mousevsc_on_channel_callback(void *context) @@ -484,7 +316,6 @@ 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 *input_dev; u32 bytes_recvd; u64 req_id; @@ -493,12 +324,6 @@ static void mousevsc_on_channel_callback(void *context) unsigned char *buffer = packet; int bufferlen = packetSize; - input_dev = must_get_input_device(device); - - if (!input_dev) { - pr_err("unable to get input device...device being destroyed?"); - return; - } do { ret = vmbus_recvpacket_raw(device->channel, buffer, @@ -509,22 +334,20 @@ static void mousevsc_on_channel_callback(void *context) desc = (struct vmpacket_descriptor *)buffer; switch (desc->type) { - case VM_PKT_COMP: - mousevsc_on_send_completion( - device, desc); - break; - - case VM_PKT_DATA_INBAND: - mousevsc_on_receive( - device, desc); - break; - - default: - pr_err("unhandled packet type %d, tid %llx len %d\n", - desc->type, - req_id, - bytes_recvd); - break; + case VM_PKT_COMP: + break; + + case VM_PKT_DATA_INBAND: + mousevsc_on_receive( + device, desc); + break; + + default: + pr_err("unhandled packet type %d, tid %llx len %d\n", + desc->type, + req_id, + bytes_recvd); + break; } /* reset */ @@ -535,10 +358,6 @@ static void mousevsc_on_channel_callback(void *context) bufferlen = packetSize; } } else { - /* - * pr_debug("nothing else to read..."); - * reset - */ if (bufferlen > packetSize) { kfree(buffer); @@ -547,79 +366,57 @@ static void mousevsc_on_channel_callback(void *context) } break; } - } else if (ret == -2) { + } else if (ret == -ENOBUFS) { /* Handle large packet */ bufferlen = bytes_recvd; - buffer = kzalloc(bytes_recvd, GFP_KERNEL); + buffer = kzalloc(bytes_recvd, GFP_ATOMIC); if (buffer == NULL) { buffer = packet; bufferlen = packetSize; - - /* Try again next time around */ - pr_err("unable to allocate buffer of size %d!", - bytes_recvd); break; } } } while (1); - put_input_device(device); - return; } static int mousevsc_connect_to_vsp(struct hv_device *device) { int ret = 0; - struct mousevsc_dev *input_dev; + int t; + struct mousevsc_dev *input_dev = hv_get_drvdata(device); struct mousevsc_prt_msg *request; struct mousevsc_prt_msg *response; - input_dev = get_input_device(device); - - if (!input_dev) { - pr_err("unable to get input device...device being destroyed?"); - return -1; - } - - init_waitqueue_head(&input_dev->protocol_wait_event); - init_waitqueue_head(&input_dev->dev_info_wait_event); request = &input_dev->protocol_req; - /* - * Now, initiate the vsc/vsp initialization protocol on the open channel - */ memset(request, 0, sizeof(struct mousevsc_prt_msg)); - request->type = PipeMessageData; + request->type = PIPE_MESSAGE_DATA; request->size = sizeof(struct synthhid_protocol_request); - request->request.header.type = SynthHidProtocolRequest; - request->request.header.size = sizeof(unsigned long); + request->request.header.type = SYNTH_HID_PROTOCOL_REQUEST; + request->request.header.size = sizeof(unsigned int); request->request.version_requested.version = SYNTHHID_INPUT_VERSION; - pr_info("synthhid protocol request..."); ret = vmbus_sendpacket(device->channel, request, - sizeof(struct pipe_prt_msg) - - sizeof(unsigned char) + - sizeof(struct synthhid_protocol_request), - (unsigned long)request, - VM_PKT_DATA_INBAND, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (ret != 0) { - pr_err("unable to send synthhid protocol request."); - goto Cleanup; - } - - 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) { + sizeof(struct pipe_prt_msg) - + sizeof(unsigned char) + + sizeof(struct synthhid_protocol_request), + (unsigned long)request, + VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (ret != 0) + goto cleanup; + + t = wait_for_completion_timeout(&input_dev->wait_event, 5*HZ); + if (t == 0) { ret = -ETIMEDOUT; - goto Cleanup; + goto cleanup; } response = &input_dev->protocol_resp; @@ -627,51 +424,88 @@ static int mousevsc_connect_to_vsp(struct hv_device *device) if (!response->response.approved) { pr_err("synthhid protocol request failed (version %d)", SYNTHHID_INPUT_VERSION); - ret = -1; - goto Cleanup; + ret = -ENODEV; + goto cleanup; } - 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) { + t = wait_for_completion_timeout(&input_dev->wait_event, 5*HZ); + if (t == 0) { ret = -ETIMEDOUT; - goto Cleanup; + goto cleanup; } /* * We should have gotten the device attr, hid desc and report * desc at this point */ - if (!input_dev->dev_info_status) - pr_info("**** input channel up and running!! ****"); - else - ret = -1; + if (input_dev->dev_info_status) + ret = -ENOMEM; -Cleanup: - put_input_device(device); +cleanup: return ret; } -static int mousevsc_on_device_add(struct hv_device *device, - void *additional_info) +static int mousevsc_hid_open(struct hid_device *hid) +{ + return 0; +} + +static void mousevsc_hid_close(struct hid_device *hid) +{ +} + +static struct hid_ll_driver mousevsc_ll_driver = { + .open = mousevsc_hid_open, + .close = mousevsc_hid_close, +}; + +static struct hid_driver mousevsc_hid_driver; + +static void reportdesc_callback(struct hv_device *dev, void *packet, u32 len) +{ + struct hid_device *hid_dev; + struct mousevsc_dev *input_device = hv_get_drvdata(dev); + + hid_dev = hid_allocate_device(); + if (IS_ERR(hid_dev)) + return; + + hid_dev->ll_driver = &mousevsc_ll_driver; + hid_dev->driver = &mousevsc_hid_driver; + + if (hid_parse_report(hid_dev, packet, len)) + return; + + hid_dev->bus = BUS_VIRTUAL; + hid_dev->vendor = input_device->hid_dev_info.vendor; + hid_dev->product = input_device->hid_dev_info.product; + hid_dev->version = input_device->hid_dev_info.version; + + sprintf(hid_dev->name, "%s", "Microsoft Vmbus HID-compliant Mouse"); + + if (!hidinput_connect(hid_dev, 0)) { + hid_dev->claimed |= HID_CLAIMED_INPUT; + + input_device->connected = 1; + + } + + input_device->hid_device = hid_dev; +} + +static int mousevsc_on_device_add(struct hv_device *device) { int ret = 0; struct mousevsc_dev *input_dev; - struct hv_driver *input_drv; - struct hv_input_dev_info dev_info; input_dev = alloc_input_device(device); - if (!input_dev) { - ret = -1; - goto Cleanup; - } + if (!input_dev) + return -ENOMEM; input_dev->init_complete = false; - /* Open the channel */ ret = vmbus_open(device->channel, INPUTVSC_SEND_RING_BUFFER_SIZE, INPUTVSC_RECV_RING_BUFFER_SIZE, @@ -682,34 +516,20 @@ static int mousevsc_on_device_add(struct hv_device *device, ); if (ret != 0) { - pr_err("unable to open channel: %d", ret); free_input_device(input_dev); - return -1; + return ret; } - pr_info("InputVsc channel open: %d", ret); ret = mousevsc_connect_to_vsp(device); if (ret != 0) { - pr_err("unable to connect channel: %d", ret); - vmbus_close(device->channel); free_input_device(input_dev); return ret; } - input_drv = drv_to_hv_drv(input_dev->device->device.driver); - - 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); - - /* Send the report desc back up */ /* workaround SA-167 */ if (input_dev->report_desc[14] == 0x25) input_dev->report_desc[14] = 0x29; @@ -719,256 +539,61 @@ static int mousevsc_on_device_add(struct hv_device *device, input_dev->init_complete = true; -Cleanup: return ret; } -static int mousevsc_on_device_remove(struct hv_device *device) -{ - struct mousevsc_dev *input_dev; - int ret = 0; - - pr_info("disabling input device (%p)...", - device->ext); - - input_dev = release_input_device(device); - - - /* - * At this point, all outbound traffic should be disable. We only - * allow inbound traffic (responses) to proceed - * - * so that outstanding requests can be completed. - */ - 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); - - input_dev = final_release_input_device(device); - - pr_info("input device (%p) safe to remove", input_dev); - - /* Close the channel */ - vmbus_close(device->channel); - - free_input_device(input_dev); - - return ret; -} - - -/* - * Data types - */ -struct input_device_context { - struct hv_device *device_ctx; - struct hid_device *hid_device; - struct hv_input_dev_info device_info; - int connected; -}; - - -static void deviceinfo_callback(struct hv_device *dev, struct hv_input_dev_info *info) -{ - struct input_device_context *input_device_ctx = - dev_get_drvdata(&dev->device); - - memcpy(&input_device_ctx->device_info, info, - sizeof(struct hv_input_dev_info)); - - DPRINT_INFO(INPUTVSC_DRV, "%s", __func__); -} - -static void inputreport_callback(struct hv_device *dev, void *packet, u32 len) -{ - int ret = 0; - - struct input_device_context *input_dev_ctx = - dev_get_drvdata(&dev->device); - - ret = hid_input_report(input_dev_ctx->hid_device, - HID_INPUT_REPORT, packet, len, 1); - - DPRINT_DBG(INPUTVSC_DRV, "hid_input_report (ret %d)", ret); -} - -static int mousevsc_hid_open(struct hid_device *hid) -{ - return 0; -} - -static void mousevsc_hid_close(struct hid_device *hid) -{ -} - -static int mousevsc_probe(struct hv_device *dev) +static int mousevsc_probe(struct hv_device *dev, + const struct hv_vmbus_device_id *dev_id) { - int ret = 0; - - struct input_device_context *input_dev_ctx; - - input_dev_ctx = kmalloc(sizeof(struct input_device_context), - GFP_KERNEL); - - dev_set_drvdata(&dev->device, input_dev_ctx); - - /* Call to the vsc driver to add the device */ - ret = mousevsc_on_device_add(dev, NULL); - - if (ret != 0) { - DPRINT_ERR(INPUTVSC_DRV, "unable to add input vsc device"); - return -1; - } + return mousevsc_on_device_add(dev); - return 0; } static int mousevsc_remove(struct hv_device *dev) { - int ret = 0; - - struct input_device_context *input_dev_ctx; - - input_dev_ctx = kmalloc(sizeof(struct input_device_context), - GFP_KERNEL); - - 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; - } + struct mousevsc_dev *input_dev = hv_get_drvdata(dev); - /* - * Call to the vsc driver to let it know that the device - * is being removed - */ - ret = mousevsc_on_device_remove(dev); + vmbus_close(dev->channel); - if (ret != 0) { - DPRINT_ERR(INPUTVSC_DRV, - "unable to remove vsc device (ret %d)", ret); + if (input_dev->connected) { + hidinput_disconnect(input_dev->hid_device); + input_dev->connected = 0; + hid_destroy_device(input_dev->hid_device); } - kfree(input_dev_ctx); + free_input_device(input_dev); - return ret; + return 0; } -static void reportdesc_callback(struct hv_device *dev, void *packet, u32 len) -{ - struct input_device_context *input_device_ctx = - dev_get_drvdata(&dev->device); - struct hid_device *hid_dev; - - /* hid_debug = -1; */ - hid_dev = kmalloc(sizeof(struct hid_device), GFP_KERNEL); - - if (hid_parse_report(hid_dev, packet, len)) { - DPRINT_INFO(INPUTVSC_DRV, "Unable to call hd_parse_report"); - return; - } - - if (hid_dev) { - DPRINT_INFO(INPUTVSC_DRV, "hid_device created"); - - hid_dev->ll_driver->open = mousevsc_hid_open; - hid_dev->ll_driver->close = mousevsc_hid_close; - - hid_dev->bus = BUS_VIRTUAL; - hid_dev->vendor = input_device_ctx->device_info.vendor; - hid_dev->product = input_device_ctx->device_info.product; - hid_dev->version = input_device_ctx->device_info.version; - hid_dev->dev = dev->device; - - sprintf(hid_dev->name, "%s", - input_device_ctx->device_info.name); - - /* - * HJ Do we want to call it with a 0 - */ - if (!hidinput_connect(hid_dev, 0)) { - hid_dev->claimed |= HID_CLAIMED_INPUT; - - input_device_ctx->connected = 1; - - DPRINT_INFO(INPUTVSC_DRV, - "HID device claimed by input\n"); - } - - if (!hid_dev->claimed) { - DPRINT_ERR(INPUTVSC_DRV, - "HID device not claimed by " - "input or hiddev\n"); - } - - input_device_ctx->hid_device = hid_dev; - } - - kfree(hid_dev); -} +static const struct hv_vmbus_device_id id_table[] = { + /* Mouse guid */ + { VMBUS_DEVICE(0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c, + 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A) }, + { }, +}; +MODULE_DEVICE_TABLE(vmbus, id_table); static struct hv_driver mousevsc_drv = { + .name = "mousevsc", + .id_table = id_table, .probe = mousevsc_probe, .remove = mousevsc_remove, }; -static void mousevsc_drv_exit(void) -{ - vmbus_child_driver_unregister(&mousevsc_drv.driver); -} - static int __init mousevsc_init(void) { - struct hv_driver *drv = &mousevsc_drv; - - DPRINT_INFO(INPUTVSC_DRV, "Hyper-V Mouse driver initializing."); - - memcpy(&drv->dev_type, &mouse_guid, - sizeof(struct hv_guid)); - - drv->driver.name = driver_name; - - /* The driver belongs to vmbus */ - vmbus_child_driver_register(&drv->driver); - - return 0; + return vmbus_driver_register(&mousevsc_drv); } static void __exit mousevsc_exit(void) { - mousevsc_drv_exit(); + vmbus_driver_unregister(&mousevsc_drv); } -/* - * We don't want to automatically load this driver just yet, it's quite - * broken. It's safe if you want to load it yourself manually, but - * don't inflict it on unsuspecting users, that's just mean. - */ -#if 0 - -/* - * We use a PCI table to determine if we should autoload this driver This is - * needed by distro tools to determine if the hyperv drivers should be - * installed and/or configured. We don't do anything else with the table, but - * it needs to be present. - */ -const static struct pci_device_id microsoft_hv_pci_table[] = { - { PCI_DEVICE(0x1414, 0x5353) }, /* VGA compatible controller */ - { 0 } -}; -MODULE_DEVICE_TABLE(pci, microsoft_hv_pci_table); -#endif - MODULE_LICENSE("GPL"); MODULE_VERSION(HV_DRV_VERSION); module_init(mousevsc_init); module_exit(mousevsc_exit); - diff --git a/drivers/staging/hv/hv_util.c b/drivers/staging/hv/hv_util.c deleted file mode 100644 index c164b54..0000000 --- a/drivers/staging/hv/hv_util.c +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (c) 2010, 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> - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/sysctl.h> -#include <linux/reboot.h> -#include <linux/dmi.h> -#include <linux/pci.h> - -#include "hyperv.h" -#include "hv_kvp.h" - -static u8 *shut_txf_buf; -static u8 *time_txf_buf; -static u8 *hbeat_txf_buf; - -static void shutdown_onchannelcallback(void *context) -{ - struct vmbus_channel *channel = context; - u32 recvlen; - u64 requestid; - u8 execute_shutdown = false; - - struct shutdown_msg_data *shutdown_msg; - - struct icmsg_hdr *icmsghdrp; - struct icmsg_negotiate *negop = NULL; - - vmbus_recvpacket(channel, shut_txf_buf, - PAGE_SIZE, &recvlen, &requestid); - - if (recvlen > 0) { - icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[ - sizeof(struct vmbuspipe_hdr)]; - - if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - prep_negotiate_resp(icmsghdrp, negop, shut_txf_buf); - } else { - shutdown_msg = - (struct shutdown_msg_data *)&shut_txf_buf[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; - - switch (shutdown_msg->flags) { - case 0: - case 1: - icmsghdrp->status = HV_S_OK; - execute_shutdown = true; - - pr_info("Shutdown request received -" - " graceful shutdown initiated\n"); - break; - default: - icmsghdrp->status = HV_E_FAIL; - execute_shutdown = false; - - pr_info("Shutdown request received -" - " Invalid request\n"); - break; - } - } - - icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION - | ICMSGHDRFLAG_RESPONSE; - - vmbus_sendpacket(channel, shut_txf_buf, - recvlen, requestid, - VM_PKT_DATA_INBAND, 0); - } - - if (execute_shutdown == true) - orderly_poweroff(false); -} - -/* - * Set guest time to host UTC time. - */ -static inline void do_adj_guesttime(u64 hosttime) -{ - s64 host_tns; - struct timespec host_ts; - - host_tns = (hosttime - WLTIMEDELTA) * 100; - host_ts = ns_to_timespec(host_tns); - - do_settimeofday(&host_ts); -} - -/* - * Synchronize time with host after reboot, restore, etc. - * - * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM. - * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time - * message after the timesync channel is opened. Since the hv_utils module is - * loaded after hv_vmbus, the first message is usually missed. The other - * thing is, systime is automatically set to emulated hardware clock which may - * not be UTC time or in the same time zone. So, to override these effects, we - * use the first 50 time samples for initial system time setting. - */ -static inline void adj_guesttime(u64 hosttime, u8 flags) -{ - static s32 scnt = 50; - - if ((flags & ICTIMESYNCFLAG_SYNC) != 0) { - do_adj_guesttime(hosttime); - return; - } - - if ((flags & ICTIMESYNCFLAG_SAMPLE) != 0 && scnt > 0) { - scnt--; - do_adj_guesttime(hosttime); - } -} - -/* - * Time Sync Channel message handler. - */ -static void timesync_onchannelcallback(void *context) -{ - struct vmbus_channel *channel = context; - u32 recvlen; - u64 requestid; - struct icmsg_hdr *icmsghdrp; - struct ictimesync_data *timedatap; - - vmbus_recvpacket(channel, time_txf_buf, - PAGE_SIZE, &recvlen, &requestid); - - if (recvlen > 0) { - icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[ - sizeof(struct vmbuspipe_hdr)]; - - if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - prep_negotiate_resp(icmsghdrp, NULL, time_txf_buf); - } else { - timedatap = (struct ictimesync_data *)&time_txf_buf[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; - adj_guesttime(timedatap->parenttime, timedatap->flags); - } - - icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION - | ICMSGHDRFLAG_RESPONSE; - - vmbus_sendpacket(channel, time_txf_buf, - recvlen, requestid, - VM_PKT_DATA_INBAND, 0); - } -} - -/* - * Heartbeat functionality. - * Every two seconds, Hyper-V send us a heartbeat request message. - * we respond to this message, and Hyper-V knows we are alive. - */ -static void heartbeat_onchannelcallback(void *context) -{ - struct vmbus_channel *channel = context; - u32 recvlen; - u64 requestid; - struct icmsg_hdr *icmsghdrp; - struct heartbeat_msg_data *heartbeat_msg; - - vmbus_recvpacket(channel, hbeat_txf_buf, - PAGE_SIZE, &recvlen, &requestid); - - if (recvlen > 0) { - icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[ - sizeof(struct vmbuspipe_hdr)]; - - if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - prep_negotiate_resp(icmsghdrp, NULL, hbeat_txf_buf); - } else { - heartbeat_msg = - (struct heartbeat_msg_data *)&hbeat_txf_buf[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; - - heartbeat_msg->seq_num += 1; - } - - icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION - | ICMSGHDRFLAG_RESPONSE; - - vmbus_sendpacket(channel, hbeat_txf_buf, - recvlen, requestid, - VM_PKT_DATA_INBAND, 0); - } -} - -static const struct pci_device_id __initconst -hv_utils_pci_table[] __maybe_unused = { - { PCI_DEVICE(0x1414, 0x5353) }, /* Hyper-V emulated VGA controller */ - { 0 } -}; -MODULE_DEVICE_TABLE(pci, hv_utils_pci_table); - - -static const struct dmi_system_id __initconst -hv_utils_dmi_table[] __maybe_unused = { - { - .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, hv_utils_dmi_table); - - -static int __init init_hyperv_utils(void) -{ - pr_info("Registering HyperV Utility Driver\n"); - - if (hv_kvp_init()) - return -ENODEV; - - - if (!dmi_check_system(hv_utils_dmi_table)) - return -ENODEV; - - shut_txf_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); - time_txf_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); - hbeat_txf_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); - - if (!shut_txf_buf || !time_txf_buf || !hbeat_txf_buf) { - 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].callback = &shutdown_onchannelcallback; - - hv_cb_utils[HV_TIMESYNC_MSG].callback = ×ync_onchannelcallback; - - hv_cb_utils[HV_HEARTBEAT_MSG].callback = &heartbeat_onchannelcallback; - - hv_cb_utils[HV_KVP_MSG].callback = &hv_kvp_onchannelcallback; - - return 0; -} - -static void exit_hyperv_utils(void) -{ - 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; - - 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; - - 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; - - 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_kvp_deinit(); - - kfree(shut_txf_buf); - kfree(time_txf_buf); - kfree(hbeat_txf_buf); -} - -module_init(init_hyperv_utils); -module_exit(exit_hyperv_utils); - -MODULE_DESCRIPTION("Hyper-V Utilities"); -MODULE_VERSION(HV_DRV_VERSION); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/hv/hyperv.h b/drivers/staging/hv/hyperv.h deleted file mode 100644 index 1747a24..0000000 --- a/drivers/staging/hv/hyperv.h +++ /dev/null @@ -1,948 +0,0 @@ -/* - * - * 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_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]; -}; - -struct vmbus_close_msg { - struct vmbus_channel_msginfo info; - struct vmbus_channel_close_channel msg; -}; - -struct vmbus_channel { - struct list_head listentry; - - struct hv_device *device_obj; - - 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; - - struct vmbus_close_msg close_msg; - - /* Channel callback are invoked in this workqueue context */ - /* HANDLE dataWorkQueue; */ - - void (*onchannel_callback)(void *context); - void *channel_callback_context; -}; - -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_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/hyperv_net.h b/drivers/staging/hv/hyperv_net.h index 27f987b..ac1ec84 100644 --- a/drivers/staging/hv/hyperv_net.h +++ b/drivers/staging/hv/hyperv_net.h @@ -26,7 +26,7 @@ #define _HYPERV_NET_H #include <linux/list.h> -#include "hyperv.h" +#include <linux/hyperv.h> /* Fwd declaration */ struct hv_netvsc_packet; @@ -96,7 +96,6 @@ 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_filter_device_add(struct hv_device *dev, @@ -370,8 +369,8 @@ struct nvsp_message { struct netvsc_device { struct hv_device *dev; - atomic_t refcnt; atomic_t num_outstanding_sends; + bool destroy; /* * List of free preallocated hv_netvsc_packet to represent receive * packet @@ -393,6 +392,8 @@ struct netvsc_device { struct nvsp_message revoke_packet; /* unsigned char HwMacAddr[HW_MACADDR_LEN]; */ + struct net_device *ndev; + /* Holds rndis device info */ void *extension; }; diff --git a/drivers/staging/hv/hyperv_storage.h b/drivers/staging/hv/hyperv_storage.h deleted file mode 100644 index a01f9a0..0000000 --- a/drivers/staging/hv/hyperv_storage.h +++ /dev/null @@ -1,334 +0,0 @@ -/* - * - * 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_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 { \ - char *revision_string \ - = REVISION_STRING($Rev : 6 $) + 6; \ - RESULT_LVALUE_ = 0; \ - while (*revision_string >= '0' \ - && *revision_string <= '9') { \ - RESULT_LVALUE_ *= 10; \ - RESULT_LVALUE_ += *revision_string - '0'; \ - revision_string++; \ - } \ - } while (0) - -/* Major/minor macros. Minor version is in LSB, meaning that earlier flat */ -/* version numbers will be interpreted as "0.x" (i.e., 1 becomes 0.1). */ -#define VMSTOR_PROTOCOL_MAJOR(VERSION_) (((VERSION_) >> 8) & 0xff) -#define VMSTOR_PROTOCOL_MINOR(VERSION_) (((VERSION_)) & 0xff) -#define VMSTOR_PROTOCOL_VERSION(MAJOR_, MINOR_) ((((MAJOR_) & 0xff) << 8) | \ - (((MINOR_) & 0xff))) -#define VMSTOR_INVALID_PROTOCOL_VERSION (-1) - -/* Version history: */ -/* V1 Beta 0.1 */ -/* V1 RC < 2008/1/31 1.0 */ -/* V1 RC > 2008/1/31 2.0 */ -#define VMSTOR_PROTOCOL_VERSION_CURRENT VMSTOR_PROTOCOL_VERSION(2, 0) - - - - -/* This will get replaced with the max transfer length that is possible on */ -/* the host adapter. */ -/* The max transfer length will be published when we offer a vmbus channel. */ -#define MAX_TRANSFER_LENGTH 0x40000 -#define DEFAULT_PACKET_SIZE (sizeof(struct vmdata_gpa_direct) + \ - sizeof(struct vstor_packet) + \ - sizesizeof(u64) * (MAX_TRANSFER_LENGTH / PAGE_SIZE))) - - -/* Packet structure describing virtual storage requests. */ -enum vstor_packet_operation { - VSTOR_OPERATION_COMPLETE_IO = 1, - VSTOR_OPERATION_REMOVE_DEVICE = 2, - VSTOR_OPERATION_EXECUTE_SRB = 3, - VSTOR_OPERATION_RESET_LUN = 4, - VSTOR_OPERATION_RESET_ADAPTER = 5, - VSTOR_OPERATION_RESET_BUS = 6, - VSTOR_OPERATION_BEGIN_INITIALIZATION = 7, - VSTOR_OPERATION_END_INITIALIZATION = 8, - VSTOR_OPERATION_QUERY_PROTOCOL_VERSION = 9, - VSTOR_OPERATION_QUERY_PROPERTIES = 10, - VSTOR_OPERATION_MAXIMUM = 10 -}; - -/* - * Platform neutral description of a scsi request - - * this remains the same across the write regardless of 32/64 bit - * note: it's patterned off the SCSI_PASS_THROUGH structure - */ -#define CDB16GENERIC_LENGTH 0x10 - -#ifndef SENSE_BUFFER_SIZE -#define SENSE_BUFFER_SIZE 0x12 -#endif - -#define MAX_DATA_BUF_LEN_WITH_PADDING 0x14 - -struct vmscsi_request { - unsigned short length; - unsigned char srb_status; - unsigned char scsi_status; - - unsigned char port_number; - unsigned char path_id; - unsigned char target_id; - unsigned char lun; - - unsigned char cdb_length; - unsigned char sense_info_length; - unsigned char data_in; - unsigned char reserved; - - unsigned int data_transfer_length; - - union { - unsigned char cdb[CDB16GENERIC_LENGTH]; - unsigned char sense_data[SENSE_BUFFER_SIZE]; - unsigned char reserved_array[MAX_DATA_BUF_LEN_WITH_PADDING]; - }; -} __attribute((packed)); - - -/* - * This structure is sent during the intialization phase to get the different - * properties of the channel. - */ -struct vmstorage_channel_properties { - unsigned short protocol_version; - unsigned char path_id; - unsigned char target_id; - - /* Note: port number is only really known on the client side */ - unsigned int port_number; - unsigned int flags; - unsigned int max_transfer_bytes; - - /* This id is unique for each channel and will correspond with */ - /* vendor specific data in the inquirydata */ - unsigned long long unique_id; -} __packed; - -/* This structure is sent during the storage protocol negotiations. */ -struct vmstorage_protocol_version { - /* Major (MSW) and minor (LSW) version numbers. */ - unsigned short major_minor; - - /* - * Revision number is auto-incremented whenever this file is changed - * (See FILL_VMSTOR_REVISION macro above). Mismatch does not - * definitely indicate incompatibility--but it does indicate mismatched - * builds. - */ - unsigned short revision; -} __packed; - -/* Channel Property Flags */ -#define STORAGE_CHANNEL_REMOVABLE_FLAG 0x1 -#define STORAGE_CHANNEL_EMULATED_IDE_FLAG 0x2 - -struct vstor_packet { - /* Requested operation type */ - enum vstor_packet_operation operation; - - /* Flags - see below for values */ - unsigned int flags; - - /* Status of the request returned from the server side. */ - unsigned int status; - - /* Data payload area */ - union { - /* - * Structure used to forward SCSI commands from the - * client to the server. - */ - struct vmscsi_request vm_srb; - - /* Structure used to query channel properties. */ - struct vmstorage_channel_properties storage_channel_properties; - - /* Used during version negotiations. */ - struct vmstorage_protocol_version version; - }; -} __packed; - -/* Packet flags */ -/* - * This flag indicates that the server should send back a completion for this - * packet. - */ -#define REQUEST_COMPLETION_FLAG 0x1 - -/* 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 deleted file mode 100644 index 349ad80..0000000 --- a/drivers/staging/hv/hyperv_vmbus.h +++ /dev/null @@ -1,629 +0,0 @@ -/* - * - * 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_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/netvsc.c b/drivers/staging/hv/netvsc.c index dc5e5c4..b902579 100644 --- a/drivers/staging/hv/netvsc.c +++ b/drivers/staging/hv/netvsc.c @@ -27,122 +27,63 @@ #include <linux/delay.h> #include <linux/io.h> #include <linux/slab.h> +#include <linux/netdevice.h> -#include "hyperv.h" #include "hyperv_net.h" -/* Globals */ -static const char *driver_name = "netvsc"; - -/* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */ -static const struct hv_guid netvsc_device_type = { - .data = { - 0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, - 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E - } -}; - - static struct netvsc_device *alloc_net_device(struct hv_device *device) { struct netvsc_device *net_device; + struct net_device *ndev = hv_get_drvdata(device); net_device = kzalloc(sizeof(struct netvsc_device), GFP_KERNEL); if (!net_device) return NULL; - /* Set to 2 to allow both inbound and outbound traffic */ - atomic_cmpxchg(&net_device->refcnt, 0, 2); + net_device->destroy = false; net_device->dev = device; - device->ext = net_device; + net_device->ndev = ndev; + hv_set_drvdata(device, net_device); return net_device; } -static void free_net_device(struct netvsc_device *device) -{ - WARN_ON(atomic_read(&device->refcnt) != 0); - device->dev->ext = NULL; - kfree(device); -} - - -/* Get the net device object iff exists and its refcount > 1 */ static struct netvsc_device *get_outbound_net_device(struct hv_device *device) { struct netvsc_device *net_device; - net_device = device->ext; - if (net_device && atomic_read(&net_device->refcnt) > 1) - atomic_inc(&net_device->refcnt); - else + net_device = hv_get_drvdata(device); + if (net_device && net_device->destroy) net_device = NULL; return net_device; } -/* Get the net device object iff exists and its refcount > 0 */ static struct netvsc_device *get_inbound_net_device(struct hv_device *device) { struct netvsc_device *net_device; - net_device = device->ext; - if (net_device && atomic_read(&net_device->refcnt)) - atomic_inc(&net_device->refcnt); - else - net_device = NULL; - - return net_device; -} - -static void put_net_device(struct hv_device *device) -{ - struct netvsc_device *net_device; - - net_device = device->ext; - - atomic_dec(&net_device->refcnt); -} - -static struct netvsc_device *release_outbound_net_device( - struct hv_device *device) -{ - struct netvsc_device *net_device; + net_device = hv_get_drvdata(device); - net_device = device->ext; - if (net_device == NULL) - return NULL; + if (!net_device) + goto get_in_err; - /* Busy wait until the ref drop to 2, then set it to 1 */ - while (atomic_cmpxchg(&net_device->refcnt, 2, 1) != 2) - udelay(100); + if (net_device->destroy && + atomic_read(&net_device->num_outstanding_sends) == 0) + net_device = NULL; +get_in_err: return net_device; } -static struct netvsc_device *release_inbound_net_device( - struct hv_device *device) -{ - struct netvsc_device *net_device; - - net_device = device->ext; - if (net_device == NULL) - return NULL; - - /* Busy wait until the ref drop to 1, then set it to 0 */ - while (atomic_cmpxchg(&net_device->refcnt, 1, 0) != 1) - udelay(100); - - device->ext = NULL; - return net_device; -} static int netvsc_destroy_recv_buf(struct netvsc_device *net_device) { struct nvsp_message *revoke_packet; int ret = 0; + struct net_device *ndev = net_device->ndev; /* * If we got a section count, it means we received a @@ -170,9 +111,9 @@ static int netvsc_destroy_recv_buf(struct netvsc_device *net_device) * 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; + netdev_err(ndev, "unable to send " + "revoke receive buffer to netvsp\n"); + return ret; } } @@ -185,9 +126,9 @@ static int netvsc_destroy_recv_buf(struct netvsc_device *net_device) * rather than continue and a bugchk */ if (ret != 0) { - dev_err(&net_device->dev->device, - "unable to teardown receive buffer's gpadl"); - return -1; + netdev_err(ndev, + "unable to teardown receive buffer's gpadl\n"); + return ret; } net_device->recv_buf_gpadl_handle = 0; } @@ -214,21 +155,20 @@ static int netvsc_init_recv_buf(struct hv_device *device) int t; struct netvsc_device *net_device; struct nvsp_message *init_packet; + struct net_device *ndev; net_device = get_outbound_net_device(device); - if (!net_device) { - dev_err(&device->device, "unable to get net device..." - "device being destroyed?"); - return -1; - } + if (!net_device) + return -ENODEV; + ndev = net_device->ndev; net_device->recv_buf = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, get_order(net_device->recv_buf_size)); if (!net_device->recv_buf) { - dev_err(&device->device, "unable to allocate receive " - "buffer of size %d", net_device->recv_buf_size); - ret = -1; + netdev_err(ndev, "unable to allocate receive " + "buffer of size %d\n", net_device->recv_buf_size); + ret = -ENOMEM; goto cleanup; } @@ -241,8 +181,8 @@ static int netvsc_init_recv_buf(struct hv_device *device) net_device->recv_buf_size, &net_device->recv_buf_gpadl_handle); if (ret != 0) { - dev_err(&device->device, - "unable to establish receive buffer's gpadl"); + netdev_err(ndev, + "unable to establish receive buffer's gpadl\n"); goto cleanup; } @@ -265,8 +205,8 @@ static int netvsc_init_recv_buf(struct hv_device *device) VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); if (ret != 0) { - dev_err(&device->device, - "unable to send receive buffer's gpadl to netvsp"); + netdev_err(ndev, + "unable to send receive buffer's gpadl to netvsp\n"); goto cleanup; } @@ -277,11 +217,11 @@ static int netvsc_init_recv_buf(struct hv_device *device) /* Check the response */ if (init_packet->msg.v1_msg. send_recv_buf_complete.status != NVSP_STAT_SUCCESS) { - dev_err(&device->device, "Unable to complete receive buffer " - "initialzation with NetVsp - status %d", + netdev_err(ndev, "Unable to complete receive buffer " + "initialization with NetVsp - status %d\n", init_packet->msg.v1_msg. send_recv_buf_complete.status); - ret = -1; + ret = -EINVAL; goto cleanup; } @@ -293,7 +233,7 @@ static int netvsc_init_recv_buf(struct hv_device *device) net_device->recv_section = kmalloc(net_device->recv_section_cnt * sizeof(struct nvsp_1_receive_buffer_section), GFP_KERNEL); if (net_device->recv_section == NULL) { - ret = -1; + ret = -EINVAL; goto cleanup; } @@ -309,7 +249,7 @@ static int netvsc_init_recv_buf(struct hv_device *device) */ if (net_device->recv_section_cnt != 1 || net_device->recv_section->offset != 0) { - ret = -1; + ret = -EINVAL; goto cleanup; } @@ -319,7 +259,6 @@ cleanup: netvsc_destroy_recv_buf(net_device); exit: - put_net_device(device); return ret; } @@ -330,13 +269,12 @@ static int netvsc_connect_vsp(struct hv_device *device) struct netvsc_device *net_device; struct nvsp_message *init_packet; int ndis_version; + struct net_device *ndev; net_device = get_outbound_net_device(device); - if (!net_device) { - dev_err(&device->device, "unable to get net device..." - "device being destroyed?"); - return -1; - } + if (!net_device) + return -ENODEV; + ndev = net_device->ndev; init_packet = &net_device->channel_init_pkt; @@ -366,13 +304,13 @@ static int netvsc_connect_vsp(struct hv_device *device) if (init_packet->msg.init_msg.init_complete.status != NVSP_STAT_SUCCESS) { - ret = -1; + ret = -EINVAL; goto cleanup; } if (init_packet->msg.init_msg.init_complete. negotiated_protocol_ver != NVSP_PROTOCOL_VERSION_1) { - ret = -1; + ret = -EPROTO; goto cleanup; } /* Send the ndis version */ @@ -393,16 +331,13 @@ static int netvsc_connect_vsp(struct hv_device *device) sizeof(struct nvsp_message), (unsigned long)init_packet, VM_PKT_DATA_INBAND, 0); - if (ret != 0) { - ret = -1; + if (ret != 0) goto cleanup; - } /* Post the big receive buffer to NetVSP */ ret = netvsc_init_recv_buf(device); cleanup: - put_net_device(device); return ret; } @@ -418,29 +353,40 @@ int netvsc_device_remove(struct hv_device *device) { struct netvsc_device *net_device; struct hv_netvsc_packet *netvsc_packet, *pos; + unsigned long flags; - /* Stop outbound traffic ie sends and receives completions */ - net_device = release_outbound_net_device(device); - if (!net_device) { - dev_err(&device->device, "No net device present!!"); - return -1; - } + net_device = hv_get_drvdata(device); + spin_lock_irqsave(&device->channel->inbound_lock, flags); + net_device->destroy = true; + spin_unlock_irqrestore(&device->channel->inbound_lock, flags); /* Wait for all send completions */ while (atomic_read(&net_device->num_outstanding_sends)) { - dev_err(&device->device, - "waiting for %d requests to complete...", + dev_info(&device->device, + "waiting for %d requests to complete...\n", atomic_read(&net_device->num_outstanding_sends)); udelay(100); } netvsc_disconnect_vsp(net_device); - /* Stop inbound traffic ie receives and sends completions */ - net_device = release_inbound_net_device(device); + /* + * Since we have already drained, we don't need to busy wait + * as was done in final_release_stor_device() + * Note that we cannot set the ext pointer to NULL until + * we have drained - to drain the outgoing packets, we need to + * allow incoming packets. + */ + + spin_lock_irqsave(&device->channel->inbound_lock, flags); + hv_set_drvdata(device, NULL); + spin_unlock_irqrestore(&device->channel->inbound_lock, flags); - /* At this point, no one should be accessing netDevice except in here */ - dev_notice(&device->device, "net device safe to remove"); + /* + * At this point, no one should be accessing net_device + * except in here + */ + dev_notice(&device->device, "net device safe to remove\n"); /* Now, we can close the channel safely */ vmbus_close(device->channel); @@ -452,7 +398,7 @@ int netvsc_device_remove(struct hv_device *device) kfree(netvsc_packet); } - free_net_device(net_device); + kfree(net_device); return 0; } @@ -462,13 +408,12 @@ static void netvsc_send_completion(struct hv_device *device, struct netvsc_device *net_device; struct nvsp_message *nvsp_packet; struct hv_netvsc_packet *nvsc_packet; + struct net_device *ndev; net_device = get_inbound_net_device(device); - if (!net_device) { - dev_err(&device->device, "unable to get net device..." - "device being destroyed?"); + if (!net_device) return; - } + ndev = net_device->ndev; nvsp_packet = (struct nvsp_message *)((unsigned long)packet + (packet->offset8 << 3)); @@ -494,11 +439,10 @@ static void netvsc_send_completion(struct hv_device *device, atomic_dec(&net_device->num_outstanding_sends); } else { - dev_err(&device->device, "Unknown send completion packet type- " - "%d received!!", nvsp_packet->hdr.msg_type); + netdev_err(ndev, "Unknown send completion packet type- " + "%d received!!\n", nvsp_packet->hdr.msg_type); } - put_net_device(device); } int netvsc_send(struct hv_device *device, @@ -506,15 +450,13 @@ int netvsc_send(struct hv_device *device, { struct netvsc_device *net_device; int ret = 0; - struct nvsp_message sendMessage; + struct net_device *ndev; net_device = get_outbound_net_device(device); - if (!net_device) { - dev_err(&device->device, "net device (%p) shutting down..." - "ignoring outbound packets", net_device); - return -2; - } + if (!net_device) + return -ENODEV; + ndev = net_device->ndev; sendMessage.hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT; if (packet->is_data_pkt) { @@ -547,11 +489,11 @@ int netvsc_send(struct hv_device *device, } if (ret != 0) - dev_err(&device->device, "Unable to send packet %p ret %d", + netdev_err(ndev, "Unable to send packet %p ret %d\n", packet, ret); + else + atomic_inc(&net_device->num_outstanding_sends); - atomic_inc(&net_device->num_outstanding_sends); - put_net_device(device); return ret; } @@ -561,6 +503,10 @@ static void netvsc_send_recv_completion(struct hv_device *device, struct nvsp_message recvcompMessage; int retries = 0; int ret; + struct net_device *ndev; + struct netvsc_device *net_device = hv_get_drvdata(device); + + ndev = net_device->ndev; recvcompMessage.hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE; @@ -577,23 +523,23 @@ retry_send_cmplt: if (ret == 0) { /* success */ /* no-op */ - } else if (ret == -1) { + } else if (ret == -EAGAIN) { /* 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); + netdev_err(ndev, "unable to send receive completion pkt" + " (tid %llx)...retrying %d\n", 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", + netdev_err(ndev, "unable to send receive " + "completion pkt (tid %llx)...give up retrying\n", transaction_id); } } else { - dev_err(&device->device, "unable to send receive " - "completion pkt - %llx", transaction_id); + netdev_err(ndev, "unable to send receive " + "completion pkt - %llx\n", transaction_id); } } @@ -606,6 +552,7 @@ static void netvsc_receive_completion(void *context) u64 transaction_id = 0; bool fsend_receive_comp = false; unsigned long flags; + struct net_device *ndev; /* * Even though it seems logical to do a GetOutboundNetDevice() here to @@ -613,11 +560,9 @@ static void netvsc_receive_completion(void *context) * 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?"); + if (!net_device) return; - } + ndev = net_device->ndev; /* Overloading use of the lock. */ spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags); @@ -644,7 +589,6 @@ static void netvsc_receive_completion(void *context) if (fsend_receive_comp) netvsc_send_recv_completion(device, transaction_id); - put_net_device(device); } static void netvsc_receive(struct hv_device *device, @@ -661,24 +605,22 @@ static void netvsc_receive(struct hv_device *device, int i, j; int count = 0, bytes_remain = 0; unsigned long flags; + struct net_device *ndev; LIST_HEAD(listHead); net_device = get_inbound_net_device(device); - if (!net_device) { - dev_err(&device->device, "unable to get net device..." - "device being destroyed?"); + if (!net_device) return; - } + ndev = net_device->ndev; /* * All inbound packets other than send completion should be xfer page * packet */ if (packet->type != VM_PKT_DATA_USING_XFER_PAGES) { - dev_err(&device->device, "Unknown packet type received - %d", + netdev_err(ndev, "Unknown packet type received - %d\n", packet->type); - put_net_device(device); return; } @@ -688,19 +630,17 @@ 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) { - dev_err(&device->device, "Unknown nvsp packet type received-" - " %d", nvsp_packet->hdr.msg_type); - put_net_device(device); + netdev_err(ndev, "Unknown nvsp packet type received-" + " %d\n", nvsp_packet->hdr.msg_type); return; } vmxferpage_packet = (struct vmtransfer_page_packet_header *)packet; if (vmxferpage_packet->xfer_pageset_id != NETVSC_RECEIVE_BUFFER_ID) { - dev_err(&device->device, "Invalid xfer page set id - " - "expecting %x got %x", NETVSC_RECEIVE_BUFFER_ID, + netdev_err(ndev, "Invalid xfer page set id - " + "expecting %x got %x\n", NETVSC_RECEIVE_BUFFER_ID, vmxferpage_packet->xfer_pageset_id); - put_net_device(device); return; } @@ -724,8 +664,8 @@ static void netvsc_receive(struct hv_device *device, * some of the xfer page packet ranges... */ if (count < 2) { - dev_err(&device->device, "Got only %d netvsc pkt...needed " - "%d pkts. Dropping this xfer page packet completely!", + netdev_err(ndev, "Got only %d netvsc pkt...needed " + "%d pkts. Dropping this xfer page packet completely!\n", count, vmxferpage_packet->range_cnt + 1); /* Return it to the freelist */ @@ -740,7 +680,6 @@ static void netvsc_receive(struct hv_device *device, netvsc_send_recv_completion(device, vmxferpage_packet->d.trans_id); - put_net_device(device); return; } @@ -752,8 +691,8 @@ static void netvsc_receive(struct hv_device *device, xferpage_packet->count = count - 1; if (xferpage_packet->count != vmxferpage_packet->range_cnt) { - dev_err(&device->device, "Needed %d netvsc pkts to satisy " - "this xfer page...got %d", + netdev_err(ndev, "Needed %d netvsc pkts to satisfy " + "this xfer page...got %d\n", vmxferpage_packet->range_cnt, xferpage_packet->count); } @@ -828,7 +767,6 @@ static void netvsc_receive(struct hv_device *device, completion.recv.recv_completion_ctx); } - put_net_device(device); } static void netvsc_channel_cb(void *context) @@ -842,6 +780,7 @@ static void netvsc_channel_cb(void *context) struct vmpacket_descriptor *desc; unsigned char *buffer; int bufferlen = NETVSC_PACKET_SIZE; + struct net_device *ndev; packet = kzalloc(NETVSC_PACKET_SIZE * sizeof(unsigned char), GFP_ATOMIC); @@ -850,11 +789,9 @@ static void netvsc_channel_cb(void *context) buffer = packet; net_device = get_inbound_net_device(device); - if (!net_device) { - dev_err(&device->device, "net device (%p) shutting down..." - "ignoring inbound packets", net_device); + if (!net_device) goto out; - } + ndev = net_device->ndev; do { ret = vmbus_recvpacket_raw(device->channel, buffer, bufferlen, @@ -872,7 +809,7 @@ static void netvsc_channel_cb(void *context) break; default: - dev_err(&device->device, + netdev_err(ndev, "unhandled packet type %d, " "tid %llx len %d\n", desc->type, request_id, @@ -896,14 +833,14 @@ static void netvsc_channel_cb(void *context) break; } - } else if (ret == -2) { + } else if (ret == -ENOBUFS) { /* Handle large packet */ buffer = kmalloc(bytes_recvd, GFP_ATOMIC); if (buffer == NULL) { /* Try again next time around */ - dev_err(&device->device, + netdev_err(ndev, "unable to allocate buffer of size " - "(%d)!!", bytes_recvd); + "(%d)!!\n", bytes_recvd); break; } @@ -911,7 +848,6 @@ static void netvsc_channel_cb(void *context) } } while (1); - put_net_device(device); out: kfree(buffer); return; @@ -929,13 +865,23 @@ int netvsc_device_add(struct hv_device *device, void *additional_info) ((struct netvsc_device_info *)additional_info)->ring_size; struct netvsc_device *net_device; struct hv_netvsc_packet *packet, *pos; + struct net_device *ndev; net_device = alloc_net_device(device); if (!net_device) { - ret = -1; + ret = -ENOMEM; goto cleanup; } + /* + * Coming into this function, struct net_device * is + * registered as the driver private data. + * In alloc_net_device(), we register struct netvsc_device * + * as the driver private data and stash away struct net_device * + * in struct netvsc_device *. + */ + ndev = net_device->ndev; + /* Initialize the NetVSC channel extension */ net_device->recv_buf_size = NETVSC_RECEIVE_BUFFER_SIZE; spin_lock_init(&net_device->recv_pkt_list_lock); @@ -960,20 +906,18 @@ int netvsc_device_add(struct hv_device *device, void *additional_info) netvsc_channel_cb, device); if (ret != 0) { - dev_err(&device->device, "unable to open channel: %d", ret); - ret = -1; + netdev_err(ndev, "unable to open channel: %d\n", ret); goto cleanup; } /* Channel is opened */ - pr_info("hv_netvsc channel opened successfully"); + pr_info("hv_netvsc channel opened successfully\n"); /* 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; + netdev_err(ndev, + "unable to connect to NetVSP - %d\n", ret); goto close; } @@ -993,23 +937,8 @@ cleanup: kfree(packet); } - release_outbound_net_device(device); - release_inbound_net_device(device); - - free_net_device(net_device); + kfree(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_drv.c b/drivers/staging/hv/netvsc_drv.c index 4c7739f..93b0e91 100644 --- a/drivers/staging/hv/netvsc_drv.c +++ b/drivers/staging/hv/netvsc_drv.c @@ -33,14 +33,11 @@ #include <linux/skbuff.h> #include <linux/in.h> #include <linux/slab.h> -#include <linux/dmi.h> -#include <linux/pci.h> #include <net/arp.h> #include <net/route.h> #include <net/sock.h> #include <net/pkt_sched.h> -#include "hyperv.h" #include "hyperv_net.h" struct net_device_context { @@ -72,20 +69,15 @@ static int netvsc_open(struct net_device *net) struct hv_device *device_obj = net_device_ctx->device_ctx; int ret = 0; - if (netif_carrier_ok(net)) { - /* Open up the device */ - ret = rndis_filter_open(device_obj); - if (ret != 0) { - netdev_err(net, "unable to open device (ret %d).\n", - ret); - return ret; - } - - netif_start_queue(net); - } else { - netdev_err(net, "unable to open device...link is down.\n"); + /* Open up the device */ + ret = rndis_filter_open(device_obj); + if (ret != 0) { + netdev_err(net, "unable to open device (ret %d).\n", ret); + return ret; } + netif_start_queue(net); + return ret; } @@ -143,12 +135,12 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) (num_pages * sizeof(struct hv_page_buffer)) + sizeof(struct rndis_filter_packet), GFP_ATOMIC); if (!packet) { - /* out of memory, silently drop packet */ + /* out of memory, drop packet */ netdev_err(net, "unable to allocate hv_netvsc_packet\n"); dev_kfree_skb(skb); net->stats.tx_dropped++; - return NETDEV_TX_OK; + return NETDEV_TX_BUSY; } packet->extension = (void *)(unsigned long)packet + @@ -193,10 +185,11 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) } else { /* we are shutting down or bus overloaded, just drop packet */ net->stats.tx_dropped++; - netvsc_xmit_completion(packet); + kfree(packet); + dev_kfree_skb_any(skb); } - return NETDEV_TX_OK; + return ret ? NETDEV_TX_BUSY : NETDEV_TX_OK; } /* @@ -205,8 +198,12 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) 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 *net; struct net_device_context *ndev_ctx; + struct netvsc_device *net_device; + + net_device = hv_get_drvdata(device_obj); + net = net_device->ndev; if (!net) { netdev_err(net, "got link status but net device " @@ -217,8 +214,8 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, if (status == 1) { netif_carrier_on(net); netif_wake_queue(net); - netif_notify_peers(net); ndev_ctx = netdev_priv(net); + schedule_delayed_work(&ndev_ctx->dwork, 0); schedule_delayed_work(&ndev_ctx->dwork, msecs_to_jiffies(20)); } else { netif_carrier_off(net); @@ -238,6 +235,10 @@ int netvsc_recv_callback(struct hv_device *device_obj, void *data; int i; unsigned long flags; + struct netvsc_device *net_device; + + net_device = hv_get_drvdata(device_obj); + net = net_device->ndev; if (!net) { netdev_err(net, "got receive callback but net device" @@ -324,14 +325,17 @@ static void netvsc_send_garp(struct work_struct *w) { struct net_device_context *ndev_ctx; struct net_device *net; + struct netvsc_device *net_device; ndev_ctx = container_of(w, struct net_device_context, dwork.work); - net = dev_get_drvdata(&ndev_ctx->device_ctx->device); + net_device = hv_get_drvdata(ndev_ctx->device_ctx); + net = net_device->ndev; netif_notify_peers(net); } -static int netvsc_probe(struct hv_device *dev) +static int netvsc_probe(struct hv_device *dev, + const struct hv_vmbus_device_id *dev_id) { struct net_device *net = NULL; struct net_device_context *net_device_ctx; @@ -340,7 +344,7 @@ static int netvsc_probe(struct hv_device *dev) net = alloc_etherdev(sizeof(struct net_device_context)); if (!net) - return -1; + return -ENOMEM; /* Set initial state */ netif_carrier_off(net); @@ -348,24 +352,9 @@ static int netvsc_probe(struct hv_device *dev) net_device_ctx = netdev_priv(net); net_device_ctx->device_ctx = dev; atomic_set(&net_device_ctx->avail, ring_size); - dev_set_drvdata(&dev->device, net); + hv_set_drvdata(dev, net); INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_send_garp); - /* Notify the netvsc driver of the new device */ - device_info.ring_size = ring_size; - ret = rndis_filter_device_add(dev, &device_info); - if (ret != 0) { - free_netdev(net); - dev_set_drvdata(&dev->device, NULL); - - netdev_err(net, "unable to add netvsc device (ret %d)\n", ret); - return ret; - } - - netif_carrier_on(net); - - memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN); - net->netdev_ops = &device_ops; /* TODO: Add GSO and Checksum offload */ @@ -377,18 +366,37 @@ static int netvsc_probe(struct hv_device *dev) ret = register_netdev(net); if (ret != 0) { - /* Remove the device and release the resource */ - rndis_filter_device_remove(dev); + pr_err("Unable to register netdev.\n"); + free_netdev(net); + goto out; + } + + /* Notify the netvsc driver of the new device */ + device_info.ring_size = ring_size; + ret = rndis_filter_device_add(dev, &device_info); + if (ret != 0) { + netdev_err(net, "unable to add netvsc device (ret %d)\n", ret); + unregister_netdev(net); free_netdev(net); + hv_set_drvdata(dev, NULL); + return ret; } + memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN); + + netif_carrier_on(net); +out: return ret; } static int netvsc_remove(struct hv_device *dev) { - struct net_device *net = dev_get_drvdata(&dev->device); + struct net_device *net; struct net_device_context *ndev_ctx; + struct netvsc_device *net_device; + + net_device = hv_get_drvdata(dev); + net = net_device->ndev; if (net == NULL) { dev_err(&dev->device, "No net device to remove\n"); @@ -413,61 +421,33 @@ static int netvsc_remove(struct hv_device *dev) return 0; } +static const struct hv_vmbus_device_id id_table[] = { + /* Network guid */ + { VMBUS_DEVICE(0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, + 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E) }, + { }, +}; + +MODULE_DEVICE_TABLE(vmbus, id_table); + /* The one and only one */ static struct hv_driver netvsc_drv = { + .name = "netvsc", + .id_table = id_table, .probe = netvsc_probe, .remove = netvsc_remove, }; static void __exit netvsc_drv_exit(void) { - vmbus_child_driver_unregister(&netvsc_drv.driver); + vmbus_driver_unregister(&netvsc_drv); } - -static const struct dmi_system_id __initconst -hv_netvsc_dmi_table[] __maybe_unused = { - { - .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, hv_netvsc_dmi_table); - static int __init netvsc_drv_init(void) { - struct hv_driver *drv = &netvsc_drv; - int ret; - - pr_info("initializing...."); - - if (!dmi_check_system(hv_netvsc_dmi_table)) - return -ENODEV; - - - /* 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; + return vmbus_driver_register(&netvsc_drv); } -static const struct pci_device_id __initconst -hv_netvsc_pci_table[] __maybe_unused = { - { PCI_DEVICE(0x1414, 0x5353) }, /* VGA compatible controller */ - { 0 } -}; -MODULE_DEVICE_TABLE(pci, hv_netvsc_pci_table); - MODULE_LICENSE("GPL"); MODULE_VERSION(HV_DRV_VERSION); MODULE_DESCRIPTION("Microsoft Hyper-V network driver"); diff --git a/drivers/staging/hv/ring_buffer.c b/drivers/staging/hv/ring_buffer.c deleted file mode 100644 index 42f7672..0000000 --- a/drivers/staging/hv/ring_buffer.c +++ /dev/null @@ -1,526 +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> - * K. Y. Srinivasan <kys@microsoft.com> - * - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/kernel.h> -#include <linux/mm.h> - -#include "hyperv.h" -#include "hyperv_vmbus.h" - - -/* #defines */ - - -/* Amount of space to write to */ -#define BYTES_AVAIL_TO_WRITE(r, w, z) ((w) >= (r)) ? ((z) - ((w) - (r))) : ((r) - (w)) - - -/* - * - * hv_get_ringbuffer_availbytes() - * - * Get number of bytes available to read and to write to - * for the specified ring buffer - */ -static inline void -hv_get_ringbuffer_availbytes(struct hv_ring_buffer_info *rbi, - u32 *read, u32 *write) -{ - u32 read_loc, write_loc; - - smp_read_barrier_depends(); - - /* Capture the read/write indices before they changed */ - read_loc = rbi->ring_buffer->read_index; - write_loc = rbi->ring_buffer->write_index; - - *write = BYTES_AVAIL_TO_WRITE(read_loc, write_loc, rbi->ring_datasize); - *read = rbi->ring_datasize - *write; -} - -/* - * hv_get_next_write_location() - * - * Get the next write location for the specified ring buffer - * - */ -static inline u32 -hv_get_next_write_location(struct hv_ring_buffer_info *ring_info) -{ - u32 next = ring_info->ring_buffer->write_index; - - return next; -} - -/* - * hv_set_next_write_location() - * - * Set the next write location for the specified ring buffer - * - */ -static inline void -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; -} - -/* - * hv_get_next_read_location() - * - * Get the next read location for the specified ring buffer - */ -static inline u32 -hv_get_next_read_location(struct hv_ring_buffer_info *ring_info) -{ - u32 next = ring_info->ring_buffer->read_index; - - return next; -} - -/* - * 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 -hv_get_next_readlocation_withoffset(struct hv_ring_buffer_info *ring_info, - u32 offset) -{ - u32 next = ring_info->ring_buffer->read_index; - - next += offset; - next %= ring_info->ring_datasize; - - return next; -} - -/* - * - * hv_set_next_read_location() - * - * Set the next read location for the specified ring buffer - * - */ -static inline void -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; -} - - -/* - * - * hv_get_ring_buffer() - * - * Get the start of the ring buffer - */ -static inline void * -hv_get_ring_buffer(struct hv_ring_buffer_info *ring_info) -{ - return (void *)ring_info->ring_buffer->buffer; -} - - -/* - * - * hv_get_ring_buffersize() - * - * Get the size of the ring buffer - */ -static inline u32 -hv_get_ring_buffersize(struct hv_ring_buffer_info *ring_info) -{ - return ring_info->ring_datasize; -} - -/* - * - * hv_get_ring_bufferindices() - * - * Get the read and write indices as u64 of the specified ring buffer - * - */ -static inline u64 -hv_get_ring_bufferindices(struct hv_ring_buffer_info *ring_info) -{ - return (u64)ring_info->ring_buffer->write_index << 32; -} - - -/* - * - * 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; - - hv_get_ringbuffer_availbytes(ring_info, - &bytes_avail_toread, - &bytes_avail_towrite); - - DPRINT(VMBUS, - DEBUG_RING_LVL, - "%s <<ringinfo %p buffer %p avail write %u " - "avail read %u read idx %u write idx %u>>", - prefix, - ring_info, - ring_info->ring_buffer->buffer, - bytes_avail_towrite, - bytes_avail_toread, - ring_info->ring_buffer->read_index, - ring_info->ring_buffer->write_index); -} - - -/* - * - * 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) -{ - 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); - - start_write_offset += srclen; - start_write_offset %= ring_buffer_size; - - return start_write_offset; -} - -/* - * - * 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) { - hv_get_ringbuffer_availbytes(ring_info, - &bytes_avail_toread, - &bytes_avail_towrite); - - debug_info->bytes_avail_toread = bytes_avail_toread; - debug_info->bytes_avail_towrite = bytes_avail_towrite; - debug_info->current_read_index = - ring_info->ring_buffer->read_index; - debug_info->current_write_index = - ring_info->ring_buffer->write_index; - debug_info->current_interrupt_mask = - ring_info->ring_buffer->interrupt_mask; - } -} - - -/* - * - * 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; -} - -/* - * - * 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) - return -EINVAL; - - memset(ring_info, 0, sizeof(struct hv_ring_buffer_info)); - - ring_info->ring_buffer = (struct hv_ring_buffer *)buffer; - ring_info->ring_buffer->read_index = - ring_info->ring_buffer->write_index = 0; - - ring_info->ring_size = buflen; - ring_info->ring_datasize = buflen - sizeof(struct hv_ring_buffer); - - spin_lock_init(&ring_info->ring_lock); - - return 0; -} - -/* - * - * hv_ringbuffer_cleanup() - * - * Cleanup the ring buffer - * - */ -void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_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; - u32 bytes_avail_towrite; - u32 bytes_avail_toread; - u32 totalbytes_towrite = 0; - - struct scatterlist *sg; - u32 next_write_location; - u64 prev_indices = 0; - unsigned long flags; - - for_each_sg(sglist, sg, sgcount, i) - { - totalbytes_towrite += sg->length; - } - - totalbytes_towrite += sizeof(u64); - - spin_lock_irqsave(&outring_info->ring_lock, flags); - - hv_get_ringbuffer_availbytes(outring_info, - &bytes_avail_toread, - &bytes_avail_towrite); - - - /* 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) { - spin_unlock_irqrestore(&outring_info->ring_lock, flags); - return -1; - } - - /* Write to the ring buffer */ - next_write_location = hv_get_next_write_location(outring_info); - - for_each_sg(sglist, sg, sgcount, i) - { - next_write_location = hv_copyto_ringbuffer(outring_info, - next_write_location, - sg_virt(sg), - sg->length); - } - - /* Set previous packet start */ - prev_indices = hv_get_ring_bufferindices(outring_info); - - next_write_location = hv_copyto_ringbuffer(outring_info, - next_write_location, - &prev_indices, - sizeof(u64)); - - /* Make sure we flush all writes before updating the writeIndex */ - smp_wmb(); - - /* Now, update the write location */ - hv_set_next_write_location(outring_info, next_write_location); - - - spin_unlock_irqrestore(&outring_info->ring_lock, flags); - return 0; -} - - -/* - * - * 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; - u32 bytes_avail_toread; - u32 next_read_location = 0; - unsigned long flags; - - spin_lock_irqsave(&Inring_info->ring_lock, flags); - - hv_get_ringbuffer_availbytes(Inring_info, - &bytes_avail_toread, - &bytes_avail_towrite); - - /* Make sure there is something to read */ - if (bytes_avail_toread < buflen) { - - spin_unlock_irqrestore(&Inring_info->ring_lock, flags); - - return -1; - } - - /* Convert to byte offset */ - next_read_location = hv_get_next_read_location(Inring_info); - - next_read_location = hv_copyfrom_ringbuffer(Inring_info, - Buffer, - buflen, - next_read_location); - - spin_unlock_irqrestore(&Inring_info->ring_lock, flags); - - return 0; -} - - -/* - * - * 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; - u32 bytes_avail_toread; - u32 next_read_location = 0; - u64 prev_indices = 0; - unsigned long flags; - - if (buflen <= 0) - return -EINVAL; - - spin_lock_irqsave(&inring_info->ring_lock, flags); - - hv_get_ringbuffer_availbytes(inring_info, - &bytes_avail_toread, - &bytes_avail_towrite); - - /* Make sure there is something to read */ - if (bytes_avail_toread < buflen) { - spin_unlock_irqrestore(&inring_info->ring_lock, flags); - - return -1; - } - - next_read_location = - hv_get_next_readlocation_withoffset(inring_info, offset); - - next_read_location = hv_copyfrom_ringbuffer(inring_info, - buffer, - buflen, - next_read_location); - - next_read_location = hv_copyfrom_ringbuffer(inring_info, - &prev_indices, - sizeof(u64), - next_read_location); - - /* Make sure all reads are done before we update the read index since */ - /* the writer may start writing to the read area once the read index */ - /*is updated */ - smp_mb(); - - /* Update the read index */ - hv_set_next_read_location(inring_info, next_read_location); - - spin_unlock_irqrestore(&inring_info->ring_lock, flags); - - return 0; -} diff --git a/drivers/staging/hv/rndis_filter.c b/drivers/staging/hv/rndis_filter.c index dbb5201..bafccb3 100644 --- a/drivers/staging/hv/rndis_filter.c +++ b/drivers/staging/hv/rndis_filter.c @@ -27,7 +27,6 @@ #include <linux/if_ether.h> #include <linux/netdevice.h> -#include "hyperv.h" #include "hyperv_net.h" @@ -42,7 +41,7 @@ struct rndis_device { struct netvsc_device *net_dev; enum rndis_device_state state; - u32 link_stat; + bool link_state; atomic_t new_req_id; spinlock_t request_lock; @@ -142,7 +141,11 @@ static void put_rndis_request(struct rndis_device *dev, static void dump_rndis_message(struct hv_device *hv_dev, struct rndis_message *rndis_msg) { - struct net_device *netdev = dev_get_drvdata(&hv_dev->device); + struct net_device *netdev; + struct netvsc_device *net_device; + + net_device = hv_get_drvdata(hv_dev); + netdev = net_device->ndev; switch (rndis_msg->ndis_msg_type) { case REMOTE_NDIS_PACKET_MSG: @@ -250,6 +253,9 @@ static void rndis_filter_receive_response(struct rndis_device *dev, struct rndis_request *request = NULL; bool found = false; unsigned long flags; + struct net_device *ndev; + + ndev = dev->net_dev->ndev; spin_lock_irqsave(&dev->request_lock, flags); list_for_each_entry(request, &dev->req_list, list_ent) { @@ -270,7 +276,7 @@ static void rndis_filter_receive_response(struct rndis_device *dev, memcpy(&request->response_msg, resp, resp->msg_len); } else { - dev_err(&dev->net_dev->dev->device, + netdev_err(ndev, "rndis response buffer overflow " "detected (size %u max %zu)\n", resp->msg_len, @@ -290,7 +296,7 @@ static void rndis_filter_receive_response(struct rndis_device *dev, complete(&request->wait_event); } else { - dev_err(&dev->net_dev->dev->device, + netdev_err(ndev, "no rndis request found for this response " "(id 0x%x res type 0x%x)\n", resp->msg.init_complete.req_id, @@ -323,6 +329,7 @@ static void rndis_filter_receive_data(struct rndis_device *dev, { struct rndis_packet *rndis_pkt; u32 data_offset; + int i; rndis_pkt = &msg->msg.pkt; @@ -338,6 +345,15 @@ static void rndis_filter_receive_data(struct rndis_device *dev, pkt->page_buf[0].offset += data_offset; pkt->page_buf[0].len -= data_offset; + /* Drop the 0th page, if rndis data go beyond page boundary */ + if (pkt->page_buf[0].offset >= PAGE_SIZE) { + pkt->page_buf[1].offset = pkt->page_buf[0].offset - PAGE_SIZE; + pkt->page_buf[1].len -= pkt->page_buf[1].offset; + pkt->page_buf_cnt--; + for (i = 0; i < pkt->page_buf_cnt; i++) + pkt->page_buf[i] = pkt->page_buf[i+1]; + } + pkt->is_data_pkt = true; netvsc_recv_callback(dev->net_dev->dev, pkt); @@ -346,26 +362,29 @@ static void rndis_filter_receive_data(struct rndis_device *dev, int rndis_filter_receive(struct hv_device *dev, struct hv_netvsc_packet *pkt) { - struct netvsc_device *net_dev = dev->ext; + struct netvsc_device *net_dev = hv_get_drvdata(dev); struct rndis_device *rndis_dev; struct rndis_message rndis_msg; struct rndis_message *rndis_hdr; + struct net_device *ndev; if (!net_dev) return -EINVAL; + ndev = net_dev->ndev; + /* Make sure the rndis device state is initialized */ if (!net_dev->extension) { - dev_err(&dev->device, "got rndis message but no rndis device - " + netdev_err(ndev, "got rndis message but no rndis device - " "dropping this message!\n"); - return -1; + return -ENODEV; } rndis_dev = (struct rndis_device *)net_dev->extension; if (rndis_dev->state == RNDIS_DEV_UNINITIALIZED) { - dev_err(&dev->device, "got rndis message but rndis device " + netdev_err(ndev, "got rndis message but rndis device " "uninitialized...dropping this message!\n"); - return -1; + return -ENODEV; } rndis_hdr = (struct rndis_message *)kmap_atomic( @@ -377,7 +396,7 @@ int rndis_filter_receive(struct hv_device *dev, /* Make sure we got a valid rndis message */ if ((rndis_hdr->ndis_msg_type != REMOTE_NDIS_PACKET_MSG) && (rndis_hdr->msg_len > sizeof(struct rndis_message))) { - dev_err(&dev->device, "incoming rndis message buffer overflow " + netdev_err(ndev, "incoming rndis message buffer overflow " "detected (got %u, max %zu)..marking it an error!\n", rndis_hdr->msg_len, sizeof(struct rndis_message)); @@ -410,7 +429,7 @@ int rndis_filter_receive(struct hv_device *dev, rndis_filter_receive_indicate_status(rndis_dev, &rndis_msg); break; default: - dev_err(&dev->device, + netdev_err(ndev, "unhandled rndis message (type %u len %u)\n", rndis_msg.ndis_msg_type, rndis_msg.msg_len); @@ -437,8 +456,8 @@ static int rndis_filter_query_device(struct rndis_device *dev, u32 oid, request = get_rndis_request(dev, REMOTE_NDIS_QUERY_MSG, RNDIS_MESSAGE_SIZE(struct rndis_query_request)); if (!request) { - ret = -1; - goto Cleanup; + ret = -ENOMEM; + goto cleanup; } /* Setup the rndis query */ @@ -450,12 +469,12 @@ static int rndis_filter_query_device(struct rndis_device *dev, u32 oid, ret = rndis_filter_send_request(dev, request); if (ret != 0) - goto Cleanup; + goto cleanup; t = wait_for_completion_timeout(&request->wait_event, 5*HZ); if (t == 0) { ret = -ETIMEDOUT; - goto Cleanup; + goto cleanup; } /* Copy the response back */ @@ -463,7 +482,7 @@ static int rndis_filter_query_device(struct rndis_device *dev, u32 oid, if (query_complete->info_buflen > inresult_size) { ret = -1; - goto Cleanup; + goto cleanup; } memcpy(result, @@ -473,7 +492,7 @@ static int rndis_filter_query_device(struct rndis_device *dev, u32 oid, *result_size = query_complete->info_buflen; -Cleanup: +cleanup: if (request) put_rndis_request(dev, request); @@ -492,10 +511,15 @@ static int rndis_filter_query_device_mac(struct rndis_device *dev) static int rndis_filter_query_device_link_status(struct rndis_device *dev) { u32 size = sizeof(u32); + u32 link_status; + int ret; - return rndis_filter_query_device(dev, + ret = rndis_filter_query_device(dev, RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, - &dev->link_stat, &size); + &link_status, &size); + dev->link_state = (link_status != 0) ? true : false; + + return ret; } static int rndis_filter_set_packet_filter(struct rndis_device *dev, @@ -506,13 +530,16 @@ static int rndis_filter_set_packet_filter(struct rndis_device *dev, struct rndis_set_complete *set_complete; u32 status; int ret, t; + struct net_device *ndev; + + ndev = dev->net_dev->ndev; request = get_rndis_request(dev, REMOTE_NDIS_SET_MSG, RNDIS_MESSAGE_SIZE(struct rndis_set_request) + sizeof(u32)); if (!request) { - ret = -1; - goto Cleanup; + ret = -ENOMEM; + goto cleanup; } /* Setup the rndis set */ @@ -526,30 +553,27 @@ static int rndis_filter_set_packet_filter(struct rndis_device *dev, ret = rndis_filter_send_request(dev, request); if (ret != 0) - goto Cleanup; + goto cleanup; t = wait_for_completion_timeout(&request->wait_event, 5*HZ); if (t == 0) { - ret = -1; - dev_err(&dev->net_dev->dev->device, + netdev_err(ndev, "timeout before we got a set response...\n"); /* * We can't deallocate the request since we may still receive a * send completion for it. */ - goto Exit; + goto exit; } else { - if (ret > 0) - ret = 0; set_complete = &request->response_msg.msg.set_complete; status = set_complete->status; } -Cleanup: +cleanup: if (request) put_rndis_request(dev, request); -Exit: +exit: return ret; } @@ -565,8 +589,8 @@ static int rndis_filter_init_device(struct rndis_device *dev) request = get_rndis_request(dev, REMOTE_NDIS_INITIALIZE_MSG, RNDIS_MESSAGE_SIZE(struct rndis_initialize_request)); if (!request) { - ret = -1; - goto Cleanup; + ret = -ENOMEM; + goto cleanup; } /* Setup the rndis set */ @@ -581,7 +605,7 @@ static int rndis_filter_init_device(struct rndis_device *dev) ret = rndis_filter_send_request(dev, request); if (ret != 0) { dev->state = RNDIS_DEV_UNINITIALIZED; - goto Cleanup; + goto cleanup; } @@ -589,7 +613,7 @@ static int rndis_filter_init_device(struct rndis_device *dev) if (t == 0) { ret = -ETIMEDOUT; - goto Cleanup; + goto cleanup; } init_complete = &request->response_msg.msg.init_complete; @@ -599,10 +623,10 @@ static int rndis_filter_init_device(struct rndis_device *dev) ret = 0; } else { dev->state = RNDIS_DEV_UNINITIALIZED; - ret = -1; + ret = -EINVAL; } -Cleanup: +cleanup: if (request) put_rndis_request(dev, request); @@ -618,7 +642,7 @@ static void rndis_filter_halt_device(struct rndis_device *dev) request = get_rndis_request(dev, REMOTE_NDIS_HALT_MSG, RNDIS_MESSAGE_SIZE(struct rndis_halt_request)); if (!request) - goto Cleanup; + goto cleanup; /* Setup the rndis set */ halt = &request->request_msg.msg.halt_req; @@ -629,7 +653,7 @@ static void rndis_filter_halt_device(struct rndis_device *dev) dev->state = RNDIS_DEV_UNINITIALIZED; -Cleanup: +cleanup: if (request) put_rndis_request(dev, request); return; @@ -670,13 +694,13 @@ int rndis_filter_device_add(struct hv_device *dev, void *additional_info) { int ret; - struct netvsc_device *netDevice; - struct rndis_device *rndisDevice; - struct netvsc_device_info *deviceInfo = additional_info; + struct netvsc_device *net_device; + struct rndis_device *rndis_device; + struct netvsc_device_info *device_info = additional_info; - rndisDevice = get_rndis_device(); - if (!rndisDevice) - return -1; + rndis_device = get_rndis_device(); + if (!rndis_device) + return -ENODEV; /* * Let the inner driver handle this first to create the netvsc channel @@ -685,19 +709,19 @@ int rndis_filter_device_add(struct hv_device *dev, */ ret = netvsc_device_add(dev, additional_info); if (ret != 0) { - kfree(rndisDevice); + kfree(rndis_device); return ret; } /* Initialize the rndis device */ - netDevice = dev->ext; + net_device = hv_get_drvdata(dev); - netDevice->extension = rndisDevice; - rndisDevice->net_dev = netDevice; + net_device->extension = rndis_device; + rndis_device->net_dev = net_device; /* Send the rndis initialization message */ - ret = rndis_filter_init_device(rndisDevice); + ret = rndis_filter_init_device(rndis_device); if (ret != 0) { /* * TODO: If rndis init failed, we will need to shut down the @@ -706,29 +730,29 @@ int rndis_filter_device_add(struct hv_device *dev, } /* Get the mac address */ - ret = rndis_filter_query_device_mac(rndisDevice); + ret = rndis_filter_query_device_mac(rndis_device); if (ret != 0) { /* * TODO: shutdown rndis device and the channel */ } - memcpy(deviceInfo->mac_adr, rndisDevice->hw_mac_adr, ETH_ALEN); + memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN); - rndis_filter_query_device_link_status(rndisDevice); + rndis_filter_query_device_link_status(rndis_device); - deviceInfo->link_state = rndisDevice->link_stat; + device_info->link_state = rndis_device->link_state; - dev_info(&dev->device, "Device MAC %pM link state %s", - rndisDevice->hw_mac_adr, - ((deviceInfo->link_state) ? ("down\n") : ("up\n"))); + dev_info(&dev->device, "Device MAC %pM link state %s\n", + rndis_device->hw_mac_adr, + device_info->link_state ? "down" : "up"); return ret; } void rndis_filter_device_remove(struct hv_device *dev) { - struct netvsc_device *net_dev = dev->ext; + struct netvsc_device *net_dev = hv_get_drvdata(dev); struct rndis_device *rndis_dev = net_dev->extension; /* Halt and release the rndis device */ @@ -743,17 +767,17 @@ void rndis_filter_device_remove(struct hv_device *dev) int rndis_filter_open(struct hv_device *dev) { - struct netvsc_device *netDevice = dev->ext; + struct netvsc_device *net_device = hv_get_drvdata(dev); - if (!netDevice) + if (!net_device) return -EINVAL; - return rndis_filter_open_device(netDevice->extension); + return rndis_filter_open_device(net_device->extension); } int rndis_filter_close(struct hv_device *dev) { - struct netvsc_device *netDevice = dev->ext; + struct netvsc_device *netDevice = hv_get_drvdata(dev); if (!netDevice) return -EINVAL; diff --git a/drivers/staging/hv/storvsc.c b/drivers/staging/hv/storvsc.c deleted file mode 100644 index 3029786..0000000 --- a/drivers/staging/hv/storvsc.c +++ /dev/null @@ -1,564 +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> - * K. Y. Srinivasan <kys@microsoft.com> - * - */ -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/completion.h> -#include <linux/string.h> -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/delay.h> - -#include "hyperv.h" -#include "hyperv_storage.h" - - -static inline struct storvsc_device *alloc_stor_device(struct hv_device *device) -{ - struct storvsc_device *stor_device; - - stor_device = kzalloc(sizeof(struct storvsc_device), GFP_KERNEL); - if (!stor_device) - return NULL; - - /* Set to 2 to allow both inbound and outbound traffics */ - /* (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; - - return stor_device; -} - -static inline void free_stor_device(struct storvsc_device *device) -{ - kfree(device); -} - -/* Get the stordevice object iff exists and its refcount > 0 */ -static inline struct storvsc_device *must_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)) - atomic_inc(&stor_device->ref_count); - else - stor_device = NULL; - - return stor_device; -} - -/* Drop ref count to 1 to effectively disable get_stor_device() */ -static inline struct storvsc_device *release_stor_device( - struct hv_device *device) -{ - struct storvsc_device *stor_device; - - stor_device = (struct storvsc_device *)device->ext; - - /* Busy wait until the ref drop to 2, then set it to 1 */ - while (atomic_cmpxchg(&stor_device->ref_count, 2, 1) != 2) - udelay(100); - - return stor_device; -} - -/* Drop ref count to 0. No one can use stor_device object. */ -static inline struct storvsc_device *final_release_stor_device( - struct hv_device *device) -{ - struct storvsc_device *stor_device; - - stor_device = (struct storvsc_device *)device->ext; - - /* Busy wait until the ref drop to 1, then set it to 0 */ - while (atomic_cmpxchg(&stor_device->ref_count, 1, 0) != 1) - udelay(100); - - device->ext = NULL; - return stor_device; -} - -static int storvsc_channel_init(struct hv_device *device) -{ - struct storvsc_device *stor_device; - struct hv_storvsc_request *request; - struct vstor_packet *vstor_packet; - int ret, t; - - stor_device = get_stor_device(device); - if (!stor_device) - return -1; - - request = &stor_device->init_request; - vstor_packet = &request->vstor_packet; - - /* - * Now, initiate the vsc/vsp initialization protocol on the open - * channel - */ - 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..."); - - 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) - goto cleanup; - - t = wait_for_completion_timeout(&request->wait_event, 5*HZ); - if (t == 0) { - ret = -ETIMEDOUT; - goto cleanup; - } - - if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || - vstor_packet->status != 0) - goto cleanup; - - DPRINT_INFO(STORVSC, "QUERY_PROTOCOL_VERSION_OPERATION..."); - - /* reuse the packet for version range supported */ - memset(vstor_packet, 0, sizeof(struct vstor_packet)); - vstor_packet->operation = VSTOR_OPERATION_QUERY_PROTOCOL_VERSION; - vstor_packet->flags = REQUEST_COMPLETION_FLAG; - - vstor_packet->version.major_minor = VMSTOR_PROTOCOL_VERSION_CURRENT; - FILL_VMSTOR_REVISION(vstor_packet->version.revision); - - 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) - goto cleanup; - - t = wait_for_completion_timeout(&request->wait_event, 5*HZ); - if (t == 0) { - ret = -ETIMEDOUT; - goto cleanup; - } - - /* TODO: Check returned version */ - if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || - vstor_packet->status != 0) - goto cleanup; - - /* Query channel properties */ - DPRINT_INFO(STORVSC, "QUERY_PROPERTIES_OPERATION..."); - - memset(vstor_packet, 0, sizeof(struct vstor_packet)); - vstor_packet->operation = VSTOR_OPERATION_QUERY_PROPERTIES; - vstor_packet->flags = REQUEST_COMPLETION_FLAG; - vstor_packet->storage_channel_properties.port_number = - stor_device->port_number; - - 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) - goto cleanup; - - t = wait_for_completion_timeout(&request->wait_event, 5*HZ); - if (t == 0) { - ret = -ETIMEDOUT; - goto cleanup; - } - - /* TODO: Check returned version */ - if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || - 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_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; - - 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) - goto cleanup; - - t = wait_for_completion_timeout(&request->wait_event, 5*HZ); - if (t == 0) { - ret = -ETIMEDOUT; - goto cleanup; - } - - if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || - vstor_packet->status != 0) - goto cleanup; - - DPRINT_INFO(STORVSC, "**** storage channel up and running!! ****"); - -cleanup: - put_stor_device(device); - return ret; -} - -static void storvsc_on_io_completion(struct hv_device *device, - struct vstor_packet *vstor_packet, - 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) - return; - - stor_pkt = &request->vstor_packet; - - - /* Copy over the status...etc */ - 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 (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", - stor_pkt->vm_srb.cdb[0], - vstor_packet->vm_srb.scsi_status, - vstor_packet->vm_srb.srb_status); - } - - 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, - vstor_packet->vm_srb.sense_info_length); - - memcpy(request->sense_buffer, - vstor_packet->vm_srb.sense_data, - vstor_packet->vm_srb.sense_info_length); - - } - } - - stor_pkt->vm_srb.data_transfer_length = - vstor_packet->vm_srb.data_transfer_length; - - request->on_io_completion(request); - - 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 storvsc_on_receive(struct hv_device *device, - struct vstor_packet *vstor_packet, - struct hv_storvsc_request *request) -{ - switch (vstor_packet->operation) { - case VSTOR_OPERATION_COMPLETE_IO: - storvsc_on_io_completion(device, vstor_packet, request); - break; - case VSTOR_OPERATION_REMOVE_DEVICE: - DPRINT_INFO(STORVSC, "REMOVE_DEVICE_OPERATION"); - /* TODO: */ - break; - - default: - DPRINT_INFO(STORVSC, "Unknown operation received - %d", - vstor_packet->operation); - break; - } -} - -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 hv_storvsc_request *request; - int ret; - - - stor_device = must_get_stor_device(device); - 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) { - - request = (struct hv_storvsc_request *) - (unsigned long)request_id; - - if ((request == &stor_device->init_request) || - (request == &stor_device->reset_request)) { - - memcpy(&request->vstor_packet, packet, - sizeof(struct vstor_packet)); - complete(&request->wait_event); - } else { - storvsc_on_receive(device, - (struct vstor_packet *)packet, - request); - } - } else { - break; - } - } while (1); - - put_stor_device(device); - return; -} - -static int storvsc_connect_to_vsp(struct hv_device *device, u32 ring_size) -{ - struct vmstorage_channel_properties props; - int ret; - - memset(&props, 0, sizeof(struct vmstorage_channel_properties)); - - /* Open the channel */ - ret = vmbus_open(device->channel, - ring_size, - ring_size, - (void *)&props, - sizeof(struct vmstorage_channel_properties), - storvsc_on_channel_callback, device); - - if (ret != 0) - return -1; - - ret = storvsc_channel_init(device); - - return ret; -} - -int storvsc_dev_add(struct hv_device *device, - void *additional_info) -{ - struct storvsc_device *stor_device; - struct storvsc_device_info *device_info; - int ret = 0; - - device_info = (struct storvsc_device_info *)additional_info; - stor_device = alloc_stor_device(device); - if (!stor_device) { - ret = -1; - goto cleanup; - } - - /* Save the channel properties to our storvsc channel */ - - /* FIXME: */ - /* - * If we support more than 1 scsi channel, we need to set the - * port number here to the scsi channel but how do we get the - * scsi channel prior to the bus scan - */ - - stor_device->port_number = device_info->port_number; - /* Send it back up */ - ret = storvsc_connect_to_vsp(device, device_info->ring_buffer_size); - - device_info->path_id = stor_device->path_id; - device_info->target_id = stor_device->target_id; - -cleanup: - return ret; -} - -int storvsc_dev_remove(struct hv_device *device) -{ - struct storvsc_device *stor_device; - - DPRINT_INFO(STORVSC, "disabling storage device (%p)...", - device->ext); - - stor_device = release_stor_device(device); - - /* - * At this point, all outbound traffic should be disable. We - * only allow inbound traffic (responses) to proceed so that - * outstanding requests can be completed. - */ - - storvsc_wait_to_drain(stor_device); - - stor_device = final_release_stor_device(device); - - /* Close the channel */ - vmbus_close(device->channel); - - free_stor_device(stor_device); - return 0; -} - -int storvsc_do_io(struct hv_device *device, - struct hv_storvsc_request *request) -{ - struct storvsc_device *stor_device; - struct vstor_packet *vstor_packet; - int ret = 0; - - vstor_packet = &request->vstor_packet; - stor_device = get_stor_device(device); - - if (!stor_device) - return -2; - - - request->device = device; - - - vstor_packet->flags |= REQUEST_COMPLETION_FLAG; - - vstor_packet->vm_srb.length = sizeof(struct vmscsi_request); - - - vstor_packet->vm_srb.sense_info_length = SENSE_BUFFER_SIZE; - - - vstor_packet->vm_srb.data_transfer_length = - request->data_buffer.len; - - vstor_packet->operation = VSTOR_OPERATION_EXECUTE_SRB; - - if (request->data_buffer.len) { - ret = vmbus_sendpacket_multipagebuffer(device->channel, - &request->data_buffer, - vstor_packet, - sizeof(struct vstor_packet), - (unsigned long)request); - } else { - 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) - return ret; - - atomic_inc(&stor_device->num_outstanding_req); - - put_stor_device(device); - return ret; -} - -/* - * 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. - * - */ - -int storvsc_get_major_info(struct storvsc_device_info *device_info, - struct storvsc_major_info *major_info) -{ - static bool ide0_registered; - static bool ide1_registered; - - /* - * For now we only support IDE disks. - */ - 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 -ENODEV; -} - diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c index 7effaf3..ae8c33e 100644 --- a/drivers/staging/hv/storvsc_drv.c +++ b/drivers/staging/hv/storvsc_drv.c @@ -19,12 +19,19 @@ * Hank Janssen <hjanssen@microsoft.com> * K. Y. Srinivasan <kys@microsoft.com> */ + +#include <linux/kernel.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/completion.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/delay.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/device.h> -#include <linux/blkdev.h> -#include <linux/dmi.h> +#include <linux/hyperv.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_host.h> @@ -34,22 +41,247 @@ #include <scsi/scsi_devinfo.h> #include <scsi/scsi_dbg.h> -#include "hyperv.h" -#include "hyperv_storage.h" +#define STORVSC_RING_BUFFER_SIZE (20*PAGE_SIZE) 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"; +/* 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 { \ + char *revision_string \ + = REVISION_STRING($Rev : 6 $) + 6; \ + RESULT_LVALUE_ = 0; \ + while (*revision_string >= '0' \ + && *revision_string <= '9') { \ + RESULT_LVALUE_ *= 10; \ + RESULT_LVALUE_ += *revision_string - '0'; \ + revision_string++; \ + } \ + } while (0) + +/* Major/minor macros. Minor version is in LSB, meaning that earlier flat */ +/* version numbers will be interpreted as "0.x" (i.e., 1 becomes 0.1). */ +#define VMSTOR_PROTOCOL_MAJOR(VERSION_) (((VERSION_) >> 8) & 0xff) +#define VMSTOR_PROTOCOL_MINOR(VERSION_) (((VERSION_)) & 0xff) +#define VMSTOR_PROTOCOL_VERSION(MAJOR_, MINOR_) ((((MAJOR_) & 0xff) << 8) | \ + (((MINOR_) & 0xff))) +#define VMSTOR_INVALID_PROTOCOL_VERSION (-1) + +/* Version history: */ +/* V1 Beta 0.1 */ +/* V1 RC < 2008/1/31 1.0 */ +/* V1 RC > 2008/1/31 2.0 */ +#define VMSTOR_PROTOCOL_VERSION_CURRENT VMSTOR_PROTOCOL_VERSION(2, 0) + + + + +/* This will get replaced with the max transfer length that is possible on */ +/* the host adapter. */ +/* The max transfer length will be published when we offer a vmbus channel. */ +#define MAX_TRANSFER_LENGTH 0x40000 +#define DEFAULT_PACKET_SIZE (sizeof(struct vmdata_gpa_direct) + \ + sizeof(struct vstor_packet) + \ + sizesizeof(u64) * (MAX_TRANSFER_LENGTH / PAGE_SIZE))) + + +/* Packet structure describing virtual storage requests. */ +enum vstor_packet_operation { + VSTOR_OPERATION_COMPLETE_IO = 1, + VSTOR_OPERATION_REMOVE_DEVICE = 2, + VSTOR_OPERATION_EXECUTE_SRB = 3, + VSTOR_OPERATION_RESET_LUN = 4, + VSTOR_OPERATION_RESET_ADAPTER = 5, + VSTOR_OPERATION_RESET_BUS = 6, + VSTOR_OPERATION_BEGIN_INITIALIZATION = 7, + VSTOR_OPERATION_END_INITIALIZATION = 8, + VSTOR_OPERATION_QUERY_PROTOCOL_VERSION = 9, + VSTOR_OPERATION_QUERY_PROPERTIES = 10, + VSTOR_OPERATION_MAXIMUM = 10 +}; -/* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */ -static const struct hv_guid stor_vsci_device_type = { - .data = { - 0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, - 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f - } +/* + * Platform neutral description of a scsi request - + * this remains the same across the write regardless of 32/64 bit + * note: it's patterned off the SCSI_PASS_THROUGH structure + */ +#define CDB16GENERIC_LENGTH 0x10 + +#ifndef SENSE_BUFFER_SIZE +#define SENSE_BUFFER_SIZE 0x12 +#endif + +#define MAX_DATA_BUF_LEN_WITH_PADDING 0x14 + +struct vmscsi_request { + unsigned short length; + unsigned char srb_status; + unsigned char scsi_status; + + unsigned char port_number; + unsigned char path_id; + unsigned char target_id; + unsigned char lun; + + unsigned char cdb_length; + unsigned char sense_info_length; + unsigned char data_in; + unsigned char reserved; + + unsigned int data_transfer_length; + + union { + unsigned char cdb[CDB16GENERIC_LENGTH]; + unsigned char sense_data[SENSE_BUFFER_SIZE]; + unsigned char reserved_array[MAX_DATA_BUF_LEN_WITH_PADDING]; + }; +} __attribute((packed)); + + +/* + * This structure is sent during the intialization phase to get the different + * properties of the channel. + */ +struct vmstorage_channel_properties { + unsigned short protocol_version; + unsigned char path_id; + unsigned char target_id; + + /* Note: port number is only really known on the client side */ + unsigned int port_number; + unsigned int flags; + unsigned int max_transfer_bytes; + + /* This id is unique for each channel and will correspond with */ + /* vendor specific data in the inquirydata */ + unsigned long long unique_id; +} __packed; + +/* This structure is sent during the storage protocol negotiations. */ +struct vmstorage_protocol_version { + /* Major (MSW) and minor (LSW) version numbers. */ + unsigned short major_minor; + + /* + * Revision number is auto-incremented whenever this file is changed + * (See FILL_VMSTOR_REVISION macro above). Mismatch does not + * definitely indicate incompatibility--but it does indicate mismatched + * builds. + */ + unsigned short revision; +} __packed; + +/* Channel Property Flags */ +#define STORAGE_CHANNEL_REMOVABLE_FLAG 0x1 +#define STORAGE_CHANNEL_EMULATED_IDE_FLAG 0x2 + +struct vstor_packet { + /* Requested operation type */ + enum vstor_packet_operation operation; + + /* Flags - see below for values */ + unsigned int flags; + + /* Status of the request returned from the server side. */ + unsigned int status; + + /* Data payload area */ + union { + /* + * Structure used to forward SCSI commands from the + * client to the server. + */ + struct vmscsi_request vm_srb; + + /* Structure used to query channel properties. */ + struct vmstorage_channel_properties storage_channel_properties; + + /* Used during version negotiations. */ + struct vmstorage_protocol_version version; + }; +} __packed; + +/* Packet flags */ +/* + * This flag indicates that the server should send back a completion for this + * packet. + */ +#define REQUEST_COMPLETION_FLAG 0x1 + +/* This is the set of flags that the vsc can set in any packets it sends */ +#define VSC_LEGAL_FLAGS (REQUEST_COMPLETION_FLAG) + + +/* Defines */ + +#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 +#define STORVSC_MAX_CMD_LEN 16 + +struct hv_storvsc_request; + +/* Matches Windows-end */ +enum storvsc_request_type { + WRITE_TYPE, + READ_TYPE, + UNKNOWN_TYPE, +}; + + +struct hv_storvsc_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; +}; + + +/* A storvsc device is a device object that contains a vmbus channel */ +struct storvsc_device { + struct hv_device *device; + + bool destroy; + bool drain_notify; + atomic_t num_outstanding_req; + struct Scsi_Host *host; + + 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; }; struct hv_host_device { @@ -70,6 +302,430 @@ struct storvsc_cmd_request { struct hv_storvsc_request request; }; +static inline struct storvsc_device *get_out_stor_device( + struct hv_device *device) +{ + struct storvsc_device *stor_device; + + stor_device = hv_get_drvdata(device); + + if (stor_device && stor_device->destroy) + stor_device = NULL; + + return stor_device; +} + + +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; +} + +static inline struct storvsc_device *get_in_stor_device( + struct hv_device *device) +{ + struct storvsc_device *stor_device; + + stor_device = hv_get_drvdata(device); + + if (!stor_device) + goto get_in_err; + + /* + * If the device is being destroyed; allow incoming + * traffic only to cleanup outstanding requests. + */ + + if (stor_device->destroy && + (atomic_read(&stor_device->num_outstanding_req) == 0)) + stor_device = NULL; + +get_in_err: + return stor_device; + +} + +static int storvsc_channel_init(struct hv_device *device) +{ + struct storvsc_device *stor_device; + struct hv_storvsc_request *request; + struct vstor_packet *vstor_packet; + int ret, t; + + stor_device = get_out_stor_device(device); + if (!stor_device) + return -ENODEV; + + request = &stor_device->init_request; + vstor_packet = &request->vstor_packet; + + /* + * Now, initiate the vsc/vsp initialization protocol on the open + * channel + */ + 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; + + 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) + goto cleanup; + + t = wait_for_completion_timeout(&request->wait_event, 5*HZ); + if (t == 0) { + ret = -ETIMEDOUT; + goto cleanup; + } + + if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || + vstor_packet->status != 0) + goto cleanup; + + + /* reuse the packet for version range supported */ + memset(vstor_packet, 0, sizeof(struct vstor_packet)); + vstor_packet->operation = VSTOR_OPERATION_QUERY_PROTOCOL_VERSION; + vstor_packet->flags = REQUEST_COMPLETION_FLAG; + + vstor_packet->version.major_minor = VMSTOR_PROTOCOL_VERSION_CURRENT; + FILL_VMSTOR_REVISION(vstor_packet->version.revision); + + 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) + goto cleanup; + + t = wait_for_completion_timeout(&request->wait_event, 5*HZ); + if (t == 0) { + ret = -ETIMEDOUT; + goto cleanup; + } + + if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || + vstor_packet->status != 0) + goto cleanup; + + + memset(vstor_packet, 0, sizeof(struct vstor_packet)); + vstor_packet->operation = VSTOR_OPERATION_QUERY_PROPERTIES; + vstor_packet->flags = REQUEST_COMPLETION_FLAG; + vstor_packet->storage_channel_properties.port_number = + stor_device->port_number; + + 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) + goto cleanup; + + t = wait_for_completion_timeout(&request->wait_event, 5*HZ); + if (t == 0) { + ret = -ETIMEDOUT; + goto cleanup; + } + + if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || + 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; + + memset(vstor_packet, 0, sizeof(struct vstor_packet)); + vstor_packet->operation = VSTOR_OPERATION_END_INITIALIZATION; + vstor_packet->flags = REQUEST_COMPLETION_FLAG; + + 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) + goto cleanup; + + t = wait_for_completion_timeout(&request->wait_event, 5*HZ); + if (t == 0) { + ret = -ETIMEDOUT; + goto cleanup; + } + + if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || + vstor_packet->status != 0) + goto cleanup; + + +cleanup: + return ret; +} + +static void storvsc_on_io_completion(struct hv_device *device, + struct vstor_packet *vstor_packet, + struct hv_storvsc_request *request) +{ + struct storvsc_device *stor_device; + struct vstor_packet *stor_pkt; + + stor_device = hv_get_drvdata(device); + stor_pkt = &request->vstor_packet; + + /* + * The current SCSI handling on the host side does + * not correctly handle: + * INQUIRY command with page code parameter set to 0x80 + * MODE_SENSE command with cmd[2] == 0x1c + * + * Setup srb and scsi status so this won't be fatal. + * We do this so we can distinguish truly fatal failues + * (srb status == 0x4) and off-line the device in that case. + */ + + if ((stor_pkt->vm_srb.cdb[0] == INQUIRY) || + (stor_pkt->vm_srb.cdb[0] == MODE_SENSE)) { + vstor_packet->vm_srb.scsi_status = 0; + vstor_packet->vm_srb.srb_status = 0x1; + } + + + /* Copy over the status...etc */ + 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 (vstor_packet->vm_srb.scsi_status != 0 || + vstor_packet->vm_srb.srb_status != 1){ + dev_warn(&device->device, + "cmd 0x%x scsi status 0x%x srb status 0x%x\n", + stor_pkt->vm_srb.cdb[0], + vstor_packet->vm_srb.scsi_status, + vstor_packet->vm_srb.srb_status); + } + + if ((vstor_packet->vm_srb.scsi_status & 0xFF) == 0x02) { + /* CHECK_CONDITION */ + if (vstor_packet->vm_srb.srb_status & 0x80) { + /* autosense data available */ + dev_warn(&device->device, + "stor pkt %p autosense data valid - len %d\n", + request, + vstor_packet->vm_srb.sense_info_length); + + memcpy(request->sense_buffer, + vstor_packet->vm_srb.sense_data, + vstor_packet->vm_srb.sense_info_length); + + } + } + + stor_pkt->vm_srb.data_transfer_length = + vstor_packet->vm_srb.data_transfer_length; + + request->on_io_completion(request); + + if (atomic_dec_and_test(&stor_device->num_outstanding_req) && + stor_device->drain_notify) + wake_up(&stor_device->waiting_to_drain); + + +} + +static void storvsc_on_receive(struct hv_device *device, + struct vstor_packet *vstor_packet, + struct hv_storvsc_request *request) +{ + switch (vstor_packet->operation) { + case VSTOR_OPERATION_COMPLETE_IO: + storvsc_on_io_completion(device, vstor_packet, request); + break; + case VSTOR_OPERATION_REMOVE_DEVICE: + + default: + break; + } +} + +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 hv_storvsc_request *request; + int ret; + + + stor_device = get_in_stor_device(device); + 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) { + + request = (struct hv_storvsc_request *) + (unsigned long)request_id; + + if ((request == &stor_device->init_request) || + (request == &stor_device->reset_request)) { + + memcpy(&request->vstor_packet, packet, + sizeof(struct vstor_packet)); + complete(&request->wait_event); + } else { + storvsc_on_receive(device, + (struct vstor_packet *)packet, + request); + } + } else { + break; + } + } while (1); + + return; +} + +static int storvsc_connect_to_vsp(struct hv_device *device, u32 ring_size) +{ + struct vmstorage_channel_properties props; + int ret; + + memset(&props, 0, sizeof(struct vmstorage_channel_properties)); + + /* Open the channel */ + ret = vmbus_open(device->channel, + ring_size, + ring_size, + (void *)&props, + sizeof(struct vmstorage_channel_properties), + storvsc_on_channel_callback, device); + + if (ret != 0) + return ret; + + ret = storvsc_channel_init(device); + + return ret; +} + +static int storvsc_dev_remove(struct hv_device *device) +{ + struct storvsc_device *stor_device; + unsigned long flags; + + stor_device = hv_get_drvdata(device); + + spin_lock_irqsave(&device->channel->inbound_lock, flags); + stor_device->destroy = true; + spin_unlock_irqrestore(&device->channel->inbound_lock, flags); + + /* + * At this point, all outbound traffic should be disable. We + * only allow inbound traffic (responses) to proceed so that + * outstanding requests can be completed. + */ + + storvsc_wait_to_drain(stor_device); + + /* + * Since we have already drained, we don't need to busy wait + * as was done in final_release_stor_device() + * Note that we cannot set the ext pointer to NULL until + * we have drained - to drain the outgoing packets, we need to + * allow incoming packets. + */ + spin_lock_irqsave(&device->channel->inbound_lock, flags); + hv_set_drvdata(device, NULL); + spin_unlock_irqrestore(&device->channel->inbound_lock, flags); + + /* Close the channel */ + vmbus_close(device->channel); + + kfree(stor_device); + return 0; +} + +static int storvsc_do_io(struct hv_device *device, + struct hv_storvsc_request *request) +{ + struct storvsc_device *stor_device; + struct vstor_packet *vstor_packet; + int ret = 0; + + vstor_packet = &request->vstor_packet; + stor_device = get_out_stor_device(device); + + if (!stor_device) + return -ENODEV; + + + request->device = device; + + + vstor_packet->flags |= REQUEST_COMPLETION_FLAG; + + vstor_packet->vm_srb.length = sizeof(struct vmscsi_request); + + + vstor_packet->vm_srb.sense_info_length = SENSE_BUFFER_SIZE; + + + vstor_packet->vm_srb.data_transfer_length = + request->data_buffer.len; + + vstor_packet->operation = VSTOR_OPERATION_EXECUTE_SRB; + + if (request->data_buffer.len) { + ret = vmbus_sendpacket_multipagebuffer(device->channel, + &request->data_buffer, + vstor_packet, + sizeof(struct vstor_packet), + (unsigned long)request); + } else { + 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) + return ret; + + atomic_inc(&stor_device->num_outstanding_req); + + return ret; +} + +static void storvsc_get_ide_info(struct hv_device *dev, int *target, int *path) +{ + *target = + dev->dev_instance.b[5] << 8 | dev->dev_instance.b[4]; + + *path = + dev->dev_instance.b[3] << 24 | + dev->dev_instance.b[2] << 16 | + dev->dev_instance.b[1] << 8 | dev->dev_instance.b[0]; +} + static int storvsc_device_alloc(struct scsi_device *sdevice) { @@ -307,7 +963,8 @@ static unsigned int copy_to_bounce_buffer(struct scatterlist *orig_sgl, static int storvsc_remove(struct hv_device *dev) { - struct Scsi_Host *host = dev_get_drvdata(&dev->device); + struct storvsc_device *stor_device = hv_get_drvdata(dev); + struct Scsi_Host *host = stor_device->host; struct hv_host_device *host_dev = (struct hv_host_device *)host->hostdata; @@ -355,9 +1012,9 @@ static int storvsc_host_reset(struct hv_device *device) int ret, t; - stor_device = get_stor_device(device); + stor_device = get_out_stor_device(device); if (!stor_device) - return -1; + return -ENODEV; request = &stor_device->reset_request; vstor_packet = &request->vstor_packet; @@ -389,7 +1046,6 @@ static int storvsc_host_reset(struct hv_device *device) */ cleanup: - put_stor_device(device); return ret; } @@ -413,9 +1069,9 @@ static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd) /* - * storvsc_commmand_completion - Command completion processing + * storvsc_command_completion - Command completion processing */ -static void storvsc_commmand_completion(struct hv_storvsc_request *request) +static void storvsc_command_completion(struct hv_storvsc_request *request) { struct storvsc_cmd_request *cmd_request = (struct storvsc_cmd_request *)request->context; @@ -426,18 +1082,26 @@ static void storvsc_commmand_completion(struct hv_storvsc_request *request) struct scsi_sense_hdr sense_hdr; struct vmscsi_request *vm_srb; + vm_srb = &request->vstor_packet.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), + if (vm_srb->data_in == READ_TYPE) { + copy_from_bounce_buffer(scsi_sglist(scmnd), cmd_request->bounce_sgl, scsi_sg_count(scmnd)); - destroy_bounce_buffer(cmd_request->bounce_sgl, - cmd_request->bounce_sgl_count); + 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 there is an error; offline the device since all + * error recovery strategies would have already been + * deployed on the host side. + */ + if (vm_srb->srb_status == 0x4) + scmnd->result = DID_TARGET_FAILURE << 16; + else + scmnd->result = vm_srb->scsi_status; if (scmnd->result) { if (scsi_normalize_sense(scmnd->sense_buffer, @@ -459,6 +1123,22 @@ static void storvsc_commmand_completion(struct hv_storvsc_request *request) kmem_cache_free(host_dev->request_pool, cmd_request); } +static bool storvsc_check_scsi_cmd(struct scsi_cmnd *scmnd) +{ + bool allowed = true; + u8 scsi_op = scmnd->cmnd[0]; + + switch (scsi_op) { + /* smartd sends this command, which will offline the device */ + case SET_WINDOW: + scmnd->result = DID_ERROR << 16; + allowed = false; + break; + default: + break; + } + return allowed; +} /* * storvsc_queuecommand - Initiate command processing @@ -478,6 +1158,10 @@ static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, unsigned int sg_count = 0; struct vmscsi_request *vm_srb; + if (storvsc_check_scsi_cmd(scmnd) == false) { + done(scmnd); + return 0; + } /* If retrying, no need to prep the cmd */ if (scmnd->host_scribble) { @@ -523,7 +1207,7 @@ static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, break; } - request->on_io_completion = storvsc_commmand_completion; + request->on_io_completion = storvsc_command_completion; request->context = cmd_request;/* scmnd; */ vm_srb->port_number = host_dev->port; @@ -561,12 +1245,10 @@ static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, ALIGN(scsi_bufflen(scmnd), PAGE_SIZE) >> PAGE_SHIFT; - /* - * FIXME: We can optimize on reads by just skipping - * this - */ - copy_to_bounce_buffer(sgl, cmd_request->bounce_sgl, - scsi_sg_count(scmnd)); + if (vm_srb->data_in == WRITE_TYPE) + copy_to_bounce_buffer(sgl, + cmd_request->bounce_sgl, + scsi_sg_count(scmnd)); sgl = cmd_request->bounce_sgl; sg_count = cmd_request->bounce_sgl_count; @@ -589,20 +1271,12 @@ retry_request: /* Invokes the vsc to start an IO */ ret = storvsc_do_io(dev, &cmd_request->request); - if (ret == -1) { + if (ret == -EAGAIN) { /* no more space */ - 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)); + if (cmd_request->bounce_sgl_count) destroy_bounce_buffer(cmd_request->bounce_sgl, - cmd_request->bounce_sgl_count); - } + cmd_request->bounce_sgl_count); kmem_cache_free(host_dev->request_pool, cmd_request); @@ -646,25 +1320,46 @@ static struct scsi_host_template scsi_driver = { .dma_boundary = PAGE_SIZE-1, }; +enum { + SCSI_GUID, + IDE_GUID, +}; + +static const struct hv_vmbus_device_id id_table[] = { + /* SCSI guid */ + { VMBUS_DEVICE(0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, + 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f) + .driver_data = SCSI_GUID }, + /* IDE guid */ + { VMBUS_DEVICE(0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, + 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5) + .driver_data = IDE_GUID }, + { }, +}; + +MODULE_DEVICE_TABLE(vmbus, id_table); + /* * storvsc_probe - Add a new device for this driver */ -static int storvsc_probe(struct hv_device *device) +static int storvsc_probe(struct hv_device *device, + const struct hv_vmbus_device_id *dev_id) { int ret; struct Scsi_Host *host; struct hv_host_device *host_dev; - struct storvsc_device_info device_info; + bool dev_is_ide = ((dev_id->driver_data == IDE_GUID) ? true : false); + int path = 0; + int target = 0; + struct storvsc_device *stor_device; host = scsi_host_alloc(&scsi_driver, sizeof(struct hv_host_device)); if (!host) return -ENOMEM; - dev_set_drvdata(&device->device, host); - host_dev = (struct hv_host_device *)host->hostdata; memset(host_dev, 0, sizeof(struct hv_host_device)); @@ -681,19 +1376,33 @@ static int storvsc_probe(struct hv_device *device) return -ENOMEM; } - 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); + stor_device = kzalloc(sizeof(struct storvsc_device), GFP_KERNEL); + if (!stor_device) { + kmem_cache_destroy(host_dev->request_pool); + scsi_host_put(host); + return -ENOMEM; + } - if (ret != 0) { + stor_device->destroy = false; + init_waitqueue_head(&stor_device->waiting_to_drain); + stor_device->device = device; + stor_device->host = host; + hv_set_drvdata(device, stor_device); + + stor_device->port_number = host->host_no; + ret = storvsc_connect_to_vsp(device, storvsc_ringbuffer_size); + if (ret) { kmem_cache_destroy(host_dev->request_pool); scsi_host_put(host); - return -1; + kfree(stor_device); + return ret; } - host_dev->path = device_info.path_id; - host_dev->target = device_info.target_id; + if (dev_is_ide) + storvsc_get_ide_info(device, &target, &path); + + host_dev->path = stor_device->path_id; + host_dev->target = stor_device->target_id; /* max # of devices per target */ host->max_lun = STORVSC_MAX_LUNS_PER_TARGET; @@ -701,54 +1410,43 @@ static int storvsc_probe(struct hv_device *device) host->max_id = STORVSC_MAX_TARGETS; /* max # of channels */ host->max_channel = STORVSC_MAX_CHANNELS - 1; + /* max cmd length */ + host->max_cmd_len = STORVSC_MAX_CMD_LEN; /* Register the HBA and start the scsi bus scan */ ret = scsi_add_host(host, &device->device); - if (ret != 0) { - - storvsc_dev_remove(device); + if (ret != 0) + goto err_out; - kmem_cache_destroy(host_dev->request_pool); - scsi_host_put(host); - return -1; + if (!dev_is_ide) { + scsi_scan_host(host); + return 0; + } + ret = scsi_add_device(host, 0, target, 0); + if (ret) { + scsi_remove_host(host); + goto err_out; } + return 0; - scsi_scan_host(host); - return ret; +err_out: + storvsc_dev_remove(device); + kmem_cache_destroy(host_dev->request_pool); + scsi_host_put(host); + return -ENODEV; } /* The one and only one */ static struct hv_driver storvsc_drv = { + .name = "storvsc", + .id_table = id_table, .probe = storvsc_probe, .remove = storvsc_remove, }; -/* - * We use a DMI table to determine if we should autoload this driver This is - * needed by distro tools to determine if the hyperv drivers should be - * installed and/or configured. We don't do anything else with the table, but - * it needs to be present. - */ - -static const struct dmi_system_id __initconst -hv_stor_dmi_table[] __maybe_unused = { - { - .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, hv_stor_dmi_table); - static int __init storvsc_drv_init(void) { - int ret; - struct hv_driver *drv = &storvsc_drv; u32 max_outstanding_req_per_channel; /* @@ -757,32 +1455,22 @@ static int __init storvsc_drv_init(void) * the ring buffer indices) by the max request size (which is * vmbus_channel_packet_multipage_buffer + struct vstor_packet + u64) */ - max_outstanding_req_per_channel = - ((storvsc_ringbuffer_size - PAGE_SIZE) / - ALIGN(MAX_MULTIPAGE_BUFFER_PACKET + - sizeof(struct vstor_packet) + sizeof(u64), - sizeof(u64))); - - memcpy(&drv->dev_type, &stor_vsci_device_type, - sizeof(struct hv_guid)); + ((storvsc_ringbuffer_size - PAGE_SIZE) / + ALIGN(MAX_MULTIPAGE_BUFFER_PACKET + + sizeof(struct vstor_packet) + sizeof(u64), + sizeof(u64))); if (max_outstanding_req_per_channel < STORVSC_MAX_IO_REQUESTS) - return -1; - - drv->driver.name = driver_name; - + return -EINVAL; - /* The driver belongs to vmbus */ - ret = vmbus_child_driver_register(&drv->driver); - - return ret; + return vmbus_driver_register(&storvsc_drv); } static void __exit storvsc_drv_exit(void) { - vmbus_child_driver_unregister(&storvsc_drv.driver); + vmbus_driver_unregister(&storvsc_drv); } MODULE_LICENSE("GPL"); diff --git a/drivers/staging/hv/tools/hv_kvp_daemon.c b/drivers/staging/hv/tools/hv_kvp_daemon.c deleted file mode 100644 index a4a407f..0000000 --- a/drivers/staging/hv/tools/hv_kvp_daemon.c +++ /dev/null @@ -1,493 +0,0 @@ -/* - * An implementation of key value pair (KVP) functionality for Linux. - * - * - * Copyright (C) 2010, Novell, Inc. - * Author : K. Y. Srinivasan <ksrinivasan@novell.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/poll.h> -#include <sys/utsname.h> -#include <linux/types.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <arpa/inet.h> -#include <linux/connector.h> -#include <linux/netlink.h> -#include <ifaddrs.h> -#include <netdb.h> -#include <syslog.h> - -/* - * KYS: TODO. Need to register these in the kernel. - * - * The following definitions are shared with the in-kernel component; do not - * change any of this without making the corresponding changes in - * the KVP kernel component. - */ -#define CN_KVP_IDX 0x9 /* MSFT KVP functionality */ -#define CN_KVP_VAL 0x1 /* This supports queries from the kernel */ -#define CN_KVP_USER_VAL 0x2 /* This supports queries from the user */ - -/* - * KVP protocol: The user mode component first registers with the - * the kernel component. Subsequently, the kernel component requests, data - * for the specified keys. In response to this message the user mode component - * fills in the value corresponding to the specified key. We overload the - * sequence field in the cn_msg header to define our KVP message types. - * - * We use this infrastructure for also supporting queries from user mode - * application for state that may be maintained in the KVP kernel component. - * - * XXXKYS: Have a shared header file between the user and kernel (TODO) - */ - -enum kvp_op { - KVP_REGISTER = 0, /* Register the user mode component*/ - KVP_KERNEL_GET, /*Kernel is requesting the value for the specified key*/ - KVP_KERNEL_SET, /*Kernel is providing the value for the specified key*/ - KVP_USER_GET, /*User is requesting the value for the specified key*/ - KVP_USER_SET /*User is providing the value for the specified key*/ -}; - -#define HV_KVP_EXCHANGE_MAX_KEY_SIZE 512 -#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE 2048 - -struct hv_ku_msg { - __u32 kvp_index; - __u8 kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */ - __u8 kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key value */ -}; - -enum key_index { - FullyQualifiedDomainName = 0, - IntegrationServicesVersion, /*This key is serviced in the kernel*/ - NetworkAddressIPv4, - NetworkAddressIPv6, - OSBuildNumber, - OSName, - OSMajorVersion, - OSMinorVersion, - OSVersion, - ProcessorArchitecture -}; - -/* - * End of shared definitions. - */ - -static char kvp_send_buffer[4096]; -static char kvp_recv_buffer[4096]; -static struct sockaddr_nl addr; - -static char *os_name = ""; -static char *os_major = ""; -static char *os_minor = ""; -static char *processor_arch; -static char *os_build; -static char *lic_version; -static struct utsname uts_buf; - -void kvp_get_os_info(void) -{ - FILE *file; - char *p, buf[512]; - - uname(&uts_buf); - os_build = uts_buf.release; - processor_arch= uts_buf.machine; - - file = fopen("/etc/SuSE-release", "r"); - if (file != NULL) - goto kvp_osinfo_found; - file = fopen("/etc/redhat-release", "r"); - if (file != NULL) - goto kvp_osinfo_found; - /* - * Add code for other supported platforms. - */ - - /* - * We don't have information about the os. - */ - os_name = uts_buf.sysname; - return; - -kvp_osinfo_found: - /* up to three lines */ - p = fgets(buf, sizeof(buf), file); - if (p) { - p = strchr(buf, '\n'); - if (p) - *p = '\0'; - p = strdup(buf); - if (!p) - goto done; - os_name = p; - - /* second line */ - p = fgets(buf, sizeof(buf), file); - if (p) { - p = strchr(buf, '\n'); - if (p) - *p = '\0'; - p = strdup(buf); - if (!p) - goto done; - os_major = p; - - /* third line */ - p = fgets(buf, sizeof(buf), file); - if (p) { - p = strchr(buf, '\n'); - if (p) - *p = '\0'; - p = strdup(buf); - if (p) - os_minor = p; - } - } - } - -done: - fclose(file); - return; -} - -static int -kvp_get_ip_address(int family, char *buffer, int length) -{ - struct ifaddrs *ifap; - struct ifaddrs *curp; - int ipv4_len = strlen("255.255.255.255") + 1; - int ipv6_len = strlen("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")+1; - int offset = 0; - const char *str; - char tmp[50]; - int error = 0; - - /* - * On entry into this function, the buffer is capable of holding the - * maximum key value (2048 bytes). - */ - - if (getifaddrs(&ifap)) { - strcpy(buffer, "getifaddrs failed\n"); - return 1; - } - - curp = ifap; - while (curp != NULL) { - if ((curp->ifa_addr != NULL) && - (curp->ifa_addr->sa_family == family)) { - if (family == AF_INET) { - struct sockaddr_in *addr = - (struct sockaddr_in *) curp->ifa_addr; - - str = inet_ntop(family, &addr->sin_addr, - tmp, 50); - if (str == NULL) { - strcpy(buffer, "inet_ntop failed\n"); - error = 1; - goto getaddr_done; - } - if (offset == 0) - strcpy(buffer, tmp); - else - strcat(buffer, tmp); - strcat(buffer, ";"); - - offset += strlen(str) + 1; - if ((length - offset) < (ipv4_len + 1)) - goto getaddr_done; - - } else { - - /* - * We only support AF_INET and AF_INET6 - * and the list of addresses is separated by a ";". - */ - struct sockaddr_in6 *addr = - (struct sockaddr_in6 *) curp->ifa_addr; - - str = inet_ntop(family, - &addr->sin6_addr.s6_addr, - tmp, 50); - if (str == NULL) { - strcpy(buffer, "inet_ntop failed\n"); - error = 1; - goto getaddr_done; - } - if (offset == 0) - strcpy(buffer, tmp); - else - strcat(buffer, tmp); - strcat(buffer, ";"); - offset += strlen(str) + 1; - if ((length - offset) < (ipv6_len + 1)) - goto getaddr_done; - - } - - } - curp = curp->ifa_next; - } - -getaddr_done: - freeifaddrs(ifap); - return error; -} - - -static int -kvp_get_domain_name(char *buffer, int length) -{ - struct addrinfo hints, *info ; - gethostname(buffer, length); - int error = 0; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; /*Get only ipv4 addrinfo. */ - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_CANONNAME; - - error = getaddrinfo(buffer, "http", &hints, &info); - if (error != 0) { - strcpy(buffer, "getaddrinfo failed\n"); - error = 1; - goto get_domain_done; - } - strcpy(buffer, info->ai_canonname); -get_domain_done: - freeaddrinfo(info); - return error; -} - -static int -netlink_send(int fd, struct cn_msg *msg) -{ - struct nlmsghdr *nlh; - unsigned int size; - struct msghdr message; - char buffer[64]; - struct iovec iov[2]; - - size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len); - - nlh = (struct nlmsghdr *)buffer; - nlh->nlmsg_seq = 0; - nlh->nlmsg_pid = getpid(); - nlh->nlmsg_type = NLMSG_DONE; - nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh)); - nlh->nlmsg_flags = 0; - - iov[0].iov_base = nlh; - iov[0].iov_len = sizeof(*nlh); - - iov[1].iov_base = msg; - iov[1].iov_len = size; - - memset(&message, 0, sizeof(message)); - message.msg_name = &addr; - message.msg_namelen = sizeof(addr); - message.msg_iov = iov; - message.msg_iovlen = 2; - - return sendmsg(fd, &message, 0); -} - -int main(void) -{ - int fd, len, sock_opt; - int error; - struct cn_msg *message; - struct pollfd pfd; - struct nlmsghdr *incoming_msg; - struct cn_msg *incoming_cn_msg; - struct hv_ku_msg *hv_msg; - char *p; - char *key_value; - char *key_name; - - daemon(1, 0); - openlog("KVP", 0, LOG_USER); - syslog(LOG_INFO, "KVP starting; pid is:%d", getpid()); - /* - * Retrieve OS release information. - */ - kvp_get_os_info(); - - fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); - if (fd < 0) { - syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd); - exit(-1); - } - addr.nl_family = AF_NETLINK; - addr.nl_pad = 0; - addr.nl_pid = 0; - addr.nl_groups = CN_KVP_IDX; - - - error = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); - if (error < 0) { - syslog(LOG_ERR, "bind failed; error:%d", error); - close(fd); - exit(-1); - } - sock_opt = addr.nl_groups; - setsockopt(fd, 270, 1, &sock_opt, sizeof(sock_opt)); - /* - * Register ourselves with the kernel. - */ - message = (struct cn_msg *)kvp_send_buffer; - message->id.idx = CN_KVP_IDX; - message->id.val = CN_KVP_VAL; - message->seq = KVP_REGISTER; - message->ack = 0; - message->len = 0; - - len = netlink_send(fd, message); - if (len < 0) { - syslog(LOG_ERR, "netlink_send failed; error:%d", len); - close(fd); - exit(-1); - } - - pfd.fd = fd; - - while (1) { - pfd.events = POLLIN; - pfd.revents = 0; - poll(&pfd, 1, -1); - - len = recv(fd, kvp_recv_buffer, sizeof(kvp_recv_buffer), 0); - - if (len < 0) { - syslog(LOG_ERR, "recv failed; error:%d", len); - close(fd); - return -1; - } - - incoming_msg = (struct nlmsghdr *)kvp_recv_buffer; - incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg); - - switch (incoming_cn_msg->seq) { - case KVP_REGISTER: - /* - * Driver is registering with us; stash away the version - * information. - */ - p = (char *)incoming_cn_msg->data; - lic_version = malloc(strlen(p) + 1); - if (lic_version) { - strcpy(lic_version, p); - syslog(LOG_INFO, "KVP LIC Version: %s", - lic_version); - } else { - syslog(LOG_ERR, "malloc failed"); - } - continue; - - case KVP_KERNEL_GET: - break; - default: - continue; - } - - hv_msg = (struct hv_ku_msg *)incoming_cn_msg->data; - key_name = (char *)hv_msg->kvp_key; - key_value = (char *)hv_msg->kvp_value; - - switch (hv_msg->kvp_index) { - case FullyQualifiedDomainName: - kvp_get_domain_name(key_value, - HV_KVP_EXCHANGE_MAX_VALUE_SIZE); - strcpy(key_name, "FullyQualifiedDomainName"); - break; - case IntegrationServicesVersion: - strcpy(key_name, "IntegrationServicesVersion"); - strcpy(key_value, lic_version); - break; - case NetworkAddressIPv4: - kvp_get_ip_address(AF_INET, key_value, - HV_KVP_EXCHANGE_MAX_VALUE_SIZE); - strcpy(key_name, "NetworkAddressIPv4"); - break; - case NetworkAddressIPv6: - kvp_get_ip_address(AF_INET6, key_value, - HV_KVP_EXCHANGE_MAX_VALUE_SIZE); - strcpy(key_name, "NetworkAddressIPv6"); - break; - case OSBuildNumber: - strcpy(key_value, os_build); - strcpy(key_name, "OSBuildNumber"); - break; - case OSName: - strcpy(key_value, os_name); - strcpy(key_name, "OSName"); - break; - case OSMajorVersion: - strcpy(key_value, os_major); - strcpy(key_name, "OSMajorVersion"); - break; - case OSMinorVersion: - strcpy(key_value, os_minor); - strcpy(key_name, "OSMinorVersion"); - break; - case OSVersion: - strcpy(key_value, os_build); - strcpy(key_name, "OSVersion"); - break; - case ProcessorArchitecture: - strcpy(key_value, processor_arch); - strcpy(key_name, "ProcessorArchitecture"); - break; - default: - strcpy(key_value, "Unknown Key"); - /* - * We use a null key name to terminate enumeration. - */ - strcpy(key_name, ""); - break; - } - /* - * Send the value back to the kernel. The response is - * already in the receive buffer. Update the cn_msg header to - * reflect the key value that has been added to the message - */ - - incoming_cn_msg->id.idx = CN_KVP_IDX; - incoming_cn_msg->id.val = CN_KVP_VAL; - incoming_cn_msg->seq = KVP_USER_SET; - incoming_cn_msg->ack = 0; - incoming_cn_msg->len = sizeof(struct hv_ku_msg); - - len = netlink_send(fd, incoming_cn_msg); - if (len < 0) { - syslog(LOG_ERR, "net_link send failed; error:%d", len); - exit(-1); - } - } - -} diff --git a/drivers/staging/hv/vmbus_drv.c b/drivers/staging/hv/vmbus_drv.c deleted file mode 100644 index 1c949f5..0000000 --- a/drivers/staging/hv/vmbus_drv.c +++ /dev/null @@ -1,802 +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> - * 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> -#include <linux/irq.h> -#include <linux/interrupt.h> -#include <linux/sysctl.h> -#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 "hyperv.h" -#include "hyperv_vmbus.h" - - -static struct acpi_device *hv_acpi_dev; - -static struct tasklet_struct msg_dpc; -static struct tasklet_struct event_dpc; - -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 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; - - 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) -{ - struct hv_device *hv_dev = device_to_hv_device(dev); - struct hv_device_info device_info; - - memset(&device_info, 0, sizeof(struct hv_device_info)); - - get_channel_info(hv_dev, &device_info); - - 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[] = { - __ATTR(id, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(state, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(class_id, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(device_id, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(monitor_id, S_IRUGO, vmbus_show_device_attr, NULL), - - __ATTR(server_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(server_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(server_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL), - - __ATTR(client_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(client_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(client_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL), - - __ATTR(out_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(out_read_index, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(out_write_index, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(out_read_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(out_write_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL), - - __ATTR(in_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(in_read_index, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(in_write_index, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(in_read_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(in_write_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR_NULL -}; - - -/* - * 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; -} - - -/* - * 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 *hv_dev = device_to_hv_device(device); - - /* We found our driver ? */ - if (memcmp(&hv_dev->dev_type, &drv->dev_type, - sizeof(struct hv_guid)) == 0) - match = 1; - - return match; -} - -/* - * 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 (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 = -ENODEV; - } - 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); - - if (drv->remove) { - ret = drv->remove(dev); - } else { - pr_err("remove not set for driver %s\n", - dev_name(child_device)); - ret = -ENODEV; - } - } - - return 0; -} - - -/* - * vmbus_shutdown - Shutdown a vmbus device - */ -static void vmbus_shutdown(struct device *child_device) -{ - struct hv_driver *drv; - struct hv_device *dev = device_to_hv_device(child_device); - - - /* The device may not be attached yet */ - if (!child_device->driver) - return; - - drv = drv_to_hv_drv(child_device->driver); - - if (drv->shutdown) - drv->shutdown(dev); - - return; -} - - -/* - * vmbus_device_release - Final callback release of the vmbus child device - */ -static void vmbus_device_release(struct device *device) -{ - struct hv_device *hv_dev = device_to_hv_device(device); - - kfree(hv_dev); - -} - -/* 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; -}; - -static void vmbus_onmessage_work(struct work_struct *work) -{ - struct onmessage_work_context *ctx; - - ctx = container_of(work, struct onmessage_work_context, - work); - vmbus_onmessage(&ctx->msg); - kfree(ctx); -} - -/* - * vmbus_on_msg_dpc - DPC routine to handle messages from the hypervisior - */ -static void vmbus_on_msg_dpc(unsigned long data) -{ - int cpu = smp_processor_id(); - void *page_addr = hv_context.synic_message_page[cpu]; - struct hv_message *msg = (struct hv_message *)page_addr + - VMBUS_MESSAGE_SINT; - struct onmessage_work_context *ctx; - - while (1) { - if (msg->header.message_type == HVMSG_NONE) { - /* no msg */ - break; - } else { - ctx = kmalloc(sizeof(*ctx), GFP_ATOMIC); - if (ctx == NULL) - continue; - INIT_WORK(&ctx->work, vmbus_onmessage_work); - memcpy(&ctx->msg, msg, sizeof(*msg)); - queue_work(vmbus_connection.work_queue, &ctx->work); - } - - msg->header.message_type = HVMSG_NONE; - - /* - * Make sure the write to MessageType (ie set to - * HVMSG_NONE) happens before we read the - * MessagePending and EOMing. Otherwise, the EOMing - * will not deliver any more messages since there is - * no empty slot - */ - smp_mb(); - - if (msg->header.message_flags.msg_pending) { - /* - * This will cause message queue rescan to - * possibly deliver another msg from the - * hypervisor - */ - wrmsrl(HV_X64_MSR_EOM, 0); - } - } -} - -/* - * vmbus_on_isr - ISR routine - */ -static int vmbus_on_isr(void) -{ - int ret = 0; - int cpu = smp_processor_id(); - void *page_addr; - struct hv_message *msg; - union hv_synic_event_flags *event; - - page_addr = hv_context.synic_message_page[cpu]; - 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) - ret |= 0x1; - - 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])) - ret |= 0x2; - - return ret; -} - - -static irqreturn_t vmbus_isr(int irq, void *dev_id) -{ - int ret; - - ret = vmbus_on_isr(); - - /* Schedules a dpc if necessary */ - if (ret > 0) { - if (test_bit(0, (unsigned long *)&ret)) - tasklet_schedule(&msg_dpc); - - if (test_bit(1, (unsigned long *)&ret)) - tasklet_schedule(&event_dpc); - - return IRQ_HANDLED; - } else { - return IRQ_NONE; - } -} - -/* - * vmbus_bus_init -Main vmbus driver initialization routine. - * - * Here, we - * - initialize the vmbus driver context - * - invoke the vmbus hv main init routine - * - get the irq resource - * - retrieve the channel offers - */ -static int vmbus_bus_init(int irq) -{ - int ret; - unsigned int vector; - - /* Hypervisor initialization...setup hypercall page..etc */ - ret = hv_init(); - if (ret != 0) { - pr_err("Unable to initialize the hypervisor - 0x%x\n", ret); - return ret; - } - - /* Initialize the bus context */ - 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(&hv_bus); - if (ret) - return ret; - - /* Get the interrupt resource */ - ret = request_irq(irq, vmbus_isr, IRQF_SAMPLE_RANDOM, - driver_name, hv_acpi_dev); - - if (ret != 0) { - pr_err("Unable to request IRQ %d\n", - irq); - - bus_unregister(&hv_bus); - - return ret; - } - - vector = IRQ0_VECTOR + irq; - - /* - * 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) { - free_irq(irq, hv_acpi_dev); - bus_unregister(&hv_bus); - return ret; - } - - - vmbus_request_offers(); - - return 0; -} - -/** - * vmbus_child_driver_register() - Register a vmbus's child driver - * @drv: Pointer to driver structure you want to register - * - * - * Registers the given driver with Linux through the 'driver_register()' call - * And sets up the hyper-v vmbus handling for this driver. - * It will return the state of the 'driver_register()' call. - * - * Mainly used by Hyper-V drivers. - */ -int vmbus_child_driver_register(struct device_driver *drv) -{ - int ret; - - pr_info("child driver registering - name %s\n", drv->name); - - /* The child driver on this vmbus */ - drv->bus = &hv_bus; - - ret = driver_register(drv); - - vmbus_request_offers(); - - return ret; -} -EXPORT_SYMBOL(vmbus_child_driver_register); - -/** - * vmbus_child_driver_unregister() - Unregister a vmbus's child driver - * @drv: Pointer to driver structure you want to un-register - * - * - * Un-register the given driver with Linux through the 'driver_unregister()' - * call. And ungegisters the driver from the Hyper-V vmbus handler. - * - * Mainly used by Hyper-V drivers. - */ -void vmbus_child_driver_unregister(struct device_driver *drv) -{ - pr_info("child driver unregistering - name %s\n", drv->name); - - driver_unregister(drv); - -} -EXPORT_SYMBOL(vmbus_child_driver_unregister); - -/* - * vmbus_child_device_create - Creates and registers a new child device - * on the vmbus. - */ -struct hv_device *vmbus_child_device_create(struct hv_guid *type, - struct hv_guid *instance, - struct vmbus_channel *channel) -{ - struct hv_device *child_device_obj; - - /* Allocate the new child device */ - child_device_obj = kzalloc(sizeof(struct hv_device), GFP_KERNEL); - if (!child_device_obj) { - pr_err("Unable to allocate device object for child device\n"); - return NULL; - } - - child_device_obj->channel = channel; - memcpy(&child_device_obj->dev_type, type, sizeof(struct hv_guid)); - memcpy(&child_device_obj->dev_instance, instance, - sizeof(struct hv_guid)); - - - return child_device_obj; -} - -/* - * vmbus_child_device_register - Register the child device - */ -int vmbus_child_device_register(struct hv_device *child_device_obj) -{ - int ret = 0; - - static atomic_t device_num = ATOMIC_INIT(0); - - /* 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 = &hv_bus; /* device->dev.bus; */ - child_device_obj->device.parent = &hv_acpi_dev->dev; - child_device_obj->device.release = vmbus_device_release; - - /* - * Register with the LDM. This will kick off the driver/device - * binding...which will eventually call vmbus_match() and vmbus_probe() - */ - ret = device_register(&child_device_obj->device); - - if (ret) - pr_err("Unable to register child device\n"); - else - pr_info("child device %s registered\n", - dev_name(&child_device_obj->device)); - - return ret; -} - -/* - * vmbus_child_device_unregister - Remove the specified child device - * from the vmbus. - */ -void vmbus_child_device_unregister(struct hv_device *device_obj) -{ - /* - * Kick off the process of unregistering the device. - * This will call vmbus_remove() and eventually vmbus_device_release() - */ - device_unregister(&device_obj->device); - - pr_info("child device %s unregistered\n", - dev_name(&device_obj->device)); -} - - -/* - * VMBUS is an acpi enumerated device. Get the the IRQ information - * from DSDT. - */ - -static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *irq) -{ - - if (res->type == ACPI_RESOURCE_TYPE_IRQ) { - struct acpi_resource_irq *irqp; - irqp = &res->data.irq; - - *((unsigned int *)irq) = irqp->interrupts[0]; - } - - return AE_OK; -} - -static int vmbus_acpi_add(struct acpi_device *device) -{ - acpi_status result; - - hv_acpi_dev = device; - - result = - acpi_walk_resources(device->handle, METHOD_NAME__CRS, - vmbus_walk_resources, &irq); - - if (ACPI_FAILURE(result)) { - complete(&probe_event); - return -ENODEV; - } - complete(&probe_event); - return 0; -} - -static const struct acpi_device_id vmbus_acpi_device_ids[] = { - {"VMBUS", 0}, - {"VMBus", 0}, - {"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, vmbus_acpi_device_ids); - -static struct acpi_driver vmbus_acpi_driver = { - .name = "vmbus", - .ids = vmbus_acpi_device_ids, - .ops = { - .add = vmbus_acpi_add, - }, -}; - -/* - * We use a PCI table to determine if we should autoload this driver This is - * needed by distro tools to determine if the hyperv drivers should be - * installed and/or configured. We don't do anything else with the table, but - * it needs to be present. - */ -static const struct pci_device_id microsoft_hv_pci_table[] = { - { PCI_DEVICE(0x1414, 0x5353) }, /* VGA compatible controller */ - { 0 } -}; -MODULE_DEVICE_TABLE(pci, microsoft_hv_pci_table); - -static int __init hv_acpi_init(void) -{ - int ret; - - init_completion(&probe_event); - - /* - * Get irq resources first. - */ - - ret = acpi_bus_register_driver(&vmbus_acpi_driver); - - if (ret) - return ret; - - wait_for_completion(&probe_event); - - if (irq <= 0) { - acpi_bus_unregister_driver(&vmbus_acpi_driver); - return -ENODEV; - } - - ret = vmbus_bus_init(irq); - if (ret) - acpi_bus_unregister_driver(&vmbus_acpi_driver); - return ret; -} - - -MODULE_LICENSE("GPL"); -MODULE_VERSION(HV_DRV_VERSION); -module_param(vmbus_loglevel, int, S_IRUGO|S_IWUSR); - -module_init(hv_acpi_init); |