diff options
| author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
|---|---|---|
| committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
| commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
| tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/staging/hv | |
| parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) | |
Diffstat (limited to 'drivers/staging/hv')
25 files changed, 13858 insertions, 0 deletions
diff --git a/drivers/staging/hv/Kconfig b/drivers/staging/hv/Kconfig new file mode 100644 index 00000000000..5e0c9f6c745 --- /dev/null +++ b/drivers/staging/hv/Kconfig | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | config HYPERV | ||
| 2 | tristate "Microsoft Hyper-V client drivers" | ||
| 3 | depends on X86 && ACPI && PCI && m | ||
| 4 | default n | ||
| 5 | help | ||
| 6 | Select this option to run Linux as a Hyper-V client operating | ||
| 7 | system. | ||
| 8 | |||
| 9 | if HYPERV | ||
| 10 | |||
| 11 | config HYPERV_STORAGE | ||
| 12 | tristate "Microsoft Hyper-V virtual storage driver" | ||
| 13 | depends on SCSI | ||
| 14 | default HYPERV | ||
| 15 | help | ||
| 16 | Select this option to enable the Hyper-V virtual storage driver. | ||
| 17 | |||
| 18 | config HYPERV_BLOCK | ||
| 19 | tristate "Microsoft Hyper-V virtual block driver" | ||
| 20 | depends on BLOCK && SCSI && (LBDAF || 64BIT) | ||
| 21 | default HYPERV | ||
| 22 | help | ||
| 23 | Select this option to enable the Hyper-V virtual block driver. | ||
| 24 | |||
| 25 | config HYPERV_NET | ||
| 26 | tristate "Microsoft Hyper-V virtual network driver" | ||
| 27 | depends on NET | ||
| 28 | default HYPERV | ||
| 29 | help | ||
| 30 | Select this option to enable the Hyper-V virtual network driver. | ||
| 31 | |||
| 32 | config HYPERV_UTILS | ||
| 33 | tristate "Microsoft Hyper-V Utilities driver" | ||
| 34 | depends on CONNECTOR && NLS | ||
| 35 | default HYPERV | ||
| 36 | help | ||
| 37 | Select this option to enable the Hyper-V Utilities. | ||
| 38 | |||
| 39 | config HYPERV_MOUSE | ||
| 40 | tristate "Microsoft Hyper-V mouse driver" | ||
| 41 | depends on HID | ||
| 42 | default HYPERV | ||
| 43 | help | ||
| 44 | Select this option to enable the Hyper-V mouse driver. | ||
| 45 | |||
| 46 | endif | ||
diff --git a/drivers/staging/hv/Makefile b/drivers/staging/hv/Makefile new file mode 100644 index 00000000000..30046743a0b --- /dev/null +++ b/drivers/staging/hv/Makefile | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | obj-$(CONFIG_HYPERV) += hv_vmbus.o hv_timesource.o | ||
| 2 | obj-$(CONFIG_HYPERV_STORAGE) += hv_storvsc.o | ||
| 3 | obj-$(CONFIG_HYPERV_BLOCK) += hv_blkvsc.o | ||
| 4 | obj-$(CONFIG_HYPERV_NET) += hv_netvsc.o | ||
| 5 | obj-$(CONFIG_HYPERV_UTILS) += hv_utils.o | ||
| 6 | obj-$(CONFIG_HYPERV_MOUSE) += hv_mouse.o | ||
| 7 | |||
| 8 | hv_vmbus-y := vmbus_drv.o \ | ||
| 9 | hv.o connection.o channel.o \ | ||
| 10 | channel_mgmt.o ring_buffer.o | ||
| 11 | hv_storvsc-y := storvsc_drv.o storvsc.o | ||
| 12 | hv_blkvsc-y := blkvsc_drv.o storvsc.o | ||
| 13 | hv_netvsc-y := netvsc_drv.o netvsc.o rndis_filter.o | ||
| 14 | hv_utils-y := hv_util.o hv_kvp.o | ||
diff --git a/drivers/staging/hv/TODO b/drivers/staging/hv/TODO new file mode 100644 index 00000000000..582fd4ab3f1 --- /dev/null +++ b/drivers/staging/hv/TODO | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | TODO: | ||
| 2 | - fix remaining checkpatch warnings and errors | ||
| 3 | - audit the vmbus to verify it is working properly with the | ||
| 4 | driver model | ||
| 5 | - see if the vmbus can be merged with the other virtual busses | ||
| 6 | in the kernel | ||
| 7 | - audit the network driver | ||
| 8 | - checking for carrier inside open is wrong, network device API | ||
| 9 | confusion?? | ||
| 10 | - audit the block driver | ||
| 11 | - audit the scsi driver | ||
| 12 | |||
| 13 | Please send patches for this code to Greg Kroah-Hartman <gregkh@suse.de>, | ||
| 14 | Hank Janssen <hjanssen@microsoft.com>, and Haiyang Zhang <haiyangz@microsoft.com>. | ||
diff --git a/drivers/staging/hv/blkvsc_drv.c b/drivers/staging/hv/blkvsc_drv.c new file mode 100644 index 00000000000..d286b222318 --- /dev/null +++ b/drivers/staging/hv/blkvsc_drv.c | |||
| @@ -0,0 +1,1026 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2009, Microsoft Corporation. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms and conditions of the GNU General Public License, | ||
| 6 | * version 2, as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License along with | ||
| 14 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
| 15 | * Place - Suite 330, Boston, MA 02111-1307 USA. | ||
| 16 | * | ||
| 17 | * Authors: | ||
| 18 | * Haiyang Zhang <haiyangz@microsoft.com> | ||
| 19 | * Hank Janssen <hjanssen@microsoft.com> | ||
| 20 | * K. Y. Srinivasan <kys@microsoft.com> | ||
| 21 | */ | ||
| 22 | #include <linux/init.h> | ||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/device.h> | ||
| 25 | #include <linux/blkdev.h> | ||
| 26 | #include <linux/major.h> | ||
| 27 | #include <linux/delay.h> | ||
| 28 | #include <linux/hdreg.h> | ||
| 29 | #include <linux/slab.h> | ||
| 30 | #include <scsi/scsi.h> | ||
| 31 | #include <scsi/scsi_cmnd.h> | ||
| 32 | #include <scsi/scsi_eh.h> | ||
| 33 | #include <scsi/scsi_dbg.h> | ||
| 34 | |||
| 35 | #include "hyperv.h" | ||
| 36 | #include "hyperv_storage.h" | ||
| 37 | |||
| 38 | |||
| 39 | #define BLKVSC_MINORS 64 | ||
| 40 | |||
| 41 | enum blkvsc_device_type { | ||
| 42 | UNKNOWN_DEV_TYPE, | ||
| 43 | HARDDISK_TYPE, | ||
| 44 | DVD_TYPE, | ||
| 45 | }; | ||
| 46 | |||
| 47 | enum blkvsc_op_type { | ||
| 48 | DO_INQUIRY, | ||
| 49 | DO_CAPACITY, | ||
| 50 | DO_FLUSH, | ||
| 51 | }; | ||
| 52 | |||
| 53 | /* | ||
| 54 | * This request ties the struct request and struct | ||
| 55 | * blkvsc_request/hv_storvsc_request together A struct request may be | ||
| 56 | * represented by 1 or more struct blkvsc_request | ||
| 57 | */ | ||
| 58 | struct blkvsc_request_group { | ||
| 59 | int outstanding; | ||
| 60 | int status; | ||
| 61 | struct list_head blkvsc_req_list; /* list of blkvsc_requests */ | ||
| 62 | }; | ||
| 63 | |||
| 64 | struct blkvsc_request { | ||
| 65 | /* blkvsc_request_group.blkvsc_req_list */ | ||
| 66 | struct list_head req_entry; | ||
| 67 | |||
| 68 | /* block_device_context.pending_list */ | ||
| 69 | struct list_head pend_entry; | ||
| 70 | |||
| 71 | /* This may be null if we generate a request internally */ | ||
| 72 | struct request *req; | ||
| 73 | |||
| 74 | struct block_device_context *dev; | ||
| 75 | |||
| 76 | /* The group this request is part of. Maybe null */ | ||
| 77 | struct blkvsc_request_group *group; | ||
| 78 | |||
| 79 | int write; | ||
| 80 | sector_t sector_start; | ||
| 81 | unsigned long sector_count; | ||
| 82 | |||
| 83 | unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE]; | ||
| 84 | unsigned char cmd_len; | ||
| 85 | unsigned char cmnd[MAX_COMMAND_SIZE]; | ||
| 86 | |||
| 87 | struct hv_storvsc_request request; | ||
| 88 | }; | ||
| 89 | |||
| 90 | /* Per device structure */ | ||
| 91 | struct block_device_context { | ||
| 92 | /* point back to our device context */ | ||
| 93 | struct hv_device *device_ctx; | ||
| 94 | struct kmem_cache *request_pool; | ||
| 95 | spinlock_t lock; | ||
| 96 | struct gendisk *gd; | ||
| 97 | enum blkvsc_device_type device_type; | ||
| 98 | struct list_head pending_list; | ||
| 99 | |||
| 100 | unsigned char device_id[64]; | ||
| 101 | unsigned int device_id_len; | ||
| 102 | int num_outstanding_reqs; | ||
| 103 | int shutting_down; | ||
| 104 | unsigned int sector_size; | ||
| 105 | sector_t capacity; | ||
| 106 | unsigned int port; | ||
| 107 | unsigned char path; | ||
| 108 | unsigned char target; | ||
| 109 | int users; | ||
| 110 | }; | ||
| 111 | |||
| 112 | static const char *drv_name = "blkvsc"; | ||
| 113 | |||
| 114 | /* {32412632-86cb-44a2-9b5c-50d1417354f5} */ | ||
| 115 | static const struct hv_guid dev_type = { | ||
| 116 | .data = { | ||
| 117 | 0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, | ||
| 118 | 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5 | ||
| 119 | } | ||
| 120 | }; | ||
| 121 | |||
| 122 | /* | ||
| 123 | * There is a circular dependency involving blkvsc_request_completion() | ||
| 124 | * and blkvsc_do_request(). | ||
| 125 | */ | ||
| 126 | static void blkvsc_request_completion(struct hv_storvsc_request *request); | ||
| 127 | |||
| 128 | static int blkvsc_ringbuffer_size = BLKVSC_RING_BUFFER_SIZE; | ||
| 129 | |||
| 130 | module_param(blkvsc_ringbuffer_size, int, S_IRUGO); | ||
| 131 | MODULE_PARM_DESC(ring_size, "Ring buffer size (in bytes)"); | ||
| 132 | |||
| 133 | /* | ||
| 134 | * There is a circular dependency involving blkvsc_probe() | ||
| 135 | * and block_ops. | ||
| 136 | */ | ||
| 137 | static int blkvsc_probe(struct hv_device *dev); | ||
| 138 | |||
| 139 | static int blkvsc_device_add(struct hv_device *device, | ||
| 140 | void *additional_info) | ||
| 141 | { | ||
| 142 | struct storvsc_device_info *device_info; | ||
| 143 | int ret = 0; | ||
| 144 | |||
| 145 | device_info = (struct storvsc_device_info *)additional_info; | ||
| 146 | |||
| 147 | device_info->ring_buffer_size = blkvsc_ringbuffer_size; | ||
| 148 | |||
| 149 | ret = storvsc_dev_add(device, additional_info); | ||
| 150 | if (ret != 0) | ||
| 151 | return ret; | ||
| 152 | |||
| 153 | /* | ||
| 154 | * We need to use the device instance guid to set the path and target | ||
| 155 | * id. For IDE devices, the device instance id is formatted as | ||
| 156 | * <bus id> * - <device id> - 8899 - 000000000000. | ||
| 157 | */ | ||
| 158 | device_info->path_id = device->dev_instance.data[3] << 24 | | ||
| 159 | device->dev_instance.data[2] << 16 | | ||
| 160 | device->dev_instance.data[1] << 8 | | ||
| 161 | device->dev_instance.data[0]; | ||
| 162 | |||
| 163 | device_info->target_id = device->dev_instance.data[5] << 8 | | ||
| 164 | device->dev_instance.data[4]; | ||
| 165 | |||
| 166 | return ret; | ||
| 167 | } | ||
| 168 | |||
| 169 | static int blkvsc_submit_request(struct blkvsc_request *blkvsc_req, | ||
| 170 | void (*request_completion)(struct hv_storvsc_request *)) | ||
| 171 | { | ||
| 172 | struct block_device_context *blkdev = blkvsc_req->dev; | ||
| 173 | struct hv_storvsc_request *storvsc_req; | ||
| 174 | struct vmscsi_request *vm_srb; | ||
| 175 | int ret; | ||
| 176 | |||
| 177 | |||
| 178 | storvsc_req = &blkvsc_req->request; | ||
| 179 | vm_srb = &storvsc_req->vstor_packet.vm_srb; | ||
| 180 | |||
| 181 | vm_srb->data_in = blkvsc_req->write ? WRITE_TYPE : READ_TYPE; | ||
| 182 | |||
| 183 | storvsc_req->on_io_completion = request_completion; | ||
| 184 | storvsc_req->context = blkvsc_req; | ||
| 185 | |||
| 186 | vm_srb->port_number = blkdev->port; | ||
| 187 | vm_srb->path_id = blkdev->path; | ||
| 188 | vm_srb->target_id = blkdev->target; | ||
| 189 | vm_srb->lun = 0; /* this is not really used at all */ | ||
| 190 | |||
| 191 | vm_srb->cdb_length = blkvsc_req->cmd_len; | ||
| 192 | |||
| 193 | memcpy(vm_srb->cdb, blkvsc_req->cmnd, vm_srb->cdb_length); | ||
| 194 | |||
| 195 | storvsc_req->sense_buffer = blkvsc_req->sense_buffer; | ||
| 196 | |||
| 197 | ret = storvsc_do_io(blkdev->device_ctx, | ||
| 198 | &blkvsc_req->request); | ||
| 199 | if (ret == 0) | ||
| 200 | blkdev->num_outstanding_reqs++; | ||
| 201 | |||
| 202 | return ret; | ||
| 203 | } | ||
| 204 | |||
| 205 | |||
| 206 | static int blkvsc_open(struct block_device *bdev, fmode_t mode) | ||
| 207 | { | ||
| 208 | struct block_device_context *blkdev = bdev->bd_disk->private_data; | ||
| 209 | unsigned long flags; | ||
| 210 | |||
| 211 | spin_lock_irqsave(&blkdev->lock, flags); | ||
| 212 | |||
| 213 | blkdev->users++; | ||
| 214 | |||
| 215 | spin_unlock_irqrestore(&blkdev->lock, flags); | ||
| 216 | |||
| 217 | return 0; | ||
| 218 | } | ||
| 219 | |||
| 220 | |||
| 221 | static int blkvsc_getgeo(struct block_device *bd, struct hd_geometry *hg) | ||
| 222 | { | ||
| 223 | sector_t nsect = get_capacity(bd->bd_disk); | ||
| 224 | sector_t cylinders = nsect; | ||
| 225 | |||
| 226 | /* | ||
| 227 | * We are making up these values; let us keep it simple. | ||
| 228 | */ | ||
| 229 | hg->heads = 0xff; | ||
| 230 | hg->sectors = 0x3f; | ||
| 231 | sector_div(cylinders, hg->heads * hg->sectors); | ||
| 232 | hg->cylinders = cylinders; | ||
| 233 | if ((sector_t)(hg->cylinders + 1) * hg->heads * hg->sectors < nsect) | ||
| 234 | hg->cylinders = 0xffff; | ||
| 235 | return 0; | ||
| 236 | |||
| 237 | } | ||
| 238 | |||
| 239 | |||
| 240 | static void blkvsc_init_rw(struct blkvsc_request *blkvsc_req) | ||
| 241 | { | ||
| 242 | |||
| 243 | blkvsc_req->cmd_len = 16; | ||
| 244 | |||
| 245 | if (rq_data_dir(blkvsc_req->req)) { | ||
| 246 | blkvsc_req->write = 1; | ||
| 247 | blkvsc_req->cmnd[0] = WRITE_16; | ||
| 248 | } else { | ||
| 249 | blkvsc_req->write = 0; | ||
| 250 | blkvsc_req->cmnd[0] = READ_16; | ||
| 251 | } | ||
| 252 | |||
| 253 | blkvsc_req->cmnd[1] |= | ||
| 254 | (blkvsc_req->req->cmd_flags & REQ_FUA) ? 0x8 : 0; | ||
| 255 | |||
| 256 | *(unsigned long long *)&blkvsc_req->cmnd[2] = | ||
| 257 | cpu_to_be64(blkvsc_req->sector_start); | ||
| 258 | *(unsigned int *)&blkvsc_req->cmnd[10] = | ||
| 259 | cpu_to_be32(blkvsc_req->sector_count); | ||
| 260 | } | ||
| 261 | |||
| 262 | |||
| 263 | static int blkvsc_ioctl(struct block_device *bd, fmode_t mode, | ||
| 264 | unsigned cmd, unsigned long arg) | ||
| 265 | { | ||
| 266 | struct block_device_context *blkdev = bd->bd_disk->private_data; | ||
| 267 | int ret = 0; | ||
| 268 | |||
| 269 | switch (cmd) { | ||
| 270 | case HDIO_GET_IDENTITY: | ||
| 271 | if (copy_to_user((void __user *)arg, blkdev->device_id, | ||
| 272 | blkdev->device_id_len)) | ||
| 273 | ret = -EFAULT; | ||
| 274 | break; | ||
| 275 | default: | ||
| 276 | ret = -EINVAL; | ||
| 277 | break; | ||
| 278 | } | ||
| 279 | |||
| 280 | return ret; | ||
| 281 | } | ||
| 282 | |||
| 283 | static void blkvsc_cmd_completion(struct hv_storvsc_request *request) | ||
| 284 | { | ||
| 285 | struct blkvsc_request *blkvsc_req = | ||
| 286 | (struct blkvsc_request *)request->context; | ||
| 287 | struct block_device_context *blkdev = | ||
| 288 | (struct block_device_context *)blkvsc_req->dev; | ||
| 289 | struct scsi_sense_hdr sense_hdr; | ||
| 290 | struct vmscsi_request *vm_srb; | ||
| 291 | unsigned long flags; | ||
| 292 | |||
| 293 | |||
| 294 | vm_srb = &blkvsc_req->request.vstor_packet.vm_srb; | ||
| 295 | |||
| 296 | spin_lock_irqsave(&blkdev->lock, flags); | ||
| 297 | blkdev->num_outstanding_reqs--; | ||
| 298 | spin_unlock_irqrestore(&blkdev->lock, flags); | ||
| 299 | |||
| 300 | if (vm_srb->scsi_status) | ||
| 301 | if (scsi_normalize_sense(blkvsc_req->sense_buffer, | ||
| 302 | SCSI_SENSE_BUFFERSIZE, &sense_hdr)) | ||
| 303 | scsi_print_sense_hdr("blkvsc", &sense_hdr); | ||
| 304 | |||
| 305 | complete(&blkvsc_req->request.wait_event); | ||
| 306 | } | ||
| 307 | |||
| 308 | |||
| 309 | static int blkvsc_do_operation(struct block_device_context *blkdev, | ||
| 310 | enum blkvsc_op_type op) | ||
| 311 | { | ||
| 312 | struct blkvsc_request *blkvsc_req; | ||
| 313 | struct page *page_buf; | ||
| 314 | unsigned char *buf; | ||
| 315 | unsigned char device_type; | ||
| 316 | struct scsi_sense_hdr sense_hdr; | ||
| 317 | struct vmscsi_request *vm_srb; | ||
| 318 | unsigned long flags; | ||
| 319 | |||
| 320 | int ret = 0; | ||
| 321 | |||
| 322 | blkvsc_req = kmem_cache_zalloc(blkdev->request_pool, GFP_KERNEL); | ||
| 323 | if (!blkvsc_req) | ||
| 324 | return -ENOMEM; | ||
| 325 | |||
| 326 | page_buf = alloc_page(GFP_KERNEL); | ||
| 327 | if (!page_buf) { | ||
| 328 | kmem_cache_free(blkdev->request_pool, blkvsc_req); | ||
| 329 | return -ENOMEM; | ||
| 330 | } | ||
| 331 | |||
| 332 | vm_srb = &blkvsc_req->request.vstor_packet.vm_srb; | ||
| 333 | init_completion(&blkvsc_req->request.wait_event); | ||
| 334 | blkvsc_req->dev = blkdev; | ||
| 335 | blkvsc_req->req = NULL; | ||
| 336 | blkvsc_req->write = 0; | ||
| 337 | |||
| 338 | blkvsc_req->request.data_buffer.pfn_array[0] = | ||
| 339 | page_to_pfn(page_buf); | ||
| 340 | blkvsc_req->request.data_buffer.offset = 0; | ||
| 341 | |||
| 342 | switch (op) { | ||
| 343 | case DO_INQUIRY: | ||
| 344 | blkvsc_req->cmnd[0] = INQUIRY; | ||
| 345 | blkvsc_req->cmnd[1] = 0x1; /* Get product data */ | ||
| 346 | blkvsc_req->cmnd[2] = 0x83; /* mode page 83 */ | ||
| 347 | blkvsc_req->cmnd[4] = 64; | ||
| 348 | blkvsc_req->cmd_len = 6; | ||
| 349 | blkvsc_req->request.data_buffer.len = 64; | ||
| 350 | break; | ||
| 351 | |||
| 352 | case DO_CAPACITY: | ||
| 353 | blkdev->sector_size = 0; | ||
| 354 | blkdev->capacity = 0; | ||
| 355 | |||
| 356 | blkvsc_req->cmnd[0] = READ_CAPACITY; | ||
| 357 | blkvsc_req->cmd_len = 16; | ||
| 358 | blkvsc_req->request.data_buffer.len = 8; | ||
| 359 | break; | ||
| 360 | |||
| 361 | case DO_FLUSH: | ||
| 362 | blkvsc_req->cmnd[0] = SYNCHRONIZE_CACHE; | ||
| 363 | blkvsc_req->cmd_len = 10; | ||
| 364 | blkvsc_req->request.data_buffer.pfn_array[0] = 0; | ||
| 365 | blkvsc_req->request.data_buffer.len = 0; | ||
| 366 | break; | ||
| 367 | default: | ||
| 368 | ret = -EINVAL; | ||
| 369 | goto cleanup; | ||
| 370 | } | ||
| 371 | |||
| 372 | spin_lock_irqsave(&blkdev->lock, flags); | ||
| 373 | blkvsc_submit_request(blkvsc_req, blkvsc_cmd_completion); | ||
| 374 | spin_unlock_irqrestore(&blkdev->lock, flags); | ||
| 375 | |||
| 376 | wait_for_completion_interruptible(&blkvsc_req->request.wait_event); | ||
| 377 | |||
| 378 | /* check error */ | ||
| 379 | if (vm_srb->scsi_status) { | ||
| 380 | scsi_normalize_sense(blkvsc_req->sense_buffer, | ||
| 381 | SCSI_SENSE_BUFFERSIZE, &sense_hdr); | ||
| 382 | |||
| 383 | return 0; | ||
| 384 | } | ||
| 385 | |||
| 386 | buf = kmap(page_buf); | ||
| 387 | |||
| 388 | switch (op) { | ||
| 389 | case DO_INQUIRY: | ||
| 390 | device_type = buf[0] & 0x1F; | ||
| 391 | |||
| 392 | if (device_type == 0x0) | ||
| 393 | blkdev->device_type = HARDDISK_TYPE; | ||
| 394 | else | ||
| 395 | blkdev->device_type = UNKNOWN_DEV_TYPE; | ||
| 396 | |||
| 397 | blkdev->device_id_len = buf[7]; | ||
| 398 | if (blkdev->device_id_len > 64) | ||
| 399 | blkdev->device_id_len = 64; | ||
| 400 | |||
| 401 | memcpy(blkdev->device_id, &buf[8], blkdev->device_id_len); | ||
| 402 | break; | ||
| 403 | |||
| 404 | case DO_CAPACITY: | ||
| 405 | /* be to le */ | ||
| 406 | blkdev->capacity = | ||
| 407 | ((buf[0] << 24) | (buf[1] << 16) | | ||
| 408 | (buf[2] << 8) | buf[3]) + 1; | ||
| 409 | |||
| 410 | blkdev->sector_size = | ||
| 411 | (buf[4] << 24) | (buf[5] << 16) | | ||
| 412 | (buf[6] << 8) | buf[7]; | ||
| 413 | break; | ||
| 414 | default: | ||
| 415 | break; | ||
| 416 | |||
| 417 | } | ||
| 418 | |||
| 419 | cleanup: | ||
| 420 | |||
| 421 | kunmap(page_buf); | ||
| 422 | |||
| 423 | __free_page(page_buf); | ||
| 424 | |||
| 425 | kmem_cache_free(blkdev->request_pool, blkvsc_req); | ||
| 426 | |||
| 427 | return ret; | ||
| 428 | } | ||
| 429 | |||
| 430 | |||
| 431 | static int blkvsc_cancel_pending_reqs(struct block_device_context *blkdev) | ||
| 432 | { | ||
| 433 | struct blkvsc_request *pend_req, *tmp; | ||
| 434 | struct blkvsc_request *comp_req, *tmp2; | ||
| 435 | struct vmscsi_request *vm_srb; | ||
| 436 | |||
| 437 | int ret = 0; | ||
| 438 | |||
| 439 | |||
| 440 | /* Flush the pending list first */ | ||
| 441 | list_for_each_entry_safe(pend_req, tmp, &blkdev->pending_list, | ||
| 442 | pend_entry) { | ||
| 443 | /* | ||
| 444 | * The pend_req could be part of a partially completed | ||
| 445 | * request. If so, complete those req first until we | ||
| 446 | * hit the pend_req | ||
| 447 | */ | ||
| 448 | list_for_each_entry_safe(comp_req, tmp2, | ||
| 449 | &pend_req->group->blkvsc_req_list, | ||
| 450 | req_entry) { | ||
| 451 | |||
| 452 | if (comp_req == pend_req) | ||
| 453 | break; | ||
| 454 | |||
| 455 | list_del(&comp_req->req_entry); | ||
| 456 | |||
| 457 | if (comp_req->req) { | ||
| 458 | vm_srb = | ||
| 459 | &comp_req->request.vstor_packet. | ||
| 460 | vm_srb; | ||
| 461 | ret = __blk_end_request(comp_req->req, | ||
| 462 | (!vm_srb->scsi_status ? 0 : -EIO), | ||
| 463 | comp_req->sector_count * | ||
| 464 | blkdev->sector_size); | ||
| 465 | |||
| 466 | /* FIXME: shouldn't this do more than return? */ | ||
| 467 | if (ret) | ||
| 468 | goto out; | ||
| 469 | } | ||
| 470 | |||
| 471 | kmem_cache_free(blkdev->request_pool, comp_req); | ||
| 472 | } | ||
| 473 | |||
| 474 | list_del(&pend_req->pend_entry); | ||
| 475 | |||
| 476 | list_del(&pend_req->req_entry); | ||
| 477 | |||
| 478 | if (comp_req->req) { | ||
| 479 | if (!__blk_end_request(pend_req->req, -EIO, | ||
| 480 | pend_req->sector_count * | ||
| 481 | blkdev->sector_size)) { | ||
| 482 | /* | ||
| 483 | * All the sectors have been xferred ie the | ||
| 484 | * request is done | ||
| 485 | */ | ||
| 486 | kmem_cache_free(blkdev->request_pool, | ||
| 487 | pend_req->group); | ||
| 488 | } | ||
| 489 | } | ||
| 490 | |||
| 491 | kmem_cache_free(blkdev->request_pool, pend_req); | ||
| 492 | } | ||
| 493 | |||
| 494 | out: | ||
| 495 | return ret; | ||
| 496 | } | ||
| 497 | |||
| 498 | |||
| 499 | /* | ||
| 500 | * blkvsc_remove() - Callback when our device is removed | ||
| 501 | */ | ||
| 502 | static int blkvsc_remove(struct hv_device *dev) | ||
| 503 | { | ||
| 504 | struct block_device_context *blkdev = dev_get_drvdata(&dev->device); | ||
| 505 | unsigned long flags; | ||
| 506 | |||
| 507 | |||
| 508 | /* Get to a known state */ | ||
| 509 | spin_lock_irqsave(&blkdev->lock, flags); | ||
| 510 | |||
| 511 | blkdev->shutting_down = 1; | ||
| 512 | |||
| 513 | blk_stop_queue(blkdev->gd->queue); | ||
| 514 | |||
| 515 | blkvsc_cancel_pending_reqs(blkdev); | ||
| 516 | |||
| 517 | spin_unlock_irqrestore(&blkdev->lock, flags); | ||
| 518 | |||
| 519 | blkvsc_do_operation(blkdev, DO_FLUSH); | ||
| 520 | |||
| 521 | if (blkdev->users == 0) { | ||
| 522 | del_gendisk(blkdev->gd); | ||
| 523 | put_disk(blkdev->gd); | ||
| 524 | blk_cleanup_queue(blkdev->gd->queue); | ||
| 525 | |||
| 526 | storvsc_dev_remove(blkdev->device_ctx); | ||
| 527 | |||
| 528 | kmem_cache_destroy(blkdev->request_pool); | ||
| 529 | kfree(blkdev); | ||
| 530 | } | ||
| 531 | |||
| 532 | return 0; | ||
| 533 | } | ||
| 534 | |||
| 535 | static void blkvsc_shutdown(struct hv_device *dev) | ||
| 536 | { | ||
| 537 | struct block_device_context *blkdev = dev_get_drvdata(&dev->device); | ||
| 538 | unsigned long flags; | ||
| 539 | |||
| 540 | if (!blkdev) | ||
| 541 | return; | ||
| 542 | |||
| 543 | spin_lock_irqsave(&blkdev->lock, flags); | ||
| 544 | |||
| 545 | blkdev->shutting_down = 1; | ||
| 546 | |||
| 547 | blk_stop_queue(blkdev->gd->queue); | ||
| 548 | |||
| 549 | blkvsc_cancel_pending_reqs(blkdev); | ||
| 550 | |||
| 551 | spin_unlock_irqrestore(&blkdev->lock, flags); | ||
| 552 | |||
| 553 | blkvsc_do_operation(blkdev, DO_FLUSH); | ||
| 554 | |||
| 555 | /* | ||
| 556 | * Now wait for all outgoing I/O to be drained. | ||
| 557 | */ | ||
| 558 | storvsc_wait_to_drain((struct storvsc_device *)dev->ext); | ||
| 559 | |||
| 560 | } | ||
| 561 | |||
| 562 | static int blkvsc_release(struct gendisk *disk, fmode_t mode) | ||
| 563 | { | ||
| 564 | struct block_device_context *blkdev = disk->private_data; | ||
| 565 | unsigned long flags; | ||
| 566 | |||
| 567 | spin_lock_irqsave(&blkdev->lock, flags); | ||
| 568 | |||
| 569 | if ((--blkdev->users == 0) && (blkdev->shutting_down)) { | ||
| 570 | blk_stop_queue(blkdev->gd->queue); | ||
| 571 | spin_unlock_irqrestore(&blkdev->lock, flags); | ||
| 572 | |||
| 573 | blkvsc_do_operation(blkdev, DO_FLUSH); | ||
| 574 | del_gendisk(blkdev->gd); | ||
| 575 | put_disk(blkdev->gd); | ||
| 576 | blk_cleanup_queue(blkdev->gd->queue); | ||
| 577 | |||
| 578 | storvsc_dev_remove(blkdev->device_ctx); | ||
| 579 | |||
| 580 | kmem_cache_destroy(blkdev->request_pool); | ||
| 581 | kfree(blkdev); | ||
| 582 | } else | ||
| 583 | spin_unlock_irqrestore(&blkdev->lock, flags); | ||
| 584 | |||
| 585 | return 0; | ||
| 586 | } | ||
| 587 | |||
| 588 | |||
| 589 | /* | ||
| 590 | * We break the request into 1 or more blkvsc_requests and submit | ||
| 591 | * them. If we cant submit them all, we put them on the | ||
| 592 | * pending_list. The blkvsc_request() will work on the pending_list. | ||
| 593 | */ | ||
| 594 | static int blkvsc_do_request(struct block_device_context *blkdev, | ||
| 595 | struct request *req) | ||
| 596 | { | ||
| 597 | struct bio *bio = NULL; | ||
| 598 | struct bio_vec *bvec = NULL; | ||
| 599 | struct bio_vec *prev_bvec = NULL; | ||
| 600 | struct blkvsc_request *blkvsc_req = NULL; | ||
| 601 | struct blkvsc_request *tmp; | ||
| 602 | int databuf_idx = 0; | ||
| 603 | int seg_idx = 0; | ||
| 604 | sector_t start_sector; | ||
| 605 | unsigned long num_sectors = 0; | ||
| 606 | int ret = 0; | ||
| 607 | int pending = 0; | ||
| 608 | struct blkvsc_request_group *group = NULL; | ||
| 609 | |||
| 610 | /* Create a group to tie req to list of blkvsc_reqs */ | ||
| 611 | group = kmem_cache_zalloc(blkdev->request_pool, GFP_ATOMIC); | ||
| 612 | if (!group) | ||
| 613 | return -ENOMEM; | ||
| 614 | |||
| 615 | INIT_LIST_HEAD(&group->blkvsc_req_list); | ||
| 616 | group->outstanding = group->status = 0; | ||
| 617 | |||
| 618 | start_sector = blk_rq_pos(req); | ||
| 619 | |||
| 620 | /* foreach bio in the request */ | ||
| 621 | if (req->bio) { | ||
| 622 | for (bio = req->bio; bio; bio = bio->bi_next) { | ||
| 623 | /* | ||
| 624 | * Map this bio into an existing or new storvsc request | ||
| 625 | */ | ||
| 626 | bio_for_each_segment(bvec, bio, seg_idx) { | ||
| 627 | /* Get a new storvsc request */ | ||
| 628 | /* 1st-time */ | ||
| 629 | if ((!blkvsc_req) || | ||
| 630 | (databuf_idx >= MAX_MULTIPAGE_BUFFER_COUNT) | ||
| 631 | /* hole at the begin of page */ | ||
| 632 | || (bvec->bv_offset != 0) || | ||
| 633 | /* hold at the end of page */ | ||
| 634 | (prev_bvec && | ||
| 635 | (prev_bvec->bv_len != PAGE_SIZE))) { | ||
| 636 | /* submit the prev one */ | ||
| 637 | if (blkvsc_req) { | ||
| 638 | blkvsc_req->sector_start = | ||
| 639 | start_sector; | ||
| 640 | sector_div( | ||
| 641 | blkvsc_req->sector_start, | ||
| 642 | (blkdev->sector_size >> 9)); | ||
| 643 | |||
| 644 | blkvsc_req->sector_count = | ||
| 645 | num_sectors / | ||
| 646 | (blkdev->sector_size >> 9); | ||
| 647 | blkvsc_init_rw(blkvsc_req); | ||
| 648 | } | ||
| 649 | |||
| 650 | /* | ||
| 651 | * Create new blkvsc_req to represent | ||
| 652 | * the current bvec | ||
| 653 | */ | ||
| 654 | blkvsc_req = | ||
| 655 | kmem_cache_zalloc( | ||
| 656 | blkdev->request_pool, GFP_ATOMIC); | ||
| 657 | if (!blkvsc_req) { | ||
| 658 | /* free up everything */ | ||
| 659 | list_for_each_entry_safe( | ||
| 660 | blkvsc_req, tmp, | ||
| 661 | &group->blkvsc_req_list, | ||
| 662 | req_entry) { | ||
| 663 | list_del( | ||
| 664 | &blkvsc_req->req_entry); | ||
| 665 | kmem_cache_free( | ||
| 666 | blkdev->request_pool, | ||
| 667 | blkvsc_req); | ||
| 668 | } | ||
| 669 | |||
| 670 | kmem_cache_free( | ||
| 671 | blkdev->request_pool, group); | ||
| 672 | return -ENOMEM; | ||
| 673 | } | ||
| 674 | |||
| 675 | memset(blkvsc_req, 0, | ||
| 676 | sizeof(struct blkvsc_request)); | ||
| 677 | |||
| 678 | blkvsc_req->dev = blkdev; | ||
| 679 | blkvsc_req->req = req; | ||
| 680 | blkvsc_req->request. | ||
| 681 | data_buffer.offset | ||
| 682 | = bvec->bv_offset; | ||
| 683 | blkvsc_req->request. | ||
| 684 | data_buffer.len = 0; | ||
| 685 | |||
| 686 | /* Add to the group */ | ||
| 687 | blkvsc_req->group = group; | ||
| 688 | blkvsc_req->group->outstanding++; | ||
| 689 | list_add_tail(&blkvsc_req->req_entry, | ||
| 690 | &blkvsc_req->group->blkvsc_req_list); | ||
| 691 | |||
| 692 | start_sector += num_sectors; | ||
| 693 | num_sectors = 0; | ||
| 694 | databuf_idx = 0; | ||
| 695 | } | ||
| 696 | |||
| 697 | /* | ||
| 698 | * Add the curr bvec/segment to the curr | ||
| 699 | * blkvsc_req | ||
| 700 | */ | ||
| 701 | blkvsc_req->request.data_buffer. | ||
| 702 | pfn_array[databuf_idx] | ||
| 703 | = page_to_pfn(bvec->bv_page); | ||
| 704 | blkvsc_req->request.data_buffer.len | ||
| 705 | += bvec->bv_len; | ||
| 706 | |||
| 707 | prev_bvec = bvec; | ||
| 708 | |||
| 709 | databuf_idx++; | ||
| 710 | num_sectors += bvec->bv_len >> 9; | ||
| 711 | |||
| 712 | } /* bio_for_each_segment */ | ||
| 713 | |||
| 714 | } /* rq_for_each_bio */ | ||
| 715 | } | ||
| 716 | |||
| 717 | /* Handle the last one */ | ||
| 718 | if (blkvsc_req) { | ||
| 719 | blkvsc_req->sector_start = start_sector; | ||
| 720 | sector_div(blkvsc_req->sector_start, | ||
| 721 | (blkdev->sector_size >> 9)); | ||
| 722 | |||
| 723 | blkvsc_req->sector_count = num_sectors / | ||
| 724 | (blkdev->sector_size >> 9); | ||
| 725 | |||
| 726 | blkvsc_init_rw(blkvsc_req); | ||
| 727 | } | ||
| 728 | |||
| 729 | list_for_each_entry(blkvsc_req, &group->blkvsc_req_list, req_entry) { | ||
| 730 | if (pending) { | ||
| 731 | |||
| 732 | list_add_tail(&blkvsc_req->pend_entry, | ||
| 733 | &blkdev->pending_list); | ||
| 734 | } else { | ||
| 735 | ret = blkvsc_submit_request(blkvsc_req, | ||
| 736 | blkvsc_request_completion); | ||
| 737 | if (ret == -1) { | ||
| 738 | pending = 1; | ||
| 739 | list_add_tail(&blkvsc_req->pend_entry, | ||
| 740 | &blkdev->pending_list); | ||
| 741 | } | ||
| 742 | |||
| 743 | } | ||
| 744 | } | ||
| 745 | |||
| 746 | return pending; | ||
| 747 | } | ||
| 748 | |||
| 749 | static int blkvsc_do_pending_reqs(struct block_device_context *blkdev) | ||
| 750 | { | ||
| 751 | struct blkvsc_request *pend_req, *tmp; | ||
| 752 | int ret = 0; | ||
| 753 | |||
| 754 | /* Flush the pending list first */ | ||
| 755 | list_for_each_entry_safe(pend_req, tmp, &blkdev->pending_list, | ||
| 756 | pend_entry) { | ||
| 757 | |||
| 758 | ret = blkvsc_submit_request(pend_req, | ||
| 759 | blkvsc_request_completion); | ||
| 760 | if (ret != 0) | ||
| 761 | break; | ||
| 762 | else | ||
| 763 | list_del(&pend_req->pend_entry); | ||
| 764 | } | ||
| 765 | |||
| 766 | return ret; | ||
| 767 | } | ||
| 768 | |||
| 769 | |||
| 770 | static void blkvsc_request(struct request_queue *queue) | ||
| 771 | { | ||
| 772 | struct block_device_context *blkdev = NULL; | ||
| 773 | struct request *req; | ||
| 774 | int ret = 0; | ||
| 775 | |||
| 776 | while ((req = blk_peek_request(queue)) != NULL) { | ||
| 777 | |||
| 778 | blkdev = req->rq_disk->private_data; | ||
| 779 | if (blkdev->shutting_down || req->cmd_type != REQ_TYPE_FS) { | ||
| 780 | __blk_end_request_cur(req, 0); | ||
| 781 | continue; | ||
| 782 | } | ||
| 783 | |||
| 784 | ret = blkvsc_do_pending_reqs(blkdev); | ||
| 785 | |||
| 786 | if (ret != 0) { | ||
| 787 | blk_stop_queue(queue); | ||
| 788 | break; | ||
| 789 | } | ||
| 790 | |||
| 791 | blk_start_request(req); | ||
| 792 | |||
| 793 | ret = blkvsc_do_request(blkdev, req); | ||
| 794 | if (ret > 0) { | ||
| 795 | blk_stop_queue(queue); | ||
| 796 | break; | ||
| 797 | } else if (ret < 0) { | ||
| 798 | blk_requeue_request(queue, req); | ||
| 799 | blk_stop_queue(queue); | ||
| 800 | break; | ||
| 801 | } | ||
| 802 | } | ||
| 803 | } | ||
| 804 | |||
| 805 | |||
| 806 | |||
| 807 | /* The one and only one */ | ||
| 808 | static struct hv_driver blkvsc_drv = { | ||
| 809 | .probe = blkvsc_probe, | ||
| 810 | .remove = blkvsc_remove, | ||
| 811 | .shutdown = blkvsc_shutdown, | ||
| 812 | }; | ||
| 813 | |||
| 814 | static const struct block_device_operations block_ops = { | ||
| 815 | .owner = THIS_MODULE, | ||
| 816 | .open = blkvsc_open, | ||
| 817 | .release = blkvsc_release, | ||
| 818 | .getgeo = blkvsc_getgeo, | ||
| 819 | .ioctl = blkvsc_ioctl, | ||
| 820 | }; | ||
| 821 | |||
| 822 | /* | ||
| 823 | * blkvsc_drv_init - BlkVsc driver initialization. | ||
| 824 | */ | ||
| 825 | static int blkvsc_drv_init(void) | ||
| 826 | { | ||
| 827 | struct hv_driver *drv = &blkvsc_drv; | ||
| 828 | int ret; | ||
| 829 | |||
| 830 | BUILD_BUG_ON(sizeof(sector_t) != 8); | ||
| 831 | |||
| 832 | memcpy(&drv->dev_type, &dev_type, sizeof(struct hv_guid)); | ||
| 833 | drv->driver.name = drv_name; | ||
| 834 | |||
| 835 | /* The driver belongs to vmbus */ | ||
| 836 | ret = vmbus_child_driver_register(&drv->driver); | ||
| 837 | |||
| 838 | return ret; | ||
| 839 | } | ||
| 840 | |||
| 841 | |||
| 842 | static void blkvsc_drv_exit(void) | ||
| 843 | { | ||
| 844 | |||
| 845 | vmbus_child_driver_unregister(&blkvsc_drv.driver); | ||
| 846 | } | ||
| 847 | |||
| 848 | /* | ||
| 849 | * blkvsc_probe - Add a new device for this driver | ||
| 850 | */ | ||
| 851 | static int blkvsc_probe(struct hv_device *dev) | ||
| 852 | { | ||
| 853 | struct block_device_context *blkdev = NULL; | ||
| 854 | struct storvsc_device_info device_info; | ||
| 855 | struct storvsc_major_info major_info; | ||
| 856 | int ret = 0; | ||
| 857 | |||
| 858 | blkdev = kzalloc(sizeof(struct block_device_context), GFP_KERNEL); | ||
| 859 | if (!blkdev) { | ||
| 860 | ret = -ENOMEM; | ||
| 861 | goto cleanup; | ||
| 862 | } | ||
| 863 | |||
| 864 | INIT_LIST_HEAD(&blkdev->pending_list); | ||
| 865 | |||
| 866 | /* Initialize what we can here */ | ||
| 867 | spin_lock_init(&blkdev->lock); | ||
| 868 | |||
| 869 | |||
| 870 | blkdev->request_pool = kmem_cache_create(dev_name(&dev->device), | ||
| 871 | sizeof(struct blkvsc_request), 0, | ||
| 872 | SLAB_HWCACHE_ALIGN, NULL); | ||
| 873 | if (!blkdev->request_pool) { | ||
| 874 | ret = -ENOMEM; | ||
| 875 | goto cleanup; | ||
| 876 | } | ||
| 877 | |||
| 878 | |||
| 879 | ret = blkvsc_device_add(dev, &device_info); | ||
| 880 | if (ret != 0) | ||
| 881 | goto cleanup; | ||
| 882 | |||
| 883 | blkdev->device_ctx = dev; | ||
| 884 | /* this identified the device 0 or 1 */ | ||
| 885 | blkdev->target = device_info.target_id; | ||
| 886 | /* this identified the ide ctrl 0 or 1 */ | ||
| 887 | blkdev->path = device_info.path_id; | ||
| 888 | |||
| 889 | dev_set_drvdata(&dev->device, blkdev); | ||
| 890 | |||
| 891 | ret = storvsc_get_major_info(&device_info, &major_info); | ||
| 892 | |||
| 893 | if (ret) | ||
| 894 | goto cleanup; | ||
| 895 | |||
| 896 | if (major_info.do_register) { | ||
| 897 | ret = register_blkdev(major_info.major, major_info.devname); | ||
| 898 | |||
| 899 | if (ret != 0) { | ||
| 900 | DPRINT_ERR(BLKVSC_DRV, | ||
| 901 | "register_blkdev() failed! ret %d", ret); | ||
| 902 | goto remove; | ||
| 903 | } | ||
| 904 | } | ||
| 905 | |||
| 906 | DPRINT_INFO(BLKVSC_DRV, "blkvsc registered for major %d!!", | ||
| 907 | major_info.major); | ||
| 908 | |||
| 909 | blkdev->gd = alloc_disk(BLKVSC_MINORS); | ||
| 910 | if (!blkdev->gd) { | ||
| 911 | ret = -1; | ||
| 912 | goto cleanup; | ||
| 913 | } | ||
| 914 | |||
| 915 | blkdev->gd->queue = blk_init_queue(blkvsc_request, &blkdev->lock); | ||
| 916 | |||
| 917 | blk_queue_max_segment_size(blkdev->gd->queue, PAGE_SIZE); | ||
| 918 | blk_queue_max_segments(blkdev->gd->queue, MAX_MULTIPAGE_BUFFER_COUNT); | ||
| 919 | blk_queue_segment_boundary(blkdev->gd->queue, PAGE_SIZE-1); | ||
| 920 | blk_queue_bounce_limit(blkdev->gd->queue, BLK_BOUNCE_ANY); | ||
| 921 | blk_queue_dma_alignment(blkdev->gd->queue, 511); | ||
| 922 | |||
| 923 | blkdev->gd->major = major_info.major; | ||
| 924 | if (major_info.index == 1 || major_info.index == 3) | ||
| 925 | blkdev->gd->first_minor = BLKVSC_MINORS; | ||
| 926 | else | ||
| 927 | blkdev->gd->first_minor = 0; | ||
| 928 | blkdev->gd->fops = &block_ops; | ||
| 929 | blkdev->gd->private_data = blkdev; | ||
| 930 | blkdev->gd->driverfs_dev = &(blkdev->device_ctx->device); | ||
| 931 | sprintf(blkdev->gd->disk_name, "hd%c", 'a' + major_info.index); | ||
| 932 | |||
| 933 | blkvsc_do_operation(blkdev, DO_INQUIRY); | ||
| 934 | blkvsc_do_operation(blkdev, DO_CAPACITY); | ||
| 935 | |||
| 936 | set_capacity(blkdev->gd, blkdev->capacity * (blkdev->sector_size/512)); | ||
| 937 | blk_queue_logical_block_size(blkdev->gd->queue, blkdev->sector_size); | ||
| 938 | /* go! */ | ||
| 939 | add_disk(blkdev->gd); | ||
| 940 | |||
| 941 | DPRINT_INFO(BLKVSC_DRV, "%s added!! capacity %lu sector_size %d", | ||
| 942 | blkdev->gd->disk_name, (unsigned long)blkdev->capacity, | ||
| 943 | blkdev->sector_size); | ||
| 944 | |||
| 945 | return ret; | ||
| 946 | |||
| 947 | remove: | ||
| 948 | storvsc_dev_remove(dev); | ||
| 949 | |||
| 950 | cleanup: | ||
| 951 | if (blkdev) { | ||
| 952 | if (blkdev->request_pool) { | ||
| 953 | kmem_cache_destroy(blkdev->request_pool); | ||
| 954 | blkdev->request_pool = NULL; | ||
| 955 | } | ||
| 956 | kfree(blkdev); | ||
| 957 | blkdev = NULL; | ||
| 958 | } | ||
| 959 | |||
| 960 | return ret; | ||
| 961 | } | ||
| 962 | |||
| 963 | static void blkvsc_request_completion(struct hv_storvsc_request *request) | ||
| 964 | { | ||
| 965 | struct blkvsc_request *blkvsc_req = | ||
| 966 | (struct blkvsc_request *)request->context; | ||
| 967 | struct block_device_context *blkdev = | ||
| 968 | (struct block_device_context *)blkvsc_req->dev; | ||
| 969 | unsigned long flags; | ||
| 970 | struct blkvsc_request *comp_req, *tmp; | ||
| 971 | struct vmscsi_request *vm_srb; | ||
| 972 | |||
| 973 | |||
| 974 | spin_lock_irqsave(&blkdev->lock, flags); | ||
| 975 | |||
| 976 | blkdev->num_outstanding_reqs--; | ||
| 977 | blkvsc_req->group->outstanding--; | ||
| 978 | |||
| 979 | /* | ||
| 980 | * Only start processing when all the blkvsc_reqs are | ||
| 981 | * completed. This guarantees no out-of-order blkvsc_req | ||
| 982 | * completion when calling end_that_request_first() | ||
| 983 | */ | ||
| 984 | if (blkvsc_req->group->outstanding == 0) { | ||
| 985 | list_for_each_entry_safe(comp_req, tmp, | ||
| 986 | &blkvsc_req->group->blkvsc_req_list, | ||
| 987 | req_entry) { | ||
| 988 | |||
| 989 | list_del(&comp_req->req_entry); | ||
| 990 | |||
| 991 | vm_srb = | ||
| 992 | &comp_req->request.vstor_packet.vm_srb; | ||
| 993 | if (!__blk_end_request(comp_req->req, | ||
| 994 | (!vm_srb->scsi_status ? 0 : -EIO), | ||
| 995 | comp_req->sector_count * blkdev->sector_size)) { | ||
| 996 | /* | ||
| 997 | * All the sectors have been xferred ie the | ||
| 998 | * request is done | ||
| 999 | */ | ||
| 1000 | kmem_cache_free(blkdev->request_pool, | ||
| 1001 | comp_req->group); | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | kmem_cache_free(blkdev->request_pool, comp_req); | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | if (!blkdev->shutting_down) { | ||
| 1008 | blkvsc_do_pending_reqs(blkdev); | ||
| 1009 | blk_start_queue(blkdev->gd->queue); | ||
| 1010 | blkvsc_request(blkdev->gd->queue); | ||
| 1011 | } | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | spin_unlock_irqrestore(&blkdev->lock, flags); | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | static void __exit blkvsc_exit(void) | ||
| 1018 | { | ||
| 1019 | blkvsc_drv_exit(); | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | MODULE_LICENSE("GPL"); | ||
| 1023 | MODULE_VERSION(HV_DRV_VERSION); | ||
| 1024 | MODULE_DESCRIPTION("Microsoft Hyper-V virtual block driver"); | ||
| 1025 | module_init(blkvsc_drv_init); | ||
| 1026 | module_exit(blkvsc_exit); | ||
diff --git a/drivers/staging/hv/channel.c b/drivers/staging/hv/channel.c new file mode 100644 index 00000000000..455f47a891f --- /dev/null +++ b/drivers/staging/hv/channel.c | |||
| @@ -0,0 +1,877 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2009, Microsoft Corporation. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms and conditions of the GNU General Public License, | ||
| 6 | * version 2, as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License along with | ||
| 14 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
| 15 | * Place - Suite 330, Boston, MA 02111-1307 USA. | ||
| 16 | * | ||
| 17 | * Authors: | ||
| 18 | * Haiyang Zhang <haiyangz@microsoft.com> | ||
| 19 | * Hank Janssen <hjanssen@microsoft.com> | ||
| 20 | */ | ||
| 21 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 22 | |||
| 23 | #include <linux/kernel.h> | ||
| 24 | #include <linux/sched.h> | ||
| 25 | #include <linux/wait.h> | ||
| 26 | #include <linux/mm.h> | ||
| 27 | #include <linux/slab.h> | ||
| 28 | #include <linux/module.h> | ||
| 29 | |||
| 30 | #include "hyperv.h" | ||
| 31 | #include "hyperv_vmbus.h" | ||
| 32 | |||
| 33 | #define NUM_PAGES_SPANNED(addr, len) \ | ||
| 34 | ((PAGE_ALIGN(addr + len) >> PAGE_SHIFT) - (addr >> PAGE_SHIFT)) | ||
| 35 | |||
| 36 | /* Internal routines */ | ||
| 37 | static int create_gpadl_header( | ||
| 38 | void *kbuffer, /* must be phys and virt contiguous */ | ||
| 39 | u32 size, /* page-size multiple */ | ||
| 40 | struct vmbus_channel_msginfo **msginfo, | ||
| 41 | u32 *messagecount); | ||
| 42 | static void vmbus_setevent(struct vmbus_channel *channel); | ||
| 43 | |||
| 44 | /* | ||
| 45 | * vmbus_setevent- Trigger an event notification on the specified | ||
| 46 | * channel. | ||
| 47 | */ | ||
| 48 | static void vmbus_setevent(struct vmbus_channel *channel) | ||
| 49 | { | ||
| 50 | struct hv_monitor_page *monitorpage; | ||
| 51 | |||
| 52 | if (channel->offermsg.monitor_allocated) { | ||
| 53 | /* Each u32 represents 32 channels */ | ||
| 54 | sync_set_bit(channel->offermsg.child_relid & 31, | ||
| 55 | (unsigned long *) vmbus_connection.send_int_page + | ||
| 56 | (channel->offermsg.child_relid >> 5)); | ||
| 57 | |||
| 58 | monitorpage = vmbus_connection.monitor_pages; | ||
| 59 | monitorpage++; /* Get the child to parent monitor page */ | ||
| 60 | |||
| 61 | sync_set_bit(channel->monitor_bit, | ||
| 62 | (unsigned long *)&monitorpage->trigger_group | ||
| 63 | [channel->monitor_grp].pending); | ||
| 64 | |||
| 65 | } else { | ||
| 66 | vmbus_set_event(channel->offermsg.child_relid); | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | /* | ||
| 71 | * vmbus_get_debug_info -Retrieve various channel debug info | ||
| 72 | */ | ||
| 73 | void vmbus_get_debug_info(struct vmbus_channel *channel, | ||
| 74 | struct vmbus_channel_debug_info *debuginfo) | ||
| 75 | { | ||
| 76 | struct hv_monitor_page *monitorpage; | ||
| 77 | u8 monitor_group = (u8)channel->offermsg.monitorid / 32; | ||
| 78 | u8 monitor_offset = (u8)channel->offermsg.monitorid % 32; | ||
| 79 | /* u32 monitorBit = 1 << monitorOffset; */ | ||
| 80 | |||
| 81 | debuginfo->relid = channel->offermsg.child_relid; | ||
| 82 | debuginfo->state = channel->state; | ||
| 83 | memcpy(&debuginfo->interfacetype, | ||
| 84 | &channel->offermsg.offer.if_type, sizeof(struct hv_guid)); | ||
| 85 | memcpy(&debuginfo->interface_instance, | ||
| 86 | &channel->offermsg.offer.if_instance, | ||
| 87 | sizeof(struct hv_guid)); | ||
| 88 | |||
| 89 | monitorpage = (struct hv_monitor_page *)vmbus_connection.monitor_pages; | ||
| 90 | |||
| 91 | debuginfo->monitorid = channel->offermsg.monitorid; | ||
| 92 | |||
| 93 | debuginfo->servermonitor_pending = | ||
| 94 | monitorpage->trigger_group[monitor_group].pending; | ||
| 95 | debuginfo->servermonitor_latency = | ||
| 96 | monitorpage->latency[monitor_group][monitor_offset]; | ||
| 97 | debuginfo->servermonitor_connectionid = | ||
| 98 | monitorpage->parameter[monitor_group] | ||
| 99 | [monitor_offset].connectionid.u.id; | ||
| 100 | |||
| 101 | monitorpage++; | ||
| 102 | |||
| 103 | debuginfo->clientmonitor_pending = | ||
| 104 | monitorpage->trigger_group[monitor_group].pending; | ||
| 105 | debuginfo->clientmonitor_latency = | ||
| 106 | monitorpage->latency[monitor_group][monitor_offset]; | ||
| 107 | debuginfo->clientmonitor_connectionid = | ||
| 108 | monitorpage->parameter[monitor_group] | ||
| 109 | [monitor_offset].connectionid.u.id; | ||
| 110 | |||
| 111 | hv_ringbuffer_get_debuginfo(&channel->inbound, &debuginfo->inbound); | ||
| 112 | hv_ringbuffer_get_debuginfo(&channel->outbound, &debuginfo->outbound); | ||
| 113 | } | ||
| 114 | |||
| 115 | /* | ||
| 116 | * vmbus_open - Open the specified channel. | ||
| 117 | */ | ||
| 118 | int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, | ||
| 119 | u32 recv_ringbuffer_size, void *userdata, u32 userdatalen, | ||
| 120 | void (*onchannelcallback)(void *context), void *context) | ||
| 121 | { | ||
| 122 | struct vmbus_channel_open_channel *openMsg; | ||
| 123 | struct vmbus_channel_msginfo *openInfo = NULL; | ||
| 124 | void *in, *out; | ||
| 125 | unsigned long flags; | ||
| 126 | int ret, t, err = 0; | ||
| 127 | |||
| 128 | newchannel->onchannel_callback = onchannelcallback; | ||
| 129 | newchannel->channel_callback_context = context; | ||
| 130 | |||
| 131 | /* Allocate the ring buffer */ | ||
| 132 | out = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, | ||
| 133 | get_order(send_ringbuffer_size + recv_ringbuffer_size)); | ||
| 134 | |||
| 135 | if (!out) | ||
| 136 | return -ENOMEM; | ||
| 137 | |||
| 138 | |||
| 139 | in = (void *)((unsigned long)out + send_ringbuffer_size); | ||
| 140 | |||
| 141 | newchannel->ringbuffer_pages = out; | ||
| 142 | newchannel->ringbuffer_pagecount = (send_ringbuffer_size + | ||
| 143 | recv_ringbuffer_size) >> PAGE_SHIFT; | ||
| 144 | |||
| 145 | ret = hv_ringbuffer_init( | ||
| 146 | &newchannel->outbound, out, send_ringbuffer_size); | ||
| 147 | |||
| 148 | if (ret != 0) { | ||
| 149 | err = ret; | ||
| 150 | goto errorout; | ||
| 151 | } | ||
| 152 | |||
| 153 | ret = hv_ringbuffer_init( | ||
| 154 | &newchannel->inbound, in, recv_ringbuffer_size); | ||
| 155 | if (ret != 0) { | ||
| 156 | err = ret; | ||
| 157 | goto errorout; | ||
| 158 | } | ||
| 159 | |||
| 160 | |||
| 161 | /* Establish the gpadl for the ring buffer */ | ||
| 162 | newchannel->ringbuffer_gpadlhandle = 0; | ||
| 163 | |||
| 164 | ret = vmbus_establish_gpadl(newchannel, | ||
| 165 | newchannel->outbound.ring_buffer, | ||
| 166 | send_ringbuffer_size + | ||
| 167 | recv_ringbuffer_size, | ||
| 168 | &newchannel->ringbuffer_gpadlhandle); | ||
| 169 | |||
| 170 | if (ret != 0) { | ||
| 171 | err = ret; | ||
| 172 | goto errorout; | ||
| 173 | } | ||
| 174 | |||
| 175 | /* Create and init the channel open message */ | ||
| 176 | openInfo = kmalloc(sizeof(*openInfo) + | ||
| 177 | sizeof(struct vmbus_channel_open_channel), | ||
| 178 | GFP_KERNEL); | ||
| 179 | if (!openInfo) { | ||
| 180 | err = -ENOMEM; | ||
| 181 | goto errorout; | ||
| 182 | } | ||
| 183 | |||
| 184 | init_completion(&openInfo->waitevent); | ||
| 185 | |||
| 186 | openMsg = (struct vmbus_channel_open_channel *)openInfo->msg; | ||
| 187 | openMsg->header.msgtype = CHANNELMSG_OPENCHANNEL; | ||
| 188 | openMsg->openid = newchannel->offermsg.child_relid; | ||
| 189 | openMsg->child_relid = newchannel->offermsg.child_relid; | ||
| 190 | openMsg->ringbuffer_gpadlhandle = newchannel->ringbuffer_gpadlhandle; | ||
| 191 | openMsg->downstream_ringbuffer_pageoffset = send_ringbuffer_size >> | ||
| 192 | PAGE_SHIFT; | ||
| 193 | openMsg->server_contextarea_gpadlhandle = 0; | ||
| 194 | |||
| 195 | if (userdatalen > MAX_USER_DEFINED_BYTES) { | ||
| 196 | err = -EINVAL; | ||
| 197 | goto errorout; | ||
| 198 | } | ||
| 199 | |||
| 200 | if (userdatalen) | ||
| 201 | memcpy(openMsg->userdata, userdata, userdatalen); | ||
| 202 | |||
| 203 | spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); | ||
| 204 | list_add_tail(&openInfo->msglistentry, | ||
| 205 | &vmbus_connection.chn_msg_list); | ||
| 206 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); | ||
| 207 | |||
| 208 | ret = vmbus_post_msg(openMsg, | ||
| 209 | sizeof(struct vmbus_channel_open_channel)); | ||
| 210 | |||
| 211 | if (ret != 0) | ||
| 212 | goto cleanup; | ||
| 213 | |||
| 214 | t = wait_for_completion_timeout(&openInfo->waitevent, 5*HZ); | ||
| 215 | if (t == 0) { | ||
| 216 | err = -ETIMEDOUT; | ||
| 217 | goto errorout; | ||
| 218 | } | ||
| 219 | |||
| 220 | |||
| 221 | if (openInfo->response.open_result.status) | ||
| 222 | err = openInfo->response.open_result.status; | ||
| 223 | |||
| 224 | cleanup: | ||
| 225 | spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); | ||
| 226 | list_del(&openInfo->msglistentry); | ||
| 227 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); | ||
| 228 | |||
| 229 | kfree(openInfo); | ||
| 230 | return err; | ||
| 231 | |||
| 232 | errorout: | ||
| 233 | hv_ringbuffer_cleanup(&newchannel->outbound); | ||
| 234 | hv_ringbuffer_cleanup(&newchannel->inbound); | ||
| 235 | free_pages((unsigned long)out, | ||
| 236 | get_order(send_ringbuffer_size + recv_ringbuffer_size)); | ||
| 237 | kfree(openInfo); | ||
| 238 | return err; | ||
| 239 | } | ||
| 240 | EXPORT_SYMBOL_GPL(vmbus_open); | ||
| 241 | |||
| 242 | /* | ||
| 243 | * dump_gpadl_body - Dump the gpadl body message to the console for | ||
| 244 | * debugging purposes. | ||
| 245 | */ | ||
| 246 | static void dump_gpadl_body(struct vmbus_channel_gpadl_body *gpadl, u32 len) | ||
| 247 | { | ||
| 248 | int i; | ||
| 249 | int pfncount; | ||
| 250 | |||
| 251 | pfncount = (len - sizeof(struct vmbus_channel_gpadl_body)) / | ||
| 252 | sizeof(u64); | ||
| 253 | |||
| 254 | DPRINT_DBG(VMBUS, "gpadl body - len %d pfn count %d", len, pfncount); | ||
| 255 | |||
| 256 | for (i = 0; i < pfncount; i++) | ||
| 257 | DPRINT_DBG(VMBUS, "gpadl body - %d) pfn %llu", | ||
| 258 | i, gpadl->pfn[i]); | ||
| 259 | } | ||
| 260 | |||
| 261 | /* | ||
| 262 | * dump_gpadl_header - Dump the gpadl header message to the console for | ||
| 263 | * debugging purposes. | ||
| 264 | */ | ||
| 265 | static void dump_gpadl_header(struct vmbus_channel_gpadl_header *gpadl) | ||
| 266 | { | ||
| 267 | int i, j; | ||
| 268 | int pagecount; | ||
| 269 | |||
| 270 | DPRINT_DBG(VMBUS, | ||
| 271 | "gpadl header - relid %d, range count %d, range buflen %d", | ||
| 272 | gpadl->child_relid, gpadl->rangecount, gpadl->range_buflen); | ||
| 273 | for (i = 0; i < gpadl->rangecount; i++) { | ||
| 274 | pagecount = gpadl->range[i].byte_count >> PAGE_SHIFT; | ||
| 275 | pagecount = (pagecount > 26) ? 26 : pagecount; | ||
| 276 | |||
| 277 | DPRINT_DBG(VMBUS, "gpadl range %d - len %d offset %d " | ||
| 278 | "page count %d", i, gpadl->range[i].byte_count, | ||
| 279 | gpadl->range[i].byte_offset, pagecount); | ||
| 280 | |||
| 281 | for (j = 0; j < pagecount; j++) | ||
| 282 | DPRINT_DBG(VMBUS, "%d) pfn %llu", j, | ||
| 283 | gpadl->range[i].pfn_array[j]); | ||
| 284 | } | ||
| 285 | } | ||
| 286 | |||
| 287 | /* | ||
| 288 | * create_gpadl_header - Creates a gpadl for the specified buffer | ||
| 289 | */ | ||
| 290 | static int create_gpadl_header(void *kbuffer, u32 size, | ||
| 291 | struct vmbus_channel_msginfo **msginfo, | ||
| 292 | u32 *messagecount) | ||
| 293 | { | ||
| 294 | int i; | ||
| 295 | int pagecount; | ||
| 296 | unsigned long long pfn; | ||
| 297 | struct vmbus_channel_gpadl_header *gpadl_header; | ||
| 298 | struct vmbus_channel_gpadl_body *gpadl_body; | ||
| 299 | struct vmbus_channel_msginfo *msgheader; | ||
| 300 | struct vmbus_channel_msginfo *msgbody = NULL; | ||
| 301 | u32 msgsize; | ||
| 302 | |||
| 303 | int pfnsum, pfncount, pfnleft, pfncurr, pfnsize; | ||
| 304 | |||
| 305 | pagecount = size >> PAGE_SHIFT; | ||
| 306 | pfn = virt_to_phys(kbuffer) >> PAGE_SHIFT; | ||
| 307 | |||
| 308 | /* do we need a gpadl body msg */ | ||
| 309 | pfnsize = MAX_SIZE_CHANNEL_MESSAGE - | ||
| 310 | sizeof(struct vmbus_channel_gpadl_header) - | ||
| 311 | sizeof(struct gpa_range); | ||
| 312 | pfncount = pfnsize / sizeof(u64); | ||
| 313 | |||
| 314 | if (pagecount > pfncount) { | ||
| 315 | /* we need a gpadl body */ | ||
| 316 | /* fill in the header */ | ||
| 317 | msgsize = sizeof(struct vmbus_channel_msginfo) + | ||
| 318 | sizeof(struct vmbus_channel_gpadl_header) + | ||
| 319 | sizeof(struct gpa_range) + pfncount * sizeof(u64); | ||
| 320 | msgheader = kzalloc(msgsize, GFP_KERNEL); | ||
| 321 | if (!msgheader) | ||
| 322 | goto nomem; | ||
| 323 | |||
| 324 | INIT_LIST_HEAD(&msgheader->submsglist); | ||
| 325 | msgheader->msgsize = msgsize; | ||
| 326 | |||
| 327 | gpadl_header = (struct vmbus_channel_gpadl_header *) | ||
| 328 | msgheader->msg; | ||
| 329 | gpadl_header->rangecount = 1; | ||
| 330 | gpadl_header->range_buflen = sizeof(struct gpa_range) + | ||
| 331 | pagecount * sizeof(u64); | ||
| 332 | gpadl_header->range[0].byte_offset = 0; | ||
| 333 | gpadl_header->range[0].byte_count = size; | ||
| 334 | for (i = 0; i < pfncount; i++) | ||
| 335 | gpadl_header->range[0].pfn_array[i] = pfn+i; | ||
| 336 | *msginfo = msgheader; | ||
| 337 | *messagecount = 1; | ||
| 338 | |||
| 339 | pfnsum = pfncount; | ||
| 340 | pfnleft = pagecount - pfncount; | ||
| 341 | |||
| 342 | /* how many pfns can we fit */ | ||
| 343 | pfnsize = MAX_SIZE_CHANNEL_MESSAGE - | ||
| 344 | sizeof(struct vmbus_channel_gpadl_body); | ||
| 345 | pfncount = pfnsize / sizeof(u64); | ||
| 346 | |||
| 347 | /* fill in the body */ | ||
| 348 | while (pfnleft) { | ||
| 349 | if (pfnleft > pfncount) | ||
| 350 | pfncurr = pfncount; | ||
| 351 | else | ||
| 352 | pfncurr = pfnleft; | ||
| 353 | |||
| 354 | msgsize = sizeof(struct vmbus_channel_msginfo) + | ||
| 355 | sizeof(struct vmbus_channel_gpadl_body) + | ||
| 356 | pfncurr * sizeof(u64); | ||
| 357 | msgbody = kzalloc(msgsize, GFP_KERNEL); | ||
| 358 | |||
| 359 | if (!msgbody) { | ||
| 360 | struct vmbus_channel_msginfo *pos = NULL; | ||
| 361 | struct vmbus_channel_msginfo *tmp = NULL; | ||
| 362 | /* | ||
| 363 | * Free up all the allocated messages. | ||
| 364 | */ | ||
| 365 | list_for_each_entry_safe(pos, tmp, | ||
| 366 | &msgheader->submsglist, | ||
| 367 | msglistentry) { | ||
| 368 | |||
| 369 | list_del(&pos->msglistentry); | ||
| 370 | kfree(pos); | ||
| 371 | } | ||
| 372 | |||
| 373 | goto nomem; | ||
| 374 | } | ||
| 375 | |||
| 376 | msgbody->msgsize = msgsize; | ||
| 377 | (*messagecount)++; | ||
| 378 | gpadl_body = | ||
| 379 | (struct vmbus_channel_gpadl_body *)msgbody->msg; | ||
| 380 | |||
| 381 | /* | ||
| 382 | * Gpadl is u32 and we are using a pointer which could | ||
| 383 | * be 64-bit | ||
| 384 | * This is governed by the guest/host protocol and | ||
| 385 | * so the hypervisor gurantees that this is ok. | ||
| 386 | */ | ||
| 387 | for (i = 0; i < pfncurr; i++) | ||
| 388 | gpadl_body->pfn[i] = pfn + pfnsum + i; | ||
| 389 | |||
| 390 | /* add to msg header */ | ||
| 391 | list_add_tail(&msgbody->msglistentry, | ||
| 392 | &msgheader->submsglist); | ||
| 393 | pfnsum += pfncurr; | ||
| 394 | pfnleft -= pfncurr; | ||
| 395 | } | ||
| 396 | } else { | ||
| 397 | /* everything fits in a header */ | ||
| 398 | msgsize = sizeof(struct vmbus_channel_msginfo) + | ||
| 399 | sizeof(struct vmbus_channel_gpadl_header) + | ||
| 400 | sizeof(struct gpa_range) + pagecount * sizeof(u64); | ||
| 401 | msgheader = kzalloc(msgsize, GFP_KERNEL); | ||
| 402 | if (msgheader == NULL) | ||
| 403 | goto nomem; | ||
| 404 | msgheader->msgsize = msgsize; | ||
| 405 | |||
| 406 | gpadl_header = (struct vmbus_channel_gpadl_header *) | ||
| 407 | msgheader->msg; | ||
| 408 | gpadl_header->rangecount = 1; | ||
| 409 | gpadl_header->range_buflen = sizeof(struct gpa_range) + | ||
| 410 | pagecount * sizeof(u64); | ||
| 411 | gpadl_header->range[0].byte_offset = 0; | ||
| 412 | gpadl_header->range[0].byte_count = size; | ||
| 413 | for (i = 0; i < pagecount; i++) | ||
| 414 | gpadl_header->range[0].pfn_array[i] = pfn+i; | ||
| 415 | |||
| 416 | *msginfo = msgheader; | ||
| 417 | *messagecount = 1; | ||
| 418 | } | ||
| 419 | |||
| 420 | return 0; | ||
| 421 | nomem: | ||
| 422 | kfree(msgheader); | ||
| 423 | kfree(msgbody); | ||
| 424 | return -ENOMEM; | ||
| 425 | } | ||
| 426 | |||
| 427 | /* | ||
| 428 | * vmbus_establish_gpadl - Estabish a GPADL for the specified buffer | ||
| 429 | * | ||
| 430 | * @channel: a channel | ||
| 431 | * @kbuffer: from kmalloc | ||
| 432 | * @size: page-size multiple | ||
| 433 | * @gpadl_handle: some funky thing | ||
| 434 | */ | ||
| 435 | int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, | ||
| 436 | u32 size, u32 *gpadl_handle) | ||
| 437 | { | ||
| 438 | struct vmbus_channel_gpadl_header *gpadlmsg; | ||
| 439 | struct vmbus_channel_gpadl_body *gpadl_body; | ||
| 440 | /* struct vmbus_channel_gpadl_created *gpadlCreated; */ | ||
| 441 | struct vmbus_channel_msginfo *msginfo = NULL; | ||
| 442 | struct vmbus_channel_msginfo *submsginfo; | ||
| 443 | u32 msgcount; | ||
| 444 | struct list_head *curr; | ||
| 445 | u32 next_gpadl_handle; | ||
| 446 | unsigned long flags; | ||
| 447 | int ret = 0; | ||
| 448 | int t; | ||
| 449 | |||
| 450 | next_gpadl_handle = atomic_read(&vmbus_connection.next_gpadl_handle); | ||
| 451 | atomic_inc(&vmbus_connection.next_gpadl_handle); | ||
| 452 | |||
| 453 | ret = create_gpadl_header(kbuffer, size, &msginfo, &msgcount); | ||
| 454 | if (ret) | ||
| 455 | return ret; | ||
| 456 | |||
| 457 | init_completion(&msginfo->waitevent); | ||
| 458 | |||
| 459 | gpadlmsg = (struct vmbus_channel_gpadl_header *)msginfo->msg; | ||
| 460 | gpadlmsg->header.msgtype = CHANNELMSG_GPADL_HEADER; | ||
| 461 | gpadlmsg->child_relid = channel->offermsg.child_relid; | ||
| 462 | gpadlmsg->gpadl = next_gpadl_handle; | ||
| 463 | |||
| 464 | dump_gpadl_header(gpadlmsg); | ||
| 465 | |||
| 466 | spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); | ||
| 467 | list_add_tail(&msginfo->msglistentry, | ||
| 468 | &vmbus_connection.chn_msg_list); | ||
| 469 | |||
| 470 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); | ||
| 471 | |||
| 472 | ret = vmbus_post_msg(gpadlmsg, msginfo->msgsize - | ||
| 473 | sizeof(*msginfo)); | ||
| 474 | if (ret != 0) | ||
| 475 | goto cleanup; | ||
| 476 | |||
| 477 | if (msgcount > 1) { | ||
| 478 | list_for_each(curr, &msginfo->submsglist) { | ||
| 479 | |||
| 480 | submsginfo = (struct vmbus_channel_msginfo *)curr; | ||
| 481 | gpadl_body = | ||
| 482 | (struct vmbus_channel_gpadl_body *)submsginfo->msg; | ||
| 483 | |||
| 484 | gpadl_body->header.msgtype = | ||
| 485 | CHANNELMSG_GPADL_BODY; | ||
| 486 | gpadl_body->gpadl = next_gpadl_handle; | ||
| 487 | |||
| 488 | dump_gpadl_body(gpadl_body, submsginfo->msgsize - | ||
| 489 | sizeof(*submsginfo)); | ||
| 490 | ret = vmbus_post_msg(gpadl_body, | ||
| 491 | submsginfo->msgsize - | ||
| 492 | sizeof(*submsginfo)); | ||
| 493 | if (ret != 0) | ||
| 494 | goto cleanup; | ||
| 495 | |||
| 496 | } | ||
| 497 | } | ||
| 498 | t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); | ||
| 499 | BUG_ON(t == 0); | ||
| 500 | |||
| 501 | |||
| 502 | /* At this point, we received the gpadl created msg */ | ||
| 503 | *gpadl_handle = gpadlmsg->gpadl; | ||
| 504 | |||
| 505 | cleanup: | ||
| 506 | spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); | ||
| 507 | list_del(&msginfo->msglistentry); | ||
| 508 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); | ||
| 509 | |||
| 510 | kfree(msginfo); | ||
| 511 | return ret; | ||
| 512 | } | ||
| 513 | EXPORT_SYMBOL_GPL(vmbus_establish_gpadl); | ||
| 514 | |||
| 515 | /* | ||
| 516 | * vmbus_teardown_gpadl -Teardown the specified GPADL handle | ||
| 517 | */ | ||
| 518 | int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) | ||
| 519 | { | ||
| 520 | struct vmbus_channel_gpadl_teardown *msg; | ||
| 521 | struct vmbus_channel_msginfo *info; | ||
| 522 | unsigned long flags; | ||
| 523 | int ret, t; | ||
| 524 | |||
| 525 | /* ASSERT(gpadl_handle != 0); */ | ||
| 526 | |||
| 527 | info = kmalloc(sizeof(*info) + | ||
| 528 | sizeof(struct vmbus_channel_gpadl_teardown), GFP_KERNEL); | ||
| 529 | if (!info) | ||
| 530 | return -ENOMEM; | ||
| 531 | |||
| 532 | init_completion(&info->waitevent); | ||
| 533 | |||
| 534 | msg = (struct vmbus_channel_gpadl_teardown *)info->msg; | ||
| 535 | |||
| 536 | msg->header.msgtype = CHANNELMSG_GPADL_TEARDOWN; | ||
| 537 | msg->child_relid = channel->offermsg.child_relid; | ||
| 538 | msg->gpadl = gpadl_handle; | ||
| 539 | |||
| 540 | spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); | ||
| 541 | list_add_tail(&info->msglistentry, | ||
| 542 | &vmbus_connection.chn_msg_list); | ||
| 543 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); | ||
| 544 | ret = vmbus_post_msg(msg, | ||
| 545 | sizeof(struct vmbus_channel_gpadl_teardown)); | ||
| 546 | |||
| 547 | BUG_ON(ret != 0); | ||
| 548 | t = wait_for_completion_timeout(&info->waitevent, 5*HZ); | ||
| 549 | BUG_ON(t == 0); | ||
| 550 | |||
| 551 | /* Received a torndown response */ | ||
| 552 | spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); | ||
| 553 | list_del(&info->msglistentry); | ||
| 554 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); | ||
| 555 | |||
| 556 | kfree(info); | ||
| 557 | return ret; | ||
| 558 | } | ||
| 559 | EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl); | ||
| 560 | |||
| 561 | /* | ||
| 562 | * vmbus_close - Close the specified channel | ||
| 563 | */ | ||
| 564 | void vmbus_close(struct vmbus_channel *channel) | ||
| 565 | { | ||
| 566 | struct vmbus_channel_close_channel *msg; | ||
| 567 | int ret; | ||
| 568 | |||
| 569 | /* Stop callback and cancel the timer asap */ | ||
| 570 | channel->onchannel_callback = NULL; | ||
| 571 | |||
| 572 | /* Send a closing message */ | ||
| 573 | |||
| 574 | msg = &channel->close_msg.msg; | ||
| 575 | |||
| 576 | msg->header.msgtype = CHANNELMSG_CLOSECHANNEL; | ||
| 577 | msg->child_relid = channel->offermsg.child_relid; | ||
| 578 | |||
| 579 | ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_close_channel)); | ||
| 580 | |||
| 581 | BUG_ON(ret != 0); | ||
| 582 | /* Tear down the gpadl for the channel's ring buffer */ | ||
| 583 | if (channel->ringbuffer_gpadlhandle) | ||
| 584 | vmbus_teardown_gpadl(channel, | ||
| 585 | channel->ringbuffer_gpadlhandle); | ||
| 586 | |||
| 587 | /* Cleanup the ring buffers for this channel */ | ||
| 588 | hv_ringbuffer_cleanup(&channel->outbound); | ||
| 589 | hv_ringbuffer_cleanup(&channel->inbound); | ||
| 590 | |||
| 591 | free_pages((unsigned long)channel->ringbuffer_pages, | ||
| 592 | get_order(channel->ringbuffer_pagecount * PAGE_SIZE)); | ||
| 593 | |||
| 594 | |||
| 595 | } | ||
| 596 | EXPORT_SYMBOL_GPL(vmbus_close); | ||
| 597 | |||
| 598 | /** | ||
| 599 | * vmbus_sendpacket() - Send the specified buffer on the given channel | ||
| 600 | * @channel: Pointer to vmbus_channel structure. | ||
| 601 | * @buffer: Pointer to the buffer you want to receive the data into. | ||
| 602 | * @bufferlen: Maximum size of what the the buffer will hold | ||
| 603 | * @requestid: Identifier of the request | ||
| 604 | * @type: Type of packet that is being send e.g. negotiate, time | ||
| 605 | * packet etc. | ||
| 606 | * | ||
| 607 | * Sends data in @buffer directly to hyper-v via the vmbus | ||
| 608 | * This will send the data unparsed to hyper-v. | ||
| 609 | * | ||
| 610 | * Mainly used by Hyper-V drivers. | ||
| 611 | */ | ||
| 612 | int vmbus_sendpacket(struct vmbus_channel *channel, const void *buffer, | ||
| 613 | u32 bufferlen, u64 requestid, | ||
| 614 | enum vmbus_packet_type type, u32 flags) | ||
| 615 | { | ||
| 616 | struct vmpacket_descriptor desc; | ||
| 617 | u32 packetlen = sizeof(struct vmpacket_descriptor) + bufferlen; | ||
| 618 | u32 packetlen_aligned = ALIGN(packetlen, sizeof(u64)); | ||
| 619 | struct scatterlist bufferlist[3]; | ||
| 620 | u64 aligned_data = 0; | ||
| 621 | int ret; | ||
| 622 | |||
| 623 | |||
| 624 | /* Setup the descriptor */ | ||
| 625 | desc.type = type; /* VmbusPacketTypeDataInBand; */ | ||
| 626 | desc.flags = flags; /* VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; */ | ||
| 627 | /* in 8-bytes granularity */ | ||
| 628 | desc.offset8 = sizeof(struct vmpacket_descriptor) >> 3; | ||
| 629 | desc.len8 = (u16)(packetlen_aligned >> 3); | ||
| 630 | desc.trans_id = requestid; | ||
| 631 | |||
| 632 | sg_init_table(bufferlist, 3); | ||
| 633 | sg_set_buf(&bufferlist[0], &desc, sizeof(struct vmpacket_descriptor)); | ||
| 634 | sg_set_buf(&bufferlist[1], buffer, bufferlen); | ||
| 635 | sg_set_buf(&bufferlist[2], &aligned_data, | ||
| 636 | packetlen_aligned - packetlen); | ||
| 637 | |||
| 638 | ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); | ||
| 639 | |||
| 640 | if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) | ||
| 641 | vmbus_setevent(channel); | ||
| 642 | |||
| 643 | return ret; | ||
| 644 | } | ||
| 645 | EXPORT_SYMBOL(vmbus_sendpacket); | ||
| 646 | |||
| 647 | /* | ||
| 648 | * vmbus_sendpacket_pagebuffer - Send a range of single-page buffer | ||
| 649 | * packets using a GPADL Direct packet type. | ||
| 650 | */ | ||
| 651 | int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, | ||
| 652 | struct hv_page_buffer pagebuffers[], | ||
| 653 | u32 pagecount, void *buffer, u32 bufferlen, | ||
| 654 | u64 requestid) | ||
| 655 | { | ||
| 656 | int ret; | ||
| 657 | int i; | ||
| 658 | struct vmbus_channel_packet_page_buffer desc; | ||
| 659 | u32 descsize; | ||
| 660 | u32 packetlen; | ||
| 661 | u32 packetlen_aligned; | ||
| 662 | struct scatterlist bufferlist[3]; | ||
| 663 | u64 aligned_data = 0; | ||
| 664 | |||
| 665 | if (pagecount > MAX_PAGE_BUFFER_COUNT) | ||
| 666 | return -EINVAL; | ||
| 667 | |||
| 668 | |||
| 669 | /* | ||
| 670 | * Adjust the size down since vmbus_channel_packet_page_buffer is the | ||
| 671 | * largest size we support | ||
| 672 | */ | ||
| 673 | descsize = sizeof(struct vmbus_channel_packet_page_buffer) - | ||
| 674 | ((MAX_PAGE_BUFFER_COUNT - pagecount) * | ||
| 675 | sizeof(struct hv_page_buffer)); | ||
| 676 | packetlen = descsize + bufferlen; | ||
| 677 | packetlen_aligned = ALIGN(packetlen, sizeof(u64)); | ||
| 678 | |||
| 679 | /* Setup the descriptor */ | ||
| 680 | desc.type = VM_PKT_DATA_USING_GPA_DIRECT; | ||
| 681 | desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; | ||
| 682 | desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */ | ||
| 683 | desc.length8 = (u16)(packetlen_aligned >> 3); | ||
| 684 | desc.transactionid = requestid; | ||
| 685 | desc.rangecount = pagecount; | ||
| 686 | |||
| 687 | for (i = 0; i < pagecount; i++) { | ||
| 688 | desc.range[i].len = pagebuffers[i].len; | ||
| 689 | desc.range[i].offset = pagebuffers[i].offset; | ||
| 690 | desc.range[i].pfn = pagebuffers[i].pfn; | ||
| 691 | } | ||
| 692 | |||
| 693 | sg_init_table(bufferlist, 3); | ||
| 694 | sg_set_buf(&bufferlist[0], &desc, descsize); | ||
| 695 | sg_set_buf(&bufferlist[1], buffer, bufferlen); | ||
| 696 | sg_set_buf(&bufferlist[2], &aligned_data, | ||
| 697 | packetlen_aligned - packetlen); | ||
| 698 | |||
| 699 | ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); | ||
| 700 | |||
| 701 | if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) | ||
| 702 | vmbus_setevent(channel); | ||
| 703 | |||
| 704 | return ret; | ||
| 705 | } | ||
| 706 | EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer); | ||
| 707 | |||
| 708 | /* | ||
| 709 | * vmbus_sendpacket_multipagebuffer - Send a multi-page buffer packet | ||
| 710 | * using a GPADL Direct packet type. | ||
| 711 | */ | ||
| 712 | int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, | ||
| 713 | struct hv_multipage_buffer *multi_pagebuffer, | ||
| 714 | void *buffer, u32 bufferlen, u64 requestid) | ||
| 715 | { | ||
| 716 | int ret; | ||
| 717 | struct vmbus_channel_packet_multipage_buffer desc; | ||
| 718 | u32 descsize; | ||
| 719 | u32 packetlen; | ||
| 720 | u32 packetlen_aligned; | ||
| 721 | struct scatterlist bufferlist[3]; | ||
| 722 | u64 aligned_data = 0; | ||
| 723 | u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset, | ||
| 724 | multi_pagebuffer->len); | ||
| 725 | |||
| 726 | |||
| 727 | if ((pfncount < 0) || (pfncount > MAX_MULTIPAGE_BUFFER_COUNT)) | ||
| 728 | return -EINVAL; | ||
| 729 | |||
| 730 | /* | ||
| 731 | * Adjust the size down since vmbus_channel_packet_multipage_buffer is | ||
| 732 | * the largest size we support | ||
| 733 | */ | ||
| 734 | descsize = sizeof(struct vmbus_channel_packet_multipage_buffer) - | ||
| 735 | ((MAX_MULTIPAGE_BUFFER_COUNT - pfncount) * | ||
| 736 | sizeof(u64)); | ||
| 737 | packetlen = descsize + bufferlen; | ||
| 738 | packetlen_aligned = ALIGN(packetlen, sizeof(u64)); | ||
| 739 | |||
| 740 | |||
| 741 | /* Setup the descriptor */ | ||
| 742 | desc.type = VM_PKT_DATA_USING_GPA_DIRECT; | ||
| 743 | desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; | ||
| 744 | desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */ | ||
| 745 | desc.length8 = (u16)(packetlen_aligned >> 3); | ||
| 746 | desc.transactionid = requestid; | ||
| 747 | desc.rangecount = 1; | ||
| 748 | |||
| 749 | desc.range.len = multi_pagebuffer->len; | ||
| 750 | desc.range.offset = multi_pagebuffer->offset; | ||
| 751 | |||
| 752 | memcpy(desc.range.pfn_array, multi_pagebuffer->pfn_array, | ||
| 753 | pfncount * sizeof(u64)); | ||
| 754 | |||
| 755 | sg_init_table(bufferlist, 3); | ||
| 756 | sg_set_buf(&bufferlist[0], &desc, descsize); | ||
| 757 | sg_set_buf(&bufferlist[1], buffer, bufferlen); | ||
| 758 | sg_set_buf(&bufferlist[2], &aligned_data, | ||
| 759 | packetlen_aligned - packetlen); | ||
| 760 | |||
| 761 | ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); | ||
| 762 | |||
| 763 | if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) | ||
| 764 | vmbus_setevent(channel); | ||
| 765 | |||
| 766 | return ret; | ||
| 767 | } | ||
| 768 | EXPORT_SYMBOL_GPL(vmbus_sendpacket_multipagebuffer); | ||
| 769 | |||
| 770 | /** | ||
| 771 | * vmbus_recvpacket() - Retrieve the user packet on the specified channel | ||
| 772 | * @channel: Pointer to vmbus_channel structure. | ||
| 773 | * @buffer: Pointer to the buffer you want to receive the data into. | ||
| 774 | * @bufferlen: Maximum size of what the the buffer will hold | ||
| 775 | * @buffer_actual_len: The actual size of the data after it was received | ||
| 776 | * @requestid: Identifier of the request | ||
| 777 | * | ||
| 778 | * Receives directly from the hyper-v vmbus and puts the data it received | ||
| 779 | * into Buffer. This will receive the data unparsed from hyper-v. | ||
| 780 | * | ||
| 781 | * Mainly used by Hyper-V drivers. | ||
| 782 | */ | ||
| 783 | int vmbus_recvpacket(struct vmbus_channel *channel, void *buffer, | ||
| 784 | u32 bufferlen, u32 *buffer_actual_len, u64 *requestid) | ||
| 785 | { | ||
| 786 | struct vmpacket_descriptor desc; | ||
| 787 | u32 packetlen; | ||
| 788 | u32 userlen; | ||
| 789 | int ret; | ||
| 790 | unsigned long flags; | ||
| 791 | |||
| 792 | *buffer_actual_len = 0; | ||
| 793 | *requestid = 0; | ||
| 794 | |||
| 795 | spin_lock_irqsave(&channel->inbound_lock, flags); | ||
| 796 | |||
| 797 | ret = hv_ringbuffer_peek(&channel->inbound, &desc, | ||
| 798 | sizeof(struct vmpacket_descriptor)); | ||
| 799 | if (ret != 0) { | ||
| 800 | spin_unlock_irqrestore(&channel->inbound_lock, flags); | ||
| 801 | return 0; | ||
| 802 | } | ||
| 803 | |||
| 804 | packetlen = desc.len8 << 3; | ||
| 805 | userlen = packetlen - (desc.offset8 << 3); | ||
| 806 | |||
| 807 | *buffer_actual_len = userlen; | ||
| 808 | |||
| 809 | if (userlen > bufferlen) { | ||
| 810 | spin_unlock_irqrestore(&channel->inbound_lock, flags); | ||
| 811 | |||
| 812 | pr_err("Buffer too small - got %d needs %d\n", | ||
| 813 | bufferlen, userlen); | ||
| 814 | return -ETOOSMALL; | ||
| 815 | } | ||
| 816 | |||
| 817 | *requestid = desc.trans_id; | ||
| 818 | |||
| 819 | /* Copy over the packet to the user buffer */ | ||
| 820 | ret = hv_ringbuffer_read(&channel->inbound, buffer, userlen, | ||
| 821 | (desc.offset8 << 3)); | ||
| 822 | |||
| 823 | spin_unlock_irqrestore(&channel->inbound_lock, flags); | ||
| 824 | |||
| 825 | return 0; | ||
| 826 | } | ||
| 827 | EXPORT_SYMBOL(vmbus_recvpacket); | ||
| 828 | |||
| 829 | /* | ||
| 830 | * vmbus_recvpacket_raw - Retrieve the raw packet on the specified channel | ||
| 831 | */ | ||
| 832 | int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer, | ||
| 833 | u32 bufferlen, u32 *buffer_actual_len, | ||
| 834 | u64 *requestid) | ||
| 835 | { | ||
| 836 | struct vmpacket_descriptor desc; | ||
| 837 | u32 packetlen; | ||
| 838 | u32 userlen; | ||
| 839 | int ret; | ||
| 840 | unsigned long flags; | ||
| 841 | |||
| 842 | *buffer_actual_len = 0; | ||
| 843 | *requestid = 0; | ||
| 844 | |||
| 845 | spin_lock_irqsave(&channel->inbound_lock, flags); | ||
| 846 | |||
| 847 | ret = hv_ringbuffer_peek(&channel->inbound, &desc, | ||
| 848 | sizeof(struct vmpacket_descriptor)); | ||
| 849 | if (ret != 0) { | ||
| 850 | spin_unlock_irqrestore(&channel->inbound_lock, flags); | ||
| 851 | return 0; | ||
| 852 | } | ||
| 853 | |||
| 854 | |||
| 855 | packetlen = desc.len8 << 3; | ||
| 856 | userlen = packetlen - (desc.offset8 << 3); | ||
| 857 | |||
| 858 | *buffer_actual_len = packetlen; | ||
| 859 | |||
| 860 | if (packetlen > bufferlen) { | ||
| 861 | spin_unlock_irqrestore(&channel->inbound_lock, flags); | ||
| 862 | |||
| 863 | pr_err("Buffer too small - needed %d bytes but " | ||
| 864 | "got space for only %d bytes\n", | ||
| 865 | packetlen, bufferlen); | ||
| 866 | return -2; | ||
| 867 | } | ||
| 868 | |||
| 869 | *requestid = desc.trans_id; | ||
| 870 | |||
| 871 | /* Copy over the entire packet to the user buffer */ | ||
| 872 | ret = hv_ringbuffer_read(&channel->inbound, buffer, packetlen, 0); | ||
| 873 | |||
| 874 | spin_unlock_irqrestore(&channel->inbound_lock, flags); | ||
| 875 | return 0; | ||
| 876 | } | ||
| 877 | EXPORT_SYMBOL_GPL(vmbus_recvpacket_raw); | ||
diff --git a/drivers/staging/hv/channel_mgmt.c b/drivers/staging/hv/channel_mgmt.c new file mode 100644 index 00000000000..bf011f3fb85 --- /dev/null +++ b/drivers/staging/hv/channel_mgmt.c | |||
| @@ -0,0 +1,784 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2009, Microsoft Corporation. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms and conditions of the GNU General Public License, | ||
| 6 | * version 2, as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License along with | ||
| 14 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
| 15 | * Place - Suite 330, Boston, MA 02111-1307 USA. | ||
| 16 | * | ||
| 17 | * Authors: | ||
| 18 | * Haiyang Zhang <haiyangz@microsoft.com> | ||
| 19 | * Hank Janssen <hjanssen@microsoft.com> | ||
| 20 | */ | ||
| 21 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 22 | |||
| 23 | #include <linux/kernel.h> | ||
| 24 | #include <linux/sched.h> | ||
| 25 | #include <linux/wait.h> | ||
| 26 | #include <linux/mm.h> | ||
| 27 | #include <linux/slab.h> | ||
| 28 | #include <linux/list.h> | ||
| 29 | #include <linux/module.h> | ||
| 30 | #include <linux/completion.h> | ||
| 31 | |||
| 32 | #include "hyperv.h" | ||
| 33 | #include "hyperv_vmbus.h" | ||
| 34 | |||
| 35 | struct vmbus_channel_message_table_entry { | ||
| 36 | enum vmbus_channel_message_type message_type; | ||
| 37 | void (*message_handler)(struct vmbus_channel_message_header *msg); | ||
| 38 | }; | ||
| 39 | |||
| 40 | #define MAX_MSG_TYPES 4 | ||
| 41 | #define MAX_NUM_DEVICE_CLASSES_SUPPORTED 8 | ||
| 42 | |||
| 43 | static const struct hv_guid | ||
| 44 | supported_device_classes[MAX_NUM_DEVICE_CLASSES_SUPPORTED] = { | ||
| 45 | /* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */ | ||
| 46 | /* Storage - SCSI */ | ||
| 47 | { | ||
| 48 | .data = { | ||
| 49 | 0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, | ||
| 50 | 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f | ||
| 51 | } | ||
| 52 | }, | ||
| 53 | |||
| 54 | /* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */ | ||
| 55 | /* Network */ | ||
| 56 | { | ||
| 57 | .data = { | ||
| 58 | 0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, | ||
| 59 | 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E | ||
| 60 | } | ||
| 61 | }, | ||
| 62 | |||
| 63 | /* {CFA8B69E-5B4A-4cc0-B98B-8BA1A1F3F95A} */ | ||
| 64 | /* Input */ | ||
| 65 | { | ||
| 66 | .data = { | ||
| 67 | 0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c, | ||
| 68 | 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A | ||
| 69 | } | ||
| 70 | }, | ||
| 71 | |||
| 72 | /* {32412632-86cb-44a2-9b5c-50d1417354f5} */ | ||
| 73 | /* IDE */ | ||
| 74 | { | ||
| 75 | .data = { | ||
| 76 | 0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, | ||
| 77 | 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5 | ||
| 78 | } | ||
| 79 | }, | ||
| 80 | /* 0E0B6031-5213-4934-818B-38D90CED39DB */ | ||
| 81 | /* Shutdown */ | ||
| 82 | { | ||
| 83 | .data = { | ||
| 84 | 0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49, | ||
| 85 | 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB | ||
| 86 | } | ||
| 87 | }, | ||
| 88 | /* {9527E630-D0AE-497b-ADCE-E80AB0175CAF} */ | ||
| 89 | /* TimeSync */ | ||
| 90 | { | ||
| 91 | .data = { | ||
| 92 | 0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, | ||
| 93 | 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf | ||
| 94 | } | ||
| 95 | }, | ||
| 96 | /* {57164f39-9115-4e78-ab55-382f3bd5422d} */ | ||
| 97 | /* Heartbeat */ | ||
| 98 | { | ||
| 99 | .data = { | ||
| 100 | 0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, | ||
| 101 | 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d | ||
| 102 | } | ||
| 103 | }, | ||
| 104 | /* {A9A0F4E7-5A45-4d96-B827-8A841E8C03E6} */ | ||
| 105 | /* KVP */ | ||
| 106 | { | ||
| 107 | .data = { | ||
| 108 | 0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, | ||
| 109 | 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6 | ||
| 110 | } | ||
| 111 | }, | ||
| 112 | |||
| 113 | }; | ||
| 114 | |||
| 115 | |||
| 116 | /** | ||
| 117 | * prep_negotiate_resp() - Create default response for Hyper-V Negotiate message | ||
| 118 | * @icmsghdrp: Pointer to msg header structure | ||
| 119 | * @icmsg_negotiate: Pointer to negotiate message structure | ||
| 120 | * @buf: Raw buffer channel data | ||
| 121 | * | ||
| 122 | * @icmsghdrp is of type &struct icmsg_hdr. | ||
| 123 | * @negop is of type &struct icmsg_negotiate. | ||
| 124 | * Set up and fill in default negotiate response message. This response can | ||
| 125 | * come from both the vmbus driver and the hv_utils driver. The current api | ||
| 126 | * will respond properly to both Windows 2008 and Windows 2008-R2 operating | ||
| 127 | * systems. | ||
| 128 | * | ||
| 129 | * Mainly used by Hyper-V drivers. | ||
| 130 | */ | ||
| 131 | void prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, | ||
| 132 | struct icmsg_negotiate *negop, | ||
| 133 | u8 *buf) | ||
| 134 | { | ||
| 135 | if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { | ||
| 136 | icmsghdrp->icmsgsize = 0x10; | ||
| 137 | |||
| 138 | negop = (struct icmsg_negotiate *)&buf[ | ||
| 139 | sizeof(struct vmbuspipe_hdr) + | ||
| 140 | sizeof(struct icmsg_hdr)]; | ||
| 141 | |||
| 142 | if (negop->icframe_vercnt == 2 && | ||
| 143 | negop->icversion_data[1].major == 3) { | ||
| 144 | negop->icversion_data[0].major = 3; | ||
| 145 | negop->icversion_data[0].minor = 0; | ||
| 146 | negop->icversion_data[1].major = 3; | ||
| 147 | negop->icversion_data[1].minor = 0; | ||
| 148 | } else { | ||
| 149 | negop->icversion_data[0].major = 1; | ||
| 150 | negop->icversion_data[0].minor = 0; | ||
| 151 | negop->icversion_data[1].major = 1; | ||
| 152 | negop->icversion_data[1].minor = 0; | ||
| 153 | } | ||
| 154 | |||
| 155 | negop->icframe_vercnt = 1; | ||
| 156 | negop->icmsg_vercnt = 1; | ||
| 157 | } | ||
| 158 | } | ||
| 159 | EXPORT_SYMBOL(prep_negotiate_resp); | ||
| 160 | |||
| 161 | /** | ||
| 162 | * chn_cb_negotiate() - Default handler for non IDE/SCSI/NETWORK | ||
| 163 | * Hyper-V requests | ||
| 164 | * @context: Pointer to argument structure. | ||
| 165 | * | ||
| 166 | * Set up the default handler for non device driver specific requests | ||
| 167 | * from Hyper-V. This stub responds to the default negotiate messages | ||
| 168 | * that come in for every non IDE/SCSI/Network request. | ||
| 169 | * This behavior is normally overwritten in the hv_utils driver. That | ||
| 170 | * driver handles requests like graceful shutdown, heartbeats etc. | ||
| 171 | * | ||
| 172 | * Mainly used by Hyper-V drivers. | ||
| 173 | */ | ||
| 174 | void chn_cb_negotiate(void *context) | ||
| 175 | { | ||
| 176 | struct vmbus_channel *channel = context; | ||
| 177 | u8 *buf; | ||
| 178 | u32 buflen, recvlen; | ||
| 179 | u64 requestid; | ||
| 180 | |||
| 181 | struct icmsg_hdr *icmsghdrp; | ||
| 182 | struct icmsg_negotiate *negop = NULL; | ||
| 183 | |||
| 184 | if (channel->util_index >= 0) { | ||
| 185 | /* | ||
| 186 | * This is a properly initialized util channel. | ||
| 187 | * Route this callback appropriately and setup state | ||
| 188 | * so that we don't need to reroute again. | ||
| 189 | */ | ||
| 190 | if (hv_cb_utils[channel->util_index].callback != NULL) { | ||
| 191 | /* | ||
| 192 | * The util driver has established a handler for | ||
| 193 | * this service; do the magic. | ||
| 194 | */ | ||
| 195 | channel->onchannel_callback = | ||
| 196 | hv_cb_utils[channel->util_index].callback; | ||
| 197 | (hv_cb_utils[channel->util_index].callback)(channel); | ||
| 198 | return; | ||
| 199 | } | ||
| 200 | } | ||
| 201 | |||
| 202 | buflen = PAGE_SIZE; | ||
| 203 | buf = kmalloc(buflen, GFP_ATOMIC); | ||
| 204 | |||
| 205 | vmbus_recvpacket(channel, buf, buflen, &recvlen, &requestid); | ||
| 206 | |||
| 207 | if (recvlen > 0) { | ||
| 208 | icmsghdrp = (struct icmsg_hdr *)&buf[ | ||
| 209 | sizeof(struct vmbuspipe_hdr)]; | ||
| 210 | |||
| 211 | prep_negotiate_resp(icmsghdrp, negop, buf); | ||
| 212 | |||
| 213 | icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ||
| 214 | | ICMSGHDRFLAG_RESPONSE; | ||
| 215 | |||
| 216 | vmbus_sendpacket(channel, buf, | ||
| 217 | recvlen, requestid, | ||
| 218 | VM_PKT_DATA_INBAND, 0); | ||
| 219 | } | ||
| 220 | |||
| 221 | kfree(buf); | ||
| 222 | } | ||
| 223 | EXPORT_SYMBOL(chn_cb_negotiate); | ||
| 224 | |||
| 225 | /* | ||
| 226 | * Function table used for message responses for non IDE/SCSI/Network type | ||
| 227 | * messages. (Such as KVP/Shutdown etc) | ||
| 228 | */ | ||
| 229 | struct hyperv_service_callback hv_cb_utils[MAX_MSG_TYPES] = { | ||
| 230 | /* 0E0B6031-5213-4934-818B-38D90CED39DB */ | ||
| 231 | /* Shutdown */ | ||
| 232 | { | ||
| 233 | .msg_type = HV_SHUTDOWN_MSG, | ||
| 234 | .data = { | ||
| 235 | 0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49, | ||
| 236 | 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB | ||
| 237 | }, | ||
| 238 | .log_msg = "Shutdown channel functionality initialized" | ||
| 239 | }, | ||
| 240 | |||
| 241 | /* {9527E630-D0AE-497b-ADCE-E80AB0175CAF} */ | ||
| 242 | /* TimeSync */ | ||
| 243 | { | ||
| 244 | .msg_type = HV_TIMESYNC_MSG, | ||
| 245 | .data = { | ||
| 246 | 0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, | ||
| 247 | 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf | ||
| 248 | }, | ||
| 249 | .log_msg = "Timesync channel functionality initialized" | ||
| 250 | }, | ||
| 251 | /* {57164f39-9115-4e78-ab55-382f3bd5422d} */ | ||
| 252 | /* Heartbeat */ | ||
| 253 | { | ||
| 254 | .msg_type = HV_HEARTBEAT_MSG, | ||
| 255 | .data = { | ||
| 256 | 0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, | ||
| 257 | 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d | ||
| 258 | }, | ||
| 259 | .log_msg = "Heartbeat channel functionality initialized" | ||
| 260 | }, | ||
| 261 | /* {A9A0F4E7-5A45-4d96-B827-8A841E8C03E6} */ | ||
| 262 | /* KVP */ | ||
| 263 | { | ||
| 264 | .data = { | ||
| 265 | 0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, | ||
| 266 | 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6 | ||
| 267 | }, | ||
| 268 | .log_msg = "KVP channel functionality initialized" | ||
| 269 | }, | ||
| 270 | }; | ||
| 271 | EXPORT_SYMBOL(hv_cb_utils); | ||
| 272 | |||
| 273 | /* | ||
| 274 | * alloc_channel - Allocate and initialize a vmbus channel object | ||
| 275 | */ | ||
| 276 | static struct vmbus_channel *alloc_channel(void) | ||
| 277 | { | ||
| 278 | struct vmbus_channel *channel; | ||
| 279 | |||
| 280 | channel = kzalloc(sizeof(*channel), GFP_ATOMIC); | ||
| 281 | if (!channel) | ||
| 282 | return NULL; | ||
| 283 | |||
| 284 | spin_lock_init(&channel->inbound_lock); | ||
| 285 | |||
| 286 | channel->controlwq = create_workqueue("hv_vmbus_ctl"); | ||
| 287 | if (!channel->controlwq) { | ||
| 288 | kfree(channel); | ||
| 289 | return NULL; | ||
| 290 | } | ||
| 291 | |||
| 292 | return channel; | ||
| 293 | } | ||
| 294 | |||
| 295 | /* | ||
| 296 | * release_hannel - Release the vmbus channel object itself | ||
| 297 | */ | ||
| 298 | static void release_channel(struct work_struct *work) | ||
| 299 | { | ||
| 300 | struct vmbus_channel *channel = container_of(work, | ||
| 301 | struct vmbus_channel, | ||
| 302 | work); | ||
| 303 | |||
| 304 | destroy_workqueue(channel->controlwq); | ||
| 305 | |||
| 306 | kfree(channel); | ||
| 307 | } | ||
| 308 | |||
| 309 | /* | ||
| 310 | * free_channel - Release the resources used by the vmbus channel object | ||
| 311 | */ | ||
| 312 | void free_channel(struct vmbus_channel *channel) | ||
| 313 | { | ||
| 314 | |||
| 315 | /* | ||
| 316 | * We have to release the channel's workqueue/thread in the vmbus's | ||
| 317 | * workqueue/thread context | ||
| 318 | * ie we can't destroy ourselves. | ||
| 319 | */ | ||
| 320 | INIT_WORK(&channel->work, release_channel); | ||
| 321 | queue_work(vmbus_connection.work_queue, &channel->work); | ||
| 322 | } | ||
| 323 | |||
| 324 | |||
| 325 | |||
| 326 | /* | ||
| 327 | * vmbus_process_rescind_offer - | ||
| 328 | * Rescind the offer by initiating a device removal | ||
| 329 | */ | ||
| 330 | static void vmbus_process_rescind_offer(struct work_struct *work) | ||
| 331 | { | ||
| 332 | struct vmbus_channel *channel = container_of(work, | ||
| 333 | struct vmbus_channel, | ||
| 334 | work); | ||
| 335 | |||
| 336 | vmbus_child_device_unregister(channel->device_obj); | ||
| 337 | } | ||
| 338 | |||
| 339 | /* | ||
| 340 | * vmbus_process_offer - Process the offer by creating a channel/device | ||
| 341 | * associated with this offer | ||
| 342 | */ | ||
| 343 | static void vmbus_process_offer(struct work_struct *work) | ||
| 344 | { | ||
| 345 | struct vmbus_channel *newchannel = container_of(work, | ||
| 346 | struct vmbus_channel, | ||
| 347 | work); | ||
| 348 | struct vmbus_channel *channel; | ||
| 349 | bool fnew = true; | ||
| 350 | int ret; | ||
| 351 | int cnt; | ||
| 352 | unsigned long flags; | ||
| 353 | |||
| 354 | /* The next possible work is rescind handling */ | ||
| 355 | INIT_WORK(&newchannel->work, vmbus_process_rescind_offer); | ||
| 356 | |||
| 357 | /* Make sure this is a new offer */ | ||
| 358 | spin_lock_irqsave(&vmbus_connection.channel_lock, flags); | ||
| 359 | |||
| 360 | list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { | ||
| 361 | if (!memcmp(&channel->offermsg.offer.if_type, | ||
| 362 | &newchannel->offermsg.offer.if_type, | ||
| 363 | sizeof(struct hv_guid)) && | ||
| 364 | !memcmp(&channel->offermsg.offer.if_instance, | ||
| 365 | &newchannel->offermsg.offer.if_instance, | ||
| 366 | sizeof(struct hv_guid))) { | ||
| 367 | fnew = false; | ||
| 368 | break; | ||
| 369 | } | ||
| 370 | } | ||
| 371 | |||
| 372 | if (fnew) | ||
| 373 | list_add_tail(&newchannel->listentry, | ||
| 374 | &vmbus_connection.chn_list); | ||
| 375 | |||
| 376 | spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); | ||
| 377 | |||
| 378 | if (!fnew) { | ||
| 379 | free_channel(newchannel); | ||
| 380 | return; | ||
| 381 | } | ||
| 382 | |||
| 383 | /* | ||
| 384 | * Start the process of binding this offer to the driver | ||
| 385 | * We need to set the DeviceObject field before calling | ||
| 386 | * vmbus_child_dev_add() | ||
| 387 | */ | ||
| 388 | newchannel->device_obj = vmbus_child_device_create( | ||
| 389 | &newchannel->offermsg.offer.if_type, | ||
| 390 | &newchannel->offermsg.offer.if_instance, | ||
| 391 | newchannel); | ||
| 392 | |||
| 393 | /* | ||
| 394 | * Add the new device to the bus. This will kick off device-driver | ||
| 395 | * binding which eventually invokes the device driver's AddDevice() | ||
| 396 | * method. | ||
| 397 | */ | ||
| 398 | ret = vmbus_child_device_register(newchannel->device_obj); | ||
| 399 | if (ret != 0) { | ||
| 400 | pr_err("unable to add child device object (relid %d)\n", | ||
| 401 | newchannel->offermsg.child_relid); | ||
| 402 | |||
| 403 | spin_lock_irqsave(&vmbus_connection.channel_lock, flags); | ||
| 404 | list_del(&newchannel->listentry); | ||
| 405 | spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); | ||
| 406 | |||
| 407 | free_channel(newchannel); | ||
| 408 | } else { | ||
| 409 | /* | ||
| 410 | * This state is used to indicate a successful open | ||
| 411 | * so that when we do close the channel normally, we | ||
| 412 | * can cleanup properly | ||
| 413 | */ | ||
| 414 | newchannel->state = CHANNEL_OPEN_STATE; | ||
| 415 | newchannel->util_index = -1; /* Invalid index */ | ||
| 416 | |||
| 417 | /* Open IC channels */ | ||
| 418 | for (cnt = 0; cnt < MAX_MSG_TYPES; cnt++) { | ||
| 419 | if (memcmp(&newchannel->offermsg.offer.if_type, | ||
| 420 | &hv_cb_utils[cnt].data, | ||
| 421 | sizeof(struct hv_guid)) == 0 && | ||
| 422 | vmbus_open(newchannel, 2 * PAGE_SIZE, | ||
| 423 | 2 * PAGE_SIZE, NULL, 0, | ||
| 424 | chn_cb_negotiate, | ||
| 425 | newchannel) == 0) { | ||
| 426 | hv_cb_utils[cnt].channel = newchannel; | ||
| 427 | newchannel->util_index = cnt; | ||
| 428 | |||
| 429 | pr_info("%s\n", hv_cb_utils[cnt].log_msg); | ||
| 430 | |||
| 431 | } | ||
| 432 | } | ||
| 433 | } | ||
| 434 | } | ||
| 435 | |||
| 436 | /* | ||
| 437 | * vmbus_onoffer - Handler for channel offers from vmbus in parent partition. | ||
| 438 | * | ||
| 439 | * We ignore all offers except network and storage offers. For each network and | ||
| 440 | * storage offers, we create a channel object and queue a work item to the | ||
| 441 | * channel object to process the offer synchronously | ||
| 442 | */ | ||
| 443 | static void vmbus_onoffer(struct vmbus_channel_message_header *hdr) | ||
| 444 | { | ||
| 445 | struct vmbus_channel_offer_channel *offer; | ||
| 446 | struct vmbus_channel *newchannel; | ||
| 447 | struct hv_guid *guidtype; | ||
| 448 | struct hv_guid *guidinstance; | ||
| 449 | int i; | ||
| 450 | int fsupported = 0; | ||
| 451 | |||
| 452 | offer = (struct vmbus_channel_offer_channel *)hdr; | ||
| 453 | for (i = 0; i < MAX_NUM_DEVICE_CLASSES_SUPPORTED; i++) { | ||
| 454 | if (memcmp(&offer->offer.if_type, | ||
| 455 | &supported_device_classes[i], | ||
| 456 | sizeof(struct hv_guid)) == 0) { | ||
| 457 | fsupported = 1; | ||
| 458 | break; | ||
| 459 | } | ||
| 460 | } | ||
| 461 | |||
| 462 | if (!fsupported) | ||
| 463 | return; | ||
| 464 | |||
| 465 | guidtype = &offer->offer.if_type; | ||
| 466 | guidinstance = &offer->offer.if_instance; | ||
| 467 | |||
| 468 | /* Allocate the channel object and save this offer. */ | ||
| 469 | newchannel = alloc_channel(); | ||
| 470 | if (!newchannel) { | ||
| 471 | pr_err("Unable to allocate channel object\n"); | ||
| 472 | return; | ||
| 473 | } | ||
| 474 | |||
| 475 | memcpy(&newchannel->offermsg, offer, | ||
| 476 | sizeof(struct vmbus_channel_offer_channel)); | ||
| 477 | newchannel->monitor_grp = (u8)offer->monitorid / 32; | ||
| 478 | newchannel->monitor_bit = (u8)offer->monitorid % 32; | ||
| 479 | |||
| 480 | INIT_WORK(&newchannel->work, vmbus_process_offer); | ||
| 481 | queue_work(newchannel->controlwq, &newchannel->work); | ||
| 482 | } | ||
| 483 | |||
| 484 | /* | ||
| 485 | * vmbus_onoffer_rescind - Rescind offer handler. | ||
| 486 | * | ||
| 487 | * We queue a work item to process this offer synchronously | ||
| 488 | */ | ||
| 489 | static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) | ||
| 490 | { | ||
| 491 | struct vmbus_channel_rescind_offer *rescind; | ||
| 492 | struct vmbus_channel *channel; | ||
| 493 | |||
| 494 | rescind = (struct vmbus_channel_rescind_offer *)hdr; | ||
| 495 | channel = relid2channel(rescind->child_relid); | ||
| 496 | |||
| 497 | if (channel == NULL) | ||
| 498 | /* Just return here, no channel found */ | ||
| 499 | return; | ||
| 500 | |||
| 501 | /* work is initialized for vmbus_process_rescind_offer() from | ||
| 502 | * vmbus_process_offer() where the channel got created */ | ||
| 503 | queue_work(channel->controlwq, &channel->work); | ||
| 504 | } | ||
| 505 | |||
| 506 | /* | ||
| 507 | * vmbus_onoffers_delivered - | ||
| 508 | * This is invoked when all offers have been delivered. | ||
| 509 | * | ||
| 510 | * Nothing to do here. | ||
| 511 | */ | ||
| 512 | static void vmbus_onoffers_delivered( | ||
| 513 | struct vmbus_channel_message_header *hdr) | ||
| 514 | { | ||
| 515 | } | ||
| 516 | |||
| 517 | /* | ||
| 518 | * vmbus_onopen_result - Open result handler. | ||
| 519 | * | ||
| 520 | * This is invoked when we received a response to our channel open request. | ||
| 521 | * Find the matching request, copy the response and signal the requesting | ||
| 522 | * thread. | ||
| 523 | */ | ||
| 524 | static void vmbus_onopen_result(struct vmbus_channel_message_header *hdr) | ||
| 525 | { | ||
| 526 | struct vmbus_channel_open_result *result; | ||
| 527 | struct vmbus_channel_msginfo *msginfo; | ||
| 528 | struct vmbus_channel_message_header *requestheader; | ||
| 529 | struct vmbus_channel_open_channel *openmsg; | ||
| 530 | unsigned long flags; | ||
| 531 | |||
| 532 | result = (struct vmbus_channel_open_result *)hdr; | ||
| 533 | |||
| 534 | /* | ||
| 535 | * Find the open msg, copy the result and signal/unblock the wait event | ||
| 536 | */ | ||
| 537 | spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); | ||
| 538 | |||
| 539 | list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, | ||
| 540 | msglistentry) { | ||
| 541 | requestheader = | ||
| 542 | (struct vmbus_channel_message_header *)msginfo->msg; | ||
| 543 | |||
| 544 | if (requestheader->msgtype == CHANNELMSG_OPENCHANNEL) { | ||
| 545 | openmsg = | ||
| 546 | (struct vmbus_channel_open_channel *)msginfo->msg; | ||
| 547 | if (openmsg->child_relid == result->child_relid && | ||
| 548 | openmsg->openid == result->openid) { | ||
| 549 | memcpy(&msginfo->response.open_result, | ||
| 550 | result, | ||
| 551 | sizeof( | ||
| 552 | struct vmbus_channel_open_result)); | ||
| 553 | complete(&msginfo->waitevent); | ||
| 554 | break; | ||
| 555 | } | ||
| 556 | } | ||
| 557 | } | ||
| 558 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); | ||
| 559 | } | ||
| 560 | |||
| 561 | /* | ||
| 562 | * vmbus_ongpadl_created - GPADL created handler. | ||
| 563 | * | ||
| 564 | * This is invoked when we received a response to our gpadl create request. | ||
| 565 | * Find the matching request, copy the response and signal the requesting | ||
| 566 | * thread. | ||
| 567 | */ | ||
| 568 | static void vmbus_ongpadl_created(struct vmbus_channel_message_header *hdr) | ||
| 569 | { | ||
| 570 | struct vmbus_channel_gpadl_created *gpadlcreated; | ||
| 571 | struct vmbus_channel_msginfo *msginfo; | ||
| 572 | struct vmbus_channel_message_header *requestheader; | ||
| 573 | struct vmbus_channel_gpadl_header *gpadlheader; | ||
| 574 | unsigned long flags; | ||
| 575 | |||
| 576 | gpadlcreated = (struct vmbus_channel_gpadl_created *)hdr; | ||
| 577 | |||
| 578 | /* | ||
| 579 | * Find the establish msg, copy the result and signal/unblock the wait | ||
| 580 | * event | ||
| 581 | */ | ||
| 582 | spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); | ||
| 583 | |||
| 584 | list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, | ||
| 585 | msglistentry) { | ||
| 586 | requestheader = | ||
| 587 | (struct vmbus_channel_message_header *)msginfo->msg; | ||
| 588 | |||
| 589 | if (requestheader->msgtype == CHANNELMSG_GPADL_HEADER) { | ||
| 590 | gpadlheader = | ||
| 591 | (struct vmbus_channel_gpadl_header *)requestheader; | ||
| 592 | |||
| 593 | if ((gpadlcreated->child_relid == | ||
| 594 | gpadlheader->child_relid) && | ||
| 595 | (gpadlcreated->gpadl == gpadlheader->gpadl)) { | ||
| 596 | memcpy(&msginfo->response.gpadl_created, | ||
| 597 | gpadlcreated, | ||
| 598 | sizeof( | ||
| 599 | struct vmbus_channel_gpadl_created)); | ||
| 600 | complete(&msginfo->waitevent); | ||
| 601 | break; | ||
| 602 | } | ||
| 603 | } | ||
| 604 | } | ||
| 605 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); | ||
| 606 | } | ||
| 607 | |||
| 608 | /* | ||
| 609 | * vmbus_ongpadl_torndown - GPADL torndown handler. | ||
| 610 | * | ||
| 611 | * This is invoked when we received a response to our gpadl teardown request. | ||
| 612 | * Find the matching request, copy the response and signal the requesting | ||
| 613 | * thread. | ||
| 614 | */ | ||
| 615 | static void vmbus_ongpadl_torndown( | ||
| 616 | struct vmbus_channel_message_header *hdr) | ||
| 617 | { | ||
| 618 | struct vmbus_channel_gpadl_torndown *gpadl_torndown; | ||
| 619 | struct vmbus_channel_msginfo *msginfo; | ||
| 620 | struct vmbus_channel_message_header *requestheader; | ||
| 621 | struct vmbus_channel_gpadl_teardown *gpadl_teardown; | ||
| 622 | unsigned long flags; | ||
| 623 | |||
| 624 | gpadl_torndown = (struct vmbus_channel_gpadl_torndown *)hdr; | ||
| 625 | |||
| 626 | /* | ||
| 627 | * Find the open msg, copy the result and signal/unblock the wait event | ||
| 628 | */ | ||
| 629 | spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); | ||
| 630 | |||
| 631 | list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, | ||
| 632 | msglistentry) { | ||
| 633 | requestheader = | ||
| 634 | (struct vmbus_channel_message_header *)msginfo->msg; | ||
| 635 | |||
| 636 | if (requestheader->msgtype == CHANNELMSG_GPADL_TEARDOWN) { | ||
| 637 | gpadl_teardown = | ||
| 638 | (struct vmbus_channel_gpadl_teardown *)requestheader; | ||
| 639 | |||
| 640 | if (gpadl_torndown->gpadl == gpadl_teardown->gpadl) { | ||
| 641 | memcpy(&msginfo->response.gpadl_torndown, | ||
| 642 | gpadl_torndown, | ||
| 643 | sizeof( | ||
| 644 | struct vmbus_channel_gpadl_torndown)); | ||
| 645 | complete(&msginfo->waitevent); | ||
| 646 | break; | ||
| 647 | } | ||
| 648 | } | ||
| 649 | } | ||
| 650 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); | ||
| 651 | } | ||
| 652 | |||
| 653 | /* | ||
| 654 | * vmbus_onversion_response - Version response handler | ||
| 655 | * | ||
| 656 | * This is invoked when we received a response to our initiate contact request. | ||
| 657 | * Find the matching request, copy the response and signal the requesting | ||
| 658 | * thread. | ||
| 659 | */ | ||
| 660 | static void vmbus_onversion_response( | ||
| 661 | struct vmbus_channel_message_header *hdr) | ||
| 662 | { | ||
| 663 | struct vmbus_channel_msginfo *msginfo; | ||
| 664 | struct vmbus_channel_message_header *requestheader; | ||
| 665 | struct vmbus_channel_initiate_contact *initiate; | ||
| 666 | struct vmbus_channel_version_response *version_response; | ||
| 667 | unsigned long flags; | ||
| 668 | |||
| 669 | version_response = (struct vmbus_channel_version_response *)hdr; | ||
| 670 | spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); | ||
| 671 | |||
| 672 | list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, | ||
| 673 | msglistentry) { | ||
| 674 | requestheader = | ||
| 675 | (struct vmbus_channel_message_header *)msginfo->msg; | ||
| 676 | |||
| 677 | if (requestheader->msgtype == | ||
| 678 | CHANNELMSG_INITIATE_CONTACT) { | ||
| 679 | initiate = | ||
| 680 | (struct vmbus_channel_initiate_contact *)requestheader; | ||
| 681 | memcpy(&msginfo->response.version_response, | ||
| 682 | version_response, | ||
| 683 | sizeof(struct vmbus_channel_version_response)); | ||
| 684 | complete(&msginfo->waitevent); | ||
| 685 | } | ||
| 686 | } | ||
| 687 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); | ||
| 688 | } | ||
| 689 | |||
| 690 | /* Channel message dispatch table */ | ||
| 691 | static struct vmbus_channel_message_table_entry | ||
| 692 | channel_message_table[CHANNELMSG_COUNT] = { | ||
| 693 | {CHANNELMSG_INVALID, NULL}, | ||
| 694 | {CHANNELMSG_OFFERCHANNEL, vmbus_onoffer}, | ||
| 695 | {CHANNELMSG_RESCIND_CHANNELOFFER, vmbus_onoffer_rescind}, | ||
| 696 | {CHANNELMSG_REQUESTOFFERS, NULL}, | ||
| 697 | {CHANNELMSG_ALLOFFERS_DELIVERED, vmbus_onoffers_delivered}, | ||
| 698 | {CHANNELMSG_OPENCHANNEL, NULL}, | ||
| 699 | {CHANNELMSG_OPENCHANNEL_RESULT, vmbus_onopen_result}, | ||
| 700 | {CHANNELMSG_CLOSECHANNEL, NULL}, | ||
| 701 | {CHANNELMSG_GPADL_HEADER, NULL}, | ||
| 702 | {CHANNELMSG_GPADL_BODY, NULL}, | ||
| 703 | {CHANNELMSG_GPADL_CREATED, vmbus_ongpadl_created}, | ||
| 704 | {CHANNELMSG_GPADL_TEARDOWN, NULL}, | ||
| 705 | {CHANNELMSG_GPADL_TORNDOWN, vmbus_ongpadl_torndown}, | ||
| 706 | {CHANNELMSG_RELID_RELEASED, NULL}, | ||
| 707 | {CHANNELMSG_INITIATE_CONTACT, NULL}, | ||
| 708 | {CHANNELMSG_VERSION_RESPONSE, vmbus_onversion_response}, | ||
| 709 | {CHANNELMSG_UNLOAD, NULL}, | ||
| 710 | }; | ||
| 711 | |||
| 712 | /* | ||
| 713 | * vmbus_onmessage - Handler for channel protocol messages. | ||
| 714 | * | ||
| 715 | * This is invoked in the vmbus worker thread context. | ||
| 716 | */ | ||
| 717 | void vmbus_onmessage(void *context) | ||
| 718 | { | ||
| 719 | struct hv_message *msg = context; | ||
| 720 | struct vmbus_channel_message_header *hdr; | ||
| 721 | int size; | ||
| 722 | |||
| 723 | hdr = (struct vmbus_channel_message_header *)msg->u.payload; | ||
| 724 | size = msg->header.payload_size; | ||
| 725 | |||
| 726 | if (hdr->msgtype >= CHANNELMSG_COUNT) { | ||
| 727 | pr_err("Received invalid channel message type %d size %d\n", | ||
| 728 | hdr->msgtype, size); | ||
| 729 | print_hex_dump_bytes("", DUMP_PREFIX_NONE, | ||
| 730 | (unsigned char *)msg->u.payload, size); | ||
| 731 | return; | ||
| 732 | } | ||
| 733 | |||
| 734 | if (channel_message_table[hdr->msgtype].message_handler) | ||
| 735 | channel_message_table[hdr->msgtype].message_handler(hdr); | ||
| 736 | else | ||
| 737 | pr_err("Unhandled channel message type %d\n", hdr->msgtype); | ||
| 738 | } | ||
| 739 | |||
| 740 | /* | ||
| 741 | * vmbus_request_offers - Send a request to get all our pending offers. | ||
| 742 | */ | ||
| 743 | int vmbus_request_offers(void) | ||
| 744 | { | ||
| 745 | struct vmbus_channel_message_header *msg; | ||
| 746 | struct vmbus_channel_msginfo *msginfo; | ||
| 747 | int ret, t; | ||
| 748 | |||
| 749 | msginfo = kmalloc(sizeof(*msginfo) + | ||
| 750 | sizeof(struct vmbus_channel_message_header), | ||
| 751 | GFP_KERNEL); | ||
| 752 | if (!msginfo) | ||
| 753 | return -ENOMEM; | ||
| 754 | |||
| 755 | init_completion(&msginfo->waitevent); | ||
| 756 | |||
| 757 | msg = (struct vmbus_channel_message_header *)msginfo->msg; | ||
| 758 | |||
| 759 | msg->msgtype = CHANNELMSG_REQUESTOFFERS; | ||
| 760 | |||
| 761 | |||
| 762 | ret = vmbus_post_msg(msg, | ||
| 763 | sizeof(struct vmbus_channel_message_header)); | ||
| 764 | if (ret != 0) { | ||
| 765 | pr_err("Unable to request offers - %d\n", ret); | ||
| 766 | |||
| 767 | goto cleanup; | ||
| 768 | } | ||
| 769 | |||
| 770 | t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); | ||
| 771 | if (t == 0) { | ||
| 772 | ret = -ETIMEDOUT; | ||
| 773 | goto cleanup; | ||
| 774 | } | ||
| 775 | |||
| 776 | |||
| 777 | |||
| 778 | cleanup: | ||
| 779 | kfree(msginfo); | ||
| 780 | |||
| 781 | return ret; | ||
| 782 | } | ||
| 783 | |||
| 784 | /* eof */ | ||
diff --git a/drivers/staging/hv/connection.c b/drivers/staging/hv/connection.c new file mode 100644 index 00000000000..e6b40392e08 --- /dev/null +++ b/drivers/staging/hv/connection.c | |||
| @@ -0,0 +1,290 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * Copyright (c) 2009, Microsoft Corporation. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms and conditions of the GNU General Public License, | ||
| 7 | * version 2, as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | * more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License along with | ||
| 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
| 16 | * Place - Suite 330, Boston, MA 02111-1307 USA. | ||
| 17 | * | ||
| 18 | * Authors: | ||
| 19 | * Haiyang Zhang <haiyangz@microsoft.com> | ||
| 20 | * Hank Janssen <hjanssen@microsoft.com> | ||
| 21 | * | ||
| 22 | */ | ||
| 23 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 24 | |||
| 25 | #include <linux/kernel.h> | ||
| 26 | #include <linux/sched.h> | ||
| 27 | #include <linux/wait.h> | ||
| 28 | #include <linux/mm.h> | ||
| 29 | #include <linux/slab.h> | ||
| 30 | #include <linux/vmalloc.h> | ||
| 31 | |||
| 32 | #include "hyperv.h" | ||
| 33 | #include "hyperv_vmbus.h" | ||
| 34 | |||
| 35 | |||
| 36 | struct vmbus_connection vmbus_connection = { | ||
| 37 | .conn_state = DISCONNECTED, | ||
| 38 | .next_gpadl_handle = ATOMIC_INIT(0xE1E10), | ||
| 39 | }; | ||
| 40 | |||
| 41 | /* | ||
| 42 | * vmbus_connect - Sends a connect request on the partition service connection | ||
| 43 | */ | ||
| 44 | int vmbus_connect(void) | ||
| 45 | { | ||
| 46 | int ret = 0; | ||
| 47 | int t; | ||
| 48 | struct vmbus_channel_msginfo *msginfo = NULL; | ||
| 49 | struct vmbus_channel_initiate_contact *msg; | ||
| 50 | unsigned long flags; | ||
| 51 | |||
| 52 | /* Make sure we are not connecting or connected */ | ||
| 53 | if (vmbus_connection.conn_state != DISCONNECTED) | ||
| 54 | return -EISCONN; | ||
| 55 | |||
| 56 | /* Initialize the vmbus connection */ | ||
| 57 | vmbus_connection.conn_state = CONNECTING; | ||
| 58 | vmbus_connection.work_queue = create_workqueue("hv_vmbus_con"); | ||
| 59 | if (!vmbus_connection.work_queue) { | ||
| 60 | ret = -ENOMEM; | ||
| 61 | goto cleanup; | ||
| 62 | } | ||
| 63 | |||
| 64 | INIT_LIST_HEAD(&vmbus_connection.chn_msg_list); | ||
| 65 | spin_lock_init(&vmbus_connection.channelmsg_lock); | ||
| 66 | |||
| 67 | INIT_LIST_HEAD(&vmbus_connection.chn_list); | ||
| 68 | spin_lock_init(&vmbus_connection.channel_lock); | ||
| 69 | |||
| 70 | /* | ||
| 71 | * Setup the vmbus event connection for channel interrupt | ||
| 72 | * abstraction stuff | ||
| 73 | */ | ||
| 74 | vmbus_connection.int_page = | ||
| 75 | (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, 0); | ||
| 76 | if (vmbus_connection.int_page == NULL) { | ||
| 77 | ret = -ENOMEM; | ||
| 78 | goto cleanup; | ||
| 79 | } | ||
| 80 | |||
| 81 | vmbus_connection.recv_int_page = vmbus_connection.int_page; | ||
| 82 | vmbus_connection.send_int_page = | ||
| 83 | (void *)((unsigned long)vmbus_connection.int_page + | ||
| 84 | (PAGE_SIZE >> 1)); | ||
| 85 | |||
| 86 | /* | ||
| 87 | * Setup the monitor notification facility. The 1st page for | ||
| 88 | * parent->child and the 2nd page for child->parent | ||
| 89 | */ | ||
| 90 | vmbus_connection.monitor_pages = | ||
| 91 | (void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 1); | ||
| 92 | if (vmbus_connection.monitor_pages == NULL) { | ||
| 93 | ret = -ENOMEM; | ||
| 94 | goto cleanup; | ||
| 95 | } | ||
| 96 | |||
| 97 | msginfo = kzalloc(sizeof(*msginfo) + | ||
| 98 | sizeof(struct vmbus_channel_initiate_contact), | ||
| 99 | GFP_KERNEL); | ||
| 100 | if (msginfo == NULL) { | ||
| 101 | ret = -ENOMEM; | ||
| 102 | goto cleanup; | ||
| 103 | } | ||
| 104 | |||
| 105 | init_completion(&msginfo->waitevent); | ||
| 106 | |||
| 107 | msg = (struct vmbus_channel_initiate_contact *)msginfo->msg; | ||
| 108 | |||
| 109 | msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT; | ||
| 110 | msg->vmbus_version_requested = VMBUS_REVISION_NUMBER; | ||
| 111 | msg->interrupt_page = virt_to_phys(vmbus_connection.int_page); | ||
| 112 | msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages); | ||
| 113 | msg->monitor_page2 = virt_to_phys( | ||
| 114 | (void *)((unsigned long)vmbus_connection.monitor_pages + | ||
| 115 | PAGE_SIZE)); | ||
| 116 | |||
| 117 | /* | ||
| 118 | * Add to list before we send the request since we may | ||
| 119 | * receive the response before returning from this routine | ||
| 120 | */ | ||
| 121 | spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); | ||
| 122 | list_add_tail(&msginfo->msglistentry, | ||
| 123 | &vmbus_connection.chn_msg_list); | ||
| 124 | |||
| 125 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); | ||
| 126 | |||
| 127 | ret = vmbus_post_msg(msg, | ||
| 128 | sizeof(struct vmbus_channel_initiate_contact)); | ||
| 129 | if (ret != 0) { | ||
| 130 | spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); | ||
| 131 | list_del(&msginfo->msglistentry); | ||
| 132 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, | ||
| 133 | flags); | ||
| 134 | goto cleanup; | ||
| 135 | } | ||
| 136 | |||
| 137 | /* Wait for the connection response */ | ||
| 138 | t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); | ||
| 139 | if (t == 0) { | ||
| 140 | spin_lock_irqsave(&vmbus_connection.channelmsg_lock, | ||
| 141 | flags); | ||
| 142 | list_del(&msginfo->msglistentry); | ||
| 143 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, | ||
| 144 | flags); | ||
| 145 | ret = -ETIMEDOUT; | ||
| 146 | goto cleanup; | ||
| 147 | } | ||
| 148 | |||
| 149 | spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); | ||
| 150 | list_del(&msginfo->msglistentry); | ||
| 151 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); | ||
| 152 | |||
| 153 | /* Check if successful */ | ||
| 154 | if (msginfo->response.version_response.version_supported) { | ||
| 155 | vmbus_connection.conn_state = CONNECTED; | ||
| 156 | } else { | ||
| 157 | pr_err("Unable to connect, " | ||
| 158 | "Version %d not supported by Hyper-V\n", | ||
| 159 | VMBUS_REVISION_NUMBER); | ||
| 160 | ret = -ECONNREFUSED; | ||
| 161 | goto cleanup; | ||
| 162 | } | ||
| 163 | |||
| 164 | kfree(msginfo); | ||
| 165 | return 0; | ||
| 166 | |||
| 167 | cleanup: | ||
| 168 | vmbus_connection.conn_state = DISCONNECTED; | ||
| 169 | |||
| 170 | if (vmbus_connection.work_queue) | ||
| 171 | destroy_workqueue(vmbus_connection.work_queue); | ||
| 172 | |||
| 173 | if (vmbus_connection.int_page) { | ||
| 174 | free_pages((unsigned long)vmbus_connection.int_page, 0); | ||
| 175 | vmbus_connection.int_page = NULL; | ||
| 176 | } | ||
| 177 | |||
| 178 | if (vmbus_connection.monitor_pages) { | ||
| 179 | free_pages((unsigned long)vmbus_connection.monitor_pages, 1); | ||
| 180 | vmbus_connection.monitor_pages = NULL; | ||
| 181 | } | ||
| 182 | |||
| 183 | kfree(msginfo); | ||
| 184 | |||
| 185 | return ret; | ||
| 186 | } | ||
| 187 | |||
| 188 | |||
| 189 | /* | ||
| 190 | * relid2channel - Get the channel object given its | ||
| 191 | * child relative id (ie channel id) | ||
| 192 | */ | ||
| 193 | struct vmbus_channel *relid2channel(u32 relid) | ||
| 194 | { | ||
| 195 | struct vmbus_channel *channel; | ||
| 196 | struct vmbus_channel *found_channel = NULL; | ||
| 197 | unsigned long flags; | ||
| 198 | |||
| 199 | spin_lock_irqsave(&vmbus_connection.channel_lock, flags); | ||
| 200 | list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { | ||
| 201 | if (channel->offermsg.child_relid == relid) { | ||
| 202 | found_channel = channel; | ||
| 203 | break; | ||
| 204 | } | ||
| 205 | } | ||
| 206 | spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); | ||
| 207 | |||
| 208 | return found_channel; | ||
| 209 | } | ||
| 210 | |||
| 211 | /* | ||
| 212 | * process_chn_event - Process a channel event notification | ||
| 213 | */ | ||
| 214 | static void process_chn_event(u32 relid) | ||
| 215 | { | ||
| 216 | struct vmbus_channel *channel; | ||
| 217 | |||
| 218 | /* ASSERT(relId > 0); */ | ||
| 219 | |||
| 220 | /* | ||
| 221 | * Find the channel based on this relid and invokes the | ||
| 222 | * channel callback to process the event | ||
| 223 | */ | ||
| 224 | channel = relid2channel(relid); | ||
| 225 | |||
| 226 | if (channel) { | ||
| 227 | channel->onchannel_callback(channel->channel_callback_context); | ||
| 228 | } else { | ||
| 229 | pr_err("channel not found for relid - %u\n", relid); | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | /* | ||
| 234 | * vmbus_on_event - Handler for events | ||
| 235 | */ | ||
| 236 | void vmbus_on_event(unsigned long data) | ||
| 237 | { | ||
| 238 | u32 dword; | ||
| 239 | u32 maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5; | ||
| 240 | int bit; | ||
| 241 | u32 relid; | ||
| 242 | u32 *recv_int_page = vmbus_connection.recv_int_page; | ||
| 243 | |||
| 244 | /* Check events */ | ||
| 245 | if (!recv_int_page) | ||
| 246 | return; | ||
| 247 | for (dword = 0; dword < maxdword; dword++) { | ||
| 248 | if (!recv_int_page[dword]) | ||
| 249 | continue; | ||
| 250 | for (bit = 0; bit < 32; bit++) { | ||
| 251 | if (sync_test_and_clear_bit(bit, (unsigned long *)&recv_int_page[dword])) { | ||
| 252 | relid = (dword << 5) + bit; | ||
| 253 | |||
| 254 | if (relid == 0) { | ||
| 255 | /* | ||
| 256 | * Special case - vmbus | ||
| 257 | * channel protocol msg | ||
| 258 | */ | ||
| 259 | continue; | ||
| 260 | } | ||
| 261 | process_chn_event(relid); | ||
| 262 | } | ||
| 263 | } | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | /* | ||
| 268 | * vmbus_post_msg - Send a msg on the vmbus's message connection | ||
| 269 | */ | ||
| 270 | int vmbus_post_msg(void *buffer, size_t buflen) | ||
| 271 | { | ||
| 272 | union hv_connection_id conn_id; | ||
| 273 | |||
| 274 | conn_id.asu32 = 0; | ||
| 275 | conn_id.u.id = VMBUS_MESSAGE_CONNECTION_ID; | ||
| 276 | return hv_post_message(conn_id, 1, buffer, buflen); | ||
| 277 | } | ||
| 278 | |||
| 279 | /* | ||
| 280 | * vmbus_set_event - Send an event notification to the parent | ||
| 281 | */ | ||
| 282 | int vmbus_set_event(u32 child_relid) | ||
| 283 | { | ||
| 284 | /* Each u32 represents 32 channels */ | ||
| 285 | sync_set_bit(child_relid & 31, | ||
| 286 | (unsigned long *)vmbus_connection.send_int_page + | ||
| 287 | (child_relid >> 5)); | ||
| 288 | |||
| 289 | return hv_signal_event(); | ||
| 290 | } | ||
diff --git a/drivers/staging/hv/hv.c b/drivers/staging/hv/hv.c new file mode 100644 index 00000000000..824f81679ae --- /dev/null +++ b/drivers/staging/hv/hv.c | |||
| @@ -0,0 +1,438 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2009, Microsoft Corporation. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms and conditions of the GNU General Public License, | ||
| 6 | * version 2, as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License along with | ||
| 14 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
| 15 | * Place - Suite 330, Boston, MA 02111-1307 USA. | ||
| 16 | * | ||
| 17 | * Authors: | ||
| 18 | * Haiyang Zhang <haiyangz@microsoft.com> | ||
| 19 | * Hank Janssen <hjanssen@microsoft.com> | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 23 | |||
| 24 | #include <linux/kernel.h> | ||
| 25 | #include <linux/mm.h> | ||
| 26 | #include <linux/slab.h> | ||
| 27 | #include <linux/vmalloc.h> | ||
| 28 | |||
| 29 | #include "hyperv.h" | ||
| 30 | #include "hyperv_vmbus.h" | ||
| 31 | |||
| 32 | /* The one and only */ | ||
| 33 | struct hv_context hv_context = { | ||
| 34 | .synic_initialized = false, | ||
| 35 | .hypercall_page = NULL, | ||
| 36 | .signal_event_param = NULL, | ||
| 37 | .signal_event_buffer = NULL, | ||
| 38 | }; | ||
| 39 | |||
| 40 | /* | ||
| 41 | * query_hypervisor_presence | ||
| 42 | * - Query the cpuid for presence of windows hypervisor | ||
| 43 | */ | ||
| 44 | static int query_hypervisor_presence(void) | ||
| 45 | { | ||
| 46 | unsigned int eax; | ||
| 47 | unsigned int ebx; | ||
| 48 | unsigned int ecx; | ||
| 49 | unsigned int edx; | ||
| 50 | unsigned int op; | ||
| 51 | |||
| 52 | eax = 0; | ||
| 53 | ebx = 0; | ||
| 54 | ecx = 0; | ||
| 55 | edx = 0; | ||
| 56 | op = HVCPUID_VERSION_FEATURES; | ||
| 57 | cpuid(op, &eax, &ebx, &ecx, &edx); | ||
| 58 | |||
| 59 | return ecx & HV_PRESENT_BIT; | ||
| 60 | } | ||
| 61 | |||
| 62 | /* | ||
| 63 | * query_hypervisor_info - Get version info of the windows hypervisor | ||
| 64 | */ | ||
| 65 | static int query_hypervisor_info(void) | ||
| 66 | { | ||
| 67 | unsigned int eax; | ||
| 68 | unsigned int ebx; | ||
| 69 | unsigned int ecx; | ||
| 70 | unsigned int edx; | ||
| 71 | unsigned int max_leaf; | ||
| 72 | unsigned int op; | ||
| 73 | |||
| 74 | /* | ||
| 75 | * Its assumed that this is called after confirming that Viridian | ||
| 76 | * is present. Query id and revision. | ||
| 77 | */ | ||
| 78 | eax = 0; | ||
| 79 | ebx = 0; | ||
| 80 | ecx = 0; | ||
| 81 | edx = 0; | ||
| 82 | op = HVCPUID_VENDOR_MAXFUNCTION; | ||
| 83 | cpuid(op, &eax, &ebx, &ecx, &edx); | ||
| 84 | |||
| 85 | max_leaf = eax; | ||
| 86 | |||
| 87 | if (max_leaf >= HVCPUID_VERSION) { | ||
| 88 | eax = 0; | ||
| 89 | ebx = 0; | ||
| 90 | ecx = 0; | ||
| 91 | edx = 0; | ||
| 92 | op = HVCPUID_VERSION; | ||
| 93 | cpuid(op, &eax, &ebx, &ecx, &edx); | ||
| 94 | pr_info("Hyper-V Host OS Build:%d-%d.%d-%d-%d.%d\n", | ||
| 95 | eax, | ||
| 96 | ebx >> 16, | ||
| 97 | ebx & 0xFFFF, | ||
| 98 | ecx, | ||
| 99 | edx >> 24, | ||
| 100 | edx & 0xFFFFFF); | ||
| 101 | } | ||
| 102 | return max_leaf; | ||
| 103 | } | ||
| 104 | |||
| 105 | /* | ||
| 106 | * do_hypercall- Invoke the specified hypercall | ||
| 107 | */ | ||
| 108 | static u64 do_hypercall(u64 control, void *input, void *output) | ||
| 109 | { | ||
| 110 | #ifdef CONFIG_X86_64 | ||
| 111 | u64 hv_status = 0; | ||
| 112 | u64 input_address = (input) ? virt_to_phys(input) : 0; | ||
| 113 | u64 output_address = (output) ? virt_to_phys(output) : 0; | ||
| 114 | volatile void *hypercall_page = hv_context.hypercall_page; | ||
| 115 | |||
| 116 | __asm__ __volatile__("mov %0, %%r8" : : "r" (output_address) : "r8"); | ||
| 117 | __asm__ __volatile__("call *%3" : "=a" (hv_status) : | ||
| 118 | "c" (control), "d" (input_address), | ||
| 119 | "m" (hypercall_page)); | ||
| 120 | |||
| 121 | return hv_status; | ||
| 122 | |||
| 123 | #else | ||
| 124 | |||
| 125 | u32 control_hi = control >> 32; | ||
| 126 | u32 control_lo = control & 0xFFFFFFFF; | ||
| 127 | u32 hv_status_hi = 1; | ||
| 128 | u32 hv_status_lo = 1; | ||
| 129 | u64 input_address = (input) ? virt_to_phys(input) : 0; | ||
| 130 | u32 input_address_hi = input_address >> 32; | ||
| 131 | u32 input_address_lo = input_address & 0xFFFFFFFF; | ||
| 132 | u64 output_address = (output) ? virt_to_phys(output) : 0; | ||
| 133 | u32 output_address_hi = output_address >> 32; | ||
| 134 | u32 output_address_lo = output_address & 0xFFFFFFFF; | ||
| 135 | volatile void *hypercall_page = hv_context.hypercall_page; | ||
| 136 | |||
| 137 | __asm__ __volatile__ ("call *%8" : "=d"(hv_status_hi), | ||
| 138 | "=a"(hv_status_lo) : "d" (control_hi), | ||
| 139 | "a" (control_lo), "b" (input_address_hi), | ||
| 140 | "c" (input_address_lo), "D"(output_address_hi), | ||
| 141 | "S"(output_address_lo), "m" (hypercall_page)); | ||
| 142 | |||
| 143 | return hv_status_lo | ((u64)hv_status_hi << 32); | ||
| 144 | #endif /* !x86_64 */ | ||
| 145 | } | ||
| 146 | |||
| 147 | /* | ||
| 148 | * hv_init - Main initialization routine. | ||
| 149 | * | ||
| 150 | * This routine must be called before any other routines in here are called | ||
| 151 | */ | ||
| 152 | int hv_init(void) | ||
| 153 | { | ||
| 154 | int ret = 0; | ||
| 155 | int max_leaf; | ||
| 156 | union hv_x64_msr_hypercall_contents hypercall_msr; | ||
| 157 | void *virtaddr = NULL; | ||
| 158 | |||
| 159 | memset(hv_context.synic_event_page, 0, sizeof(void *) * MAX_NUM_CPUS); | ||
| 160 | memset(hv_context.synic_message_page, 0, | ||
| 161 | sizeof(void *) * MAX_NUM_CPUS); | ||
| 162 | |||
| 163 | if (!query_hypervisor_presence()) | ||
| 164 | goto cleanup; | ||
| 165 | |||
| 166 | max_leaf = query_hypervisor_info(); | ||
| 167 | /* HvQueryHypervisorFeatures(maxLeaf); */ | ||
| 168 | |||
| 169 | /* | ||
| 170 | * We only support running on top of Hyper-V | ||
| 171 | */ | ||
| 172 | rdmsrl(HV_X64_MSR_GUEST_OS_ID, hv_context.guestid); | ||
| 173 | |||
| 174 | if (hv_context.guestid != 0) | ||
| 175 | goto cleanup; | ||
| 176 | |||
| 177 | /* Write our OS info */ | ||
| 178 | wrmsrl(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID); | ||
| 179 | hv_context.guestid = HV_LINUX_GUEST_ID; | ||
| 180 | |||
| 181 | /* See if the hypercall page is already set */ | ||
| 182 | rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); | ||
| 183 | |||
| 184 | /* | ||
| 185 | * Allocate the hypercall page memory | ||
| 186 | * virtaddr = osd_page_alloc(1); | ||
| 187 | */ | ||
| 188 | virtaddr = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_EXEC); | ||
| 189 | |||
| 190 | if (!virtaddr) | ||
| 191 | goto cleanup; | ||
| 192 | |||
| 193 | hypercall_msr.enable = 1; | ||
| 194 | |||
| 195 | hypercall_msr.guest_physical_address = vmalloc_to_pfn(virtaddr); | ||
| 196 | wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); | ||
| 197 | |||
| 198 | /* Confirm that hypercall page did get setup. */ | ||
| 199 | hypercall_msr.as_uint64 = 0; | ||
| 200 | rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); | ||
| 201 | |||
| 202 | if (!hypercall_msr.enable) | ||
| 203 | goto cleanup; | ||
| 204 | |||
| 205 | hv_context.hypercall_page = virtaddr; | ||
| 206 | |||
| 207 | /* Setup the global signal event param for the signal event hypercall */ | ||
| 208 | hv_context.signal_event_buffer = | ||
| 209 | kmalloc(sizeof(struct hv_input_signal_event_buffer), | ||
| 210 | GFP_KERNEL); | ||
| 211 | if (!hv_context.signal_event_buffer) | ||
| 212 | goto cleanup; | ||
| 213 | |||
| 214 | hv_context.signal_event_param = | ||
| 215 | (struct hv_input_signal_event *) | ||
| 216 | (ALIGN((unsigned long) | ||
| 217 | hv_context.signal_event_buffer, | ||
| 218 | HV_HYPERCALL_PARAM_ALIGN)); | ||
| 219 | hv_context.signal_event_param->connectionid.asu32 = 0; | ||
| 220 | hv_context.signal_event_param->connectionid.u.id = | ||
| 221 | VMBUS_EVENT_CONNECTION_ID; | ||
| 222 | hv_context.signal_event_param->flag_number = 0; | ||
| 223 | hv_context.signal_event_param->rsvdz = 0; | ||
| 224 | |||
| 225 | return ret; | ||
| 226 | |||
| 227 | cleanup: | ||
| 228 | if (virtaddr) { | ||
| 229 | if (hypercall_msr.enable) { | ||
| 230 | hypercall_msr.as_uint64 = 0; | ||
| 231 | wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); | ||
| 232 | } | ||
| 233 | |||
| 234 | vfree(virtaddr); | ||
| 235 | } | ||
| 236 | ret = -1; | ||
| 237 | return ret; | ||
| 238 | } | ||
| 239 | |||
| 240 | /* | ||
| 241 | * hv_cleanup - Cleanup routine. | ||
| 242 | * | ||
| 243 | * This routine is called normally during driver unloading or exiting. | ||
| 244 | */ | ||
| 245 | void hv_cleanup(void) | ||
| 246 | { | ||
| 247 | union hv_x64_msr_hypercall_contents hypercall_msr; | ||
| 248 | |||
| 249 | kfree(hv_context.signal_event_buffer); | ||
| 250 | hv_context.signal_event_buffer = NULL; | ||
| 251 | hv_context.signal_event_param = NULL; | ||
| 252 | |||
| 253 | if (hv_context.hypercall_page) { | ||
| 254 | hypercall_msr.as_uint64 = 0; | ||
| 255 | wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); | ||
| 256 | vfree(hv_context.hypercall_page); | ||
| 257 | hv_context.hypercall_page = NULL; | ||
| 258 | } | ||
| 259 | } | ||
| 260 | |||
| 261 | /* | ||
| 262 | * hv_post_message - Post a message using the hypervisor message IPC. | ||
| 263 | * | ||
| 264 | * This involves a hypercall. | ||
| 265 | */ | ||
| 266 | u16 hv_post_message(union hv_connection_id connection_id, | ||
| 267 | enum hv_message_type message_type, | ||
| 268 | void *payload, size_t payload_size) | ||
| 269 | { | ||
| 270 | struct aligned_input { | ||
| 271 | u64 alignment8; | ||
| 272 | struct hv_input_post_message msg; | ||
| 273 | }; | ||
| 274 | |||
| 275 | struct hv_input_post_message *aligned_msg; | ||
| 276 | u16 status; | ||
| 277 | unsigned long addr; | ||
| 278 | |||
| 279 | if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT) | ||
| 280 | return -EMSGSIZE; | ||
| 281 | |||
| 282 | addr = (unsigned long)kmalloc(sizeof(struct aligned_input), GFP_ATOMIC); | ||
| 283 | if (!addr) | ||
| 284 | return -ENOMEM; | ||
| 285 | |||
| 286 | aligned_msg = (struct hv_input_post_message *) | ||
| 287 | (ALIGN(addr, HV_HYPERCALL_PARAM_ALIGN)); | ||
| 288 | |||
| 289 | aligned_msg->connectionid = connection_id; | ||
| 290 | aligned_msg->message_type = message_type; | ||
| 291 | aligned_msg->payload_size = payload_size; | ||
| 292 | memcpy((void *)aligned_msg->payload, payload, payload_size); | ||
| 293 | |||
| 294 | status = do_hypercall(HVCALL_POST_MESSAGE, aligned_msg, NULL) | ||
| 295 | & 0xFFFF; | ||
| 296 | |||
| 297 | kfree((void *)addr); | ||
| 298 | |||
| 299 | return status; | ||
| 300 | } | ||
| 301 | |||
| 302 | |||
| 303 | /* | ||
| 304 | * hv_signal_event - | ||
| 305 | * Signal an event on the specified connection using the hypervisor event IPC. | ||
| 306 | * | ||
| 307 | * This involves a hypercall. | ||
| 308 | */ | ||
| 309 | u16 hv_signal_event(void) | ||
| 310 | { | ||
| 311 | u16 status; | ||
| 312 | |||
| 313 | status = do_hypercall(HVCALL_SIGNAL_EVENT, | ||
| 314 | hv_context.signal_event_param, | ||
| 315 | NULL) & 0xFFFF; | ||
| 316 | return status; | ||
| 317 | } | ||
| 318 | |||
| 319 | /* | ||
| 320 | * hv_synic_init - Initialize the Synthethic Interrupt Controller. | ||
| 321 | * | ||
| 322 | * If it is already initialized by another entity (ie x2v shim), we need to | ||
| 323 | * retrieve the initialized message and event pages. Otherwise, we create and | ||
| 324 | * initialize the message and event pages. | ||
| 325 | */ | ||
| 326 | void hv_synic_init(void *irqarg) | ||
| 327 | { | ||
| 328 | u64 version; | ||
| 329 | union hv_synic_simp simp; | ||
| 330 | union hv_synic_siefp siefp; | ||
| 331 | union hv_synic_sint shared_sint; | ||
| 332 | union hv_synic_scontrol sctrl; | ||
| 333 | |||
| 334 | u32 irq_vector = *((u32 *)(irqarg)); | ||
| 335 | int cpu = smp_processor_id(); | ||
| 336 | |||
| 337 | if (!hv_context.hypercall_page) | ||
| 338 | return; | ||
| 339 | |||
| 340 | /* Check the version */ | ||
| 341 | rdmsrl(HV_X64_MSR_SVERSION, version); | ||
| 342 | |||
| 343 | hv_context.synic_message_page[cpu] = | ||
| 344 | (void *)get_zeroed_page(GFP_ATOMIC); | ||
| 345 | |||
| 346 | if (hv_context.synic_message_page[cpu] == NULL) { | ||
| 347 | pr_err("Unable to allocate SYNIC message page\n"); | ||
| 348 | goto cleanup; | ||
| 349 | } | ||
| 350 | |||
| 351 | hv_context.synic_event_page[cpu] = | ||
| 352 | (void *)get_zeroed_page(GFP_ATOMIC); | ||
| 353 | |||
| 354 | if (hv_context.synic_event_page[cpu] == NULL) { | ||
| 355 | pr_err("Unable to allocate SYNIC event page\n"); | ||
| 356 | goto cleanup; | ||
| 357 | } | ||
| 358 | |||
| 359 | /* Setup the Synic's message page */ | ||
| 360 | rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64); | ||
| 361 | simp.simp_enabled = 1; | ||
| 362 | simp.base_simp_gpa = virt_to_phys(hv_context.synic_message_page[cpu]) | ||
| 363 | >> PAGE_SHIFT; | ||
| 364 | |||
| 365 | wrmsrl(HV_X64_MSR_SIMP, simp.as_uint64); | ||
| 366 | |||
| 367 | /* Setup the Synic's event page */ | ||
| 368 | rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); | ||
| 369 | siefp.siefp_enabled = 1; | ||
| 370 | siefp.base_siefp_gpa = virt_to_phys(hv_context.synic_event_page[cpu]) | ||
| 371 | >> PAGE_SHIFT; | ||
| 372 | |||
| 373 | wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); | ||
| 374 | |||
| 375 | /* Setup the shared SINT. */ | ||
| 376 | rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); | ||
| 377 | |||
| 378 | shared_sint.as_uint64 = 0; | ||
| 379 | shared_sint.vector = irq_vector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */ | ||
| 380 | shared_sint.masked = false; | ||
| 381 | shared_sint.auto_eoi = true; | ||
| 382 | |||
| 383 | wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); | ||
| 384 | |||
| 385 | /* Enable the global synic bit */ | ||
| 386 | rdmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); | ||
| 387 | sctrl.enable = 1; | ||
| 388 | |||
| 389 | wrmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); | ||
| 390 | |||
| 391 | hv_context.synic_initialized = true; | ||
| 392 | return; | ||
| 393 | |||
| 394 | cleanup: | ||
| 395 | if (hv_context.synic_event_page[cpu]) | ||
| 396 | free_page((unsigned long)hv_context.synic_event_page[cpu]); | ||
| 397 | |||
| 398 | if (hv_context.synic_message_page[cpu]) | ||
| 399 | free_page((unsigned long)hv_context.synic_message_page[cpu]); | ||
| 400 | return; | ||
| 401 | } | ||
| 402 | |||
| 403 | /* | ||
| 404 | * hv_synic_cleanup - Cleanup routine for hv_synic_init(). | ||
| 405 | */ | ||
| 406 | void hv_synic_cleanup(void *arg) | ||
| 407 | { | ||
| 408 | union hv_synic_sint shared_sint; | ||
| 409 | union hv_synic_simp simp; | ||
| 410 | union hv_synic_siefp siefp; | ||
| 411 | int cpu = smp_processor_id(); | ||
| 412 | |||
| 413 | if (!hv_context.synic_initialized) | ||
| 414 | return; | ||
| 415 | |||
| 416 | rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); | ||
| 417 | |||
| 418 | shared_sint.masked = 1; | ||
| 419 | |||
| 420 | /* Need to correctly cleanup in the case of SMP!!! */ | ||
| 421 | /* Disable the interrupt */ | ||
| 422 | wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); | ||
| 423 | |||
| 424 | rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64); | ||
| 425 | simp.simp_enabled = 0; | ||
| 426 | simp.base_simp_gpa = 0; | ||
| 427 | |||
| 428 | wrmsrl(HV_X64_MSR_SIMP, simp.as_uint64); | ||
| 429 | |||
| 430 | rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); | ||
| 431 | siefp.siefp_enabled = 0; | ||
| 432 | siefp.base_siefp_gpa = 0; | ||
| 433 | |||
| 434 | wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); | ||
| 435 | |||
| 436 | free_page((unsigned long)hv_context.synic_message_page[cpu]); | ||
| 437 | free_page((unsigned long)hv_context.synic_event_page[cpu]); | ||
| 438 | } | ||
diff --git a/drivers/staging/hv/hv_kvp.c b/drivers/staging/hv/hv_kvp.c new file mode 100644 index 00000000000..13b0ecf7d5d --- /dev/null +++ b/drivers/staging/hv/hv_kvp.c | |||
| @@ -0,0 +1,334 @@ | |||
| 1 | /* | ||
| 2 | * An implementation of key value pair (KVP) functionality for Linux. | ||
| 3 | * | ||
| 4 | * | ||
| 5 | * Copyright (C) 2010, Novell, Inc. | ||
| 6 | * Author : K. Y. Srinivasan <ksrinivasan@novell.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify it | ||
| 9 | * under the terms of the GNU General Public License version 2 as published | ||
| 10 | * by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, but | ||
| 13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
| 15 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
| 16 | * details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License | ||
| 19 | * along with this program; if not, write to the Free Software | ||
| 20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 21 | * | ||
| 22 | */ | ||
| 23 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 24 | |||
| 25 | #include <linux/net.h> | ||
| 26 | #include <linux/nls.h> | ||
| 27 | #include <linux/connector.h> | ||
| 28 | #include <linux/workqueue.h> | ||
| 29 | |||
| 30 | #include "hyperv.h" | ||
| 31 | #include "hv_kvp.h" | ||
| 32 | |||
| 33 | |||
| 34 | |||
| 35 | /* | ||
| 36 | * Global state maintained for transaction that is being processed. | ||
| 37 | * Note that only one transaction can be active at any point in time. | ||
| 38 | * | ||
| 39 | * This state is set when we receive a request from the host; we | ||
| 40 | * cleanup this state when the transaction is completed - when we respond | ||
| 41 | * to the host with the key value. | ||
| 42 | */ | ||
| 43 | |||
| 44 | static struct { | ||
| 45 | bool active; /* transaction status - active or not */ | ||
| 46 | int recv_len; /* number of bytes received. */ | ||
| 47 | struct vmbus_channel *recv_channel; /* chn we got the request */ | ||
| 48 | u64 recv_req_id; /* request ID. */ | ||
| 49 | } kvp_transaction; | ||
| 50 | |||
| 51 | static int kvp_send_key(int index); | ||
| 52 | |||
| 53 | static void kvp_respond_to_host(char *key, char *value, int error); | ||
| 54 | static void kvp_work_func(struct work_struct *dummy); | ||
| 55 | static void kvp_register(void); | ||
| 56 | |||
| 57 | static DECLARE_DELAYED_WORK(kvp_work, kvp_work_func); | ||
| 58 | |||
| 59 | static struct cb_id kvp_id = { CN_KVP_IDX, CN_KVP_VAL }; | ||
| 60 | static const char kvp_name[] = "kvp_kernel_module"; | ||
| 61 | static int timeout_fired; | ||
| 62 | static u8 *recv_buffer; | ||
| 63 | /* | ||
| 64 | * Register the kernel component with the user-level daemon. | ||
| 65 | * As part of this registration, pass the LIC version number. | ||
| 66 | */ | ||
| 67 | |||
| 68 | static void | ||
| 69 | kvp_register(void) | ||
| 70 | { | ||
| 71 | |||
| 72 | struct cn_msg *msg; | ||
| 73 | |||
| 74 | msg = kzalloc(sizeof(*msg) + strlen(HV_DRV_VERSION) + 1 , GFP_ATOMIC); | ||
| 75 | |||
| 76 | if (msg) { | ||
| 77 | msg->id.idx = CN_KVP_IDX; | ||
| 78 | msg->id.val = CN_KVP_VAL; | ||
| 79 | msg->seq = KVP_REGISTER; | ||
| 80 | strcpy(msg->data, HV_DRV_VERSION); | ||
| 81 | msg->len = strlen(HV_DRV_VERSION) + 1; | ||
| 82 | cn_netlink_send(msg, 0, GFP_ATOMIC); | ||
| 83 | kfree(msg); | ||
| 84 | } | ||
| 85 | } | ||
| 86 | static void | ||
| 87 | kvp_work_func(struct work_struct *dummy) | ||
| 88 | { | ||
| 89 | /* | ||
| 90 | * If the timer fires, the user-mode component has not responded; | ||
| 91 | * process the pending transaction. | ||
| 92 | */ | ||
| 93 | kvp_respond_to_host("Unknown key", "Guest timed out", timeout_fired); | ||
| 94 | timeout_fired = 1; | ||
| 95 | } | ||
| 96 | |||
| 97 | /* | ||
| 98 | * Callback when data is received from user mode. | ||
| 99 | */ | ||
| 100 | |||
| 101 | static void | ||
| 102 | kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) | ||
| 103 | { | ||
| 104 | struct hv_ku_msg *message; | ||
| 105 | |||
| 106 | message = (struct hv_ku_msg *)msg->data; | ||
| 107 | if (msg->seq == KVP_REGISTER) { | ||
| 108 | pr_info("KVP: user-mode registering done.\n"); | ||
| 109 | kvp_register(); | ||
| 110 | } | ||
| 111 | |||
| 112 | if (msg->seq == KVP_USER_SET) { | ||
| 113 | /* | ||
| 114 | * Complete the transaction by forwarding the key value | ||
| 115 | * to the host. But first, cancel the timeout. | ||
| 116 | */ | ||
| 117 | if (cancel_delayed_work_sync(&kvp_work)) | ||
| 118 | kvp_respond_to_host(message->kvp_key, | ||
| 119 | message->kvp_value, | ||
| 120 | !strlen(message->kvp_key)); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | static int | ||
| 125 | kvp_send_key(int index) | ||
| 126 | { | ||
| 127 | struct cn_msg *msg; | ||
| 128 | |||
| 129 | msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC); | ||
| 130 | |||
| 131 | if (msg) { | ||
| 132 | msg->id.idx = CN_KVP_IDX; | ||
| 133 | msg->id.val = CN_KVP_VAL; | ||
| 134 | msg->seq = KVP_KERNEL_GET; | ||
| 135 | ((struct hv_ku_msg *)msg->data)->kvp_index = index; | ||
| 136 | msg->len = sizeof(struct hv_ku_msg); | ||
| 137 | cn_netlink_send(msg, 0, GFP_ATOMIC); | ||
| 138 | kfree(msg); | ||
| 139 | return 0; | ||
| 140 | } | ||
| 141 | return 1; | ||
| 142 | } | ||
| 143 | |||
| 144 | /* | ||
| 145 | * Send a response back to the host. | ||
| 146 | */ | ||
| 147 | |||
| 148 | static void | ||
| 149 | kvp_respond_to_host(char *key, char *value, int error) | ||
| 150 | { | ||
| 151 | struct hv_kvp_msg *kvp_msg; | ||
| 152 | struct hv_kvp_msg_enumerate *kvp_data; | ||
| 153 | char *key_name; | ||
| 154 | struct icmsg_hdr *icmsghdrp; | ||
| 155 | int keylen, valuelen; | ||
| 156 | u32 buf_len; | ||
| 157 | struct vmbus_channel *channel; | ||
| 158 | u64 req_id; | ||
| 159 | |||
| 160 | /* | ||
| 161 | * If a transaction is not active; log and return. | ||
| 162 | */ | ||
| 163 | |||
| 164 | if (!kvp_transaction.active) { | ||
| 165 | /* | ||
| 166 | * This is a spurious call! | ||
| 167 | */ | ||
| 168 | pr_warn("KVP: Transaction not active\n"); | ||
| 169 | return; | ||
| 170 | } | ||
| 171 | /* | ||
| 172 | * Copy the global state for completing the transaction. Note that | ||
| 173 | * only one transaction can be active at a time. | ||
| 174 | */ | ||
| 175 | |||
| 176 | buf_len = kvp_transaction.recv_len; | ||
| 177 | channel = kvp_transaction.recv_channel; | ||
| 178 | req_id = kvp_transaction.recv_req_id; | ||
| 179 | |||
| 180 | icmsghdrp = (struct icmsg_hdr *) | ||
| 181 | &recv_buffer[sizeof(struct vmbuspipe_hdr)]; | ||
| 182 | kvp_msg = (struct hv_kvp_msg *) | ||
| 183 | &recv_buffer[sizeof(struct vmbuspipe_hdr) + | ||
| 184 | sizeof(struct icmsg_hdr)]; | ||
| 185 | kvp_data = &kvp_msg->kvp_data; | ||
| 186 | key_name = key; | ||
| 187 | |||
| 188 | /* | ||
| 189 | * If the error parameter is set, terminate the host's enumeration. | ||
| 190 | */ | ||
| 191 | if (error) { | ||
| 192 | /* | ||
| 193 | * We don't support this index or the we have timedout; | ||
| 194 | * terminate the host-side iteration by returning an error. | ||
| 195 | */ | ||
| 196 | icmsghdrp->status = HV_E_FAIL; | ||
| 197 | goto response_done; | ||
| 198 | } | ||
| 199 | |||
| 200 | /* | ||
| 201 | * The windows host expects the key/value pair to be encoded | ||
| 202 | * in utf16. | ||
| 203 | */ | ||
| 204 | keylen = utf8s_to_utf16s(key_name, strlen(key_name), | ||
| 205 | (wchar_t *)kvp_data->data.key); | ||
| 206 | kvp_data->data.key_size = 2*(keylen + 1); /* utf16 encoding */ | ||
| 207 | valuelen = utf8s_to_utf16s(value, strlen(value), | ||
| 208 | (wchar_t *)kvp_data->data.value); | ||
| 209 | kvp_data->data.value_size = 2*(valuelen + 1); /* utf16 encoding */ | ||
| 210 | |||
| 211 | kvp_data->data.value_type = REG_SZ; /* all our values are strings */ | ||
| 212 | icmsghdrp->status = HV_S_OK; | ||
| 213 | |||
| 214 | response_done: | ||
| 215 | icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; | ||
| 216 | |||
| 217 | vmbus_sendpacket(channel, recv_buffer, buf_len, req_id, | ||
| 218 | VM_PKT_DATA_INBAND, 0); | ||
| 219 | |||
| 220 | kvp_transaction.active = false; | ||
| 221 | } | ||
| 222 | |||
| 223 | /* | ||
| 224 | * This callback is invoked when we get a KVP message from the host. | ||
| 225 | * The host ensures that only one KVP transaction can be active at a time. | ||
| 226 | * KVP implementation in Linux needs to forward the key to a user-mde | ||
| 227 | * component to retrive the corresponding value. Consequently, we cannot | ||
| 228 | * respond to the host in the conext of this callback. Since the host | ||
| 229 | * guarantees that at most only one transaction can be active at a time, | ||
| 230 | * we stash away the transaction state in a set of global variables. | ||
| 231 | */ | ||
| 232 | |||
| 233 | void hv_kvp_onchannelcallback(void *context) | ||
| 234 | { | ||
| 235 | struct vmbus_channel *channel = context; | ||
| 236 | u32 recvlen; | ||
| 237 | u64 requestid; | ||
| 238 | |||
| 239 | struct hv_kvp_msg *kvp_msg; | ||
| 240 | struct hv_kvp_msg_enumerate *kvp_data; | ||
| 241 | |||
| 242 | struct icmsg_hdr *icmsghdrp; | ||
| 243 | struct icmsg_negotiate *negop = NULL; | ||
| 244 | |||
| 245 | |||
| 246 | if (kvp_transaction.active) | ||
| 247 | return; | ||
| 248 | |||
| 249 | |||
| 250 | vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE, &recvlen, &requestid); | ||
| 251 | |||
| 252 | if (recvlen > 0) { | ||
| 253 | icmsghdrp = (struct icmsg_hdr *)&recv_buffer[ | ||
| 254 | sizeof(struct vmbuspipe_hdr)]; | ||
| 255 | |||
| 256 | if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { | ||
| 257 | prep_negotiate_resp(icmsghdrp, negop, recv_buffer); | ||
| 258 | } else { | ||
| 259 | kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ | ||
| 260 | sizeof(struct vmbuspipe_hdr) + | ||
| 261 | sizeof(struct icmsg_hdr)]; | ||
| 262 | |||
| 263 | kvp_data = &kvp_msg->kvp_data; | ||
| 264 | |||
| 265 | /* | ||
| 266 | * We only support the "get" operation on | ||
| 267 | * "KVP_POOL_AUTO" pool. | ||
| 268 | */ | ||
| 269 | |||
| 270 | if ((kvp_msg->kvp_hdr.pool != KVP_POOL_AUTO) || | ||
| 271 | (kvp_msg->kvp_hdr.operation != | ||
| 272 | KVP_OP_ENUMERATE)) { | ||
| 273 | icmsghdrp->status = HV_E_FAIL; | ||
| 274 | goto callback_done; | ||
| 275 | } | ||
| 276 | |||
| 277 | /* | ||
| 278 | * Stash away this global state for completing the | ||
| 279 | * transaction; note transactions are serialized. | ||
| 280 | */ | ||
| 281 | kvp_transaction.recv_len = recvlen; | ||
| 282 | kvp_transaction.recv_channel = channel; | ||
| 283 | kvp_transaction.recv_req_id = requestid; | ||
| 284 | kvp_transaction.active = true; | ||
| 285 | |||
| 286 | /* | ||
| 287 | * Get the information from the | ||
| 288 | * user-mode component. | ||
| 289 | * component. This transaction will be | ||
| 290 | * completed when we get the value from | ||
| 291 | * the user-mode component. | ||
| 292 | * Set a timeout to deal with | ||
| 293 | * user-mode not responding. | ||
| 294 | */ | ||
| 295 | kvp_send_key(kvp_data->index); | ||
| 296 | schedule_delayed_work(&kvp_work, 100); | ||
| 297 | |||
| 298 | return; | ||
| 299 | |||
| 300 | } | ||
| 301 | |||
| 302 | callback_done: | ||
| 303 | |||
| 304 | icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ||
| 305 | | ICMSGHDRFLAG_RESPONSE; | ||
| 306 | |||
| 307 | vmbus_sendpacket(channel, recv_buffer, | ||
| 308 | recvlen, requestid, | ||
| 309 | VM_PKT_DATA_INBAND, 0); | ||
| 310 | } | ||
| 311 | |||
| 312 | } | ||
| 313 | |||
| 314 | int | ||
| 315 | hv_kvp_init(void) | ||
| 316 | { | ||
| 317 | int err; | ||
| 318 | |||
| 319 | err = cn_add_callback(&kvp_id, kvp_name, kvp_cn_callback); | ||
| 320 | if (err) | ||
| 321 | return err; | ||
| 322 | recv_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
| 323 | if (!recv_buffer) | ||
| 324 | return -ENOMEM; | ||
| 325 | |||
| 326 | return 0; | ||
| 327 | } | ||
| 328 | |||
| 329 | void hv_kvp_deinit(void) | ||
| 330 | { | ||
| 331 | cn_del_callback(&kvp_id); | ||
| 332 | cancel_delayed_work_sync(&kvp_work); | ||
| 333 | kfree(recv_buffer); | ||
| 334 | } | ||
diff --git a/drivers/staging/hv/hv_kvp.h b/drivers/staging/hv/hv_kvp.h new file mode 100644 index 00000000000..8c402f357d3 --- /dev/null +++ b/drivers/staging/hv/hv_kvp.h | |||
| @@ -0,0 +1,184 @@ | |||
| 1 | /* | ||
| 2 | * An implementation of HyperV key value pair (KVP) functionality for Linux. | ||
| 3 | * | ||
| 4 | * | ||
| 5 | * Copyright (C) 2010, Novell, Inc. | ||
| 6 | * Author : K. Y. Srinivasan <ksrinivasan@novell.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify it | ||
| 9 | * under the terms of the GNU General Public License version 2 as published | ||
| 10 | * by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, but | ||
| 13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
| 15 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
| 16 | * details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License | ||
| 19 | * along with this program; if not, write to the Free Software | ||
| 20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 21 | * | ||
| 22 | */ | ||
| 23 | #ifndef _KVP_H | ||
| 24 | #define _KVP_H_ | ||
| 25 | |||
| 26 | /* | ||
| 27 | * Maximum value size - used for both key names and value data, and includes | ||
| 28 | * any applicable NULL terminators. | ||
| 29 | * | ||
| 30 | * Note: This limit is somewhat arbitrary, but falls easily within what is | ||
| 31 | * supported for all native guests (back to Win 2000) and what is reasonable | ||
| 32 | * for the IC KVP exchange functionality. Note that Windows Me/98/95 are | ||
| 33 | * limited to 255 character key names. | ||
| 34 | * | ||
| 35 | * MSDN recommends not storing data values larger than 2048 bytes in the | ||
| 36 | * registry. | ||
| 37 | * | ||
| 38 | * Note: This value is used in defining the KVP exchange message - this value | ||
| 39 | * cannot be modified without affecting the message size and compatibility. | ||
| 40 | */ | ||
| 41 | |||
| 42 | /* | ||
| 43 | * bytes, including any null terminators | ||
| 44 | */ | ||
| 45 | #define HV_KVP_EXCHANGE_MAX_VALUE_SIZE (2048) | ||
| 46 | |||
| 47 | |||
| 48 | /* | ||
| 49 | * Maximum key size - the registry limit for the length of an entry name | ||
| 50 | * is 256 characters, including the null terminator | ||
| 51 | */ | ||
| 52 | |||
| 53 | #define HV_KVP_EXCHANGE_MAX_KEY_SIZE (512) | ||
| 54 | |||
| 55 | /* | ||
| 56 | * In Linux, we implement the KVP functionality in two components: | ||
| 57 | * 1) The kernel component which is packaged as part of the hv_utils driver | ||
| 58 | * is responsible for communicating with the host and responsible for | ||
| 59 | * implementing the host/guest protocol. 2) A user level daemon that is | ||
| 60 | * responsible for data gathering. | ||
| 61 | * | ||
| 62 | * Host/Guest Protocol: The host iterates over an index and expects the guest | ||
| 63 | * to assign a key name to the index and also return the value corresponding to | ||
| 64 | * the key. The host will have atmost one KVP transaction outstanding at any | ||
| 65 | * given point in time. The host side iteration stops when the guest returns | ||
| 66 | * an error. Microsoft has specified the following mapping of key names to | ||
| 67 | * host specified index: | ||
| 68 | * | ||
| 69 | * Index Key Name | ||
| 70 | * 0 FullyQualifiedDomainName | ||
| 71 | * 1 IntegrationServicesVersion | ||
| 72 | * 2 NetworkAddressIPv4 | ||
| 73 | * 3 NetworkAddressIPv6 | ||
| 74 | * 4 OSBuildNumber | ||
| 75 | * 5 OSName | ||
| 76 | * 6 OSMajorVersion | ||
| 77 | * 7 OSMinorVersion | ||
| 78 | * 8 OSVersion | ||
| 79 | * 9 ProcessorArchitecture | ||
| 80 | * | ||
| 81 | * The Windows host expects the Key Name and Key Value to be encoded in utf16. | ||
| 82 | * | ||
| 83 | * Guest Kernel/KVP Daemon Protocol: As noted earlier, we implement all of the | ||
| 84 | * data gathering functionality in a user mode daemon. The user level daemon | ||
| 85 | * is also responsible for binding the key name to the index as well. The | ||
| 86 | * kernel and user-level daemon communicate using a connector channel. | ||
| 87 | * | ||
| 88 | * The user mode component first registers with the | ||
| 89 | * the kernel component. Subsequently, the kernel component requests, data | ||
| 90 | * for the specified keys. In response to this message the user mode component | ||
| 91 | * fills in the value corresponding to the specified key. We overload the | ||
| 92 | * sequence field in the cn_msg header to define our KVP message types. | ||
| 93 | * | ||
| 94 | * | ||
| 95 | * The kernel component simply acts as a conduit for communication between the | ||
| 96 | * Windows host and the user-level daemon. The kernel component passes up the | ||
| 97 | * index received from the Host to the user-level daemon. If the index is | ||
| 98 | * valid (supported), the corresponding key as well as its | ||
| 99 | * value (both are strings) is returned. If the index is invalid | ||
| 100 | * (not supported), a NULL key string is returned. | ||
| 101 | */ | ||
| 102 | |||
| 103 | /* | ||
| 104 | * | ||
| 105 | * The following definitions are shared with the user-mode component; do not | ||
| 106 | * change any of this without making the corresponding changes in | ||
| 107 | * the KVP user-mode component. | ||
| 108 | */ | ||
| 109 | |||
| 110 | #define CN_KVP_VAL 0x1 /* This supports queries from the kernel */ | ||
| 111 | #define CN_KVP_USER_VAL 0x2 /* This supports queries from the user */ | ||
| 112 | |||
| 113 | enum hv_ku_op { | ||
| 114 | KVP_REGISTER = 0, /* Register the user mode component */ | ||
| 115 | KVP_KERNEL_GET, /* Kernel is requesting the value */ | ||
| 116 | KVP_KERNEL_SET, /* Kernel is providing the value */ | ||
| 117 | KVP_USER_GET, /* User is requesting the value */ | ||
| 118 | KVP_USER_SET /* User is providing the value */ | ||
| 119 | }; | ||
| 120 | |||
| 121 | struct hv_ku_msg { | ||
| 122 | __u32 kvp_index; /* Key index */ | ||
| 123 | __u8 kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */ | ||
| 124 | __u8 kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key value */ | ||
| 125 | }; | ||
| 126 | |||
| 127 | |||
| 128 | |||
| 129 | |||
| 130 | #ifdef __KERNEL__ | ||
| 131 | |||
| 132 | /* | ||
| 133 | * Registry value types. | ||
| 134 | */ | ||
| 135 | |||
| 136 | #define REG_SZ 1 | ||
| 137 | |||
| 138 | enum hv_kvp_exchg_op { | ||
| 139 | KVP_OP_GET = 0, | ||
| 140 | KVP_OP_SET, | ||
| 141 | KVP_OP_DELETE, | ||
| 142 | KVP_OP_ENUMERATE, | ||
| 143 | KVP_OP_COUNT /* Number of operations, must be last. */ | ||
| 144 | }; | ||
| 145 | |||
| 146 | enum hv_kvp_exchg_pool { | ||
| 147 | KVP_POOL_EXTERNAL = 0, | ||
| 148 | KVP_POOL_GUEST, | ||
| 149 | KVP_POOL_AUTO, | ||
| 150 | KVP_POOL_AUTO_EXTERNAL, | ||
| 151 | KVP_POOL_AUTO_INTERNAL, | ||
| 152 | KVP_POOL_COUNT /* Number of pools, must be last. */ | ||
| 153 | }; | ||
| 154 | |||
| 155 | struct hv_kvp_hdr { | ||
| 156 | u8 operation; | ||
| 157 | u8 pool; | ||
| 158 | }; | ||
| 159 | |||
| 160 | struct hv_kvp_exchg_msg_value { | ||
| 161 | u32 value_type; | ||
| 162 | u32 key_size; | ||
| 163 | u32 value_size; | ||
| 164 | u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; | ||
| 165 | u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; | ||
| 166 | }; | ||
| 167 | |||
| 168 | struct hv_kvp_msg_enumerate { | ||
| 169 | u32 index; | ||
| 170 | struct hv_kvp_exchg_msg_value data; | ||
| 171 | }; | ||
| 172 | |||
| 173 | struct hv_kvp_msg { | ||
| 174 | struct hv_kvp_hdr kvp_hdr; | ||
| 175 | struct hv_kvp_msg_enumerate kvp_data; | ||
| 176 | }; | ||
| 177 | |||
| 178 | int hv_kvp_init(void); | ||
| 179 | void hv_kvp_deinit(void); | ||
| 180 | void hv_kvp_onchannelcallback(void *); | ||
| 181 | |||
| 182 | #endif /* __KERNEL__ */ | ||
| 183 | #endif /* _KVP_H */ | ||
| 184 | |||
diff --git a/drivers/staging/hv/hv_mouse.c b/drivers/staging/hv/hv_mouse.c new file mode 100644 index 00000000000..d957fc22801 --- /dev/null +++ b/drivers/staging/hv/hv_mouse.c | |||
| @@ -0,0 +1,974 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2009, Citrix Systems, Inc. | ||
| 3 | * Copyright (c) 2010, Microsoft Corporation. | ||
| 4 | * Copyright (c) 2011, Novell Inc. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | */ | ||
| 15 | #include <linux/init.h> | ||
| 16 | #include <linux/module.h> | ||
| 17 | #include <linux/delay.h> | ||
| 18 | #include <linux/device.h> | ||
| 19 | #include <linux/workqueue.h> | ||
| 20 | #include <linux/sched.h> | ||
| 21 | #include <linux/wait.h> | ||
| 22 | #include <linux/input.h> | ||
| 23 | #include <linux/hid.h> | ||
| 24 | #include <linux/hiddev.h> | ||
| 25 | #include <linux/pci.h> | ||
| 26 | #include <linux/dmi.h> | ||
| 27 | |||
| 28 | #include "hyperv.h" | ||
| 29 | |||
| 30 | |||
| 31 | /* | ||
| 32 | * Data types | ||
| 33 | */ | ||
| 34 | struct hv_input_dev_info { | ||
| 35 | unsigned short vendor; | ||
| 36 | unsigned short product; | ||
| 37 | unsigned short version; | ||
| 38 | char name[128]; | ||
| 39 | }; | ||
| 40 | |||
| 41 | /* The maximum size of a synthetic input message. */ | ||
| 42 | #define SYNTHHID_MAX_INPUT_REPORT_SIZE 16 | ||
| 43 | |||
| 44 | /* | ||
| 45 | * Current version | ||
| 46 | * | ||
| 47 | * History: | ||
| 48 | * Beta, RC < 2008/1/22 1,0 | ||
| 49 | * RC > 2008/1/22 2,0 | ||
| 50 | */ | ||
| 51 | #define SYNTHHID_INPUT_VERSION_MAJOR 2 | ||
| 52 | #define SYNTHHID_INPUT_VERSION_MINOR 0 | ||
| 53 | #define SYNTHHID_INPUT_VERSION (SYNTHHID_INPUT_VERSION_MINOR | \ | ||
| 54 | (SYNTHHID_INPUT_VERSION_MAJOR << 16)) | ||
| 55 | |||
| 56 | |||
| 57 | #pragma pack(push,1) | ||
| 58 | /* | ||
| 59 | * Message types in the synthetic input protocol | ||
| 60 | */ | ||
| 61 | enum synthhid_msg_type { | ||
| 62 | SynthHidProtocolRequest, | ||
| 63 | SynthHidProtocolResponse, | ||
| 64 | SynthHidInitialDeviceInfo, | ||
| 65 | SynthHidInitialDeviceInfoAck, | ||
| 66 | SynthHidInputReport, | ||
| 67 | SynthHidMax | ||
| 68 | }; | ||
| 69 | |||
| 70 | /* | ||
| 71 | * Basic message structures. | ||
| 72 | */ | ||
| 73 | struct synthhid_msg_hdr { | ||
| 74 | enum synthhid_msg_type type; | ||
| 75 | u32 size; | ||
| 76 | }; | ||
| 77 | |||
| 78 | struct synthhid_msg { | ||
| 79 | struct synthhid_msg_hdr header; | ||
| 80 | char data[1]; /* Enclosed message */ | ||
| 81 | }; | ||
| 82 | |||
| 83 | union synthhid_version { | ||
| 84 | struct { | ||
| 85 | u16 minor_version; | ||
| 86 | u16 major_version; | ||
| 87 | }; | ||
| 88 | u32 version; | ||
| 89 | }; | ||
| 90 | |||
| 91 | /* | ||
| 92 | * Protocol messages | ||
| 93 | */ | ||
| 94 | struct synthhid_protocol_request { | ||
| 95 | struct synthhid_msg_hdr header; | ||
| 96 | union synthhid_version version_requested; | ||
| 97 | }; | ||
| 98 | |||
| 99 | struct synthhid_protocol_response { | ||
| 100 | struct synthhid_msg_hdr header; | ||
| 101 | union synthhid_version version_requested; | ||
| 102 | unsigned char approved; | ||
| 103 | }; | ||
| 104 | |||
| 105 | struct synthhid_device_info { | ||
| 106 | struct synthhid_msg_hdr header; | ||
| 107 | struct hv_input_dev_info hid_dev_info; | ||
| 108 | struct hid_descriptor hid_descriptor; | ||
| 109 | }; | ||
| 110 | |||
| 111 | struct synthhid_device_info_ack { | ||
| 112 | struct synthhid_msg_hdr header; | ||
| 113 | unsigned char reserved; | ||
| 114 | }; | ||
| 115 | |||
| 116 | struct synthhid_input_report { | ||
| 117 | struct synthhid_msg_hdr header; | ||
| 118 | char buffer[1]; | ||
| 119 | }; | ||
| 120 | |||
| 121 | #pragma pack(pop) | ||
| 122 | |||
| 123 | #define INPUTVSC_SEND_RING_BUFFER_SIZE 10*PAGE_SIZE | ||
| 124 | #define INPUTVSC_RECV_RING_BUFFER_SIZE 10*PAGE_SIZE | ||
| 125 | |||
| 126 | #define NBITS(x) (((x)/BITS_PER_LONG)+1) | ||
| 127 | |||
| 128 | enum pipe_prot_msg_type { | ||
| 129 | PipeMessageInvalid = 0, | ||
| 130 | PipeMessageData, | ||
| 131 | PipeMessageMaximum | ||
| 132 | }; | ||
| 133 | |||
| 134 | |||
| 135 | struct pipe_prt_msg { | ||
| 136 | enum pipe_prot_msg_type type; | ||
| 137 | u32 size; | ||
| 138 | char data[1]; | ||
| 139 | }; | ||
| 140 | |||
| 141 | /* | ||
| 142 | * Data types | ||
| 143 | */ | ||
| 144 | struct mousevsc_prt_msg { | ||
| 145 | enum pipe_prot_msg_type type; | ||
| 146 | u32 size; | ||
| 147 | union { | ||
| 148 | struct synthhid_protocol_request request; | ||
| 149 | struct synthhid_protocol_response response; | ||
| 150 | struct synthhid_device_info_ack ack; | ||
| 151 | }; | ||
| 152 | }; | ||
| 153 | |||
| 154 | /* | ||
| 155 | * Represents an mousevsc device | ||
| 156 | */ | ||
| 157 | struct mousevsc_dev { | ||
| 158 | struct hv_device *device; | ||
| 159 | /* 0 indicates the device is being destroyed */ | ||
| 160 | atomic_t ref_count; | ||
| 161 | int num_outstanding_req; | ||
| 162 | unsigned char init_complete; | ||
| 163 | struct mousevsc_prt_msg protocol_req; | ||
| 164 | struct mousevsc_prt_msg protocol_resp; | ||
| 165 | /* Synchronize the request/response if needed */ | ||
| 166 | wait_queue_head_t protocol_wait_event; | ||
| 167 | wait_queue_head_t dev_info_wait_event; | ||
| 168 | int protocol_wait_condition; | ||
| 169 | int device_wait_condition; | ||
| 170 | int dev_info_status; | ||
| 171 | |||
| 172 | struct hid_descriptor *hid_desc; | ||
| 173 | unsigned char *report_desc; | ||
| 174 | u32 report_desc_size; | ||
| 175 | struct hv_input_dev_info hid_dev_info; | ||
| 176 | }; | ||
| 177 | |||
| 178 | |||
| 179 | static const char *driver_name = "mousevsc"; | ||
| 180 | |||
| 181 | /* {CFA8B69E-5B4A-4cc0-B98B-8BA1A1F3F95A} */ | ||
| 182 | static const struct hv_guid mouse_guid = { | ||
| 183 | .data = {0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c, | ||
| 184 | 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A} | ||
| 185 | }; | ||
| 186 | |||
| 187 | static void deviceinfo_callback(struct hv_device *dev, struct hv_input_dev_info *info); | ||
| 188 | static void inputreport_callback(struct hv_device *dev, void *packet, u32 len); | ||
| 189 | static void reportdesc_callback(struct hv_device *dev, void *packet, u32 len); | ||
| 190 | |||
| 191 | static struct mousevsc_dev *alloc_input_device(struct hv_device *device) | ||
| 192 | { | ||
| 193 | struct mousevsc_dev *input_dev; | ||
| 194 | |||
| 195 | input_dev = kzalloc(sizeof(struct mousevsc_dev), GFP_KERNEL); | ||
| 196 | |||
| 197 | if (!input_dev) | ||
| 198 | return NULL; | ||
| 199 | |||
| 200 | /* | ||
| 201 | * Set to 2 to allow both inbound and outbound traffics | ||
| 202 | * (ie get_input_device() and must_get_input_device()) to proceed. | ||
| 203 | */ | ||
| 204 | atomic_cmpxchg(&input_dev->ref_count, 0, 2); | ||
| 205 | |||
| 206 | input_dev->device = device; | ||
| 207 | device->ext = input_dev; | ||
| 208 | |||
| 209 | return input_dev; | ||
| 210 | } | ||
| 211 | |||
| 212 | static void free_input_device(struct mousevsc_dev *device) | ||
| 213 | { | ||
| 214 | WARN_ON(atomic_read(&device->ref_count) == 0); | ||
| 215 | kfree(device); | ||
| 216 | } | ||
| 217 | |||
| 218 | /* | ||
| 219 | * Get the inputdevice object if exists and its refcount > 1 | ||
| 220 | */ | ||
| 221 | static struct mousevsc_dev *get_input_device(struct hv_device *device) | ||
| 222 | { | ||
| 223 | struct mousevsc_dev *input_dev; | ||
| 224 | |||
| 225 | input_dev = (struct mousevsc_dev *)device->ext; | ||
| 226 | |||
| 227 | /* | ||
| 228 | * FIXME | ||
| 229 | * This sure isn't a valid thing to print for debugging, no matter | ||
| 230 | * what the intention is... | ||
| 231 | * | ||
| 232 | * printk(KERN_ERR "-------------------------> REFCOUNT = %d", | ||
| 233 | * input_dev->ref_count); | ||
| 234 | */ | ||
| 235 | |||
| 236 | if (input_dev && atomic_read(&input_dev->ref_count) > 1) | ||
| 237 | atomic_inc(&input_dev->ref_count); | ||
| 238 | else | ||
| 239 | input_dev = NULL; | ||
| 240 | |||
| 241 | return input_dev; | ||
| 242 | } | ||
| 243 | |||
| 244 | /* | ||
| 245 | * Get the inputdevice object iff exists and its refcount > 0 | ||
| 246 | */ | ||
| 247 | static struct mousevsc_dev *must_get_input_device(struct hv_device *device) | ||
| 248 | { | ||
| 249 | struct mousevsc_dev *input_dev; | ||
| 250 | |||
| 251 | input_dev = (struct mousevsc_dev *)device->ext; | ||
| 252 | |||
| 253 | if (input_dev && atomic_read(&input_dev->ref_count)) | ||
| 254 | atomic_inc(&input_dev->ref_count); | ||
| 255 | else | ||
| 256 | input_dev = NULL; | ||
| 257 | |||
| 258 | return input_dev; | ||
| 259 | } | ||
| 260 | |||
| 261 | static void put_input_device(struct hv_device *device) | ||
| 262 | { | ||
| 263 | struct mousevsc_dev *input_dev; | ||
| 264 | |||
| 265 | input_dev = (struct mousevsc_dev *)device->ext; | ||
| 266 | |||
| 267 | atomic_dec(&input_dev->ref_count); | ||
| 268 | } | ||
| 269 | |||
| 270 | /* | ||
| 271 | * Drop ref count to 1 to effectively disable get_input_device() | ||
| 272 | */ | ||
| 273 | static struct mousevsc_dev *release_input_device(struct hv_device *device) | ||
| 274 | { | ||
| 275 | struct mousevsc_dev *input_dev; | ||
| 276 | |||
| 277 | input_dev = (struct mousevsc_dev *)device->ext; | ||
| 278 | |||
| 279 | /* Busy wait until the ref drop to 2, then set it to 1 */ | ||
| 280 | while (atomic_cmpxchg(&input_dev->ref_count, 2, 1) != 2) | ||
| 281 | udelay(100); | ||
| 282 | |||
| 283 | return input_dev; | ||
| 284 | } | ||
| 285 | |||
| 286 | /* | ||
| 287 | * Drop ref count to 0. No one can use input_device object. | ||
| 288 | */ | ||
| 289 | static struct mousevsc_dev *final_release_input_device(struct hv_device *device) | ||
| 290 | { | ||
| 291 | struct mousevsc_dev *input_dev; | ||
| 292 | |||
| 293 | input_dev = (struct mousevsc_dev *)device->ext; | ||
| 294 | |||
| 295 | /* Busy wait until the ref drop to 1, then set it to 0 */ | ||
| 296 | while (atomic_cmpxchg(&input_dev->ref_count, 1, 0) != 1) | ||
| 297 | udelay(100); | ||
| 298 | |||
| 299 | device->ext = NULL; | ||
| 300 | return input_dev; | ||
| 301 | } | ||
| 302 | |||
| 303 | static void mousevsc_on_send_completion(struct hv_device *device, | ||
| 304 | struct vmpacket_descriptor *packet) | ||
| 305 | { | ||
| 306 | struct mousevsc_dev *input_dev; | ||
| 307 | void *request; | ||
| 308 | |||
| 309 | input_dev = must_get_input_device(device); | ||
| 310 | if (!input_dev) { | ||
| 311 | pr_err("unable to get input device...device being destroyed?"); | ||
| 312 | return; | ||
| 313 | } | ||
| 314 | |||
| 315 | request = (void *)(unsigned long)packet->trans_id; | ||
| 316 | |||
| 317 | if (request == &input_dev->protocol_req) { | ||
| 318 | /* FIXME */ | ||
| 319 | /* Shouldn't we be doing something here? */ | ||
| 320 | } | ||
| 321 | |||
| 322 | put_input_device(device); | ||
| 323 | } | ||
| 324 | |||
| 325 | static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, | ||
| 326 | struct synthhid_device_info *device_info) | ||
| 327 | { | ||
| 328 | int ret = 0; | ||
| 329 | struct hid_descriptor *desc; | ||
| 330 | struct mousevsc_prt_msg ack; | ||
| 331 | |||
| 332 | /* Assume success for now */ | ||
| 333 | input_device->dev_info_status = 0; | ||
| 334 | |||
| 335 | /* Save the device attr */ | ||
| 336 | memcpy(&input_device->hid_dev_info, &device_info->hid_dev_info, | ||
| 337 | sizeof(struct hv_input_dev_info)); | ||
| 338 | |||
| 339 | /* Save the hid desc */ | ||
| 340 | desc = &device_info->hid_descriptor; | ||
| 341 | WARN_ON(desc->bLength > 0); | ||
| 342 | |||
| 343 | input_device->hid_desc = kzalloc(desc->bLength, GFP_KERNEL); | ||
| 344 | |||
| 345 | if (!input_device->hid_desc) { | ||
| 346 | pr_err("unable to allocate hid descriptor - size %d", desc->bLength); | ||
| 347 | goto Cleanup; | ||
| 348 | } | ||
| 349 | |||
| 350 | memcpy(input_device->hid_desc, desc, desc->bLength); | ||
| 351 | |||
| 352 | /* Save the report desc */ | ||
| 353 | input_device->report_desc_size = desc->desc[0].wDescriptorLength; | ||
| 354 | input_device->report_desc = kzalloc(input_device->report_desc_size, | ||
| 355 | GFP_KERNEL); | ||
| 356 | |||
| 357 | if (!input_device->report_desc) { | ||
| 358 | pr_err("unable to allocate report descriptor - size %d", | ||
| 359 | input_device->report_desc_size); | ||
| 360 | goto Cleanup; | ||
| 361 | } | ||
| 362 | |||
| 363 | memcpy(input_device->report_desc, | ||
| 364 | ((unsigned char *)desc) + desc->bLength, | ||
| 365 | desc->desc[0].wDescriptorLength); | ||
| 366 | |||
| 367 | /* Send the ack */ | ||
| 368 | memset(&ack, 0, sizeof(struct mousevsc_prt_msg)); | ||
| 369 | |||
| 370 | ack.type = PipeMessageData; | ||
| 371 | ack.size = sizeof(struct synthhid_device_info_ack); | ||
| 372 | |||
| 373 | ack.ack.header.type = SynthHidInitialDeviceInfoAck; | ||
| 374 | ack.ack.header.size = 1; | ||
| 375 | ack.ack.reserved = 0; | ||
| 376 | |||
| 377 | ret = vmbus_sendpacket(input_device->device->channel, | ||
| 378 | &ack, | ||
| 379 | sizeof(struct pipe_prt_msg) - sizeof(unsigned char) + | ||
| 380 | sizeof(struct synthhid_device_info_ack), | ||
| 381 | (unsigned long)&ack, | ||
| 382 | VM_PKT_DATA_INBAND, | ||
| 383 | VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); | ||
| 384 | if (ret != 0) { | ||
| 385 | pr_err("unable to send synthhid device info ack - ret %d", | ||
| 386 | ret); | ||
| 387 | goto Cleanup; | ||
| 388 | } | ||
| 389 | |||
| 390 | input_device->device_wait_condition = 1; | ||
| 391 | wake_up(&input_device->dev_info_wait_event); | ||
| 392 | |||
| 393 | return; | ||
| 394 | |||
| 395 | Cleanup: | ||
| 396 | kfree(input_device->hid_desc); | ||
| 397 | input_device->hid_desc = NULL; | ||
| 398 | |||
| 399 | kfree(input_device->report_desc); | ||
| 400 | input_device->report_desc = NULL; | ||
| 401 | |||
| 402 | input_device->dev_info_status = -1; | ||
| 403 | input_device->device_wait_condition = 1; | ||
| 404 | wake_up(&input_device->dev_info_wait_event); | ||
| 405 | } | ||
| 406 | |||
| 407 | static void mousevsc_on_receive_input_report(struct mousevsc_dev *input_device, | ||
| 408 | struct synthhid_input_report *input_report) | ||
| 409 | { | ||
| 410 | struct hv_driver *input_drv; | ||
| 411 | |||
| 412 | if (!input_device->init_complete) { | ||
| 413 | pr_info("Initialization incomplete...ignoring input_report msg"); | ||
| 414 | return; | ||
| 415 | } | ||
| 416 | |||
| 417 | input_drv = drv_to_hv_drv(input_device->device->device.driver); | ||
| 418 | |||
| 419 | inputreport_callback(input_device->device, | ||
| 420 | input_report->buffer, | ||
| 421 | input_report->header.size); | ||
| 422 | } | ||
| 423 | |||
| 424 | static void mousevsc_on_receive(struct hv_device *device, | ||
| 425 | struct vmpacket_descriptor *packet) | ||
| 426 | { | ||
| 427 | struct pipe_prt_msg *pipe_msg; | ||
| 428 | struct synthhid_msg *hid_msg; | ||
| 429 | struct mousevsc_dev *input_dev; | ||
| 430 | |||
| 431 | input_dev = must_get_input_device(device); | ||
| 432 | if (!input_dev) { | ||
| 433 | pr_err("unable to get input device...device being destroyed?"); | ||
| 434 | return; | ||
| 435 | } | ||
| 436 | |||
| 437 | pipe_msg = (struct pipe_prt_msg *)((unsigned long)packet + | ||
| 438 | (packet->offset8 << 3)); | ||
| 439 | |||
| 440 | if (pipe_msg->type != PipeMessageData) { | ||
| 441 | pr_err("unknown pipe msg type - type %d len %d", | ||
| 442 | pipe_msg->type, pipe_msg->size); | ||
| 443 | put_input_device(device); | ||
| 444 | return ; | ||
| 445 | } | ||
| 446 | |||
| 447 | hid_msg = (struct synthhid_msg *)&pipe_msg->data[0]; | ||
| 448 | |||
| 449 | switch (hid_msg->header.type) { | ||
| 450 | case SynthHidProtocolResponse: | ||
| 451 | memcpy(&input_dev->protocol_resp, pipe_msg, | ||
| 452 | pipe_msg->size + sizeof(struct pipe_prt_msg) - | ||
| 453 | sizeof(unsigned char)); | ||
| 454 | input_dev->protocol_wait_condition = 1; | ||
| 455 | wake_up(&input_dev->protocol_wait_event); | ||
| 456 | break; | ||
| 457 | |||
| 458 | case SynthHidInitialDeviceInfo: | ||
| 459 | WARN_ON(pipe_msg->size >= sizeof(struct hv_input_dev_info)); | ||
| 460 | |||
| 461 | /* | ||
| 462 | * Parse out the device info into device attr, | ||
| 463 | * hid desc and report desc | ||
| 464 | */ | ||
| 465 | mousevsc_on_receive_device_info(input_dev, | ||
| 466 | (struct synthhid_device_info *)&pipe_msg->data[0]); | ||
| 467 | break; | ||
| 468 | case SynthHidInputReport: | ||
| 469 | mousevsc_on_receive_input_report(input_dev, | ||
| 470 | (struct synthhid_input_report *)&pipe_msg->data[0]); | ||
| 471 | |||
| 472 | break; | ||
| 473 | default: | ||
| 474 | pr_err("unsupported hid msg type - type %d len %d", | ||
| 475 | hid_msg->header.type, hid_msg->header.size); | ||
| 476 | break; | ||
| 477 | } | ||
| 478 | |||
| 479 | put_input_device(device); | ||
| 480 | } | ||
| 481 | |||
| 482 | static void mousevsc_on_channel_callback(void *context) | ||
| 483 | { | ||
| 484 | const int packetSize = 0x100; | ||
| 485 | int ret = 0; | ||
| 486 | struct hv_device *device = (struct hv_device *)context; | ||
| 487 | struct mousevsc_dev *input_dev; | ||
| 488 | |||
| 489 | u32 bytes_recvd; | ||
| 490 | u64 req_id; | ||
| 491 | unsigned char packet[0x100]; | ||
| 492 | struct vmpacket_descriptor *desc; | ||
| 493 | unsigned char *buffer = packet; | ||
| 494 | int bufferlen = packetSize; | ||
| 495 | |||
| 496 | input_dev = must_get_input_device(device); | ||
| 497 | |||
| 498 | if (!input_dev) { | ||
| 499 | pr_err("unable to get input device...device being destroyed?"); | ||
| 500 | return; | ||
| 501 | } | ||
| 502 | |||
| 503 | do { | ||
| 504 | ret = vmbus_recvpacket_raw(device->channel, buffer, | ||
| 505 | bufferlen, &bytes_recvd, &req_id); | ||
| 506 | |||
| 507 | if (ret == 0) { | ||
| 508 | if (bytes_recvd > 0) { | ||
| 509 | desc = (struct vmpacket_descriptor *)buffer; | ||
| 510 | |||
| 511 | switch (desc->type) { | ||
| 512 | case VM_PKT_COMP: | ||
| 513 | mousevsc_on_send_completion( | ||
| 514 | device, desc); | ||
| 515 | break; | ||
| 516 | |||
| 517 | case VM_PKT_DATA_INBAND: | ||
| 518 | mousevsc_on_receive( | ||
| 519 | device, desc); | ||
| 520 | break; | ||
| 521 | |||
| 522 | default: | ||
| 523 | pr_err("unhandled packet type %d, tid %llx len %d\n", | ||
| 524 | desc->type, | ||
| 525 | req_id, | ||
| 526 | bytes_recvd); | ||
| 527 | break; | ||
| 528 | } | ||
| 529 | |||
| 530 | /* reset */ | ||
| 531 | if (bufferlen > packetSize) { | ||
| 532 | kfree(buffer); | ||
| 533 | |||
| 534 | buffer = packet; | ||
| 535 | bufferlen = packetSize; | ||
| 536 | } | ||
| 537 | } else { | ||
| 538 | /* | ||
| 539 | * pr_debug("nothing else to read..."); | ||
| 540 | * reset | ||
| 541 | */ | ||
| 542 | if (bufferlen > packetSize) { | ||
| 543 | kfree(buffer); | ||
| 544 | |||
| 545 | buffer = packet; | ||
| 546 | bufferlen = packetSize; | ||
| 547 | } | ||
| 548 | break; | ||
| 549 | } | ||
| 550 | } else if (ret == -2) { | ||
| 551 | /* Handle large packet */ | ||
| 552 | bufferlen = bytes_recvd; | ||
| 553 | buffer = kzalloc(bytes_recvd, GFP_KERNEL); | ||
| 554 | |||
| 555 | if (buffer == NULL) { | ||
| 556 | buffer = packet; | ||
| 557 | bufferlen = packetSize; | ||
| 558 | |||
| 559 | /* Try again next time around */ | ||
| 560 | pr_err("unable to allocate buffer of size %d!", | ||
| 561 | bytes_recvd); | ||
| 562 | break; | ||
| 563 | } | ||
| 564 | } | ||
| 565 | } while (1); | ||
| 566 | |||
| 567 | put_input_device(device); | ||
| 568 | |||
| 569 | return; | ||
| 570 | } | ||
| 571 | |||
| 572 | static int mousevsc_connect_to_vsp(struct hv_device *device) | ||
| 573 | { | ||
| 574 | int ret = 0; | ||
| 575 | struct mousevsc_dev *input_dev; | ||
| 576 | struct mousevsc_prt_msg *request; | ||
| 577 | struct mousevsc_prt_msg *response; | ||
| 578 | |||
| 579 | input_dev = get_input_device(device); | ||
| 580 | |||
| 581 | if (!input_dev) { | ||
| 582 | pr_err("unable to get input device...device being destroyed?"); | ||
| 583 | return -1; | ||
| 584 | } | ||
| 585 | |||
| 586 | init_waitqueue_head(&input_dev->protocol_wait_event); | ||
| 587 | init_waitqueue_head(&input_dev->dev_info_wait_event); | ||
| 588 | |||
| 589 | request = &input_dev->protocol_req; | ||
| 590 | |||
| 591 | /* | ||
| 592 | * Now, initiate the vsc/vsp initialization protocol on the open channel | ||
| 593 | */ | ||
| 594 | memset(request, 0, sizeof(struct mousevsc_prt_msg)); | ||
| 595 | |||
| 596 | request->type = PipeMessageData; | ||
| 597 | request->size = sizeof(struct synthhid_protocol_request); | ||
| 598 | |||
| 599 | request->request.header.type = SynthHidProtocolRequest; | ||
| 600 | request->request.header.size = sizeof(unsigned long); | ||
| 601 | request->request.version_requested.version = SYNTHHID_INPUT_VERSION; | ||
| 602 | |||
| 603 | pr_info("synthhid protocol request..."); | ||
| 604 | |||
| 605 | ret = vmbus_sendpacket(device->channel, request, | ||
| 606 | sizeof(struct pipe_prt_msg) - | ||
| 607 | sizeof(unsigned char) + | ||
| 608 | sizeof(struct synthhid_protocol_request), | ||
| 609 | (unsigned long)request, | ||
| 610 | VM_PKT_DATA_INBAND, | ||
| 611 | VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); | ||
| 612 | if (ret != 0) { | ||
| 613 | pr_err("unable to send synthhid protocol request."); | ||
| 614 | goto Cleanup; | ||
| 615 | } | ||
| 616 | |||
| 617 | input_dev->protocol_wait_condition = 0; | ||
| 618 | wait_event_timeout(input_dev->protocol_wait_event, | ||
| 619 | input_dev->protocol_wait_condition, msecs_to_jiffies(1000)); | ||
| 620 | if (input_dev->protocol_wait_condition == 0) { | ||
| 621 | ret = -ETIMEDOUT; | ||
| 622 | goto Cleanup; | ||
| 623 | } | ||
| 624 | |||
| 625 | response = &input_dev->protocol_resp; | ||
| 626 | |||
| 627 | if (!response->response.approved) { | ||
| 628 | pr_err("synthhid protocol request failed (version %d)", | ||
| 629 | SYNTHHID_INPUT_VERSION); | ||
| 630 | ret = -1; | ||
| 631 | goto Cleanup; | ||
| 632 | } | ||
| 633 | |||
| 634 | input_dev->device_wait_condition = 0; | ||
| 635 | wait_event_timeout(input_dev->dev_info_wait_event, | ||
| 636 | input_dev->device_wait_condition, msecs_to_jiffies(1000)); | ||
| 637 | if (input_dev->device_wait_condition == 0) { | ||
| 638 | ret = -ETIMEDOUT; | ||
| 639 | goto Cleanup; | ||
| 640 | } | ||
| 641 | |||
| 642 | /* | ||
| 643 | * We should have gotten the device attr, hid desc and report | ||
| 644 | * desc at this point | ||
| 645 | */ | ||
| 646 | if (!input_dev->dev_info_status) | ||
| 647 | pr_info("**** input channel up and running!! ****"); | ||
| 648 | else | ||
| 649 | ret = -1; | ||
| 650 | |||
| 651 | Cleanup: | ||
| 652 | put_input_device(device); | ||
| 653 | |||
| 654 | return ret; | ||
| 655 | } | ||
| 656 | |||
| 657 | static int mousevsc_on_device_add(struct hv_device *device, | ||
| 658 | void *additional_info) | ||
| 659 | { | ||
| 660 | int ret = 0; | ||
| 661 | struct mousevsc_dev *input_dev; | ||
| 662 | struct hv_driver *input_drv; | ||
| 663 | struct hv_input_dev_info dev_info; | ||
| 664 | |||
| 665 | input_dev = alloc_input_device(device); | ||
| 666 | |||
| 667 | if (!input_dev) { | ||
| 668 | ret = -1; | ||
| 669 | goto Cleanup; | ||
| 670 | } | ||
| 671 | |||
| 672 | input_dev->init_complete = false; | ||
| 673 | |||
| 674 | /* Open the channel */ | ||
| 675 | ret = vmbus_open(device->channel, | ||
| 676 | INPUTVSC_SEND_RING_BUFFER_SIZE, | ||
| 677 | INPUTVSC_RECV_RING_BUFFER_SIZE, | ||
| 678 | NULL, | ||
| 679 | 0, | ||
| 680 | mousevsc_on_channel_callback, | ||
| 681 | device | ||
| 682 | ); | ||
| 683 | |||
| 684 | if (ret != 0) { | ||
| 685 | pr_err("unable to open channel: %d", ret); | ||
| 686 | free_input_device(input_dev); | ||
| 687 | return -1; | ||
| 688 | } | ||
| 689 | |||
| 690 | pr_info("InputVsc channel open: %d", ret); | ||
| 691 | |||
| 692 | ret = mousevsc_connect_to_vsp(device); | ||
| 693 | |||
| 694 | if (ret != 0) { | ||
| 695 | pr_err("unable to connect channel: %d", ret); | ||
| 696 | |||
| 697 | vmbus_close(device->channel); | ||
| 698 | free_input_device(input_dev); | ||
| 699 | return ret; | ||
| 700 | } | ||
| 701 | |||
| 702 | input_drv = drv_to_hv_drv(input_dev->device->device.driver); | ||
| 703 | |||
| 704 | dev_info.vendor = input_dev->hid_dev_info.vendor; | ||
| 705 | dev_info.product = input_dev->hid_dev_info.product; | ||
| 706 | dev_info.version = input_dev->hid_dev_info.version; | ||
| 707 | strcpy(dev_info.name, "Microsoft Vmbus HID-compliant Mouse"); | ||
| 708 | |||
| 709 | /* Send the device info back up */ | ||
| 710 | deviceinfo_callback(device, &dev_info); | ||
| 711 | |||
| 712 | /* Send the report desc back up */ | ||
| 713 | /* workaround SA-167 */ | ||
| 714 | if (input_dev->report_desc[14] == 0x25) | ||
| 715 | input_dev->report_desc[14] = 0x29; | ||
| 716 | |||
| 717 | reportdesc_callback(device, input_dev->report_desc, | ||
| 718 | input_dev->report_desc_size); | ||
| 719 | |||
| 720 | input_dev->init_complete = true; | ||
| 721 | |||
| 722 | Cleanup: | ||
| 723 | return ret; | ||
| 724 | } | ||
| 725 | |||
| 726 | static int mousevsc_on_device_remove(struct hv_device *device) | ||
| 727 | { | ||
| 728 | struct mousevsc_dev *input_dev; | ||
| 729 | int ret = 0; | ||
| 730 | |||
| 731 | pr_info("disabling input device (%p)...", | ||
| 732 | device->ext); | ||
| 733 | |||
| 734 | input_dev = release_input_device(device); | ||
| 735 | |||
| 736 | |||
| 737 | /* | ||
| 738 | * At this point, all outbound traffic should be disable. We only | ||
| 739 | * allow inbound traffic (responses) to proceed | ||
| 740 | * | ||
| 741 | * so that outstanding requests can be completed. | ||
| 742 | */ | ||
| 743 | while (input_dev->num_outstanding_req) { | ||
| 744 | pr_info("waiting for %d requests to complete...", | ||
| 745 | input_dev->num_outstanding_req); | ||
| 746 | |||
| 747 | udelay(100); | ||
| 748 | } | ||
| 749 | |||
| 750 | pr_info("removing input device (%p)...", device->ext); | ||
| 751 | |||
| 752 | input_dev = final_release_input_device(device); | ||
| 753 | |||
| 754 | pr_info("input device (%p) safe to remove", input_dev); | ||
| 755 | |||
| 756 | /* Close the channel */ | ||
| 757 | vmbus_close(device->channel); | ||
| 758 | |||
| 759 | free_input_device(input_dev); | ||
| 760 | |||
| 761 | return ret; | ||
| 762 | } | ||
| 763 | |||
| 764 | |||
| 765 | /* | ||
| 766 | * Data types | ||
| 767 | */ | ||
| 768 | struct input_device_context { | ||
| 769 | struct hv_device *device_ctx; | ||
| 770 | struct hid_device *hid_device; | ||
| 771 | struct hv_input_dev_info device_info; | ||
| 772 | int connected; | ||
| 773 | }; | ||
| 774 | |||
| 775 | |||
| 776 | static void deviceinfo_callback(struct hv_device *dev, struct hv_input_dev_info *info) | ||
| 777 | { | ||
| 778 | struct input_device_context *input_device_ctx = | ||
| 779 | dev_get_drvdata(&dev->device); | ||
| 780 | |||
| 781 | memcpy(&input_device_ctx->device_info, info, | ||
| 782 | sizeof(struct hv_input_dev_info)); | ||
| 783 | |||
| 784 | DPRINT_INFO(INPUTVSC_DRV, "%s", __func__); | ||
| 785 | } | ||
| 786 | |||
| 787 | static void inputreport_callback(struct hv_device *dev, void *packet, u32 len) | ||
| 788 | { | ||
| 789 | int ret = 0; | ||
| 790 | |||
| 791 | struct input_device_context *input_dev_ctx = | ||
| 792 | dev_get_drvdata(&dev->device); | ||
| 793 | |||
| 794 | ret = hid_input_report(input_dev_ctx->hid_device, | ||
| 795 | HID_INPUT_REPORT, packet, len, 1); | ||
| 796 | |||
| 797 | DPRINT_DBG(INPUTVSC_DRV, "hid_input_report (ret %d)", ret); | ||
| 798 | } | ||
| 799 | |||
| 800 | static int mousevsc_hid_open(struct hid_device *hid) | ||
| 801 | { | ||
| 802 | return 0; | ||
| 803 | } | ||
| 804 | |||
| 805 | static void mousevsc_hid_close(struct hid_device *hid) | ||
| 806 | { | ||
| 807 | } | ||
| 808 | |||
| 809 | static int mousevsc_probe(struct hv_device *dev) | ||
| 810 | { | ||
| 811 | int ret = 0; | ||
| 812 | |||
| 813 | struct input_device_context *input_dev_ctx; | ||
| 814 | |||
| 815 | input_dev_ctx = kmalloc(sizeof(struct input_device_context), | ||
| 816 | GFP_KERNEL); | ||
| 817 | |||
| 818 | dev_set_drvdata(&dev->device, input_dev_ctx); | ||
| 819 | |||
| 820 | /* Call to the vsc driver to add the device */ | ||
| 821 | ret = mousevsc_on_device_add(dev, NULL); | ||
| 822 | |||
| 823 | if (ret != 0) { | ||
| 824 | DPRINT_ERR(INPUTVSC_DRV, "unable to add input vsc device"); | ||
| 825 | |||
| 826 | return -1; | ||
| 827 | } | ||
| 828 | |||
| 829 | return 0; | ||
| 830 | } | ||
| 831 | |||
| 832 | static int mousevsc_remove(struct hv_device *dev) | ||
| 833 | { | ||
| 834 | int ret = 0; | ||
| 835 | |||
| 836 | struct input_device_context *input_dev_ctx; | ||
| 837 | |||
| 838 | input_dev_ctx = kmalloc(sizeof(struct input_device_context), | ||
| 839 | GFP_KERNEL); | ||
| 840 | |||
| 841 | dev_set_drvdata(&dev->device, input_dev_ctx); | ||
| 842 | |||
| 843 | if (input_dev_ctx->connected) { | ||
| 844 | hidinput_disconnect(input_dev_ctx->hid_device); | ||
| 845 | input_dev_ctx->connected = 0; | ||
| 846 | } | ||
| 847 | |||
| 848 | /* | ||
| 849 | * Call to the vsc driver to let it know that the device | ||
| 850 | * is being removed | ||
| 851 | */ | ||
| 852 | ret = mousevsc_on_device_remove(dev); | ||
| 853 | |||
| 854 | if (ret != 0) { | ||
| 855 | DPRINT_ERR(INPUTVSC_DRV, | ||
| 856 | "unable to remove vsc device (ret %d)", ret); | ||
| 857 | } | ||
| 858 | |||
| 859 | kfree(input_dev_ctx); | ||
| 860 | |||
| 861 | return ret; | ||
| 862 | } | ||
| 863 | |||
| 864 | static void reportdesc_callback(struct hv_device *dev, void *packet, u32 len) | ||
| 865 | { | ||
| 866 | struct input_device_context *input_device_ctx = | ||
| 867 | dev_get_drvdata(&dev->device); | ||
| 868 | struct hid_device *hid_dev; | ||
| 869 | |||
| 870 | /* hid_debug = -1; */ | ||
| 871 | hid_dev = kmalloc(sizeof(struct hid_device), GFP_KERNEL); | ||
| 872 | |||
| 873 | if (hid_parse_report(hid_dev, packet, len)) { | ||
| 874 | DPRINT_INFO(INPUTVSC_DRV, "Unable to call hd_parse_report"); | ||
| 875 | return; | ||
| 876 | } | ||
| 877 | |||
| 878 | if (hid_dev) { | ||
| 879 | DPRINT_INFO(INPUTVSC_DRV, "hid_device created"); | ||
| 880 | |||
| 881 | hid_dev->ll_driver->open = mousevsc_hid_open; | ||
| 882 | hid_dev->ll_driver->close = mousevsc_hid_close; | ||
| 883 | |||
| 884 | hid_dev->bus = BUS_VIRTUAL; | ||
| 885 | hid_dev->vendor = input_device_ctx->device_info.vendor; | ||
| 886 | hid_dev->product = input_device_ctx->device_info.product; | ||
| 887 | hid_dev->version = input_device_ctx->device_info.version; | ||
| 888 | hid_dev->dev = dev->device; | ||
| 889 | |||
| 890 | sprintf(hid_dev->name, "%s", | ||
| 891 | input_device_ctx->device_info.name); | ||
| 892 | |||
| 893 | /* | ||
| 894 | * HJ Do we want to call it with a 0 | ||
| 895 | */ | ||
| 896 | if (!hidinput_connect(hid_dev, 0)) { | ||
| 897 | hid_dev->claimed |= HID_CLAIMED_INPUT; | ||
| 898 | |||
| 899 | input_device_ctx->connected = 1; | ||
| 900 | |||
| 901 | DPRINT_INFO(INPUTVSC_DRV, | ||
| 902 | "HID device claimed by input\n"); | ||
| 903 | } | ||
| 904 | |||
| 905 | if (!hid_dev->claimed) { | ||
| 906 | DPRINT_ERR(INPUTVSC_DRV, | ||
| 907 | "HID device not claimed by " | ||
| 908 | "input or hiddev\n"); | ||
| 909 | } | ||
| 910 | |||
| 911 | input_device_ctx->hid_device = hid_dev; | ||
| 912 | } | ||
| 913 | |||
| 914 | kfree(hid_dev); | ||
| 915 | } | ||
| 916 | |||
| 917 | |||
| 918 | static struct hv_driver mousevsc_drv = { | ||
| 919 | .probe = mousevsc_probe, | ||
| 920 | .remove = mousevsc_remove, | ||
| 921 | }; | ||
| 922 | |||
| 923 | static void mousevsc_drv_exit(void) | ||
| 924 | { | ||
| 925 | vmbus_child_driver_unregister(&mousevsc_drv.driver); | ||
| 926 | } | ||
| 927 | |||
| 928 | static int __init mousevsc_init(void) | ||
| 929 | { | ||
| 930 | struct hv_driver *drv = &mousevsc_drv; | ||
| 931 | |||
| 932 | DPRINT_INFO(INPUTVSC_DRV, "Hyper-V Mouse driver initializing."); | ||
| 933 | |||
| 934 | memcpy(&drv->dev_type, &mouse_guid, | ||
| 935 | sizeof(struct hv_guid)); | ||
| 936 | |||
| 937 | drv->driver.name = driver_name; | ||
| 938 | |||
| 939 | /* The driver belongs to vmbus */ | ||
| 940 | vmbus_child_driver_register(&drv->driver); | ||
| 941 | |||
| 942 | return 0; | ||
| 943 | } | ||
| 944 | |||
| 945 | static void __exit mousevsc_exit(void) | ||
| 946 | { | ||
| 947 | mousevsc_drv_exit(); | ||
| 948 | } | ||
| 949 | |||
| 950 | /* | ||
| 951 | * We don't want to automatically load this driver just yet, it's quite | ||
| 952 | * broken. It's safe if you want to load it yourself manually, but | ||
| 953 | * don't inflict it on unsuspecting users, that's just mean. | ||
| 954 | */ | ||
| 955 | #if 0 | ||
| 956 | |||
| 957 | /* | ||
| 958 | * We use a PCI table to determine if we should autoload this driver This is | ||
| 959 | * needed by distro tools to determine if the hyperv drivers should be | ||
| 960 | * installed and/or configured. We don't do anything else with the table, but | ||
| 961 | * it needs to be present. | ||
| 962 | */ | ||
| 963 | const static struct pci_device_id microsoft_hv_pci_table[] = { | ||
| 964 | { PCI_DEVICE(0x1414, 0x5353) }, /* VGA compatible controller */ | ||
| 965 | { 0 } | ||
| 966 | }; | ||
| 967 | MODULE_DEVICE_TABLE(pci, microsoft_hv_pci_table); | ||
| 968 | #endif | ||
| 969 | |||
| 970 | MODULE_LICENSE("GPL"); | ||
| 971 | MODULE_VERSION(HV_DRV_VERSION); | ||
| 972 | module_init(mousevsc_init); | ||
| 973 | module_exit(mousevsc_exit); | ||
| 974 | |||
diff --git a/drivers/staging/hv/hv_timesource.c b/drivers/staging/hv/hv_timesource.c new file mode 100644 index 00000000000..2b0f9aaf912 --- /dev/null +++ b/drivers/staging/hv/hv_timesource.c | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | /* | ||
| 2 | * A clocksource for Linux running on HyperV. | ||
| 3 | * | ||
| 4 | * | ||
| 5 | * Copyright (C) 2010, Novell, Inc. | ||
| 6 | * Author : K. Y. Srinivasan <ksrinivasan@novell.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, but | ||
| 13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
| 15 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
| 16 | * details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License | ||
| 19 | * along with this program; if not, write to the Free Software | ||
| 20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 21 | * | ||
| 22 | */ | ||
| 23 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 24 | |||
| 25 | #include <linux/clocksource.h> | ||
| 26 | #include <linux/init.h> | ||
| 27 | #include <linux/module.h> | ||
| 28 | #include <linux/pci.h> | ||
| 29 | #include <linux/dmi.h> | ||
| 30 | #include <asm/hyperv.h> | ||
| 31 | #include <asm/mshyperv.h> | ||
| 32 | #include <asm/hypervisor.h> | ||
| 33 | |||
| 34 | #define HV_CLOCK_SHIFT 22 | ||
| 35 | |||
| 36 | static cycle_t read_hv_clock(struct clocksource *arg) | ||
| 37 | { | ||
| 38 | cycle_t current_tick; | ||
| 39 | /* | ||
| 40 | * Read the partition counter to get the current tick count. This count | ||
| 41 | * is set to 0 when the partition is created and is incremented in | ||
| 42 | * 100 nanosecond units. | ||
| 43 | */ | ||
| 44 | rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick); | ||
| 45 | return current_tick; | ||
| 46 | } | ||
| 47 | |||
| 48 | static struct clocksource hyperv_cs = { | ||
| 49 | .name = "hyperv_clocksource", | ||
| 50 | .rating = 400, /* use this when running on Hyperv*/ | ||
| 51 | .read = read_hv_clock, | ||
| 52 | .mask = CLOCKSOURCE_MASK(64), | ||
| 53 | /* | ||
| 54 | * The time ref counter in HyperV is in 100ns units. | ||
| 55 | * The definition of mult is: | ||
| 56 | * mult/2^shift = ns/cyc = 100 | ||
| 57 | * mult = (100 << shift) | ||
| 58 | */ | ||
| 59 | .mult = (100 << HV_CLOCK_SHIFT), | ||
| 60 | .shift = HV_CLOCK_SHIFT, | ||
| 61 | }; | ||
| 62 | |||
| 63 | static const struct dmi_system_id __initconst | ||
| 64 | hv_timesource_dmi_table[] __maybe_unused = { | ||
| 65 | { | ||
| 66 | .ident = "Hyper-V", | ||
| 67 | .matches = { | ||
| 68 | DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), | ||
| 69 | DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"), | ||
| 70 | DMI_MATCH(DMI_BOARD_NAME, "Virtual Machine"), | ||
| 71 | }, | ||
| 72 | }, | ||
| 73 | { }, | ||
| 74 | }; | ||
| 75 | MODULE_DEVICE_TABLE(dmi, hv_timesource_dmi_table); | ||
| 76 | |||
| 77 | static const struct pci_device_id __initconst | ||
| 78 | hv_timesource_pci_table[] __maybe_unused = { | ||
| 79 | { PCI_DEVICE(0x1414, 0x5353) }, /* VGA compatible controller */ | ||
| 80 | { 0 } | ||
| 81 | }; | ||
| 82 | MODULE_DEVICE_TABLE(pci, hv_timesource_pci_table); | ||
| 83 | |||
| 84 | |||
| 85 | static int __init init_hv_clocksource(void) | ||
| 86 | { | ||
| 87 | if ((x86_hyper != &x86_hyper_ms_hyperv) || | ||
| 88 | !(ms_hyperv.features & HV_X64_MSR_TIME_REF_COUNT_AVAILABLE)) | ||
| 89 | return -ENODEV; | ||
| 90 | |||
| 91 | if (!dmi_check_system(hv_timesource_dmi_table)) | ||
| 92 | return -ENODEV; | ||
| 93 | |||
| 94 | pr_info("Registering HyperV clock source\n"); | ||
| 95 | return clocksource_register(&hyperv_cs); | ||
| 96 | } | ||
| 97 | |||
| 98 | module_init(init_hv_clocksource); | ||
| 99 | MODULE_DESCRIPTION("HyperV based clocksource"); | ||
| 100 | MODULE_AUTHOR("K. Y. Srinivasan <ksrinivasan@novell.com>"); | ||
| 101 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/staging/hv/hv_util.c b/drivers/staging/hv/hv_util.c new file mode 100644 index 00000000000..c164b54b4cd --- /dev/null +++ b/drivers/staging/hv/hv_util.c | |||
| @@ -0,0 +1,306 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2010, Microsoft Corporation. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms and conditions of the GNU General Public License, | ||
| 6 | * version 2, as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License along with | ||
| 14 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
| 15 | * Place - Suite 330, Boston, MA 02111-1307 USA. | ||
| 16 | * | ||
| 17 | * Authors: | ||
| 18 | * Haiyang Zhang <haiyangz@microsoft.com> | ||
| 19 | * Hank Janssen <hjanssen@microsoft.com> | ||
| 20 | */ | ||
| 21 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 22 | |||
| 23 | #include <linux/kernel.h> | ||
| 24 | #include <linux/init.h> | ||
| 25 | #include <linux/module.h> | ||
| 26 | #include <linux/slab.h> | ||
| 27 | #include <linux/sysctl.h> | ||
| 28 | #include <linux/reboot.h> | ||
| 29 | #include <linux/dmi.h> | ||
| 30 | #include <linux/pci.h> | ||
| 31 | |||
| 32 | #include "hyperv.h" | ||
| 33 | #include "hv_kvp.h" | ||
| 34 | |||
| 35 | static u8 *shut_txf_buf; | ||
| 36 | static u8 *time_txf_buf; | ||
| 37 | static u8 *hbeat_txf_buf; | ||
| 38 | |||
| 39 | static void shutdown_onchannelcallback(void *context) | ||
| 40 | { | ||
| 41 | struct vmbus_channel *channel = context; | ||
| 42 | u32 recvlen; | ||
| 43 | u64 requestid; | ||
| 44 | u8 execute_shutdown = false; | ||
| 45 | |||
| 46 | struct shutdown_msg_data *shutdown_msg; | ||
| 47 | |||
| 48 | struct icmsg_hdr *icmsghdrp; | ||
| 49 | struct icmsg_negotiate *negop = NULL; | ||
| 50 | |||
| 51 | vmbus_recvpacket(channel, shut_txf_buf, | ||
| 52 | PAGE_SIZE, &recvlen, &requestid); | ||
| 53 | |||
| 54 | if (recvlen > 0) { | ||
| 55 | icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[ | ||
| 56 | sizeof(struct vmbuspipe_hdr)]; | ||
| 57 | |||
| 58 | if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { | ||
| 59 | prep_negotiate_resp(icmsghdrp, negop, shut_txf_buf); | ||
| 60 | } else { | ||
| 61 | shutdown_msg = | ||
| 62 | (struct shutdown_msg_data *)&shut_txf_buf[ | ||
| 63 | sizeof(struct vmbuspipe_hdr) + | ||
| 64 | sizeof(struct icmsg_hdr)]; | ||
| 65 | |||
| 66 | switch (shutdown_msg->flags) { | ||
| 67 | case 0: | ||
| 68 | case 1: | ||
| 69 | icmsghdrp->status = HV_S_OK; | ||
| 70 | execute_shutdown = true; | ||
| 71 | |||
| 72 | pr_info("Shutdown request received -" | ||
| 73 | " graceful shutdown initiated\n"); | ||
| 74 | break; | ||
| 75 | default: | ||
| 76 | icmsghdrp->status = HV_E_FAIL; | ||
| 77 | execute_shutdown = false; | ||
| 78 | |||
| 79 | pr_info("Shutdown request received -" | ||
| 80 | " Invalid request\n"); | ||
| 81 | break; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ||
| 86 | | ICMSGHDRFLAG_RESPONSE; | ||
| 87 | |||
| 88 | vmbus_sendpacket(channel, shut_txf_buf, | ||
| 89 | recvlen, requestid, | ||
| 90 | VM_PKT_DATA_INBAND, 0); | ||
| 91 | } | ||
| 92 | |||
| 93 | if (execute_shutdown == true) | ||
| 94 | orderly_poweroff(false); | ||
| 95 | } | ||
| 96 | |||
| 97 | /* | ||
| 98 | * Set guest time to host UTC time. | ||
| 99 | */ | ||
| 100 | static inline void do_adj_guesttime(u64 hosttime) | ||
| 101 | { | ||
| 102 | s64 host_tns; | ||
| 103 | struct timespec host_ts; | ||
| 104 | |||
| 105 | host_tns = (hosttime - WLTIMEDELTA) * 100; | ||
| 106 | host_ts = ns_to_timespec(host_tns); | ||
| 107 | |||
| 108 | do_settimeofday(&host_ts); | ||
| 109 | } | ||
| 110 | |||
| 111 | /* | ||
| 112 | * Synchronize time with host after reboot, restore, etc. | ||
| 113 | * | ||
| 114 | * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM. | ||
| 115 | * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time | ||
| 116 | * message after the timesync channel is opened. Since the hv_utils module is | ||
| 117 | * loaded after hv_vmbus, the first message is usually missed. The other | ||
| 118 | * thing is, systime is automatically set to emulated hardware clock which may | ||
| 119 | * not be UTC time or in the same time zone. So, to override these effects, we | ||
| 120 | * use the first 50 time samples for initial system time setting. | ||
| 121 | */ | ||
| 122 | static inline void adj_guesttime(u64 hosttime, u8 flags) | ||
| 123 | { | ||
| 124 | static s32 scnt = 50; | ||
| 125 | |||
| 126 | if ((flags & ICTIMESYNCFLAG_SYNC) != 0) { | ||
| 127 | do_adj_guesttime(hosttime); | ||
| 128 | return; | ||
| 129 | } | ||
| 130 | |||
| 131 | if ((flags & ICTIMESYNCFLAG_SAMPLE) != 0 && scnt > 0) { | ||
| 132 | scnt--; | ||
| 133 | do_adj_guesttime(hosttime); | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | /* | ||
| 138 | * Time Sync Channel message handler. | ||
| 139 | */ | ||
| 140 | static void timesync_onchannelcallback(void *context) | ||
| 141 | { | ||
| 142 | struct vmbus_channel *channel = context; | ||
| 143 | u32 recvlen; | ||
| 144 | u64 requestid; | ||
| 145 | struct icmsg_hdr *icmsghdrp; | ||
| 146 | struct ictimesync_data *timedatap; | ||
| 147 | |||
| 148 | vmbus_recvpacket(channel, time_txf_buf, | ||
| 149 | PAGE_SIZE, &recvlen, &requestid); | ||
| 150 | |||
| 151 | if (recvlen > 0) { | ||
| 152 | icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[ | ||
| 153 | sizeof(struct vmbuspipe_hdr)]; | ||
| 154 | |||
| 155 | if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { | ||
| 156 | prep_negotiate_resp(icmsghdrp, NULL, time_txf_buf); | ||
| 157 | } else { | ||
| 158 | timedatap = (struct ictimesync_data *)&time_txf_buf[ | ||
| 159 | sizeof(struct vmbuspipe_hdr) + | ||
| 160 | sizeof(struct icmsg_hdr)]; | ||
| 161 | adj_guesttime(timedatap->parenttime, timedatap->flags); | ||
| 162 | } | ||
| 163 | |||
| 164 | icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ||
| 165 | | ICMSGHDRFLAG_RESPONSE; | ||
| 166 | |||
| 167 | vmbus_sendpacket(channel, time_txf_buf, | ||
| 168 | recvlen, requestid, | ||
| 169 | VM_PKT_DATA_INBAND, 0); | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | /* | ||
| 174 | * Heartbeat functionality. | ||
| 175 | * Every two seconds, Hyper-V send us a heartbeat request message. | ||
| 176 | * we respond to this message, and Hyper-V knows we are alive. | ||
| 177 | */ | ||
| 178 | static void heartbeat_onchannelcallback(void *context) | ||
| 179 | { | ||
| 180 | struct vmbus_channel *channel = context; | ||
| 181 | u32 recvlen; | ||
| 182 | u64 requestid; | ||
| 183 | struct icmsg_hdr *icmsghdrp; | ||
| 184 | struct heartbeat_msg_data *heartbeat_msg; | ||
| 185 | |||
| 186 | vmbus_recvpacket(channel, hbeat_txf_buf, | ||
| 187 | PAGE_SIZE, &recvlen, &requestid); | ||
| 188 | |||
| 189 | if (recvlen > 0) { | ||
| 190 | icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[ | ||
| 191 | sizeof(struct vmbuspipe_hdr)]; | ||
| 192 | |||
| 193 | if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { | ||
| 194 | prep_negotiate_resp(icmsghdrp, NULL, hbeat_txf_buf); | ||
| 195 | } else { | ||
| 196 | heartbeat_msg = | ||
| 197 | (struct heartbeat_msg_data *)&hbeat_txf_buf[ | ||
| 198 | sizeof(struct vmbuspipe_hdr) + | ||
| 199 | sizeof(struct icmsg_hdr)]; | ||
| 200 | |||
| 201 | heartbeat_msg->seq_num += 1; | ||
| 202 | } | ||
| 203 | |||
| 204 | icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ||
| 205 | | ICMSGHDRFLAG_RESPONSE; | ||
| 206 | |||
| 207 | vmbus_sendpacket(channel, hbeat_txf_buf, | ||
| 208 | recvlen, requestid, | ||
| 209 | VM_PKT_DATA_INBAND, 0); | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | static const struct pci_device_id __initconst | ||
| 214 | hv_utils_pci_table[] __maybe_unused = { | ||
| 215 | { PCI_DEVICE(0x1414, 0x5353) }, /* Hyper-V emulated VGA controller */ | ||
| 216 | { 0 } | ||
| 217 | }; | ||
| 218 | MODULE_DEVICE_TABLE(pci, hv_utils_pci_table); | ||
| 219 | |||
| 220 | |||
| 221 | static const struct dmi_system_id __initconst | ||
| 222 | hv_utils_dmi_table[] __maybe_unused = { | ||
| 223 | { | ||
| 224 | .ident = "Hyper-V", | ||
| 225 | .matches = { | ||
| 226 | DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), | ||
| 227 | DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"), | ||
| 228 | DMI_MATCH(DMI_BOARD_NAME, "Virtual Machine"), | ||
| 229 | }, | ||
| 230 | }, | ||
| 231 | { }, | ||
| 232 | }; | ||
| 233 | MODULE_DEVICE_TABLE(dmi, hv_utils_dmi_table); | ||
| 234 | |||
| 235 | |||
| 236 | static int __init init_hyperv_utils(void) | ||
| 237 | { | ||
| 238 | pr_info("Registering HyperV Utility Driver\n"); | ||
| 239 | |||
| 240 | if (hv_kvp_init()) | ||
| 241 | return -ENODEV; | ||
| 242 | |||
| 243 | |||
| 244 | if (!dmi_check_system(hv_utils_dmi_table)) | ||
| 245 | return -ENODEV; | ||
| 246 | |||
| 247 | shut_txf_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
| 248 | time_txf_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
| 249 | hbeat_txf_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
| 250 | |||
| 251 | if (!shut_txf_buf || !time_txf_buf || !hbeat_txf_buf) { | ||
| 252 | pr_info("Unable to allocate memory for receive buffer\n"); | ||
| 253 | kfree(shut_txf_buf); | ||
| 254 | kfree(time_txf_buf); | ||
| 255 | kfree(hbeat_txf_buf); | ||
| 256 | return -ENOMEM; | ||
| 257 | } | ||
| 258 | |||
| 259 | hv_cb_utils[HV_SHUTDOWN_MSG].callback = &shutdown_onchannelcallback; | ||
| 260 | |||
| 261 | hv_cb_utils[HV_TIMESYNC_MSG].callback = ×ync_onchannelcallback; | ||
| 262 | |||
| 263 | hv_cb_utils[HV_HEARTBEAT_MSG].callback = &heartbeat_onchannelcallback; | ||
| 264 | |||
| 265 | hv_cb_utils[HV_KVP_MSG].callback = &hv_kvp_onchannelcallback; | ||
| 266 | |||
| 267 | return 0; | ||
| 268 | } | ||
| 269 | |||
| 270 | static void exit_hyperv_utils(void) | ||
| 271 | { | ||
| 272 | pr_info("De-Registered HyperV Utility Driver\n"); | ||
| 273 | |||
| 274 | if (hv_cb_utils[HV_SHUTDOWN_MSG].channel != NULL) | ||
| 275 | hv_cb_utils[HV_SHUTDOWN_MSG].channel->onchannel_callback = | ||
| 276 | &chn_cb_negotiate; | ||
| 277 | hv_cb_utils[HV_SHUTDOWN_MSG].callback = NULL; | ||
| 278 | |||
| 279 | if (hv_cb_utils[HV_TIMESYNC_MSG].channel != NULL) | ||
| 280 | hv_cb_utils[HV_TIMESYNC_MSG].channel->onchannel_callback = | ||
| 281 | &chn_cb_negotiate; | ||
| 282 | hv_cb_utils[HV_TIMESYNC_MSG].callback = NULL; | ||
| 283 | |||
| 284 | if (hv_cb_utils[HV_HEARTBEAT_MSG].channel != NULL) | ||
| 285 | hv_cb_utils[HV_HEARTBEAT_MSG].channel->onchannel_callback = | ||
| 286 | &chn_cb_negotiate; | ||
| 287 | hv_cb_utils[HV_HEARTBEAT_MSG].callback = NULL; | ||
| 288 | |||
| 289 | if (hv_cb_utils[HV_KVP_MSG].channel != NULL) | ||
| 290 | hv_cb_utils[HV_KVP_MSG].channel->onchannel_callback = | ||
| 291 | &chn_cb_negotiate; | ||
| 292 | hv_cb_utils[HV_KVP_MSG].callback = NULL; | ||
| 293 | |||
| 294 | hv_kvp_deinit(); | ||
| 295 | |||
| 296 | kfree(shut_txf_buf); | ||
| 297 | kfree(time_txf_buf); | ||
| 298 | kfree(hbeat_txf_buf); | ||
| 299 | } | ||
| 300 | |||
| 301 | module_init(init_hyperv_utils); | ||
| 302 | module_exit(exit_hyperv_utils); | ||
| 303 | |||
| 304 | MODULE_DESCRIPTION("Hyper-V Utilities"); | ||
| 305 | MODULE_VERSION(HV_DRV_VERSION); | ||
| 306 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/staging/hv/hyperv.h b/drivers/staging/hv/hyperv.h new file mode 100644 index 00000000000..1747a2404f6 --- /dev/null +++ b/drivers/staging/hv/hyperv.h | |||
| @@ -0,0 +1,948 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * Copyright (c) 2011, Microsoft Corporation. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms and conditions of the GNU General Public License, | ||
| 7 | * version 2, as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | * more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License along with | ||
| 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
| 16 | * Place - Suite 330, Boston, MA 02111-1307 USA. | ||
| 17 | * | ||
| 18 | * Authors: | ||
| 19 | * Haiyang Zhang <haiyangz@microsoft.com> | ||
| 20 | * Hank Janssen <hjanssen@microsoft.com> | ||
| 21 | * K. Y. Srinivasan <kys@microsoft.com> | ||
| 22 | * | ||
| 23 | */ | ||
| 24 | |||
| 25 | #ifndef _HYPERV_H | ||
| 26 | #define _HYPERV_H | ||
| 27 | |||
| 28 | #include <linux/scatterlist.h> | ||
| 29 | #include <linux/list.h> | ||
| 30 | #include <linux/timer.h> | ||
| 31 | #include <linux/workqueue.h> | ||
| 32 | #include <linux/completion.h> | ||
| 33 | #include <linux/device.h> | ||
| 34 | |||
| 35 | |||
| 36 | #include <asm/hyperv.h> | ||
| 37 | |||
| 38 | struct hv_guid { | ||
| 39 | unsigned char data[16]; | ||
| 40 | }; | ||
| 41 | |||
| 42 | #define MAX_PAGE_BUFFER_COUNT 16 | ||
| 43 | #define MAX_MULTIPAGE_BUFFER_COUNT 32 /* 128K */ | ||
| 44 | |||
| 45 | #pragma pack(push, 1) | ||
| 46 | |||
| 47 | /* Single-page buffer */ | ||
| 48 | struct hv_page_buffer { | ||
| 49 | u32 len; | ||
| 50 | u32 offset; | ||
| 51 | u64 pfn; | ||
| 52 | }; | ||
| 53 | |||
| 54 | /* Multiple-page buffer */ | ||
| 55 | struct hv_multipage_buffer { | ||
| 56 | /* Length and Offset determines the # of pfns in the array */ | ||
| 57 | u32 len; | ||
| 58 | u32 offset; | ||
| 59 | u64 pfn_array[MAX_MULTIPAGE_BUFFER_COUNT]; | ||
| 60 | }; | ||
| 61 | |||
| 62 | /* 0x18 includes the proprietary packet header */ | ||
| 63 | #define MAX_PAGE_BUFFER_PACKET (0x18 + \ | ||
| 64 | (sizeof(struct hv_page_buffer) * \ | ||
| 65 | MAX_PAGE_BUFFER_COUNT)) | ||
| 66 | #define MAX_MULTIPAGE_BUFFER_PACKET (0x18 + \ | ||
| 67 | sizeof(struct hv_multipage_buffer)) | ||
| 68 | |||
| 69 | |||
| 70 | #pragma pack(pop) | ||
| 71 | |||
| 72 | struct hv_ring_buffer { | ||
| 73 | /* Offset in bytes from the start of ring data below */ | ||
| 74 | u32 write_index; | ||
| 75 | |||
| 76 | /* Offset in bytes from the start of ring data below */ | ||
| 77 | u32 read_index; | ||
| 78 | |||
| 79 | u32 interrupt_mask; | ||
| 80 | |||
| 81 | /* Pad it to PAGE_SIZE so that data starts on page boundary */ | ||
| 82 | u8 reserved[4084]; | ||
| 83 | |||
| 84 | /* NOTE: | ||
| 85 | * The interrupt_mask field is used only for channels but since our | ||
| 86 | * vmbus connection also uses this data structure and its data starts | ||
| 87 | * here, we commented out this field. | ||
| 88 | */ | ||
| 89 | |||
| 90 | /* | ||
| 91 | * Ring data starts here + RingDataStartOffset | ||
| 92 | * !!! DO NOT place any fields below this !!! | ||
| 93 | */ | ||
| 94 | u8 buffer[0]; | ||
| 95 | } __packed; | ||
| 96 | |||
| 97 | struct hv_ring_buffer_info { | ||
| 98 | struct hv_ring_buffer *ring_buffer; | ||
| 99 | u32 ring_size; /* Include the shared header */ | ||
| 100 | spinlock_t ring_lock; | ||
| 101 | |||
| 102 | u32 ring_datasize; /* < ring_size */ | ||
| 103 | u32 ring_data_startoffset; | ||
| 104 | }; | ||
| 105 | |||
| 106 | struct hv_ring_buffer_debug_info { | ||
| 107 | u32 current_interrupt_mask; | ||
| 108 | u32 current_read_index; | ||
| 109 | u32 current_write_index; | ||
| 110 | u32 bytes_avail_toread; | ||
| 111 | u32 bytes_avail_towrite; | ||
| 112 | }; | ||
| 113 | |||
| 114 | /* | ||
| 115 | * We use the same version numbering for all Hyper-V modules. | ||
| 116 | * | ||
| 117 | * Definition of versioning is as follows; | ||
| 118 | * | ||
| 119 | * Major Number Changes for these scenarios; | ||
| 120 | * 1. When a new version of Windows Hyper-V | ||
| 121 | * is released. | ||
| 122 | * 2. A Major change has occurred in the | ||
| 123 | * Linux IC's. | ||
| 124 | * (For example the merge for the first time | ||
| 125 | * into the kernel) Every time the Major Number | ||
| 126 | * changes, the Revision number is reset to 0. | ||
| 127 | * Minor Number Changes when new functionality is added | ||
| 128 | * to the Linux IC's that is not a bug fix. | ||
| 129 | * | ||
| 130 | * 3.1 - Added completed hv_utils driver. Shutdown/Heartbeat/Timesync | ||
| 131 | */ | ||
| 132 | #define HV_DRV_VERSION "3.1" | ||
| 133 | |||
| 134 | |||
| 135 | /* | ||
| 136 | * A revision number of vmbus that is used for ensuring both ends on a | ||
| 137 | * partition are using compatible versions. | ||
| 138 | */ | ||
| 139 | #define VMBUS_REVISION_NUMBER 13 | ||
| 140 | |||
| 141 | /* Make maximum size of pipe payload of 16K */ | ||
| 142 | #define MAX_PIPE_DATA_PAYLOAD (sizeof(u8) * 16384) | ||
| 143 | |||
| 144 | /* Define PipeMode values. */ | ||
| 145 | #define VMBUS_PIPE_TYPE_BYTE 0x00000000 | ||
| 146 | #define VMBUS_PIPE_TYPE_MESSAGE 0x00000004 | ||
| 147 | |||
| 148 | /* The size of the user defined data buffer for non-pipe offers. */ | ||
| 149 | #define MAX_USER_DEFINED_BYTES 120 | ||
| 150 | |||
| 151 | /* The size of the user defined data buffer for pipe offers. */ | ||
| 152 | #define MAX_PIPE_USER_DEFINED_BYTES 116 | ||
| 153 | |||
| 154 | /* | ||
| 155 | * At the center of the Channel Management library is the Channel Offer. This | ||
| 156 | * struct contains the fundamental information about an offer. | ||
| 157 | */ | ||
| 158 | struct vmbus_channel_offer { | ||
| 159 | struct hv_guid if_type; | ||
| 160 | struct hv_guid if_instance; | ||
| 161 | u64 int_latency; /* in 100ns units */ | ||
| 162 | u32 if_revision; | ||
| 163 | u32 server_ctx_size; /* in bytes */ | ||
| 164 | u16 chn_flags; | ||
| 165 | u16 mmio_megabytes; /* in bytes * 1024 * 1024 */ | ||
| 166 | |||
| 167 | union { | ||
| 168 | /* Non-pipes: The user has MAX_USER_DEFINED_BYTES bytes. */ | ||
| 169 | struct { | ||
| 170 | unsigned char user_def[MAX_USER_DEFINED_BYTES]; | ||
| 171 | } std; | ||
| 172 | |||
| 173 | /* | ||
| 174 | * Pipes: | ||
| 175 | * The following sructure is an integrated pipe protocol, which | ||
| 176 | * is implemented on top of standard user-defined data. Pipe | ||
| 177 | * clients have MAX_PIPE_USER_DEFINED_BYTES left for their own | ||
| 178 | * use. | ||
| 179 | */ | ||
| 180 | struct { | ||
| 181 | u32 pipe_mode; | ||
| 182 | unsigned char user_def[MAX_PIPE_USER_DEFINED_BYTES]; | ||
| 183 | } pipe; | ||
| 184 | } u; | ||
| 185 | u32 padding; | ||
| 186 | } __packed; | ||
| 187 | |||
| 188 | /* Server Flags */ | ||
| 189 | #define VMBUS_CHANNEL_ENUMERATE_DEVICE_INTERFACE 1 | ||
| 190 | #define VMBUS_CHANNEL_SERVER_SUPPORTS_TRANSFER_PAGES 2 | ||
| 191 | #define VMBUS_CHANNEL_SERVER_SUPPORTS_GPADLS 4 | ||
| 192 | #define VMBUS_CHANNEL_NAMED_PIPE_MODE 0x10 | ||
| 193 | #define VMBUS_CHANNEL_LOOPBACK_OFFER 0x100 | ||
| 194 | #define VMBUS_CHANNEL_PARENT_OFFER 0x200 | ||
| 195 | #define VMBUS_CHANNEL_REQUEST_MONITORED_NOTIFICATION 0x400 | ||
| 196 | |||
| 197 | struct vmpacket_descriptor { | ||
| 198 | u16 type; | ||
| 199 | u16 offset8; | ||
| 200 | u16 len8; | ||
| 201 | u16 flags; | ||
| 202 | u64 trans_id; | ||
| 203 | } __packed; | ||
| 204 | |||
| 205 | struct vmpacket_header { | ||
| 206 | u32 prev_pkt_start_offset; | ||
| 207 | struct vmpacket_descriptor descriptor; | ||
| 208 | } __packed; | ||
| 209 | |||
| 210 | struct vmtransfer_page_range { | ||
| 211 | u32 byte_count; | ||
| 212 | u32 byte_offset; | ||
| 213 | } __packed; | ||
| 214 | |||
| 215 | struct vmtransfer_page_packet_header { | ||
| 216 | struct vmpacket_descriptor d; | ||
| 217 | u16 xfer_pageset_id; | ||
| 218 | bool sender_owns_set; | ||
| 219 | u8 reserved; | ||
| 220 | u32 range_cnt; | ||
| 221 | struct vmtransfer_page_range ranges[1]; | ||
| 222 | } __packed; | ||
| 223 | |||
| 224 | struct vmgpadl_packet_header { | ||
| 225 | struct vmpacket_descriptor d; | ||
| 226 | u32 gpadl; | ||
| 227 | u32 reserved; | ||
| 228 | } __packed; | ||
| 229 | |||
| 230 | struct vmadd_remove_transfer_page_set { | ||
| 231 | struct vmpacket_descriptor d; | ||
| 232 | u32 gpadl; | ||
| 233 | u16 xfer_pageset_id; | ||
| 234 | u16 reserved; | ||
| 235 | } __packed; | ||
| 236 | |||
| 237 | /* | ||
| 238 | * This structure defines a range in guest physical space that can be made to | ||
| 239 | * look virtually contiguous. | ||
| 240 | */ | ||
| 241 | struct gpa_range { | ||
| 242 | u32 byte_count; | ||
| 243 | u32 byte_offset; | ||
| 244 | u64 pfn_array[0]; | ||
| 245 | }; | ||
| 246 | |||
| 247 | /* | ||
| 248 | * This is the format for an Establish Gpadl packet, which contains a handle by | ||
| 249 | * which this GPADL will be known and a set of GPA ranges associated with it. | ||
| 250 | * This can be converted to a MDL by the guest OS. If there are multiple GPA | ||
| 251 | * ranges, then the resulting MDL will be "chained," representing multiple VA | ||
| 252 | * ranges. | ||
| 253 | */ | ||
| 254 | struct vmestablish_gpadl { | ||
| 255 | struct vmpacket_descriptor d; | ||
| 256 | u32 gpadl; | ||
| 257 | u32 range_cnt; | ||
| 258 | struct gpa_range range[1]; | ||
| 259 | } __packed; | ||
| 260 | |||
| 261 | /* | ||
| 262 | * This is the format for a Teardown Gpadl packet, which indicates that the | ||
| 263 | * GPADL handle in the Establish Gpadl packet will never be referenced again. | ||
| 264 | */ | ||
| 265 | struct vmteardown_gpadl { | ||
| 266 | struct vmpacket_descriptor d; | ||
| 267 | u32 gpadl; | ||
| 268 | u32 reserved; /* for alignment to a 8-byte boundary */ | ||
| 269 | } __packed; | ||
| 270 | |||
| 271 | /* | ||
| 272 | * This is the format for a GPA-Direct packet, which contains a set of GPA | ||
| 273 | * ranges, in addition to commands and/or data. | ||
| 274 | */ | ||
| 275 | struct vmdata_gpa_direct { | ||
| 276 | struct vmpacket_descriptor d; | ||
| 277 | u32 reserved; | ||
| 278 | u32 range_cnt; | ||
| 279 | struct gpa_range range[1]; | ||
| 280 | } __packed; | ||
| 281 | |||
| 282 | /* This is the format for a Additional Data Packet. */ | ||
| 283 | struct vmadditional_data { | ||
| 284 | struct vmpacket_descriptor d; | ||
| 285 | u64 total_bytes; | ||
| 286 | u32 offset; | ||
| 287 | u32 byte_cnt; | ||
| 288 | unsigned char data[1]; | ||
| 289 | } __packed; | ||
| 290 | |||
| 291 | union vmpacket_largest_possible_header { | ||
| 292 | struct vmpacket_descriptor simple_hdr; | ||
| 293 | struct vmtransfer_page_packet_header xfer_page_hdr; | ||
| 294 | struct vmgpadl_packet_header gpadl_hdr; | ||
| 295 | struct vmadd_remove_transfer_page_set add_rm_xfer_page_hdr; | ||
| 296 | struct vmestablish_gpadl establish_gpadl_hdr; | ||
| 297 | struct vmteardown_gpadl teardown_gpadl_hdr; | ||
| 298 | struct vmdata_gpa_direct data_gpa_direct_hdr; | ||
| 299 | }; | ||
| 300 | |||
| 301 | #define VMPACKET_DATA_START_ADDRESS(__packet) \ | ||
| 302 | (void *)(((unsigned char *)__packet) + \ | ||
| 303 | ((struct vmpacket_descriptor)__packet)->offset8 * 8) | ||
| 304 | |||
| 305 | #define VMPACKET_DATA_LENGTH(__packet) \ | ||
| 306 | ((((struct vmpacket_descriptor)__packet)->len8 - \ | ||
| 307 | ((struct vmpacket_descriptor)__packet)->offset8) * 8) | ||
| 308 | |||
| 309 | #define VMPACKET_TRANSFER_MODE(__packet) \ | ||
| 310 | (((struct IMPACT)__packet)->type) | ||
| 311 | |||
| 312 | enum vmbus_packet_type { | ||
| 313 | VM_PKT_INVALID = 0x0, | ||
| 314 | VM_PKT_SYNCH = 0x1, | ||
| 315 | VM_PKT_ADD_XFER_PAGESET = 0x2, | ||
| 316 | VM_PKT_RM_XFER_PAGESET = 0x3, | ||
| 317 | VM_PKT_ESTABLISH_GPADL = 0x4, | ||
| 318 | VM_PKT_TEARDOWN_GPADL = 0x5, | ||
| 319 | VM_PKT_DATA_INBAND = 0x6, | ||
| 320 | VM_PKT_DATA_USING_XFER_PAGES = 0x7, | ||
| 321 | VM_PKT_DATA_USING_GPADL = 0x8, | ||
| 322 | VM_PKT_DATA_USING_GPA_DIRECT = 0x9, | ||
| 323 | VM_PKT_CANCEL_REQUEST = 0xa, | ||
| 324 | VM_PKT_COMP = 0xb, | ||
| 325 | VM_PKT_DATA_USING_ADDITIONAL_PKT = 0xc, | ||
| 326 | VM_PKT_ADDITIONAL_DATA = 0xd | ||
| 327 | }; | ||
| 328 | |||
| 329 | #define VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED 1 | ||
| 330 | |||
| 331 | |||
| 332 | /* Version 1 messages */ | ||
| 333 | enum vmbus_channel_message_type { | ||
| 334 | CHANNELMSG_INVALID = 0, | ||
| 335 | CHANNELMSG_OFFERCHANNEL = 1, | ||
| 336 | CHANNELMSG_RESCIND_CHANNELOFFER = 2, | ||
| 337 | CHANNELMSG_REQUESTOFFERS = 3, | ||
| 338 | CHANNELMSG_ALLOFFERS_DELIVERED = 4, | ||
| 339 | CHANNELMSG_OPENCHANNEL = 5, | ||
| 340 | CHANNELMSG_OPENCHANNEL_RESULT = 6, | ||
| 341 | CHANNELMSG_CLOSECHANNEL = 7, | ||
| 342 | CHANNELMSG_GPADL_HEADER = 8, | ||
| 343 | CHANNELMSG_GPADL_BODY = 9, | ||
| 344 | CHANNELMSG_GPADL_CREATED = 10, | ||
| 345 | CHANNELMSG_GPADL_TEARDOWN = 11, | ||
| 346 | CHANNELMSG_GPADL_TORNDOWN = 12, | ||
| 347 | CHANNELMSG_RELID_RELEASED = 13, | ||
| 348 | CHANNELMSG_INITIATE_CONTACT = 14, | ||
| 349 | CHANNELMSG_VERSION_RESPONSE = 15, | ||
| 350 | CHANNELMSG_UNLOAD = 16, | ||
| 351 | #ifdef VMBUS_FEATURE_PARENT_OR_PEER_MEMORY_MAPPED_INTO_A_CHILD | ||
| 352 | CHANNELMSG_VIEWRANGE_ADD = 17, | ||
| 353 | CHANNELMSG_VIEWRANGE_REMOVE = 18, | ||
| 354 | #endif | ||
| 355 | CHANNELMSG_COUNT | ||
| 356 | }; | ||
| 357 | |||
| 358 | struct vmbus_channel_message_header { | ||
| 359 | enum vmbus_channel_message_type msgtype; | ||
| 360 | u32 padding; | ||
| 361 | } __packed; | ||
| 362 | |||
| 363 | /* Query VMBus Version parameters */ | ||
| 364 | struct vmbus_channel_query_vmbus_version { | ||
| 365 | struct vmbus_channel_message_header header; | ||
| 366 | u32 version; | ||
| 367 | } __packed; | ||
| 368 | |||
| 369 | /* VMBus Version Supported parameters */ | ||
| 370 | struct vmbus_channel_version_supported { | ||
| 371 | struct vmbus_channel_message_header header; | ||
| 372 | bool version_supported; | ||
| 373 | } __packed; | ||
| 374 | |||
| 375 | /* Offer Channel parameters */ | ||
| 376 | struct vmbus_channel_offer_channel { | ||
| 377 | struct vmbus_channel_message_header header; | ||
| 378 | struct vmbus_channel_offer offer; | ||
| 379 | u32 child_relid; | ||
| 380 | u8 monitorid; | ||
| 381 | bool monitor_allocated; | ||
| 382 | } __packed; | ||
| 383 | |||
| 384 | /* Rescind Offer parameters */ | ||
| 385 | struct vmbus_channel_rescind_offer { | ||
| 386 | struct vmbus_channel_message_header header; | ||
| 387 | u32 child_relid; | ||
| 388 | } __packed; | ||
| 389 | |||
| 390 | /* | ||
| 391 | * Request Offer -- no parameters, SynIC message contains the partition ID | ||
| 392 | * Set Snoop -- no parameters, SynIC message contains the partition ID | ||
| 393 | * Clear Snoop -- no parameters, SynIC message contains the partition ID | ||
| 394 | * All Offers Delivered -- no parameters, SynIC message contains the partition | ||
| 395 | * ID | ||
| 396 | * Flush Client -- no parameters, SynIC message contains the partition ID | ||
| 397 | */ | ||
| 398 | |||
| 399 | /* Open Channel parameters */ | ||
| 400 | struct vmbus_channel_open_channel { | ||
| 401 | struct vmbus_channel_message_header header; | ||
| 402 | |||
| 403 | /* Identifies the specific VMBus channel that is being opened. */ | ||
| 404 | u32 child_relid; | ||
| 405 | |||
| 406 | /* ID making a particular open request at a channel offer unique. */ | ||
| 407 | u32 openid; | ||
| 408 | |||
| 409 | /* GPADL for the channel's ring buffer. */ | ||
| 410 | u32 ringbuffer_gpadlhandle; | ||
| 411 | |||
| 412 | /* GPADL for the channel's server context save area. */ | ||
| 413 | u32 server_contextarea_gpadlhandle; | ||
| 414 | |||
| 415 | /* | ||
| 416 | * The upstream ring buffer begins at offset zero in the memory | ||
| 417 | * described by RingBufferGpadlHandle. The downstream ring buffer | ||
| 418 | * follows it at this offset (in pages). | ||
| 419 | */ | ||
| 420 | u32 downstream_ringbuffer_pageoffset; | ||
| 421 | |||
| 422 | /* User-specific data to be passed along to the server endpoint. */ | ||
| 423 | unsigned char userdata[MAX_USER_DEFINED_BYTES]; | ||
| 424 | } __packed; | ||
| 425 | |||
| 426 | /* Open Channel Result parameters */ | ||
| 427 | struct vmbus_channel_open_result { | ||
| 428 | struct vmbus_channel_message_header header; | ||
| 429 | u32 child_relid; | ||
| 430 | u32 openid; | ||
| 431 | u32 status; | ||
| 432 | } __packed; | ||
| 433 | |||
| 434 | /* Close channel parameters; */ | ||
| 435 | struct vmbus_channel_close_channel { | ||
| 436 | struct vmbus_channel_message_header header; | ||
| 437 | u32 child_relid; | ||
| 438 | } __packed; | ||
| 439 | |||
| 440 | /* Channel Message GPADL */ | ||
| 441 | #define GPADL_TYPE_RING_BUFFER 1 | ||
| 442 | #define GPADL_TYPE_SERVER_SAVE_AREA 2 | ||
| 443 | #define GPADL_TYPE_TRANSACTION 8 | ||
| 444 | |||
| 445 | /* | ||
| 446 | * The number of PFNs in a GPADL message is defined by the number of | ||
| 447 | * pages that would be spanned by ByteCount and ByteOffset. If the | ||
| 448 | * implied number of PFNs won't fit in this packet, there will be a | ||
| 449 | * follow-up packet that contains more. | ||
| 450 | */ | ||
| 451 | struct vmbus_channel_gpadl_header { | ||
| 452 | struct vmbus_channel_message_header header; | ||
| 453 | u32 child_relid; | ||
| 454 | u32 gpadl; | ||
| 455 | u16 range_buflen; | ||
| 456 | u16 rangecount; | ||
| 457 | struct gpa_range range[0]; | ||
| 458 | } __packed; | ||
| 459 | |||
| 460 | /* This is the followup packet that contains more PFNs. */ | ||
| 461 | struct vmbus_channel_gpadl_body { | ||
| 462 | struct vmbus_channel_message_header header; | ||
| 463 | u32 msgnumber; | ||
| 464 | u32 gpadl; | ||
| 465 | u64 pfn[0]; | ||
| 466 | } __packed; | ||
| 467 | |||
| 468 | struct vmbus_channel_gpadl_created { | ||
| 469 | struct vmbus_channel_message_header header; | ||
| 470 | u32 child_relid; | ||
| 471 | u32 gpadl; | ||
| 472 | u32 creation_status; | ||
| 473 | } __packed; | ||
| 474 | |||
| 475 | struct vmbus_channel_gpadl_teardown { | ||
| 476 | struct vmbus_channel_message_header header; | ||
| 477 | u32 child_relid; | ||
| 478 | u32 gpadl; | ||
| 479 | } __packed; | ||
| 480 | |||
| 481 | struct vmbus_channel_gpadl_torndown { | ||
| 482 | struct vmbus_channel_message_header header; | ||
| 483 | u32 gpadl; | ||
| 484 | } __packed; | ||
| 485 | |||
| 486 | #ifdef VMBUS_FEATURE_PARENT_OR_PEER_MEMORY_MAPPED_INTO_A_CHILD | ||
| 487 | struct vmbus_channel_view_range_add { | ||
| 488 | struct vmbus_channel_message_header header; | ||
| 489 | PHYSICAL_ADDRESS viewrange_base; | ||
| 490 | u64 viewrange_length; | ||
| 491 | u32 child_relid; | ||
| 492 | } __packed; | ||
| 493 | |||
| 494 | struct vmbus_channel_view_range_remove { | ||
| 495 | struct vmbus_channel_message_header header; | ||
| 496 | PHYSICAL_ADDRESS viewrange_base; | ||
| 497 | u32 child_relid; | ||
| 498 | } __packed; | ||
| 499 | #endif | ||
| 500 | |||
| 501 | struct vmbus_channel_relid_released { | ||
| 502 | struct vmbus_channel_message_header header; | ||
| 503 | u32 child_relid; | ||
| 504 | } __packed; | ||
| 505 | |||
| 506 | struct vmbus_channel_initiate_contact { | ||
| 507 | struct vmbus_channel_message_header header; | ||
| 508 | u32 vmbus_version_requested; | ||
| 509 | u32 padding2; | ||
| 510 | u64 interrupt_page; | ||
| 511 | u64 monitor_page1; | ||
| 512 | u64 monitor_page2; | ||
| 513 | } __packed; | ||
| 514 | |||
| 515 | struct vmbus_channel_version_response { | ||
| 516 | struct vmbus_channel_message_header header; | ||
| 517 | bool version_supported; | ||
| 518 | } __packed; | ||
| 519 | |||
| 520 | enum vmbus_channel_state { | ||
| 521 | CHANNEL_OFFER_STATE, | ||
| 522 | CHANNEL_OPENING_STATE, | ||
| 523 | CHANNEL_OPEN_STATE, | ||
| 524 | }; | ||
| 525 | |||
| 526 | struct vmbus_channel_debug_info { | ||
| 527 | u32 relid; | ||
| 528 | enum vmbus_channel_state state; | ||
| 529 | struct hv_guid interfacetype; | ||
| 530 | struct hv_guid interface_instance; | ||
| 531 | u32 monitorid; | ||
| 532 | u32 servermonitor_pending; | ||
| 533 | u32 servermonitor_latency; | ||
| 534 | u32 servermonitor_connectionid; | ||
| 535 | u32 clientmonitor_pending; | ||
| 536 | u32 clientmonitor_latency; | ||
| 537 | u32 clientmonitor_connectionid; | ||
| 538 | |||
| 539 | struct hv_ring_buffer_debug_info inbound; | ||
| 540 | struct hv_ring_buffer_debug_info outbound; | ||
| 541 | }; | ||
| 542 | |||
| 543 | /* | ||
| 544 | * Represents each channel msg on the vmbus connection This is a | ||
| 545 | * variable-size data structure depending on the msg type itself | ||
| 546 | */ | ||
| 547 | struct vmbus_channel_msginfo { | ||
| 548 | /* Bookkeeping stuff */ | ||
| 549 | struct list_head msglistentry; | ||
| 550 | |||
| 551 | /* So far, this is only used to handle gpadl body message */ | ||
| 552 | struct list_head submsglist; | ||
| 553 | |||
| 554 | /* Synchronize the request/response if needed */ | ||
| 555 | struct completion waitevent; | ||
| 556 | union { | ||
| 557 | struct vmbus_channel_version_supported version_supported; | ||
| 558 | struct vmbus_channel_open_result open_result; | ||
| 559 | struct vmbus_channel_gpadl_torndown gpadl_torndown; | ||
| 560 | struct vmbus_channel_gpadl_created gpadl_created; | ||
| 561 | struct vmbus_channel_version_response version_response; | ||
| 562 | } response; | ||
| 563 | |||
| 564 | u32 msgsize; | ||
| 565 | /* | ||
| 566 | * The channel message that goes out on the "wire". | ||
| 567 | * It will contain at minimum the VMBUS_CHANNEL_MESSAGE_HEADER header | ||
| 568 | */ | ||
| 569 | unsigned char msg[0]; | ||
| 570 | }; | ||
| 571 | |||
| 572 | struct vmbus_close_msg { | ||
| 573 | struct vmbus_channel_msginfo info; | ||
| 574 | struct vmbus_channel_close_channel msg; | ||
| 575 | }; | ||
| 576 | |||
| 577 | struct vmbus_channel { | ||
| 578 | struct list_head listentry; | ||
| 579 | |||
| 580 | struct hv_device *device_obj; | ||
| 581 | |||
| 582 | struct work_struct work; | ||
| 583 | |||
| 584 | enum vmbus_channel_state state; | ||
| 585 | /* | ||
| 586 | * For util channels, stash the | ||
| 587 | * the service index for easy access. | ||
| 588 | */ | ||
| 589 | s8 util_index; | ||
| 590 | |||
| 591 | struct vmbus_channel_offer_channel offermsg; | ||
| 592 | /* | ||
| 593 | * These are based on the OfferMsg.MonitorId. | ||
| 594 | * Save it here for easy access. | ||
| 595 | */ | ||
| 596 | u8 monitor_grp; | ||
| 597 | u8 monitor_bit; | ||
| 598 | |||
| 599 | u32 ringbuffer_gpadlhandle; | ||
| 600 | |||
| 601 | /* Allocated memory for ring buffer */ | ||
| 602 | void *ringbuffer_pages; | ||
| 603 | u32 ringbuffer_pagecount; | ||
| 604 | struct hv_ring_buffer_info outbound; /* send to parent */ | ||
| 605 | struct hv_ring_buffer_info inbound; /* receive from parent */ | ||
| 606 | spinlock_t inbound_lock; | ||
| 607 | struct workqueue_struct *controlwq; | ||
| 608 | |||
| 609 | struct vmbus_close_msg close_msg; | ||
| 610 | |||
| 611 | /* Channel callback are invoked in this workqueue context */ | ||
| 612 | /* HANDLE dataWorkQueue; */ | ||
| 613 | |||
| 614 | void (*onchannel_callback)(void *context); | ||
| 615 | void *channel_callback_context; | ||
| 616 | }; | ||
| 617 | |||
| 618 | void free_channel(struct vmbus_channel *channel); | ||
| 619 | |||
| 620 | void vmbus_onmessage(void *context); | ||
| 621 | |||
| 622 | int vmbus_request_offers(void); | ||
| 623 | |||
| 624 | /* The format must be the same as struct vmdata_gpa_direct */ | ||
| 625 | struct vmbus_channel_packet_page_buffer { | ||
| 626 | u16 type; | ||
| 627 | u16 dataoffset8; | ||
| 628 | u16 length8; | ||
| 629 | u16 flags; | ||
| 630 | u64 transactionid; | ||
| 631 | u32 reserved; | ||
| 632 | u32 rangecount; | ||
| 633 | struct hv_page_buffer range[MAX_PAGE_BUFFER_COUNT]; | ||
| 634 | } __packed; | ||
| 635 | |||
| 636 | /* The format must be the same as struct vmdata_gpa_direct */ | ||
| 637 | struct vmbus_channel_packet_multipage_buffer { | ||
| 638 | u16 type; | ||
| 639 | u16 dataoffset8; | ||
| 640 | u16 length8; | ||
| 641 | u16 flags; | ||
| 642 | u64 transactionid; | ||
| 643 | u32 reserved; | ||
| 644 | u32 rangecount; /* Always 1 in this case */ | ||
| 645 | struct hv_multipage_buffer range; | ||
| 646 | } __packed; | ||
| 647 | |||
| 648 | |||
| 649 | extern int vmbus_open(struct vmbus_channel *channel, | ||
| 650 | u32 send_ringbuffersize, | ||
| 651 | u32 recv_ringbuffersize, | ||
| 652 | void *userdata, | ||
| 653 | u32 userdatalen, | ||
| 654 | void(*onchannel_callback)(void *context), | ||
| 655 | void *context); | ||
| 656 | |||
| 657 | extern void vmbus_close(struct vmbus_channel *channel); | ||
| 658 | |||
| 659 | extern int vmbus_sendpacket(struct vmbus_channel *channel, | ||
| 660 | const void *buffer, | ||
| 661 | u32 bufferLen, | ||
| 662 | u64 requestid, | ||
| 663 | enum vmbus_packet_type type, | ||
| 664 | u32 flags); | ||
| 665 | |||
| 666 | extern int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, | ||
| 667 | struct hv_page_buffer pagebuffers[], | ||
| 668 | u32 pagecount, | ||
| 669 | void *buffer, | ||
| 670 | u32 bufferlen, | ||
| 671 | u64 requestid); | ||
| 672 | |||
| 673 | extern int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, | ||
| 674 | struct hv_multipage_buffer *mpb, | ||
| 675 | void *buffer, | ||
| 676 | u32 bufferlen, | ||
| 677 | u64 requestid); | ||
| 678 | |||
| 679 | extern int vmbus_establish_gpadl(struct vmbus_channel *channel, | ||
| 680 | void *kbuffer, | ||
| 681 | u32 size, | ||
| 682 | u32 *gpadl_handle); | ||
| 683 | |||
| 684 | extern int vmbus_teardown_gpadl(struct vmbus_channel *channel, | ||
| 685 | u32 gpadl_handle); | ||
| 686 | |||
| 687 | extern int vmbus_recvpacket(struct vmbus_channel *channel, | ||
| 688 | void *buffer, | ||
| 689 | u32 bufferlen, | ||
| 690 | u32 *buffer_actual_len, | ||
| 691 | u64 *requestid); | ||
| 692 | |||
| 693 | extern int vmbus_recvpacket_raw(struct vmbus_channel *channel, | ||
| 694 | void *buffer, | ||
| 695 | u32 bufferlen, | ||
| 696 | u32 *buffer_actual_len, | ||
| 697 | u64 *requestid); | ||
| 698 | |||
| 699 | |||
| 700 | extern void vmbus_get_debug_info(struct vmbus_channel *channel, | ||
| 701 | struct vmbus_channel_debug_info *debug); | ||
| 702 | |||
| 703 | extern void vmbus_ontimer(unsigned long data); | ||
| 704 | |||
| 705 | |||
| 706 | #define LOWORD(dw) ((unsigned short)(dw)) | ||
| 707 | #define HIWORD(dw) ((unsigned short)(((unsigned int) (dw) >> 16) & 0xFFFF)) | ||
| 708 | |||
| 709 | |||
| 710 | #define VMBUS 0x0001 | ||
| 711 | #define STORVSC 0x0002 | ||
| 712 | #define NETVSC 0x0004 | ||
| 713 | #define INPUTVSC 0x0008 | ||
| 714 | #define BLKVSC 0x0010 | ||
| 715 | #define VMBUS_DRV 0x0100 | ||
| 716 | #define STORVSC_DRV 0x0200 | ||
| 717 | #define NETVSC_DRV 0x0400 | ||
| 718 | #define INPUTVSC_DRV 0x0800 | ||
| 719 | #define BLKVSC_DRV 0x1000 | ||
| 720 | |||
| 721 | #define ALL_MODULES (VMBUS |\ | ||
| 722 | STORVSC |\ | ||
| 723 | NETVSC |\ | ||
| 724 | INPUTVSC |\ | ||
| 725 | BLKVSC |\ | ||
| 726 | VMBUS_DRV |\ | ||
| 727 | STORVSC_DRV |\ | ||
| 728 | NETVSC_DRV |\ | ||
| 729 | INPUTVSC_DRV|\ | ||
| 730 | BLKVSC_DRV) | ||
| 731 | |||
| 732 | /* Logging Level */ | ||
| 733 | #define ERROR_LVL 3 | ||
| 734 | #define WARNING_LVL 4 | ||
| 735 | #define INFO_LVL 6 | ||
| 736 | #define DEBUG_LVL 7 | ||
| 737 | #define DEBUG_LVL_ENTEREXIT 8 | ||
| 738 | #define DEBUG_RING_LVL 9 | ||
| 739 | |||
| 740 | extern unsigned int vmbus_loglevel; | ||
| 741 | |||
| 742 | #define DPRINT(mod, lvl, fmt, args...) do {\ | ||
| 743 | if ((mod & (HIWORD(vmbus_loglevel))) && \ | ||
| 744 | (lvl <= LOWORD(vmbus_loglevel))) \ | ||
| 745 | printk(KERN_DEBUG #mod": %s() " fmt "\n", __func__, ## args);\ | ||
| 746 | } while (0) | ||
| 747 | |||
| 748 | #define DPRINT_DBG(mod, fmt, args...) do {\ | ||
| 749 | if ((mod & (HIWORD(vmbus_loglevel))) && \ | ||
| 750 | (DEBUG_LVL <= LOWORD(vmbus_loglevel))) \ | ||
| 751 | printk(KERN_DEBUG #mod": %s() " fmt "\n", __func__, ## args);\ | ||
| 752 | } while (0) | ||
| 753 | |||
| 754 | #define DPRINT_INFO(mod, fmt, args...) do {\ | ||
| 755 | if ((mod & (HIWORD(vmbus_loglevel))) && \ | ||
| 756 | (INFO_LVL <= LOWORD(vmbus_loglevel))) \ | ||
| 757 | printk(KERN_INFO #mod": " fmt "\n", ## args);\ | ||
| 758 | } while (0) | ||
| 759 | |||
| 760 | #define DPRINT_WARN(mod, fmt, args...) do {\ | ||
| 761 | if ((mod & (HIWORD(vmbus_loglevel))) && \ | ||
| 762 | (WARNING_LVL <= LOWORD(vmbus_loglevel))) \ | ||
| 763 | printk(KERN_WARNING #mod": WARNING! " fmt "\n", ## args);\ | ||
| 764 | } while (0) | ||
| 765 | |||
| 766 | #define DPRINT_ERR(mod, fmt, args...) do {\ | ||
| 767 | if ((mod & (HIWORD(vmbus_loglevel))) && \ | ||
| 768 | (ERROR_LVL <= LOWORD(vmbus_loglevel))) \ | ||
| 769 | printk(KERN_ERR #mod": %s() ERROR!! " fmt "\n", \ | ||
| 770 | __func__, ## args);\ | ||
| 771 | } while (0) | ||
| 772 | |||
| 773 | |||
| 774 | |||
| 775 | struct hv_driver; | ||
| 776 | struct hv_device; | ||
| 777 | |||
| 778 | struct hv_dev_port_info { | ||
| 779 | u32 int_mask; | ||
| 780 | u32 read_idx; | ||
| 781 | u32 write_idx; | ||
| 782 | u32 bytes_avail_toread; | ||
| 783 | u32 bytes_avail_towrite; | ||
| 784 | }; | ||
| 785 | |||
| 786 | struct hv_device_info { | ||
| 787 | u32 chn_id; | ||
| 788 | u32 chn_state; | ||
| 789 | struct hv_guid chn_type; | ||
| 790 | struct hv_guid chn_instance; | ||
| 791 | |||
| 792 | u32 monitor_id; | ||
| 793 | u32 server_monitor_pending; | ||
| 794 | u32 server_monitor_latency; | ||
| 795 | u32 server_monitor_conn_id; | ||
| 796 | u32 client_monitor_pending; | ||
| 797 | u32 client_monitor_latency; | ||
| 798 | u32 client_monitor_conn_id; | ||
| 799 | |||
| 800 | struct hv_dev_port_info inbound; | ||
| 801 | struct hv_dev_port_info outbound; | ||
| 802 | }; | ||
| 803 | |||
| 804 | /* Base driver object */ | ||
| 805 | struct hv_driver { | ||
| 806 | const char *name; | ||
| 807 | |||
| 808 | /* the device type supported by this driver */ | ||
| 809 | struct hv_guid dev_type; | ||
| 810 | |||
| 811 | struct device_driver driver; | ||
| 812 | |||
| 813 | int (*probe)(struct hv_device *); | ||
| 814 | int (*remove)(struct hv_device *); | ||
| 815 | void (*shutdown)(struct hv_device *); | ||
| 816 | |||
| 817 | }; | ||
| 818 | |||
| 819 | /* Base device object */ | ||
| 820 | struct hv_device { | ||
| 821 | /* the device type id of this device */ | ||
| 822 | struct hv_guid dev_type; | ||
| 823 | |||
| 824 | /* the device instance id of this device */ | ||
| 825 | struct hv_guid dev_instance; | ||
| 826 | |||
| 827 | struct device device; | ||
| 828 | |||
| 829 | struct vmbus_channel *channel; | ||
| 830 | |||
| 831 | /* Device extension; */ | ||
| 832 | void *ext; | ||
| 833 | }; | ||
| 834 | |||
| 835 | |||
| 836 | static inline struct hv_device *device_to_hv_device(struct device *d) | ||
| 837 | { | ||
| 838 | return container_of(d, struct hv_device, device); | ||
| 839 | } | ||
| 840 | |||
| 841 | static inline struct hv_driver *drv_to_hv_drv(struct device_driver *d) | ||
| 842 | { | ||
| 843 | return container_of(d, struct hv_driver, driver); | ||
| 844 | } | ||
| 845 | |||
| 846 | |||
| 847 | /* Vmbus interface */ | ||
| 848 | int vmbus_child_driver_register(struct device_driver *drv); | ||
| 849 | void vmbus_child_driver_unregister(struct device_driver *drv); | ||
| 850 | |||
| 851 | /* | ||
| 852 | * Common header for Hyper-V ICs | ||
| 853 | */ | ||
| 854 | |||
| 855 | #define ICMSGTYPE_NEGOTIATE 0 | ||
| 856 | #define ICMSGTYPE_HEARTBEAT 1 | ||
| 857 | #define ICMSGTYPE_KVPEXCHANGE 2 | ||
| 858 | #define ICMSGTYPE_SHUTDOWN 3 | ||
| 859 | #define ICMSGTYPE_TIMESYNC 4 | ||
| 860 | #define ICMSGTYPE_VSS 5 | ||
| 861 | |||
| 862 | #define ICMSGHDRFLAG_TRANSACTION 1 | ||
| 863 | #define ICMSGHDRFLAG_REQUEST 2 | ||
| 864 | #define ICMSGHDRFLAG_RESPONSE 4 | ||
| 865 | |||
| 866 | #define HV_S_OK 0x00000000 | ||
| 867 | #define HV_E_FAIL 0x80004005 | ||
| 868 | #define HV_ERROR_NOT_SUPPORTED 0x80070032 | ||
| 869 | #define HV_ERROR_MACHINE_LOCKED 0x800704F7 | ||
| 870 | |||
| 871 | struct vmbuspipe_hdr { | ||
| 872 | u32 flags; | ||
| 873 | u32 msgsize; | ||
| 874 | } __packed; | ||
| 875 | |||
| 876 | struct ic_version { | ||
| 877 | u16 major; | ||
| 878 | u16 minor; | ||
| 879 | } __packed; | ||
| 880 | |||
| 881 | struct icmsg_hdr { | ||
| 882 | struct ic_version icverframe; | ||
| 883 | u16 icmsgtype; | ||
| 884 | struct ic_version icvermsg; | ||
| 885 | u16 icmsgsize; | ||
| 886 | u32 status; | ||
| 887 | u8 ictransaction_id; | ||
| 888 | u8 icflags; | ||
| 889 | u8 reserved[2]; | ||
| 890 | } __packed; | ||
| 891 | |||
| 892 | struct icmsg_negotiate { | ||
| 893 | u16 icframe_vercnt; | ||
| 894 | u16 icmsg_vercnt; | ||
| 895 | u32 reserved; | ||
| 896 | struct ic_version icversion_data[1]; /* any size array */ | ||
| 897 | } __packed; | ||
| 898 | |||
| 899 | struct shutdown_msg_data { | ||
| 900 | u32 reason_code; | ||
| 901 | u32 timeout_seconds; | ||
| 902 | u32 flags; | ||
| 903 | u8 display_message[2048]; | ||
| 904 | } __packed; | ||
| 905 | |||
| 906 | struct heartbeat_msg_data { | ||
| 907 | u64 seq_num; | ||
| 908 | u32 reserved[8]; | ||
| 909 | } __packed; | ||
| 910 | |||
| 911 | /* Time Sync IC defs */ | ||
| 912 | #define ICTIMESYNCFLAG_PROBE 0 | ||
| 913 | #define ICTIMESYNCFLAG_SYNC 1 | ||
| 914 | #define ICTIMESYNCFLAG_SAMPLE 2 | ||
| 915 | |||
| 916 | #ifdef __x86_64__ | ||
| 917 | #define WLTIMEDELTA 116444736000000000L /* in 100ns unit */ | ||
| 918 | #else | ||
| 919 | #define WLTIMEDELTA 116444736000000000LL | ||
| 920 | #endif | ||
| 921 | |||
| 922 | struct ictimesync_data { | ||
| 923 | u64 parenttime; | ||
| 924 | u64 childtime; | ||
| 925 | u64 roundtriptime; | ||
| 926 | u8 flags; | ||
| 927 | } __packed; | ||
| 928 | |||
| 929 | /* Index for each IC struct in array hv_cb_utils[] */ | ||
| 930 | #define HV_SHUTDOWN_MSG 0 | ||
| 931 | #define HV_TIMESYNC_MSG 1 | ||
| 932 | #define HV_HEARTBEAT_MSG 2 | ||
| 933 | #define HV_KVP_MSG 3 | ||
| 934 | |||
| 935 | struct hyperv_service_callback { | ||
| 936 | u8 msg_type; | ||
| 937 | char *log_msg; | ||
| 938 | unsigned char data[16]; | ||
| 939 | struct vmbus_channel *channel; | ||
| 940 | void (*callback) (void *context); | ||
| 941 | }; | ||
| 942 | |||
| 943 | extern void prep_negotiate_resp(struct icmsg_hdr *, | ||
| 944 | struct icmsg_negotiate *, u8 *); | ||
| 945 | extern void chn_cb_negotiate(void *); | ||
| 946 | extern struct hyperv_service_callback hv_cb_utils[]; | ||
| 947 | |||
| 948 | #endif /* _HYPERV_H */ | ||
diff --git a/drivers/staging/hv/hyperv_net.h b/drivers/staging/hv/hyperv_net.h new file mode 100644 index 00000000000..27f987b48df --- /dev/null +++ b/drivers/staging/hv/hyperv_net.h | |||
| @@ -0,0 +1,1057 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * Copyright (c) 2011, Microsoft Corporation. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms and conditions of the GNU General Public License, | ||
| 7 | * version 2, as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | * more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License along with | ||
| 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
| 16 | * Place - Suite 330, Boston, MA 02111-1307 USA. | ||
| 17 | * | ||
| 18 | * Authors: | ||
| 19 | * Haiyang Zhang <haiyangz@microsoft.com> | ||
| 20 | * Hank Janssen <hjanssen@microsoft.com> | ||
| 21 | * K. Y. Srinivasan <kys@microsoft.com> | ||
| 22 | * | ||
| 23 | */ | ||
| 24 | |||
| 25 | #ifndef _HYPERV_NET_H | ||
| 26 | #define _HYPERV_NET_H | ||
| 27 | |||
| 28 | #include <linux/list.h> | ||
| 29 | #include "hyperv.h" | ||
| 30 | |||
| 31 | /* Fwd declaration */ | ||
| 32 | struct hv_netvsc_packet; | ||
| 33 | |||
| 34 | /* Represent the xfer page packet which contains 1 or more netvsc packet */ | ||
| 35 | struct xferpage_packet { | ||
| 36 | struct list_head list_ent; | ||
| 37 | |||
| 38 | /* # of netvsc packets this xfer packet contains */ | ||
| 39 | u32 count; | ||
| 40 | }; | ||
| 41 | |||
| 42 | /* The number of pages which are enough to cover jumbo frame buffer. */ | ||
| 43 | #define NETVSC_PACKET_MAXPAGE 4 | ||
| 44 | |||
| 45 | /* | ||
| 46 | * Represent netvsc packet which contains 1 RNDIS and 1 ethernet frame | ||
| 47 | * within the RNDIS | ||
| 48 | */ | ||
| 49 | struct hv_netvsc_packet { | ||
| 50 | /* Bookkeeping stuff */ | ||
| 51 | struct list_head list_ent; | ||
| 52 | |||
| 53 | struct hv_device *device; | ||
| 54 | bool is_data_pkt; | ||
| 55 | |||
| 56 | /* | ||
| 57 | * Valid only for receives when we break a xfer page packet | ||
| 58 | * into multiple netvsc packets | ||
| 59 | */ | ||
| 60 | struct xferpage_packet *xfer_page_pkt; | ||
| 61 | |||
| 62 | union { | ||
| 63 | struct { | ||
| 64 | u64 recv_completion_tid; | ||
| 65 | void *recv_completion_ctx; | ||
| 66 | void (*recv_completion)(void *context); | ||
| 67 | } recv; | ||
| 68 | struct { | ||
| 69 | u64 send_completion_tid; | ||
| 70 | void *send_completion_ctx; | ||
| 71 | void (*send_completion)(void *context); | ||
| 72 | } send; | ||
| 73 | } completion; | ||
| 74 | |||
| 75 | /* This points to the memory after page_buf */ | ||
| 76 | void *extension; | ||
| 77 | |||
| 78 | u32 total_data_buflen; | ||
| 79 | /* Points to the send/receive buffer where the ethernet frame is */ | ||
| 80 | u32 page_buf_cnt; | ||
| 81 | struct hv_page_buffer page_buf[NETVSC_PACKET_MAXPAGE]; | ||
| 82 | }; | ||
| 83 | |||
| 84 | struct netvsc_device_info { | ||
| 85 | unsigned char mac_adr[6]; | ||
| 86 | bool link_state; /* 0 - link up, 1 - link down */ | ||
| 87 | int ring_size; | ||
| 88 | }; | ||
| 89 | |||
| 90 | /* Interface */ | ||
| 91 | int netvsc_device_add(struct hv_device *device, void *additional_info); | ||
| 92 | int netvsc_device_remove(struct hv_device *device); | ||
| 93 | int netvsc_send(struct hv_device *device, | ||
| 94 | struct hv_netvsc_packet *packet); | ||
| 95 | void netvsc_linkstatus_callback(struct hv_device *device_obj, | ||
| 96 | unsigned int status); | ||
| 97 | int netvsc_recv_callback(struct hv_device *device_obj, | ||
| 98 | struct hv_netvsc_packet *packet); | ||
| 99 | int netvsc_initialize(struct hv_driver *drv); | ||
| 100 | int rndis_filter_open(struct hv_device *dev); | ||
| 101 | int rndis_filter_close(struct hv_device *dev); | ||
| 102 | int rndis_filter_device_add(struct hv_device *dev, | ||
| 103 | void *additional_info); | ||
| 104 | void rndis_filter_device_remove(struct hv_device *dev); | ||
| 105 | int rndis_filter_receive(struct hv_device *dev, | ||
| 106 | struct hv_netvsc_packet *pkt); | ||
| 107 | |||
| 108 | |||
| 109 | |||
| 110 | int rndis_filter_send(struct hv_device *dev, | ||
| 111 | struct hv_netvsc_packet *pkt); | ||
| 112 | |||
| 113 | #define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF) | ||
| 114 | |||
| 115 | #define NVSP_PROTOCOL_VERSION_1 2 | ||
| 116 | #define NVSP_MIN_PROTOCOL_VERSION NVSP_PROTOCOL_VERSION_1 | ||
| 117 | #define NVSP_MAX_PROTOCOL_VERSION NVSP_PROTOCOL_VERSION_1 | ||
| 118 | |||
| 119 | enum { | ||
| 120 | NVSP_MSG_TYPE_NONE = 0, | ||
| 121 | |||
| 122 | /* Init Messages */ | ||
| 123 | NVSP_MSG_TYPE_INIT = 1, | ||
| 124 | NVSP_MSG_TYPE_INIT_COMPLETE = 2, | ||
| 125 | |||
| 126 | NVSP_VERSION_MSG_START = 100, | ||
| 127 | |||
| 128 | /* Version 1 Messages */ | ||
| 129 | NVSP_MSG1_TYPE_SEND_NDIS_VER = NVSP_VERSION_MSG_START, | ||
| 130 | |||
| 131 | NVSP_MSG1_TYPE_SEND_RECV_BUF, | ||
| 132 | NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE, | ||
| 133 | NVSP_MSG1_TYPE_REVOKE_RECV_BUF, | ||
| 134 | |||
| 135 | NVSP_MSG1_TYPE_SEND_SEND_BUF, | ||
| 136 | NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE, | ||
| 137 | NVSP_MSG1_TYPE_REVOKE_SEND_BUF, | ||
| 138 | |||
| 139 | NVSP_MSG1_TYPE_SEND_RNDIS_PKT, | ||
| 140 | NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE, | ||
| 141 | |||
| 142 | /* | ||
| 143 | * This should be set to the number of messages for the version with | ||
| 144 | * the maximum number of messages. | ||
| 145 | */ | ||
| 146 | NVSP_NUM_MSG_PER_VERSION = 9, | ||
| 147 | }; | ||
| 148 | |||
| 149 | enum { | ||
| 150 | NVSP_STAT_NONE = 0, | ||
| 151 | NVSP_STAT_SUCCESS, | ||
| 152 | NVSP_STAT_FAIL, | ||
| 153 | NVSP_STAT_PROTOCOL_TOO_NEW, | ||
| 154 | NVSP_STAT_PROTOCOL_TOO_OLD, | ||
| 155 | NVSP_STAT_INVALID_RNDIS_PKT, | ||
| 156 | NVSP_STAT_BUSY, | ||
| 157 | NVSP_STAT_MAX, | ||
| 158 | }; | ||
| 159 | |||
| 160 | struct nvsp_message_header { | ||
| 161 | u32 msg_type; | ||
| 162 | }; | ||
| 163 | |||
| 164 | /* Init Messages */ | ||
| 165 | |||
| 166 | /* | ||
| 167 | * This message is used by the VSC to initialize the channel after the channels | ||
| 168 | * has been opened. This message should never include anything other then | ||
| 169 | * versioning (i.e. this message will be the same for ever). | ||
| 170 | */ | ||
| 171 | struct nvsp_message_init { | ||
| 172 | u32 min_protocol_ver; | ||
| 173 | u32 max_protocol_ver; | ||
| 174 | } __packed; | ||
| 175 | |||
| 176 | /* | ||
| 177 | * This message is used by the VSP to complete the initialization of the | ||
| 178 | * channel. This message should never include anything other then versioning | ||
| 179 | * (i.e. this message will be the same for ever). | ||
| 180 | */ | ||
| 181 | struct nvsp_message_init_complete { | ||
| 182 | u32 negotiated_protocol_ver; | ||
| 183 | u32 max_mdl_chain_len; | ||
| 184 | u32 status; | ||
| 185 | } __packed; | ||
| 186 | |||
| 187 | union nvsp_message_init_uber { | ||
| 188 | struct nvsp_message_init init; | ||
| 189 | struct nvsp_message_init_complete init_complete; | ||
| 190 | } __packed; | ||
| 191 | |||
| 192 | /* Version 1 Messages */ | ||
| 193 | |||
| 194 | /* | ||
| 195 | * This message is used by the VSC to send the NDIS version to the VSP. The VSP | ||
| 196 | * can use this information when handling OIDs sent by the VSC. | ||
| 197 | */ | ||
| 198 | struct nvsp_1_message_send_ndis_version { | ||
| 199 | u32 ndis_major_ver; | ||
| 200 | u32 ndis_minor_ver; | ||
| 201 | } __packed; | ||
| 202 | |||
| 203 | /* | ||
| 204 | * This message is used by the VSC to send a receive buffer to the VSP. The VSP | ||
| 205 | * can then use the receive buffer to send data to the VSC. | ||
| 206 | */ | ||
| 207 | struct nvsp_1_message_send_receive_buffer { | ||
| 208 | u32 gpadl_handle; | ||
| 209 | u16 id; | ||
| 210 | } __packed; | ||
| 211 | |||
| 212 | struct nvsp_1_receive_buffer_section { | ||
| 213 | u32 offset; | ||
| 214 | u32 sub_alloc_size; | ||
| 215 | u32 num_sub_allocs; | ||
| 216 | u32 end_offset; | ||
| 217 | } __packed; | ||
| 218 | |||
| 219 | /* | ||
| 220 | * This message is used by the VSP to acknowledge a receive buffer send by the | ||
| 221 | * VSC. This message must be sent by the VSP before the VSP uses the receive | ||
| 222 | * buffer. | ||
| 223 | */ | ||
| 224 | struct nvsp_1_message_send_receive_buffer_complete { | ||
| 225 | u32 status; | ||
| 226 | u32 num_sections; | ||
| 227 | |||
| 228 | /* | ||
| 229 | * The receive buffer is split into two parts, a large suballocation | ||
| 230 | * section and a small suballocation section. These sections are then | ||
| 231 | * suballocated by a certain size. | ||
| 232 | */ | ||
| 233 | |||
| 234 | /* | ||
| 235 | * For example, the following break up of the receive buffer has 6 | ||
| 236 | * large suballocations and 10 small suballocations. | ||
| 237 | */ | ||
| 238 | |||
| 239 | /* | ||
| 240 | * | Large Section | | Small Section | | ||
| 241 | * ------------------------------------------------------------ | ||
| 242 | * | | | | | | | | | | | | | | | | | | | ||
| 243 | * | | | ||
| 244 | * LargeOffset SmallOffset | ||
| 245 | */ | ||
| 246 | |||
| 247 | struct nvsp_1_receive_buffer_section sections[1]; | ||
| 248 | } __packed; | ||
| 249 | |||
| 250 | /* | ||
| 251 | * This message is sent by the VSC to revoke the receive buffer. After the VSP | ||
| 252 | * completes this transaction, the vsp should never use the receive buffer | ||
| 253 | * again. | ||
| 254 | */ | ||
| 255 | struct nvsp_1_message_revoke_receive_buffer { | ||
| 256 | u16 id; | ||
| 257 | }; | ||
| 258 | |||
| 259 | /* | ||
| 260 | * This message is used by the VSC to send a send buffer to the VSP. The VSC | ||
| 261 | * can then use the send buffer to send data to the VSP. | ||
| 262 | */ | ||
| 263 | struct nvsp_1_message_send_send_buffer { | ||
| 264 | u32 gpadl_handle; | ||
| 265 | u16 id; | ||
| 266 | } __packed; | ||
| 267 | |||
| 268 | /* | ||
| 269 | * This message is used by the VSP to acknowledge a send buffer sent by the | ||
| 270 | * VSC. This message must be sent by the VSP before the VSP uses the sent | ||
| 271 | * buffer. | ||
| 272 | */ | ||
| 273 | struct nvsp_1_message_send_send_buffer_complete { | ||
| 274 | u32 status; | ||
| 275 | |||
| 276 | /* | ||
| 277 | * The VSC gets to choose the size of the send buffer and the VSP gets | ||
| 278 | * to choose the sections size of the buffer. This was done to enable | ||
| 279 | * dynamic reconfigurations when the cost of GPA-direct buffers | ||
| 280 | * decreases. | ||
| 281 | */ | ||
| 282 | u32 section_size; | ||
| 283 | } __packed; | ||
| 284 | |||
| 285 | /* | ||
| 286 | * This message is sent by the VSC to revoke the send buffer. After the VSP | ||
| 287 | * completes this transaction, the vsp should never use the send buffer again. | ||
| 288 | */ | ||
| 289 | struct nvsp_1_message_revoke_send_buffer { | ||
| 290 | u16 id; | ||
| 291 | }; | ||
| 292 | |||
| 293 | /* | ||
| 294 | * This message is used by both the VSP and the VSC to send a RNDIS message to | ||
| 295 | * the opposite channel endpoint. | ||
| 296 | */ | ||
| 297 | struct nvsp_1_message_send_rndis_packet { | ||
| 298 | /* | ||
| 299 | * This field is specified by RNIDS. They assume there's two different | ||
| 300 | * channels of communication. However, the Network VSP only has one. | ||
| 301 | * Therefore, the channel travels with the RNDIS packet. | ||
| 302 | */ | ||
| 303 | u32 channel_type; | ||
| 304 | |||
| 305 | /* | ||
| 306 | * This field is used to send part or all of the data through a send | ||
| 307 | * buffer. This values specifies an index into the send buffer. If the | ||
| 308 | * index is 0xFFFFFFFF, then the send buffer is not being used and all | ||
| 309 | * of the data was sent through other VMBus mechanisms. | ||
| 310 | */ | ||
| 311 | u32 send_buf_section_index; | ||
| 312 | u32 send_buf_section_size; | ||
| 313 | } __packed; | ||
| 314 | |||
| 315 | /* | ||
| 316 | * This message is used by both the VSP and the VSC to complete a RNDIS message | ||
| 317 | * to the opposite channel endpoint. At this point, the initiator of this | ||
| 318 | * message cannot use any resources associated with the original RNDIS packet. | ||
| 319 | */ | ||
| 320 | struct nvsp_1_message_send_rndis_packet_complete { | ||
| 321 | u32 status; | ||
| 322 | }; | ||
| 323 | |||
| 324 | union nvsp_1_message_uber { | ||
| 325 | struct nvsp_1_message_send_ndis_version send_ndis_ver; | ||
| 326 | |||
| 327 | struct nvsp_1_message_send_receive_buffer send_recv_buf; | ||
| 328 | struct nvsp_1_message_send_receive_buffer_complete | ||
| 329 | send_recv_buf_complete; | ||
| 330 | struct nvsp_1_message_revoke_receive_buffer revoke_recv_buf; | ||
| 331 | |||
| 332 | struct nvsp_1_message_send_send_buffer send_send_buf; | ||
| 333 | struct nvsp_1_message_send_send_buffer_complete send_send_buf_complete; | ||
| 334 | struct nvsp_1_message_revoke_send_buffer revoke_send_buf; | ||
| 335 | |||
| 336 | struct nvsp_1_message_send_rndis_packet send_rndis_pkt; | ||
| 337 | struct nvsp_1_message_send_rndis_packet_complete | ||
| 338 | send_rndis_pkt_complete; | ||
| 339 | } __packed; | ||
| 340 | |||
| 341 | union nvsp_all_messages { | ||
| 342 | union nvsp_message_init_uber init_msg; | ||
| 343 | union nvsp_1_message_uber v1_msg; | ||
| 344 | } __packed; | ||
| 345 | |||
| 346 | /* ALL Messages */ | ||
| 347 | struct nvsp_message { | ||
| 348 | struct nvsp_message_header hdr; | ||
| 349 | union nvsp_all_messages msg; | ||
| 350 | } __packed; | ||
| 351 | |||
| 352 | |||
| 353 | |||
| 354 | |||
| 355 | /* #define NVSC_MIN_PROTOCOL_VERSION 1 */ | ||
| 356 | /* #define NVSC_MAX_PROTOCOL_VERSION 1 */ | ||
| 357 | |||
| 358 | #define NETVSC_RECEIVE_BUFFER_SIZE (1024*1024) /* 1MB */ | ||
| 359 | |||
| 360 | #define NETVSC_RECEIVE_BUFFER_ID 0xcafe | ||
| 361 | |||
| 362 | #define NETVSC_RECEIVE_SG_COUNT 1 | ||
| 363 | |||
| 364 | /* Preallocated receive packets */ | ||
| 365 | #define NETVSC_RECEIVE_PACKETLIST_COUNT 256 | ||
| 366 | |||
| 367 | #define NETVSC_PACKET_SIZE 2048 | ||
| 368 | |||
| 369 | /* Per netvsc channel-specific */ | ||
| 370 | struct netvsc_device { | ||
| 371 | struct hv_device *dev; | ||
| 372 | |||
| 373 | atomic_t refcnt; | ||
| 374 | atomic_t num_outstanding_sends; | ||
| 375 | /* | ||
| 376 | * List of free preallocated hv_netvsc_packet to represent receive | ||
| 377 | * packet | ||
| 378 | */ | ||
| 379 | struct list_head recv_pkt_list; | ||
| 380 | spinlock_t recv_pkt_list_lock; | ||
| 381 | |||
| 382 | /* Receive buffer allocated by us but manages by NetVSP */ | ||
| 383 | void *recv_buf; | ||
| 384 | u32 recv_buf_size; | ||
| 385 | u32 recv_buf_gpadl_handle; | ||
| 386 | u32 recv_section_cnt; | ||
| 387 | struct nvsp_1_receive_buffer_section *recv_section; | ||
| 388 | |||
| 389 | /* Used for NetVSP initialization protocol */ | ||
| 390 | struct completion channel_init_wait; | ||
| 391 | struct nvsp_message channel_init_pkt; | ||
| 392 | |||
| 393 | struct nvsp_message revoke_packet; | ||
| 394 | /* unsigned char HwMacAddr[HW_MACADDR_LEN]; */ | ||
| 395 | |||
| 396 | /* Holds rndis device info */ | ||
| 397 | void *extension; | ||
| 398 | }; | ||
| 399 | |||
| 400 | |||
| 401 | /* Status codes */ | ||
| 402 | |||
| 403 | |||
| 404 | #ifndef STATUS_SUCCESS | ||
| 405 | #define STATUS_SUCCESS (0x00000000L) | ||
| 406 | #endif | ||
| 407 | |||
| 408 | #ifndef STATUS_UNSUCCESSFUL | ||
| 409 | #define STATUS_UNSUCCESSFUL (0xC0000001L) | ||
| 410 | #endif | ||
| 411 | |||
| 412 | #ifndef STATUS_PENDING | ||
| 413 | #define STATUS_PENDING (0x00000103L) | ||
| 414 | #endif | ||
| 415 | |||
| 416 | #ifndef STATUS_INSUFFICIENT_RESOURCES | ||
| 417 | #define STATUS_INSUFFICIENT_RESOURCES (0xC000009AL) | ||
| 418 | #endif | ||
| 419 | |||
| 420 | #ifndef STATUS_BUFFER_OVERFLOW | ||
| 421 | #define STATUS_BUFFER_OVERFLOW (0x80000005L) | ||
| 422 | #endif | ||
| 423 | |||
| 424 | #ifndef STATUS_NOT_SUPPORTED | ||
| 425 | #define STATUS_NOT_SUPPORTED (0xC00000BBL) | ||
| 426 | #endif | ||
| 427 | |||
| 428 | #define RNDIS_STATUS_SUCCESS (STATUS_SUCCESS) | ||
| 429 | #define RNDIS_STATUS_PENDING (STATUS_PENDING) | ||
| 430 | #define RNDIS_STATUS_NOT_RECOGNIZED (0x00010001L) | ||
| 431 | #define RNDIS_STATUS_NOT_COPIED (0x00010002L) | ||
| 432 | #define RNDIS_STATUS_NOT_ACCEPTED (0x00010003L) | ||
| 433 | #define RNDIS_STATUS_CALL_ACTIVE (0x00010007L) | ||
| 434 | |||
| 435 | #define RNDIS_STATUS_ONLINE (0x40010003L) | ||
| 436 | #define RNDIS_STATUS_RESET_START (0x40010004L) | ||
| 437 | #define RNDIS_STATUS_RESET_END (0x40010005L) | ||
| 438 | #define RNDIS_STATUS_RING_STATUS (0x40010006L) | ||
| 439 | #define RNDIS_STATUS_CLOSED (0x40010007L) | ||
| 440 | #define RNDIS_STATUS_WAN_LINE_UP (0x40010008L) | ||
| 441 | #define RNDIS_STATUS_WAN_LINE_DOWN (0x40010009L) | ||
| 442 | #define RNDIS_STATUS_WAN_FRAGMENT (0x4001000AL) | ||
| 443 | #define RNDIS_STATUS_MEDIA_CONNECT (0x4001000BL) | ||
| 444 | #define RNDIS_STATUS_MEDIA_DISCONNECT (0x4001000CL) | ||
| 445 | #define RNDIS_STATUS_HARDWARE_LINE_UP (0x4001000DL) | ||
| 446 | #define RNDIS_STATUS_HARDWARE_LINE_DOWN (0x4001000EL) | ||
| 447 | #define RNDIS_STATUS_INTERFACE_UP (0x4001000FL) | ||
| 448 | #define RNDIS_STATUS_INTERFACE_DOWN (0x40010010L) | ||
| 449 | #define RNDIS_STATUS_MEDIA_BUSY (0x40010011L) | ||
| 450 | #define RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION (0x40010012L) | ||
| 451 | #define RNDIS_STATUS_WW_INDICATION RDIA_SPECIFIC_INDICATION | ||
| 452 | #define RNDIS_STATUS_LINK_SPEED_CHANGE (0x40010013L) | ||
| 453 | |||
| 454 | #define RNDIS_STATUS_NOT_RESETTABLE (0x80010001L) | ||
| 455 | #define RNDIS_STATUS_SOFT_ERRORS (0x80010003L) | ||
| 456 | #define RNDIS_STATUS_HARD_ERRORS (0x80010004L) | ||
| 457 | #define RNDIS_STATUS_BUFFER_OVERFLOW (STATUS_BUFFER_OVERFLOW) | ||
| 458 | |||
| 459 | #define RNDIS_STATUS_FAILURE (STATUS_UNSUCCESSFUL) | ||
| 460 | #define RNDIS_STATUS_RESOURCES (STATUS_INSUFFICIENT_RESOURCES) | ||
| 461 | #define RNDIS_STATUS_CLOSING (0xC0010002L) | ||
| 462 | #define RNDIS_STATUS_BAD_VERSION (0xC0010004L) | ||
| 463 | #define RNDIS_STATUS_BAD_CHARACTERISTICS (0xC0010005L) | ||
| 464 | #define RNDIS_STATUS_ADAPTER_NOT_FOUND (0xC0010006L) | ||
| 465 | #define RNDIS_STATUS_OPEN_FAILED (0xC0010007L) | ||
| 466 | #define RNDIS_STATUS_DEVICE_FAILED (0xC0010008L) | ||
| 467 | #define RNDIS_STATUS_MULTICAST_FULL (0xC0010009L) | ||
| 468 | #define RNDIS_STATUS_MULTICAST_EXISTS (0xC001000AL) | ||
| 469 | #define RNDIS_STATUS_MULTICAST_NOT_FOUND (0xC001000BL) | ||
| 470 | #define RNDIS_STATUS_REQUEST_ABORTED (0xC001000CL) | ||
| 471 | #define RNDIS_STATUS_RESET_IN_PROGRESS (0xC001000DL) | ||
| 472 | #define RNDIS_STATUS_CLOSING_INDICATING (0xC001000EL) | ||
| 473 | #define RNDIS_STATUS_NOT_SUPPORTED (STATUS_NOT_SUPPORTED) | ||
| 474 | #define RNDIS_STATUS_INVALID_PACKET (0xC001000FL) | ||
| 475 | #define RNDIS_STATUS_OPEN_LIST_FULL (0xC0010010L) | ||
| 476 | #define RNDIS_STATUS_ADAPTER_NOT_READY (0xC0010011L) | ||
| 477 | #define RNDIS_STATUS_ADAPTER_NOT_OPEN (0xC0010012L) | ||
| 478 | #define RNDIS_STATUS_NOT_INDICATING (0xC0010013L) | ||
| 479 | #define RNDIS_STATUS_INVALID_LENGTH (0xC0010014L) | ||
| 480 | #define RNDIS_STATUS_INVALID_DATA (0xC0010015L) | ||
| 481 | #define RNDIS_STATUS_BUFFER_TOO_SHORT (0xC0010016L) | ||
| 482 | #define RNDIS_STATUS_INVALID_OID (0xC0010017L) | ||
| 483 | #define RNDIS_STATUS_ADAPTER_REMOVED (0xC0010018L) | ||
| 484 | #define RNDIS_STATUS_UNSUPPORTED_MEDIA (0xC0010019L) | ||
| 485 | #define RNDIS_STATUS_GROUP_ADDRESS_IN_USE (0xC001001AL) | ||
| 486 | #define RNDIS_STATUS_FILE_NOT_FOUND (0xC001001BL) | ||
| 487 | #define RNDIS_STATUS_ERROR_READING_FILE (0xC001001CL) | ||
| 488 | #define RNDIS_STATUS_ALREADY_MAPPED (0xC001001DL) | ||
| 489 | #define RNDIS_STATUS_RESOURCE_CONFLICT (0xC001001EL) | ||
| 490 | #define RNDIS_STATUS_NO_CABLE (0xC001001FL) | ||
| 491 | |||
| 492 | #define RNDIS_STATUS_INVALID_SAP (0xC0010020L) | ||
| 493 | #define RNDIS_STATUS_SAP_IN_USE (0xC0010021L) | ||
| 494 | #define RNDIS_STATUS_INVALID_ADDRESS (0xC0010022L) | ||
| 495 | #define RNDIS_STATUS_VC_NOT_ACTIVATED (0xC0010023L) | ||
| 496 | #define RNDIS_STATUS_DEST_OUT_OF_ORDER (0xC0010024L) | ||
| 497 | #define RNDIS_STATUS_VC_NOT_AVAILABLE (0xC0010025L) | ||
| 498 | #define RNDIS_STATUS_CELLRATE_NOT_AVAILABLE (0xC0010026L) | ||
| 499 | #define RNDIS_STATUS_INCOMPATABLE_QOS (0xC0010027L) | ||
| 500 | #define RNDIS_STATUS_AAL_PARAMS_UNSUPPORTED (0xC0010028L) | ||
| 501 | #define RNDIS_STATUS_NO_ROUTE_TO_DESTINATION (0xC0010029L) | ||
| 502 | |||
| 503 | #define RNDIS_STATUS_TOKEN_RING_OPEN_ERROR (0xC0011000L) | ||
| 504 | |||
| 505 | /* Object Identifiers used by NdisRequest Query/Set Information */ | ||
| 506 | /* General Objects */ | ||
| 507 | #define RNDIS_OID_GEN_SUPPORTED_LIST 0x00010101 | ||
| 508 | #define RNDIS_OID_GEN_HARDWARE_STATUS 0x00010102 | ||
| 509 | #define RNDIS_OID_GEN_MEDIA_SUPPORTED 0x00010103 | ||
| 510 | #define RNDIS_OID_GEN_MEDIA_IN_USE 0x00010104 | ||
| 511 | #define RNDIS_OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105 | ||
| 512 | #define RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106 | ||
| 513 | #define RNDIS_OID_GEN_LINK_SPEED 0x00010107 | ||
| 514 | #define RNDIS_OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108 | ||
| 515 | #define RNDIS_OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109 | ||
| 516 | #define RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A | ||
| 517 | #define RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B | ||
| 518 | #define RNDIS_OID_GEN_VENDOR_ID 0x0001010C | ||
| 519 | #define RNDIS_OID_GEN_VENDOR_DESCRIPTION 0x0001010D | ||
| 520 | #define RNDIS_OID_GEN_CURRENT_PACKET_FILTER 0x0001010E | ||
| 521 | #define RNDIS_OID_GEN_CURRENT_LOOKAHEAD 0x0001010F | ||
| 522 | #define RNDIS_OID_GEN_DRIVER_VERSION 0x00010110 | ||
| 523 | #define RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111 | ||
| 524 | #define RNDIS_OID_GEN_PROTOCOL_OPTIONS 0x00010112 | ||
| 525 | #define RNDIS_OID_GEN_MAC_OPTIONS 0x00010113 | ||
| 526 | #define RNDIS_OID_GEN_MEDIA_CONNECT_STATUS 0x00010114 | ||
| 527 | #define RNDIS_OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115 | ||
| 528 | #define RNDIS_OID_GEN_VENDOR_DRIVER_VERSION 0x00010116 | ||
| 529 | #define RNDIS_OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118 | ||
| 530 | #define RNDIS_OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119 | ||
| 531 | #define RNDIS_OID_GEN_MACHINE_NAME 0x0001021A | ||
| 532 | #define RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B | ||
| 533 | |||
| 534 | #define RNDIS_OID_GEN_XMIT_OK 0x00020101 | ||
| 535 | #define RNDIS_OID_GEN_RCV_OK 0x00020102 | ||
| 536 | #define RNDIS_OID_GEN_XMIT_ERROR 0x00020103 | ||
| 537 | #define RNDIS_OID_GEN_RCV_ERROR 0x00020104 | ||
| 538 | #define RNDIS_OID_GEN_RCV_NO_BUFFER 0x00020105 | ||
| 539 | |||
| 540 | #define RNDIS_OID_GEN_DIRECTED_BYTES_XMIT 0x00020201 | ||
| 541 | #define RNDIS_OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202 | ||
| 542 | #define RNDIS_OID_GEN_MULTICAST_BYTES_XMIT 0x00020203 | ||
| 543 | #define RNDIS_OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204 | ||
| 544 | #define RNDIS_OID_GEN_BROADCAST_BYTES_XMIT 0x00020205 | ||
| 545 | #define RNDIS_OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206 | ||
| 546 | #define RNDIS_OID_GEN_DIRECTED_BYTES_RCV 0x00020207 | ||
| 547 | #define RNDIS_OID_GEN_DIRECTED_FRAMES_RCV 0x00020208 | ||
| 548 | #define RNDIS_OID_GEN_MULTICAST_BYTES_RCV 0x00020209 | ||
| 549 | #define RNDIS_OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A | ||
| 550 | #define RNDIS_OID_GEN_BROADCAST_BYTES_RCV 0x0002020B | ||
| 551 | #define RNDIS_OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C | ||
| 552 | |||
| 553 | #define RNDIS_OID_GEN_RCV_CRC_ERROR 0x0002020D | ||
| 554 | #define RNDIS_OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E | ||
| 555 | |||
| 556 | #define RNDIS_OID_GEN_GET_TIME_CAPS 0x0002020F | ||
| 557 | #define RNDIS_OID_GEN_GET_NETCARD_TIME 0x00020210 | ||
| 558 | |||
| 559 | /* These are connection-oriented general OIDs. */ | ||
| 560 | /* These replace the above OIDs for connection-oriented media. */ | ||
| 561 | #define RNDIS_OID_GEN_CO_SUPPORTED_LIST 0x00010101 | ||
| 562 | #define RNDIS_OID_GEN_CO_HARDWARE_STATUS 0x00010102 | ||
| 563 | #define RNDIS_OID_GEN_CO_MEDIA_SUPPORTED 0x00010103 | ||
| 564 | #define RNDIS_OID_GEN_CO_MEDIA_IN_USE 0x00010104 | ||
| 565 | #define RNDIS_OID_GEN_CO_LINK_SPEED 0x00010105 | ||
| 566 | #define RNDIS_OID_GEN_CO_VENDOR_ID 0x00010106 | ||
| 567 | #define RNDIS_OID_GEN_CO_VENDOR_DESCRIPTION 0x00010107 | ||
| 568 | #define RNDIS_OID_GEN_CO_DRIVER_VERSION 0x00010108 | ||
| 569 | #define RNDIS_OID_GEN_CO_PROTOCOL_OPTIONS 0x00010109 | ||
| 570 | #define RNDIS_OID_GEN_CO_MAC_OPTIONS 0x0001010A | ||
| 571 | #define RNDIS_OID_GEN_CO_MEDIA_CONNECT_STATUS 0x0001010B | ||
| 572 | #define RNDIS_OID_GEN_CO_VENDOR_DRIVER_VERSION 0x0001010C | ||
| 573 | #define RNDIS_OID_GEN_CO_MINIMUM_LINK_SPEED 0x0001010D | ||
| 574 | |||
| 575 | #define RNDIS_OID_GEN_CO_GET_TIME_CAPS 0x00010201 | ||
| 576 | #define RNDIS_OID_GEN_CO_GET_NETCARD_TIME 0x00010202 | ||
| 577 | |||
| 578 | /* These are connection-oriented statistics OIDs. */ | ||
| 579 | #define RNDIS_OID_GEN_CO_XMIT_PDUS_OK 0x00020101 | ||
| 580 | #define RNDIS_OID_GEN_CO_RCV_PDUS_OK 0x00020102 | ||
| 581 | #define RNDIS_OID_GEN_CO_XMIT_PDUS_ERROR 0x00020103 | ||
| 582 | #define RNDIS_OID_GEN_CO_RCV_PDUS_ERROR 0x00020104 | ||
| 583 | #define RNDIS_OID_GEN_CO_RCV_PDUS_NO_BUFFER 0x00020105 | ||
| 584 | |||
| 585 | |||
| 586 | #define RNDIS_OID_GEN_CO_RCV_CRC_ERROR 0x00020201 | ||
| 587 | #define RNDIS_OID_GEN_CO_TRANSMIT_QUEUE_LENGTH 0x00020202 | ||
| 588 | #define RNDIS_OID_GEN_CO_BYTES_XMIT 0x00020203 | ||
| 589 | #define RNDIS_OID_GEN_CO_BYTES_RCV 0x00020204 | ||
| 590 | #define RNDIS_OID_GEN_CO_BYTES_XMIT_OUTSTANDING 0x00020205 | ||
| 591 | #define RNDIS_OID_GEN_CO_NETCARD_LOAD 0x00020206 | ||
| 592 | |||
| 593 | /* These are objects for Connection-oriented media call-managers. */ | ||
| 594 | #define RNDIS_OID_CO_ADD_PVC 0xFF000001 | ||
| 595 | #define RNDIS_OID_CO_DELETE_PVC 0xFF000002 | ||
| 596 | #define RNDIS_OID_CO_GET_CALL_INFORMATION 0xFF000003 | ||
| 597 | #define RNDIS_OID_CO_ADD_ADDRESS 0xFF000004 | ||
| 598 | #define RNDIS_OID_CO_DELETE_ADDRESS 0xFF000005 | ||
| 599 | #define RNDIS_OID_CO_GET_ADDRESSES 0xFF000006 | ||
| 600 | #define RNDIS_OID_CO_ADDRESS_CHANGE 0xFF000007 | ||
| 601 | #define RNDIS_OID_CO_SIGNALING_ENABLED 0xFF000008 | ||
| 602 | #define RNDIS_OID_CO_SIGNALING_DISABLED 0xFF000009 | ||
| 603 | |||
| 604 | /* 802.3 Objects (Ethernet) */ | ||
| 605 | #define RNDIS_OID_802_3_PERMANENT_ADDRESS 0x01010101 | ||
| 606 | #define RNDIS_OID_802_3_CURRENT_ADDRESS 0x01010102 | ||
| 607 | #define RNDIS_OID_802_3_MULTICAST_LIST 0x01010103 | ||
| 608 | #define RNDIS_OID_802_3_MAXIMUM_LIST_SIZE 0x01010104 | ||
| 609 | #define RNDIS_OID_802_3_MAC_OPTIONS 0x01010105 | ||
| 610 | |||
| 611 | #define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001 | ||
| 612 | |||
| 613 | #define RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101 | ||
| 614 | #define RNDIS_OID_802_3_XMIT_ONE_COLLISION 0x01020102 | ||
| 615 | #define RNDIS_OID_802_3_XMIT_MORE_COLLISIONS 0x01020103 | ||
| 616 | |||
| 617 | #define RNDIS_OID_802_3_XMIT_DEFERRED 0x01020201 | ||
| 618 | #define RNDIS_OID_802_3_XMIT_MAX_COLLISIONS 0x01020202 | ||
| 619 | #define RNDIS_OID_802_3_RCV_OVERRUN 0x01020203 | ||
| 620 | #define RNDIS_OID_802_3_XMIT_UNDERRUN 0x01020204 | ||
| 621 | #define RNDIS_OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205 | ||
| 622 | #define RNDIS_OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206 | ||
| 623 | #define RNDIS_OID_802_3_XMIT_LATE_COLLISIONS 0x01020207 | ||
| 624 | |||
| 625 | /* Remote NDIS message types */ | ||
| 626 | #define REMOTE_NDIS_PACKET_MSG 0x00000001 | ||
| 627 | #define REMOTE_NDIS_INITIALIZE_MSG 0x00000002 | ||
| 628 | #define REMOTE_NDIS_HALT_MSG 0x00000003 | ||
| 629 | #define REMOTE_NDIS_QUERY_MSG 0x00000004 | ||
| 630 | #define REMOTE_NDIS_SET_MSG 0x00000005 | ||
| 631 | #define REMOTE_NDIS_RESET_MSG 0x00000006 | ||
| 632 | #define REMOTE_NDIS_INDICATE_STATUS_MSG 0x00000007 | ||
| 633 | #define REMOTE_NDIS_KEEPALIVE_MSG 0x00000008 | ||
| 634 | |||
| 635 | #define REMOTE_CONDIS_MP_CREATE_VC_MSG 0x00008001 | ||
| 636 | #define REMOTE_CONDIS_MP_DELETE_VC_MSG 0x00008002 | ||
| 637 | #define REMOTE_CONDIS_MP_ACTIVATE_VC_MSG 0x00008005 | ||
| 638 | #define REMOTE_CONDIS_MP_DEACTIVATE_VC_MSG 0x00008006 | ||
| 639 | #define REMOTE_CONDIS_INDICATE_STATUS_MSG 0x00008007 | ||
| 640 | |||
| 641 | /* Remote NDIS message completion types */ | ||
| 642 | #define REMOTE_NDIS_INITIALIZE_CMPLT 0x80000002 | ||
| 643 | #define REMOTE_NDIS_QUERY_CMPLT 0x80000004 | ||
| 644 | #define REMOTE_NDIS_SET_CMPLT 0x80000005 | ||
| 645 | #define REMOTE_NDIS_RESET_CMPLT 0x80000006 | ||
| 646 | #define REMOTE_NDIS_KEEPALIVE_CMPLT 0x80000008 | ||
| 647 | |||
| 648 | #define REMOTE_CONDIS_MP_CREATE_VC_CMPLT 0x80008001 | ||
| 649 | #define REMOTE_CONDIS_MP_DELETE_VC_CMPLT 0x80008002 | ||
| 650 | #define REMOTE_CONDIS_MP_ACTIVATE_VC_CMPLT 0x80008005 | ||
| 651 | #define REMOTE_CONDIS_MP_DEACTIVATE_VC_CMPLT 0x80008006 | ||
| 652 | |||
| 653 | /* | ||
| 654 | * Reserved message type for private communication between lower-layer host | ||
| 655 | * driver and remote device, if necessary. | ||
| 656 | */ | ||
| 657 | #define REMOTE_NDIS_BUS_MSG 0xff000001 | ||
| 658 | |||
| 659 | /* Defines for DeviceFlags in struct rndis_initialize_complete */ | ||
| 660 | #define RNDIS_DF_CONNECTIONLESS 0x00000001 | ||
| 661 | #define RNDIS_DF_CONNECTION_ORIENTED 0x00000002 | ||
| 662 | #define RNDIS_DF_RAW_DATA 0x00000004 | ||
| 663 | |||
| 664 | /* Remote NDIS medium types. */ | ||
| 665 | #define RNDIS_MEDIUM_802_3 0x00000000 | ||
| 666 | #define RNDIS_MEDIUM_802_5 0x00000001 | ||
| 667 | #define RNDIS_MEDIUM_FDDI 0x00000002 | ||
| 668 | #define RNDIS_MEDIUM_WAN 0x00000003 | ||
| 669 | #define RNDIS_MEDIUM_LOCAL_TALK 0x00000004 | ||
| 670 | #define RNDIS_MEDIUM_ARCNET_RAW 0x00000006 | ||
| 671 | #define RNDIS_MEDIUM_ARCNET_878_2 0x00000007 | ||
| 672 | #define RNDIS_MEDIUM_ATM 0x00000008 | ||
| 673 | #define RNDIS_MEDIUM_WIRELESS_WAN 0x00000009 | ||
| 674 | #define RNDIS_MEDIUM_IRDA 0x0000000a | ||
| 675 | #define RNDIS_MEDIUM_CO_WAN 0x0000000b | ||
| 676 | /* Not a real medium, defined as an upper-bound */ | ||
| 677 | #define RNDIS_MEDIUM_MAX 0x0000000d | ||
| 678 | |||
| 679 | |||
| 680 | /* Remote NDIS medium connection states. */ | ||
| 681 | #define RNDIS_MEDIA_STATE_CONNECTED 0x00000000 | ||
| 682 | #define RNDIS_MEDIA_STATE_DISCONNECTED 0x00000001 | ||
| 683 | |||
| 684 | /* Remote NDIS version numbers */ | ||
| 685 | #define RNDIS_MAJOR_VERSION 0x00000001 | ||
| 686 | #define RNDIS_MINOR_VERSION 0x00000000 | ||
| 687 | |||
| 688 | |||
| 689 | /* NdisInitialize message */ | ||
| 690 | struct rndis_initialize_request { | ||
| 691 | u32 req_id; | ||
| 692 | u32 major_ver; | ||
| 693 | u32 minor_ver; | ||
| 694 | u32 max_xfer_size; | ||
| 695 | }; | ||
| 696 | |||
| 697 | /* Response to NdisInitialize */ | ||
| 698 | struct rndis_initialize_complete { | ||
| 699 | u32 req_id; | ||
| 700 | u32 status; | ||
| 701 | u32 major_ver; | ||
| 702 | u32 minor_ver; | ||
| 703 | u32 dev_flags; | ||
| 704 | u32 medium; | ||
| 705 | u32 max_pkt_per_msg; | ||
| 706 | u32 max_xfer_size; | ||
| 707 | u32 pkt_alignment_factor; | ||
| 708 | u32 af_list_offset; | ||
| 709 | u32 af_list_size; | ||
| 710 | }; | ||
| 711 | |||
| 712 | /* Call manager devices only: Information about an address family */ | ||
| 713 | /* supported by the device is appended to the response to NdisInitialize. */ | ||
| 714 | struct rndis_co_address_family { | ||
| 715 | u32 address_family; | ||
| 716 | u32 major_ver; | ||
| 717 | u32 minor_ver; | ||
| 718 | }; | ||
| 719 | |||
| 720 | /* NdisHalt message */ | ||
| 721 | struct rndis_halt_request { | ||
| 722 | u32 req_id; | ||
| 723 | }; | ||
| 724 | |||
| 725 | /* NdisQueryRequest message */ | ||
| 726 | struct rndis_query_request { | ||
| 727 | u32 req_id; | ||
| 728 | u32 oid; | ||
| 729 | u32 info_buflen; | ||
| 730 | u32 info_buf_offset; | ||
| 731 | u32 dev_vc_handle; | ||
| 732 | }; | ||
| 733 | |||
| 734 | /* Response to NdisQueryRequest */ | ||
| 735 | struct rndis_query_complete { | ||
| 736 | u32 req_id; | ||
| 737 | u32 status; | ||
| 738 | u32 info_buflen; | ||
| 739 | u32 info_buf_offset; | ||
| 740 | }; | ||
| 741 | |||
| 742 | /* NdisSetRequest message */ | ||
| 743 | struct rndis_set_request { | ||
| 744 | u32 req_id; | ||
| 745 | u32 oid; | ||
| 746 | u32 info_buflen; | ||
| 747 | u32 info_buf_offset; | ||
| 748 | u32 dev_vc_handle; | ||
| 749 | }; | ||
| 750 | |||
| 751 | /* Response to NdisSetRequest */ | ||
| 752 | struct rndis_set_complete { | ||
| 753 | u32 req_id; | ||
| 754 | u32 status; | ||
| 755 | }; | ||
| 756 | |||
| 757 | /* NdisReset message */ | ||
| 758 | struct rndis_reset_request { | ||
| 759 | u32 reserved; | ||
| 760 | }; | ||
| 761 | |||
| 762 | /* Response to NdisReset */ | ||
| 763 | struct rndis_reset_complete { | ||
| 764 | u32 status; | ||
| 765 | u32 addressing_reset; | ||
| 766 | }; | ||
| 767 | |||
| 768 | /* NdisMIndicateStatus message */ | ||
| 769 | struct rndis_indicate_status { | ||
| 770 | u32 status; | ||
| 771 | u32 status_buflen; | ||
| 772 | u32 status_buf_offset; | ||
| 773 | }; | ||
| 774 | |||
| 775 | /* Diagnostic information passed as the status buffer in */ | ||
| 776 | /* struct rndis_indicate_status messages signifying error conditions. */ | ||
| 777 | struct rndis_diagnostic_info { | ||
| 778 | u32 diag_status; | ||
| 779 | u32 error_offset; | ||
| 780 | }; | ||
| 781 | |||
| 782 | /* NdisKeepAlive message */ | ||
| 783 | struct rndis_keepalive_request { | ||
| 784 | u32 req_id; | ||
| 785 | }; | ||
| 786 | |||
| 787 | /* Response to NdisKeepAlive */ | ||
| 788 | struct rndis_keepalive_complete { | ||
| 789 | u32 req_id; | ||
| 790 | u32 status; | ||
| 791 | }; | ||
| 792 | |||
| 793 | /* | ||
| 794 | * Data message. All Offset fields contain byte offsets from the beginning of | ||
| 795 | * struct rndis_packet. All Length fields are in bytes. VcHandle is set | ||
| 796 | * to 0 for connectionless data, otherwise it contains the VC handle. | ||
| 797 | */ | ||
| 798 | struct rndis_packet { | ||
| 799 | u32 data_offset; | ||
| 800 | u32 data_len; | ||
| 801 | u32 oob_data_offset; | ||
| 802 | u32 oob_data_len; | ||
| 803 | u32 num_oob_data_elements; | ||
| 804 | u32 per_pkt_info_offset; | ||
| 805 | u32 per_pkt_info_len; | ||
| 806 | u32 vc_handle; | ||
| 807 | u32 reserved; | ||
| 808 | }; | ||
| 809 | |||
| 810 | /* Optional Out of Band data associated with a Data message. */ | ||
| 811 | struct rndis_oobd { | ||
| 812 | u32 size; | ||
| 813 | u32 type; | ||
| 814 | u32 class_info_offset; | ||
| 815 | }; | ||
| 816 | |||
| 817 | /* Packet extension field contents associated with a Data message. */ | ||
| 818 | struct rndis_per_packet_info { | ||
| 819 | u32 size; | ||
| 820 | u32 type; | ||
| 821 | u32 per_pkt_info_offset; | ||
| 822 | }; | ||
| 823 | |||
| 824 | /* Format of Information buffer passed in a SetRequest for the OID */ | ||
| 825 | /* OID_GEN_RNDIS_CONFIG_PARAMETER. */ | ||
| 826 | struct rndis_config_parameter_info { | ||
| 827 | u32 parameter_name_offset; | ||
| 828 | u32 parameter_name_length; | ||
| 829 | u32 parameter_type; | ||
| 830 | u32 parameter_value_offset; | ||
| 831 | u32 parameter_value_length; | ||
| 832 | }; | ||
| 833 | |||
| 834 | /* Values for ParameterType in struct rndis_config_parameter_info */ | ||
| 835 | #define RNDIS_CONFIG_PARAM_TYPE_INTEGER 0 | ||
| 836 | #define RNDIS_CONFIG_PARAM_TYPE_STRING 2 | ||
| 837 | |||
| 838 | /* CONDIS Miniport messages for connection oriented devices */ | ||
| 839 | /* that do not implement a call manager. */ | ||
| 840 | |||
| 841 | /* CoNdisMiniportCreateVc message */ | ||
| 842 | struct rcondis_mp_create_vc { | ||
| 843 | u32 req_id; | ||
| 844 | u32 ndis_vc_handle; | ||
| 845 | }; | ||
| 846 | |||
| 847 | /* Response to CoNdisMiniportCreateVc */ | ||
| 848 | struct rcondis_mp_create_vc_complete { | ||
| 849 | u32 req_id; | ||
| 850 | u32 dev_vc_handle; | ||
| 851 | u32 status; | ||
| 852 | }; | ||
| 853 | |||
| 854 | /* CoNdisMiniportDeleteVc message */ | ||
| 855 | struct rcondis_mp_delete_vc { | ||
| 856 | u32 req_id; | ||
| 857 | u32 dev_vc_handle; | ||
| 858 | }; | ||
| 859 | |||
| 860 | /* Response to CoNdisMiniportDeleteVc */ | ||
| 861 | struct rcondis_mp_delete_vc_complete { | ||
| 862 | u32 req_id; | ||
| 863 | u32 status; | ||
| 864 | }; | ||
| 865 | |||
| 866 | /* CoNdisMiniportQueryRequest message */ | ||
| 867 | struct rcondis_mp_query_request { | ||
| 868 | u32 req_id; | ||
| 869 | u32 request_type; | ||
| 870 | u32 oid; | ||
| 871 | u32 dev_vc_handle; | ||
| 872 | u32 info_buflen; | ||
| 873 | u32 info_buf_offset; | ||
| 874 | }; | ||
| 875 | |||
| 876 | /* CoNdisMiniportSetRequest message */ | ||
| 877 | struct rcondis_mp_set_request { | ||
| 878 | u32 req_id; | ||
| 879 | u32 request_type; | ||
| 880 | u32 oid; | ||
| 881 | u32 dev_vc_handle; | ||
| 882 | u32 info_buflen; | ||
| 883 | u32 info_buf_offset; | ||
| 884 | }; | ||
| 885 | |||
| 886 | /* CoNdisIndicateStatus message */ | ||
| 887 | struct rcondis_indicate_status { | ||
| 888 | u32 ndis_vc_handle; | ||
| 889 | u32 status; | ||
| 890 | u32 status_buflen; | ||
| 891 | u32 status_buf_offset; | ||
| 892 | }; | ||
| 893 | |||
| 894 | /* CONDIS Call/VC parameters */ | ||
| 895 | struct rcondis_specific_parameters { | ||
| 896 | u32 parameter_type; | ||
| 897 | u32 parameter_length; | ||
| 898 | u32 parameter_lffset; | ||
| 899 | }; | ||
| 900 | |||
| 901 | struct rcondis_media_parameters { | ||
| 902 | u32 flags; | ||
| 903 | u32 reserved1; | ||
| 904 | u32 reserved2; | ||
| 905 | struct rcondis_specific_parameters media_specific; | ||
| 906 | }; | ||
| 907 | |||
| 908 | struct rndis_flowspec { | ||
| 909 | u32 token_rate; | ||
| 910 | u32 token_bucket_size; | ||
| 911 | u32 peak_bandwidth; | ||
| 912 | u32 latency; | ||
| 913 | u32 delay_variation; | ||
| 914 | u32 service_type; | ||
| 915 | u32 max_sdu_size; | ||
| 916 | u32 minimum_policed_size; | ||
| 917 | }; | ||
| 918 | |||
| 919 | struct rcondis_call_manager_parameters { | ||
| 920 | struct rndis_flowspec transmit; | ||
| 921 | struct rndis_flowspec receive; | ||
| 922 | struct rcondis_specific_parameters call_mgr_specific; | ||
| 923 | }; | ||
| 924 | |||
| 925 | /* CoNdisMiniportActivateVc message */ | ||
| 926 | struct rcondis_mp_activate_vc_request { | ||
| 927 | u32 req_id; | ||
| 928 | u32 flags; | ||
| 929 | u32 dev_vc_handle; | ||
| 930 | u32 media_params_offset; | ||
| 931 | u32 media_params_length; | ||
| 932 | u32 call_mgr_params_offset; | ||
| 933 | u32 call_mgr_params_length; | ||
| 934 | }; | ||
| 935 | |||
| 936 | /* Response to CoNdisMiniportActivateVc */ | ||
| 937 | struct rcondis_mp_activate_vc_complete { | ||
| 938 | u32 req_id; | ||
| 939 | u32 status; | ||
| 940 | }; | ||
| 941 | |||
| 942 | /* CoNdisMiniportDeactivateVc message */ | ||
| 943 | struct rcondis_mp_deactivate_vc_request { | ||
| 944 | u32 req_id; | ||
| 945 | u32 flags; | ||
| 946 | u32 dev_vc_handle; | ||
| 947 | }; | ||
| 948 | |||
| 949 | /* Response to CoNdisMiniportDeactivateVc */ | ||
| 950 | struct rcondis_mp_deactivate_vc_complete { | ||
| 951 | u32 req_id; | ||
| 952 | u32 status; | ||
| 953 | }; | ||
| 954 | |||
| 955 | |||
| 956 | /* union with all of the RNDIS messages */ | ||
| 957 | union rndis_message_container { | ||
| 958 | struct rndis_packet pkt; | ||
| 959 | struct rndis_initialize_request init_req; | ||
| 960 | struct rndis_halt_request halt_req; | ||
| 961 | struct rndis_query_request query_req; | ||
| 962 | struct rndis_set_request set_req; | ||
| 963 | struct rndis_reset_request reset_req; | ||
| 964 | struct rndis_keepalive_request keep_alive_req; | ||
| 965 | struct rndis_indicate_status indicate_status; | ||
| 966 | struct rndis_initialize_complete init_complete; | ||
| 967 | struct rndis_query_complete query_complete; | ||
| 968 | struct rndis_set_complete set_complete; | ||
| 969 | struct rndis_reset_complete reset_complete; | ||
| 970 | struct rndis_keepalive_complete keep_alive_complete; | ||
| 971 | struct rcondis_mp_create_vc co_miniport_create_vc; | ||
| 972 | struct rcondis_mp_delete_vc co_miniport_delete_vc; | ||
| 973 | struct rcondis_indicate_status co_indicate_status; | ||
| 974 | struct rcondis_mp_activate_vc_request co_miniport_activate_vc; | ||
| 975 | struct rcondis_mp_deactivate_vc_request co_miniport_deactivate_vc; | ||
| 976 | struct rcondis_mp_create_vc_complete co_miniport_create_vc_complete; | ||
| 977 | struct rcondis_mp_delete_vc_complete co_miniport_delete_vc_complete; | ||
| 978 | struct rcondis_mp_activate_vc_complete co_miniport_activate_vc_complete; | ||
| 979 | struct rcondis_mp_deactivate_vc_complete | ||
| 980 | co_miniport_deactivate_vc_complete; | ||
| 981 | }; | ||
| 982 | |||
| 983 | /* Remote NDIS message format */ | ||
| 984 | struct rndis_message { | ||
| 985 | u32 ndis_msg_type; | ||
| 986 | |||
| 987 | /* Total length of this message, from the beginning */ | ||
| 988 | /* of the sruct rndis_message, in bytes. */ | ||
| 989 | u32 msg_len; | ||
| 990 | |||
| 991 | /* Actual message */ | ||
| 992 | union rndis_message_container msg; | ||
| 993 | }; | ||
| 994 | |||
| 995 | |||
| 996 | struct rndis_filter_packet { | ||
| 997 | void *completion_ctx; | ||
| 998 | void (*completion)(void *context); | ||
| 999 | struct rndis_message msg; | ||
| 1000 | }; | ||
| 1001 | |||
| 1002 | /* Handy macros */ | ||
| 1003 | |||
| 1004 | /* get the size of an RNDIS message. Pass in the message type, */ | ||
| 1005 | /* struct rndis_set_request, struct rndis_packet for example */ | ||
| 1006 | #define RNDIS_MESSAGE_SIZE(msg) \ | ||
| 1007 | (sizeof(msg) + (sizeof(struct rndis_message) - \ | ||
| 1008 | sizeof(union rndis_message_container))) | ||
| 1009 | |||
| 1010 | /* get pointer to info buffer with message pointer */ | ||
| 1011 | #define MESSAGE_TO_INFO_BUFFER(msg) \ | ||
| 1012 | (((unsigned char *)(msg)) + msg->info_buf_offset) | ||
| 1013 | |||
| 1014 | /* get pointer to status buffer with message pointer */ | ||
| 1015 | #define MESSAGE_TO_STATUS_BUFFER(msg) \ | ||
| 1016 | (((unsigned char *)(msg)) + msg->status_buf_offset) | ||
| 1017 | |||
| 1018 | /* get pointer to OOBD buffer with message pointer */ | ||
| 1019 | #define MESSAGE_TO_OOBD_BUFFER(msg) \ | ||
| 1020 | (((unsigned char *)(msg)) + msg->oob_data_offset) | ||
| 1021 | |||
| 1022 | /* get pointer to data buffer with message pointer */ | ||
| 1023 | #define MESSAGE_TO_DATA_BUFFER(msg) \ | ||
| 1024 | (((unsigned char *)(msg)) + msg->per_pkt_info_offset) | ||
| 1025 | |||
| 1026 | /* get pointer to contained message from NDIS_MESSAGE pointer */ | ||
| 1027 | #define RNDIS_MESSAGE_PTR_TO_MESSAGE_PTR(rndis_msg) \ | ||
| 1028 | ((void *) &rndis_msg->msg) | ||
| 1029 | |||
| 1030 | /* get pointer to contained message from NDIS_MESSAGE pointer */ | ||
| 1031 | #define RNDIS_MESSAGE_RAW_PTR_TO_MESSAGE_PTR(rndis_msg) \ | ||
| 1032 | ((void *) rndis_msg) | ||
| 1033 | |||
| 1034 | |||
| 1035 | #define __struct_bcount(x) | ||
| 1036 | |||
| 1037 | |||
| 1038 | |||
| 1039 | #define RNDIS_HEADER_SIZE (sizeof(struct rndis_message) - \ | ||
| 1040 | sizeof(union rndis_message_container)) | ||
| 1041 | |||
| 1042 | #define NDIS_PACKET_TYPE_DIRECTED 0x00000001 | ||
| 1043 | #define NDIS_PACKET_TYPE_MULTICAST 0x00000002 | ||
| 1044 | #define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 | ||
| 1045 | #define NDIS_PACKET_TYPE_BROADCAST 0x00000008 | ||
| 1046 | #define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010 | ||
| 1047 | #define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020 | ||
| 1048 | #define NDIS_PACKET_TYPE_SMT 0x00000040 | ||
| 1049 | #define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080 | ||
| 1050 | #define NDIS_PACKET_TYPE_GROUP 0x00000100 | ||
| 1051 | #define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200 | ||
| 1052 | #define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 | ||
| 1053 | #define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 | ||
| 1054 | |||
| 1055 | |||
| 1056 | |||
| 1057 | #endif /* _HYPERV_NET_H */ | ||
diff --git a/drivers/staging/hv/hyperv_storage.h b/drivers/staging/hv/hyperv_storage.h new file mode 100644 index 00000000000..5af82f4235b --- /dev/null +++ b/drivers/staging/hv/hyperv_storage.h | |||
| @@ -0,0 +1,335 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * Copyright (c) 2011, Microsoft Corporation. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms and conditions of the GNU General Public License, | ||
| 7 | * version 2, as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | * more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License along with | ||
| 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
| 16 | * Place - Suite 330, Boston, MA 02111-1307 USA. | ||
| 17 | * | ||
| 18 | * Authors: | ||
| 19 | * Haiyang Zhang <haiyangz@microsoft.com> | ||
| 20 | * Hank Janssen <hjanssen@microsoft.com> | ||
| 21 | * K. Y. Srinivasan <kys@microsoft.com> | ||
| 22 | * | ||
| 23 | */ | ||
| 24 | |||
| 25 | #ifndef _HYPERV_STORAGE_H | ||
| 26 | #define _HYPERV_STORAGE_H | ||
| 27 | |||
| 28 | |||
| 29 | /* vstorage.w revision number. This is used in the case of a version match, */ | ||
| 30 | /* to alert the user that structure sizes may be mismatched even though the */ | ||
| 31 | /* protocol versions match. */ | ||
| 32 | |||
| 33 | |||
| 34 | #define REVISION_STRING(REVISION_) #REVISION_ | ||
| 35 | #define FILL_VMSTOR_REVISION(RESULT_LVALUE_) \ | ||
| 36 | do { \ | ||
| 37 | char *revision_string \ | ||
| 38 | = REVISION_STRING($Rev : 6 $) + 6; \ | ||
| 39 | RESULT_LVALUE_ = 0; \ | ||
| 40 | while (*revision_string >= '0' \ | ||
| 41 | && *revision_string <= '9') { \ | ||
| 42 | RESULT_LVALUE_ *= 10; \ | ||
| 43 | RESULT_LVALUE_ += *revision_string - '0'; \ | ||
| 44 | revision_string++; \ | ||
| 45 | } \ | ||
| 46 | } while (0) | ||
| 47 | |||
| 48 | /* Major/minor macros. Minor version is in LSB, meaning that earlier flat */ | ||
| 49 | /* version numbers will be interpreted as "0.x" (i.e., 1 becomes 0.1). */ | ||
| 50 | #define VMSTOR_PROTOCOL_MAJOR(VERSION_) (((VERSION_) >> 8) & 0xff) | ||
| 51 | #define VMSTOR_PROTOCOL_MINOR(VERSION_) (((VERSION_)) & 0xff) | ||
| 52 | #define VMSTOR_PROTOCOL_VERSION(MAJOR_, MINOR_) ((((MAJOR_) & 0xff) << 8) | \ | ||
| 53 | (((MINOR_) & 0xff))) | ||
| 54 | #define VMSTOR_INVALID_PROTOCOL_VERSION (-1) | ||
| 55 | |||
| 56 | /* Version history: */ | ||
| 57 | /* V1 Beta 0.1 */ | ||
| 58 | /* V1 RC < 2008/1/31 1.0 */ | ||
| 59 | /* V1 RC > 2008/1/31 2.0 */ | ||
| 60 | #define VMSTOR_PROTOCOL_VERSION_CURRENT VMSTOR_PROTOCOL_VERSION(2, 0) | ||
| 61 | |||
| 62 | |||
| 63 | |||
| 64 | |||
| 65 | /* This will get replaced with the max transfer length that is possible on */ | ||
| 66 | /* the host adapter. */ | ||
| 67 | /* The max transfer length will be published when we offer a vmbus channel. */ | ||
| 68 | #define MAX_TRANSFER_LENGTH 0x40000 | ||
| 69 | #define DEFAULT_PACKET_SIZE (sizeof(struct vmdata_gpa_direct) + \ | ||
| 70 | sizeof(struct vstor_packet) + \ | ||
| 71 | sizesizeof(u64) * (MAX_TRANSFER_LENGTH / PAGE_SIZE))) | ||
| 72 | |||
| 73 | |||
| 74 | /* Packet structure describing virtual storage requests. */ | ||
| 75 | enum vstor_packet_operation { | ||
| 76 | VSTOR_OPERATION_COMPLETE_IO = 1, | ||
| 77 | VSTOR_OPERATION_REMOVE_DEVICE = 2, | ||
| 78 | VSTOR_OPERATION_EXECUTE_SRB = 3, | ||
| 79 | VSTOR_OPERATION_RESET_LUN = 4, | ||
| 80 | VSTOR_OPERATION_RESET_ADAPTER = 5, | ||
| 81 | VSTOR_OPERATION_RESET_BUS = 6, | ||
| 82 | VSTOR_OPERATION_BEGIN_INITIALIZATION = 7, | ||
| 83 | VSTOR_OPERATION_END_INITIALIZATION = 8, | ||
| 84 | VSTOR_OPERATION_QUERY_PROTOCOL_VERSION = 9, | ||
| 85 | VSTOR_OPERATION_QUERY_PROPERTIES = 10, | ||
| 86 | VSTOR_OPERATION_MAXIMUM = 10 | ||
| 87 | }; | ||
| 88 | |||
| 89 | /* | ||
| 90 | * Platform neutral description of a scsi request - | ||
| 91 | * this remains the same across the write regardless of 32/64 bit | ||
| 92 | * note: it's patterned off the SCSI_PASS_THROUGH structure | ||
| 93 | */ | ||
| 94 | #define CDB16GENERIC_LENGTH 0x10 | ||
| 95 | |||
| 96 | #ifndef SENSE_BUFFER_SIZE | ||
| 97 | #define SENSE_BUFFER_SIZE 0x12 | ||
| 98 | #endif | ||
| 99 | |||
| 100 | #define MAX_DATA_BUF_LEN_WITH_PADDING 0x14 | ||
| 101 | |||
| 102 | struct vmscsi_request { | ||
| 103 | unsigned short length; | ||
| 104 | unsigned char srb_status; | ||
| 105 | unsigned char scsi_status; | ||
| 106 | |||
| 107 | unsigned char port_number; | ||
| 108 | unsigned char path_id; | ||
| 109 | unsigned char target_id; | ||
| 110 | unsigned char lun; | ||
| 111 | |||
| 112 | unsigned char cdb_length; | ||
| 113 | unsigned char sense_info_length; | ||
| 114 | unsigned char data_in; | ||
| 115 | unsigned char reserved; | ||
| 116 | |||
| 117 | unsigned int data_transfer_length; | ||
| 118 | |||
| 119 | union { | ||
| 120 | unsigned char cdb[CDB16GENERIC_LENGTH]; | ||
| 121 | unsigned char sense_data[SENSE_BUFFER_SIZE]; | ||
| 122 | unsigned char reserved_array[MAX_DATA_BUF_LEN_WITH_PADDING]; | ||
| 123 | }; | ||
| 124 | } __attribute((packed)); | ||
| 125 | |||
| 126 | |||
| 127 | /* | ||
| 128 | * This structure is sent during the intialization phase to get the different | ||
| 129 | * properties of the channel. | ||
| 130 | */ | ||
| 131 | struct vmstorage_channel_properties { | ||
| 132 | unsigned short protocol_version; | ||
| 133 | unsigned char path_id; | ||
| 134 | unsigned char target_id; | ||
| 135 | |||
| 136 | /* Note: port number is only really known on the client side */ | ||
| 137 | unsigned int port_number; | ||
| 138 | unsigned int flags; | ||
| 139 | unsigned int max_transfer_bytes; | ||
| 140 | |||
| 141 | /* This id is unique for each channel and will correspond with */ | ||
| 142 | /* vendor specific data in the inquirydata */ | ||
| 143 | unsigned long long unique_id; | ||
| 144 | } __packed; | ||
| 145 | |||
| 146 | /* This structure is sent during the storage protocol negotiations. */ | ||
| 147 | struct vmstorage_protocol_version { | ||
| 148 | /* Major (MSW) and minor (LSW) version numbers. */ | ||
| 149 | unsigned short major_minor; | ||
| 150 | |||
| 151 | /* | ||
| 152 | * Revision number is auto-incremented whenever this file is changed | ||
| 153 | * (See FILL_VMSTOR_REVISION macro above). Mismatch does not | ||
| 154 | * definitely indicate incompatibility--but it does indicate mismatched | ||
| 155 | * builds. | ||
| 156 | */ | ||
| 157 | unsigned short revision; | ||
| 158 | } __packed; | ||
| 159 | |||
| 160 | /* Channel Property Flags */ | ||
| 161 | #define STORAGE_CHANNEL_REMOVABLE_FLAG 0x1 | ||
| 162 | #define STORAGE_CHANNEL_EMULATED_IDE_FLAG 0x2 | ||
| 163 | |||
| 164 | struct vstor_packet { | ||
| 165 | /* Requested operation type */ | ||
| 166 | enum vstor_packet_operation operation; | ||
| 167 | |||
| 168 | /* Flags - see below for values */ | ||
| 169 | unsigned int flags; | ||
| 170 | |||
| 171 | /* Status of the request returned from the server side. */ | ||
| 172 | unsigned int status; | ||
| 173 | |||
| 174 | /* Data payload area */ | ||
| 175 | union { | ||
| 176 | /* | ||
| 177 | * Structure used to forward SCSI commands from the | ||
| 178 | * client to the server. | ||
| 179 | */ | ||
| 180 | struct vmscsi_request vm_srb; | ||
| 181 | |||
| 182 | /* Structure used to query channel properties. */ | ||
| 183 | struct vmstorage_channel_properties storage_channel_properties; | ||
| 184 | |||
| 185 | /* Used during version negotiations. */ | ||
| 186 | struct vmstorage_protocol_version version; | ||
| 187 | }; | ||
| 188 | } __packed; | ||
| 189 | |||
| 190 | /* Packet flags */ | ||
| 191 | /* | ||
| 192 | * This flag indicates that the server should send back a completion for this | ||
| 193 | * packet. | ||
| 194 | */ | ||
| 195 | #define REQUEST_COMPLETION_FLAG 0x1 | ||
| 196 | |||
| 197 | /* This is the set of flags that the vsc can set in any packets it sends */ | ||
| 198 | #define VSC_LEGAL_FLAGS (REQUEST_COMPLETION_FLAG) | ||
| 199 | |||
| 200 | |||
| 201 | #include <linux/kernel.h> | ||
| 202 | #include <linux/wait.h> | ||
| 203 | #include "hyperv_storage.h" | ||
| 204 | #include "hyperv.h" | ||
| 205 | |||
| 206 | /* Defines */ | ||
| 207 | #define STORVSC_RING_BUFFER_SIZE (20*PAGE_SIZE) | ||
| 208 | #define BLKVSC_RING_BUFFER_SIZE (20*PAGE_SIZE) | ||
| 209 | |||
| 210 | #define STORVSC_MAX_IO_REQUESTS 128 | ||
| 211 | |||
| 212 | /* | ||
| 213 | * In Hyper-V, each port/path/target maps to 1 scsi host adapter. In | ||
| 214 | * reality, the path/target is not used (ie always set to 0) so our | ||
| 215 | * scsi host adapter essentially has 1 bus with 1 target that contains | ||
| 216 | * up to 256 luns. | ||
| 217 | */ | ||
| 218 | #define STORVSC_MAX_LUNS_PER_TARGET 64 | ||
| 219 | #define STORVSC_MAX_TARGETS 1 | ||
| 220 | #define STORVSC_MAX_CHANNELS 1 | ||
| 221 | #define STORVSC_MAX_CMD_LEN 16 | ||
| 222 | |||
| 223 | struct hv_storvsc_request; | ||
| 224 | |||
| 225 | /* Matches Windows-end */ | ||
| 226 | enum storvsc_request_type { | ||
| 227 | WRITE_TYPE, | ||
| 228 | READ_TYPE, | ||
| 229 | UNKNOWN_TYPE, | ||
| 230 | }; | ||
| 231 | |||
| 232 | |||
| 233 | struct hv_storvsc_request { | ||
| 234 | struct hv_storvsc_request *request; | ||
| 235 | struct hv_device *device; | ||
| 236 | |||
| 237 | /* Synchronize the request/response if needed */ | ||
| 238 | struct completion wait_event; | ||
| 239 | |||
| 240 | unsigned char *sense_buffer; | ||
| 241 | void *context; | ||
| 242 | void (*on_io_completion)(struct hv_storvsc_request *request); | ||
| 243 | struct hv_multipage_buffer data_buffer; | ||
| 244 | |||
| 245 | struct vstor_packet vstor_packet; | ||
| 246 | }; | ||
| 247 | |||
| 248 | |||
| 249 | struct storvsc_device_info { | ||
| 250 | u32 ring_buffer_size; | ||
| 251 | unsigned int port_number; | ||
| 252 | unsigned char path_id; | ||
| 253 | unsigned char target_id; | ||
| 254 | }; | ||
| 255 | |||
| 256 | struct storvsc_major_info { | ||
| 257 | int major; | ||
| 258 | int index; | ||
| 259 | bool do_register; | ||
| 260 | char *devname; | ||
| 261 | char *diskname; | ||
| 262 | }; | ||
| 263 | |||
| 264 | /* A storvsc device is a device object that contains a vmbus channel */ | ||
| 265 | struct storvsc_device { | ||
| 266 | struct hv_device *device; | ||
| 267 | |||
| 268 | /* 0 indicates the device is being destroyed */ | ||
| 269 | atomic_t ref_count; | ||
| 270 | |||
| 271 | bool drain_notify; | ||
| 272 | atomic_t num_outstanding_req; | ||
| 273 | |||
| 274 | wait_queue_head_t waiting_to_drain; | ||
| 275 | |||
| 276 | /* | ||
| 277 | * Each unique Port/Path/Target represents 1 channel ie scsi | ||
| 278 | * controller. In reality, the pathid, targetid is always 0 | ||
| 279 | * and the port is set by us | ||
| 280 | */ | ||
| 281 | unsigned int port_number; | ||
| 282 | unsigned char path_id; | ||
| 283 | unsigned char target_id; | ||
| 284 | |||
| 285 | /* Used for vsc/vsp channel reset process */ | ||
| 286 | struct hv_storvsc_request init_request; | ||
| 287 | struct hv_storvsc_request reset_request; | ||
| 288 | }; | ||
| 289 | |||
| 290 | |||
| 291 | /* Get the stordevice object iff exists and its refcount > 1 */ | ||
| 292 | static inline struct storvsc_device *get_stor_device(struct hv_device *device) | ||
| 293 | { | ||
| 294 | struct storvsc_device *stor_device; | ||
| 295 | |||
| 296 | stor_device = (struct storvsc_device *)device->ext; | ||
| 297 | if (stor_device && atomic_read(&stor_device->ref_count) > 1) | ||
| 298 | atomic_inc(&stor_device->ref_count); | ||
| 299 | else | ||
| 300 | stor_device = NULL; | ||
| 301 | |||
| 302 | return stor_device; | ||
| 303 | } | ||
| 304 | |||
| 305 | |||
| 306 | static inline void put_stor_device(struct hv_device *device) | ||
| 307 | { | ||
| 308 | struct storvsc_device *stor_device; | ||
| 309 | |||
| 310 | stor_device = (struct storvsc_device *)device->ext; | ||
| 311 | |||
| 312 | atomic_dec(&stor_device->ref_count); | ||
| 313 | } | ||
| 314 | |||
| 315 | static inline void storvsc_wait_to_drain(struct storvsc_device *dev) | ||
| 316 | { | ||
| 317 | dev->drain_notify = true; | ||
| 318 | wait_event(dev->waiting_to_drain, | ||
| 319 | atomic_read(&dev->num_outstanding_req) == 0); | ||
| 320 | dev->drain_notify = false; | ||
| 321 | } | ||
| 322 | |||
| 323 | /* Interface */ | ||
| 324 | |||
| 325 | int storvsc_dev_add(struct hv_device *device, | ||
| 326 | void *additional_info); | ||
| 327 | int storvsc_dev_remove(struct hv_device *device); | ||
| 328 | |||
| 329 | int storvsc_do_io(struct hv_device *device, | ||
| 330 | struct hv_storvsc_request *request); | ||
| 331 | |||
| 332 | int storvsc_get_major_info(struct storvsc_device_info *device_info, | ||
| 333 | struct storvsc_major_info *major_info); | ||
| 334 | |||
| 335 | #endif /* _HYPERV_STORAGE_H */ | ||
diff --git a/drivers/staging/hv/hyperv_vmbus.h b/drivers/staging/hv/hyperv_vmbus.h new file mode 100644 index 00000000000..349ad80ce32 --- /dev/null +++ b/drivers/staging/hv/hyperv_vmbus.h | |||
| @@ -0,0 +1,629 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * Copyright (c) 2011, Microsoft Corporation. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms and conditions of the GNU General Public License, | ||
| 7 | * version 2, as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | * more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License along with | ||
| 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
| 16 | * Place - Suite 330, Boston, MA 02111-1307 USA. | ||
| 17 | * | ||
| 18 | * Authors: | ||
| 19 | * Haiyang Zhang <haiyangz@microsoft.com> | ||
| 20 | * Hank Janssen <hjanssen@microsoft.com> | ||
| 21 | * K. Y. Srinivasan <kys@microsoft.com> | ||
| 22 | * | ||
| 23 | */ | ||
| 24 | |||
| 25 | #ifndef _HYPERV_VMBUS_H | ||
| 26 | #define _HYPERV_VMBUS_H | ||
| 27 | |||
| 28 | #include <linux/list.h> | ||
| 29 | #include <asm/sync_bitops.h> | ||
| 30 | #include <linux/atomic.h> | ||
| 31 | |||
| 32 | #include "hyperv.h" | ||
| 33 | |||
| 34 | /* | ||
| 35 | * The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent | ||
| 36 | * is set by CPUID(HVCPUID_VERSION_FEATURES). | ||
| 37 | */ | ||
| 38 | enum hv_cpuid_function { | ||
| 39 | HVCPUID_VERSION_FEATURES = 0x00000001, | ||
| 40 | HVCPUID_VENDOR_MAXFUNCTION = 0x40000000, | ||
| 41 | HVCPUID_INTERFACE = 0x40000001, | ||
| 42 | |||
| 43 | /* | ||
| 44 | * The remaining functions depend on the value of | ||
| 45 | * HVCPUID_INTERFACE | ||
| 46 | */ | ||
| 47 | HVCPUID_VERSION = 0x40000002, | ||
| 48 | HVCPUID_FEATURES = 0x40000003, | ||
| 49 | HVCPUID_ENLIGHTENMENT_INFO = 0x40000004, | ||
| 50 | HVCPUID_IMPLEMENTATION_LIMITS = 0x40000005, | ||
| 51 | }; | ||
| 52 | |||
| 53 | /* Define version of the synthetic interrupt controller. */ | ||
| 54 | #define HV_SYNIC_VERSION (1) | ||
| 55 | |||
| 56 | /* Define the expected SynIC version. */ | ||
| 57 | #define HV_SYNIC_VERSION_1 (0x1) | ||
| 58 | |||
| 59 | /* Define synthetic interrupt controller message constants. */ | ||
| 60 | #define HV_MESSAGE_SIZE (256) | ||
| 61 | #define HV_MESSAGE_PAYLOAD_BYTE_COUNT (240) | ||
| 62 | #define HV_MESSAGE_PAYLOAD_QWORD_COUNT (30) | ||
| 63 | #define HV_ANY_VP (0xFFFFFFFF) | ||
| 64 | |||
| 65 | /* Define synthetic interrupt controller flag constants. */ | ||
| 66 | #define HV_EVENT_FLAGS_COUNT (256 * 8) | ||
| 67 | #define HV_EVENT_FLAGS_BYTE_COUNT (256) | ||
| 68 | #define HV_EVENT_FLAGS_DWORD_COUNT (256 / sizeof(u32)) | ||
| 69 | |||
| 70 | /* Define hypervisor message types. */ | ||
| 71 | enum hv_message_type { | ||
| 72 | HVMSG_NONE = 0x00000000, | ||
| 73 | |||
| 74 | /* Memory access messages. */ | ||
| 75 | HVMSG_UNMAPPED_GPA = 0x80000000, | ||
| 76 | HVMSG_GPA_INTERCEPT = 0x80000001, | ||
| 77 | |||
| 78 | /* Timer notification messages. */ | ||
| 79 | HVMSG_TIMER_EXPIRED = 0x80000010, | ||
| 80 | |||
| 81 | /* Error messages. */ | ||
| 82 | HVMSG_INVALID_VP_REGISTER_VALUE = 0x80000020, | ||
| 83 | HVMSG_UNRECOVERABLE_EXCEPTION = 0x80000021, | ||
| 84 | HVMSG_UNSUPPORTED_FEATURE = 0x80000022, | ||
| 85 | |||
| 86 | /* Trace buffer complete messages. */ | ||
| 87 | HVMSG_EVENTLOG_BUFFERCOMPLETE = 0x80000040, | ||
| 88 | |||
| 89 | /* Platform-specific processor intercept messages. */ | ||
| 90 | HVMSG_X64_IOPORT_INTERCEPT = 0x80010000, | ||
| 91 | HVMSG_X64_MSR_INTERCEPT = 0x80010001, | ||
| 92 | HVMSG_X64_CPUID_INTERCEPT = 0x80010002, | ||
| 93 | HVMSG_X64_EXCEPTION_INTERCEPT = 0x80010003, | ||
| 94 | HVMSG_X64_APIC_EOI = 0x80010004, | ||
| 95 | HVMSG_X64_LEGACY_FP_ERROR = 0x80010005 | ||
| 96 | }; | ||
| 97 | |||
| 98 | /* Define the number of synthetic interrupt sources. */ | ||
| 99 | #define HV_SYNIC_SINT_COUNT (16) | ||
| 100 | #define HV_SYNIC_STIMER_COUNT (4) | ||
| 101 | |||
| 102 | /* Define invalid partition identifier. */ | ||
| 103 | #define HV_PARTITION_ID_INVALID ((u64)0x0) | ||
| 104 | |||
| 105 | /* Define connection identifier type. */ | ||
| 106 | union hv_connection_id { | ||
| 107 | u32 asu32; | ||
| 108 | struct { | ||
| 109 | u32 id:24; | ||
| 110 | u32 reserved:8; | ||
| 111 | } u; | ||
| 112 | }; | ||
| 113 | |||
| 114 | /* Define port identifier type. */ | ||
| 115 | union hv_port_id { | ||
| 116 | u32 asu32; | ||
| 117 | struct { | ||
| 118 | u32 id:24; | ||
| 119 | u32 reserved:8; | ||
| 120 | } u ; | ||
| 121 | }; | ||
| 122 | |||
| 123 | /* Define port type. */ | ||
| 124 | enum hv_port_type { | ||
| 125 | HVPORT_MSG = 1, | ||
| 126 | HVPORT_EVENT = 2, | ||
| 127 | HVPORT_MONITOR = 3 | ||
| 128 | }; | ||
| 129 | |||
| 130 | /* Define port information structure. */ | ||
| 131 | struct hv_port_info { | ||
| 132 | enum hv_port_type port_type; | ||
| 133 | u32 padding; | ||
| 134 | union { | ||
| 135 | struct { | ||
| 136 | u32 target_sint; | ||
| 137 | u32 target_vp; | ||
| 138 | u64 rsvdz; | ||
| 139 | } message_port_info; | ||
| 140 | struct { | ||
| 141 | u32 target_sint; | ||
| 142 | u32 target_vp; | ||
| 143 | u16 base_flag_bumber; | ||
| 144 | u16 flag_count; | ||
| 145 | u32 rsvdz; | ||
| 146 | } event_port_info; | ||
| 147 | struct { | ||
| 148 | u64 monitor_address; | ||
| 149 | u64 rsvdz; | ||
| 150 | } monitor_port_info; | ||
| 151 | }; | ||
| 152 | }; | ||
| 153 | |||
| 154 | struct hv_connection_info { | ||
| 155 | enum hv_port_type port_type; | ||
| 156 | u32 padding; | ||
| 157 | union { | ||
| 158 | struct { | ||
| 159 | u64 rsvdz; | ||
| 160 | } message_connection_info; | ||
| 161 | struct { | ||
| 162 | u64 rsvdz; | ||
| 163 | } event_connection_info; | ||
| 164 | struct { | ||
| 165 | u64 monitor_address; | ||
| 166 | } monitor_connection_info; | ||
| 167 | }; | ||
| 168 | }; | ||
| 169 | |||
| 170 | /* Define synthetic interrupt controller message flags. */ | ||
| 171 | union hv_message_flags { | ||
| 172 | u8 asu8; | ||
| 173 | struct { | ||
| 174 | u8 msg_pending:1; | ||
| 175 | u8 reserved:7; | ||
| 176 | }; | ||
| 177 | }; | ||
| 178 | |||
| 179 | /* Define synthetic interrupt controller message header. */ | ||
| 180 | struct hv_message_header { | ||
| 181 | enum hv_message_type message_type; | ||
| 182 | u8 payload_size; | ||
| 183 | union hv_message_flags message_flags; | ||
| 184 | u8 reserved[2]; | ||
| 185 | union { | ||
| 186 | u64 sender; | ||
| 187 | union hv_port_id port; | ||
| 188 | }; | ||
| 189 | }; | ||
| 190 | |||
| 191 | /* Define timer message payload structure. */ | ||
| 192 | struct hv_timer_message_payload { | ||
| 193 | u32 timer_index; | ||
| 194 | u32 reserved; | ||
| 195 | u64 expiration_time; /* When the timer expired */ | ||
| 196 | u64 delivery_time; /* When the message was delivered */ | ||
| 197 | }; | ||
| 198 | |||
| 199 | /* Define synthetic interrupt controller message format. */ | ||
| 200 | struct hv_message { | ||
| 201 | struct hv_message_header header; | ||
| 202 | union { | ||
| 203 | u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT]; | ||
| 204 | } u ; | ||
| 205 | }; | ||
| 206 | |||
| 207 | /* Define the number of message buffers associated with each port. */ | ||
| 208 | #define HV_PORT_MESSAGE_BUFFER_COUNT (16) | ||
| 209 | |||
| 210 | /* Define the synthetic interrupt message page layout. */ | ||
| 211 | struct hv_message_page { | ||
| 212 | struct hv_message sint_message[HV_SYNIC_SINT_COUNT]; | ||
| 213 | }; | ||
| 214 | |||
| 215 | /* Define the synthetic interrupt controller event flags format. */ | ||
| 216 | union hv_synic_event_flags { | ||
| 217 | u8 flags8[HV_EVENT_FLAGS_BYTE_COUNT]; | ||
| 218 | u32 flags32[HV_EVENT_FLAGS_DWORD_COUNT]; | ||
| 219 | }; | ||
| 220 | |||
| 221 | /* Define the synthetic interrupt flags page layout. */ | ||
| 222 | struct hv_synic_event_flags_page { | ||
| 223 | union hv_synic_event_flags sintevent_flags[HV_SYNIC_SINT_COUNT]; | ||
| 224 | }; | ||
| 225 | |||
| 226 | /* Define SynIC control register. */ | ||
| 227 | union hv_synic_scontrol { | ||
| 228 | u64 as_uint64; | ||
| 229 | struct { | ||
| 230 | u64 enable:1; | ||
| 231 | u64 reserved:63; | ||
| 232 | }; | ||
| 233 | }; | ||
| 234 | |||
| 235 | /* Define synthetic interrupt source. */ | ||
| 236 | union hv_synic_sint { | ||
| 237 | u64 as_uint64; | ||
| 238 | struct { | ||
| 239 | u64 vector:8; | ||
| 240 | u64 reserved1:8; | ||
| 241 | u64 masked:1; | ||
| 242 | u64 auto_eoi:1; | ||
| 243 | u64 reserved2:46; | ||
| 244 | }; | ||
| 245 | }; | ||
| 246 | |||
| 247 | /* Define the format of the SIMP register */ | ||
| 248 | union hv_synic_simp { | ||
| 249 | u64 as_uint64; | ||
| 250 | struct { | ||
| 251 | u64 simp_enabled:1; | ||
| 252 | u64 preserved:11; | ||
| 253 | u64 base_simp_gpa:52; | ||
| 254 | }; | ||
| 255 | }; | ||
| 256 | |||
| 257 | /* Define the format of the SIEFP register */ | ||
| 258 | union hv_synic_siefp { | ||
| 259 | u64 as_uint64; | ||
| 260 | struct { | ||
| 261 | u64 siefp_enabled:1; | ||
| 262 | u64 preserved:11; | ||
| 263 | u64 base_siefp_gpa:52; | ||
| 264 | }; | ||
| 265 | }; | ||
| 266 | |||
| 267 | /* Definitions for the monitored notification facility */ | ||
| 268 | union hv_monitor_trigger_group { | ||
| 269 | u64 as_uint64; | ||
| 270 | struct { | ||
| 271 | u32 pending; | ||
| 272 | u32 armed; | ||
| 273 | }; | ||
| 274 | }; | ||
| 275 | |||
| 276 | struct hv_monitor_parameter { | ||
| 277 | union hv_connection_id connectionid; | ||
| 278 | u16 flagnumber; | ||
| 279 | u16 rsvdz; | ||
| 280 | }; | ||
| 281 | |||
| 282 | union hv_monitor_trigger_state { | ||
| 283 | u32 asu32; | ||
| 284 | |||
| 285 | struct { | ||
| 286 | u32 group_enable:4; | ||
| 287 | u32 rsvdz:28; | ||
| 288 | }; | ||
| 289 | }; | ||
| 290 | |||
| 291 | /* struct hv_monitor_page Layout */ | ||
| 292 | /* ------------------------------------------------------ */ | ||
| 293 | /* | 0 | TriggerState (4 bytes) | Rsvd1 (4 bytes) | */ | ||
| 294 | /* | 8 | TriggerGroup[0] | */ | ||
| 295 | /* | 10 | TriggerGroup[1] | */ | ||
| 296 | /* | 18 | TriggerGroup[2] | */ | ||
| 297 | /* | 20 | TriggerGroup[3] | */ | ||
| 298 | /* | 28 | Rsvd2[0] | */ | ||
| 299 | /* | 30 | Rsvd2[1] | */ | ||
| 300 | /* | 38 | Rsvd2[2] | */ | ||
| 301 | /* | 40 | NextCheckTime[0][0] | NextCheckTime[0][1] | */ | ||
| 302 | /* | ... | */ | ||
| 303 | /* | 240 | Latency[0][0..3] | */ | ||
| 304 | /* | 340 | Rsvz3[0] | */ | ||
| 305 | /* | 440 | Parameter[0][0] | */ | ||
| 306 | /* | 448 | Parameter[0][1] | */ | ||
| 307 | /* | ... | */ | ||
| 308 | /* | 840 | Rsvd4[0] | */ | ||
| 309 | /* ------------------------------------------------------ */ | ||
| 310 | struct hv_monitor_page { | ||
| 311 | union hv_monitor_trigger_state trigger_state; | ||
| 312 | u32 rsvdz1; | ||
| 313 | |||
| 314 | union hv_monitor_trigger_group trigger_group[4]; | ||
| 315 | u64 rsvdz2[3]; | ||
| 316 | |||
| 317 | s32 next_checktime[4][32]; | ||
| 318 | |||
| 319 | u16 latency[4][32]; | ||
| 320 | u64 rsvdz3[32]; | ||
| 321 | |||
| 322 | struct hv_monitor_parameter parameter[4][32]; | ||
| 323 | |||
| 324 | u8 rsvdz4[1984]; | ||
| 325 | }; | ||
| 326 | |||
| 327 | /* Declare the various hypercall operations. */ | ||
| 328 | enum hv_call_code { | ||
| 329 | HVCALL_POST_MESSAGE = 0x005c, | ||
| 330 | HVCALL_SIGNAL_EVENT = 0x005d, | ||
| 331 | }; | ||
| 332 | |||
| 333 | /* Definition of the hv_post_message hypercall input structure. */ | ||
| 334 | struct hv_input_post_message { | ||
| 335 | union hv_connection_id connectionid; | ||
| 336 | u32 reserved; | ||
| 337 | enum hv_message_type message_type; | ||
| 338 | u32 payload_size; | ||
| 339 | u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT]; | ||
| 340 | }; | ||
| 341 | |||
| 342 | /* Definition of the hv_signal_event hypercall input structure. */ | ||
| 343 | struct hv_input_signal_event { | ||
| 344 | union hv_connection_id connectionid; | ||
| 345 | u16 flag_number; | ||
| 346 | u16 rsvdz; | ||
| 347 | }; | ||
| 348 | |||
| 349 | /* | ||
| 350 | * Versioning definitions used for guests reporting themselves to the | ||
| 351 | * hypervisor, and visa versa. | ||
| 352 | */ | ||
| 353 | |||
| 354 | /* Version info reported by guest OS's */ | ||
| 355 | enum hv_guest_os_vendor { | ||
| 356 | HVGUESTOS_VENDOR_MICROSOFT = 0x0001 | ||
| 357 | }; | ||
| 358 | |||
| 359 | enum hv_guest_os_microsoft_ids { | ||
| 360 | HVGUESTOS_MICROSOFT_UNDEFINED = 0x00, | ||
| 361 | HVGUESTOS_MICROSOFT_MSDOS = 0x01, | ||
| 362 | HVGUESTOS_MICROSOFT_WINDOWS3X = 0x02, | ||
| 363 | HVGUESTOS_MICROSOFT_WINDOWS9X = 0x03, | ||
| 364 | HVGUESTOS_MICROSOFT_WINDOWSNT = 0x04, | ||
| 365 | HVGUESTOS_MICROSOFT_WINDOWSCE = 0x05 | ||
| 366 | }; | ||
| 367 | |||
| 368 | /* | ||
| 369 | * Declare the MSR used to identify the guest OS. | ||
| 370 | */ | ||
| 371 | #define HV_X64_MSR_GUEST_OS_ID 0x40000000 | ||
| 372 | |||
| 373 | union hv_x64_msr_guest_os_id_contents { | ||
| 374 | u64 as_uint64; | ||
| 375 | struct { | ||
| 376 | u64 build_number:16; | ||
| 377 | u64 service_version:8; /* Service Pack, etc. */ | ||
| 378 | u64 minor_version:8; | ||
| 379 | u64 major_version:8; | ||
| 380 | u64 os_id:8; /* enum hv_guest_os_microsoft_ids (if Vendor=MS) */ | ||
| 381 | u64 vendor_id:16; /* enum hv_guest_os_vendor */ | ||
| 382 | }; | ||
| 383 | }; | ||
| 384 | |||
| 385 | /* | ||
| 386 | * Declare the MSR used to setup pages used to communicate with the hypervisor. | ||
| 387 | */ | ||
| 388 | #define HV_X64_MSR_HYPERCALL 0x40000001 | ||
| 389 | |||
| 390 | union hv_x64_msr_hypercall_contents { | ||
| 391 | u64 as_uint64; | ||
| 392 | struct { | ||
| 393 | u64 enable:1; | ||
| 394 | u64 reserved:11; | ||
| 395 | u64 guest_physical_address:52; | ||
| 396 | }; | ||
| 397 | }; | ||
| 398 | |||
| 399 | |||
| 400 | enum { | ||
| 401 | VMBUS_MESSAGE_CONNECTION_ID = 1, | ||
| 402 | VMBUS_MESSAGE_PORT_ID = 1, | ||
| 403 | VMBUS_EVENT_CONNECTION_ID = 2, | ||
| 404 | VMBUS_EVENT_PORT_ID = 2, | ||
| 405 | VMBUS_MONITOR_CONNECTION_ID = 3, | ||
| 406 | VMBUS_MONITOR_PORT_ID = 3, | ||
| 407 | VMBUS_MESSAGE_SINT = 2, | ||
| 408 | }; | ||
| 409 | |||
| 410 | /* #defines */ | ||
| 411 | |||
| 412 | #define HV_PRESENT_BIT 0x80000000 | ||
| 413 | |||
| 414 | #define HV_LINUX_GUEST_ID_LO 0x00000000 | ||
| 415 | #define HV_LINUX_GUEST_ID_HI 0xB16B00B5 | ||
| 416 | #define HV_LINUX_GUEST_ID (((u64)HV_LINUX_GUEST_ID_HI << 32) | \ | ||
| 417 | HV_LINUX_GUEST_ID_LO) | ||
| 418 | |||
| 419 | #define HV_CPU_POWER_MANAGEMENT (1 << 0) | ||
| 420 | #define HV_RECOMMENDATIONS_MAX 4 | ||
| 421 | |||
| 422 | #define HV_X64_MAX 5 | ||
| 423 | #define HV_CAPS_MAX 8 | ||
| 424 | |||
| 425 | |||
| 426 | #define HV_HYPERCALL_PARAM_ALIGN sizeof(u64) | ||
| 427 | |||
| 428 | |||
| 429 | /* Service definitions */ | ||
| 430 | |||
| 431 | #define HV_SERVICE_PARENT_PORT (0) | ||
| 432 | #define HV_SERVICE_PARENT_CONNECTION (0) | ||
| 433 | |||
| 434 | #define HV_SERVICE_CONNECT_RESPONSE_SUCCESS (0) | ||
| 435 | #define HV_SERVICE_CONNECT_RESPONSE_INVALID_PARAMETER (1) | ||
| 436 | #define HV_SERVICE_CONNECT_RESPONSE_UNKNOWN_SERVICE (2) | ||
| 437 | #define HV_SERVICE_CONNECT_RESPONSE_CONNECTION_REJECTED (3) | ||
| 438 | |||
| 439 | #define HV_SERVICE_CONNECT_REQUEST_MESSAGE_ID (1) | ||
| 440 | #define HV_SERVICE_CONNECT_RESPONSE_MESSAGE_ID (2) | ||
| 441 | #define HV_SERVICE_DISCONNECT_REQUEST_MESSAGE_ID (3) | ||
| 442 | #define HV_SERVICE_DISCONNECT_RESPONSE_MESSAGE_ID (4) | ||
| 443 | #define HV_SERVICE_MAX_MESSAGE_ID (4) | ||
| 444 | |||
| 445 | #define HV_SERVICE_PROTOCOL_VERSION (0x0010) | ||
| 446 | #define HV_CONNECT_PAYLOAD_BYTE_COUNT 64 | ||
| 447 | |||
| 448 | /* #define VMBUS_REVISION_NUMBER 6 */ | ||
| 449 | |||
| 450 | /* Our local vmbus's port and connection id. Anything >0 is fine */ | ||
| 451 | /* #define VMBUS_PORT_ID 11 */ | ||
| 452 | |||
| 453 | /* 628180B8-308D-4c5e-B7DB-1BEB62E62EF4 */ | ||
| 454 | static const struct hv_guid VMBUS_SERVICE_ID = { | ||
| 455 | .data = { | ||
| 456 | 0xb8, 0x80, 0x81, 0x62, 0x8d, 0x30, 0x5e, 0x4c, | ||
| 457 | 0xb7, 0xdb, 0x1b, 0xeb, 0x62, 0xe6, 0x2e, 0xf4 | ||
| 458 | }, | ||
| 459 | }; | ||
| 460 | |||
| 461 | #define MAX_NUM_CPUS 32 | ||
| 462 | |||
| 463 | |||
| 464 | struct hv_input_signal_event_buffer { | ||
| 465 | u64 align8; | ||
| 466 | struct hv_input_signal_event event; | ||
| 467 | }; | ||
| 468 | |||
| 469 | struct hv_context { | ||
| 470 | /* We only support running on top of Hyper-V | ||
| 471 | * So at this point this really can only contain the Hyper-V ID | ||
| 472 | */ | ||
| 473 | u64 guestid; | ||
| 474 | |||
| 475 | void *hypercall_page; | ||
| 476 | |||
| 477 | bool synic_initialized; | ||
| 478 | |||
| 479 | /* | ||
| 480 | * This is used as an input param to HvCallSignalEvent hypercall. The | ||
| 481 | * input param is immutable in our usage and must be dynamic mem (vs | ||
| 482 | * stack or global). */ | ||
| 483 | struct hv_input_signal_event_buffer *signal_event_buffer; | ||
| 484 | /* 8-bytes aligned of the buffer above */ | ||
| 485 | struct hv_input_signal_event *signal_event_param; | ||
| 486 | |||
| 487 | void *synic_message_page[MAX_NUM_CPUS]; | ||
| 488 | void *synic_event_page[MAX_NUM_CPUS]; | ||
| 489 | }; | ||
| 490 | |||
| 491 | extern struct hv_context hv_context; | ||
| 492 | |||
| 493 | |||
| 494 | /* Hv Interface */ | ||
| 495 | |||
| 496 | extern int hv_init(void); | ||
| 497 | |||
| 498 | extern void hv_cleanup(void); | ||
| 499 | |||
| 500 | extern u16 hv_post_message(union hv_connection_id connection_id, | ||
| 501 | enum hv_message_type message_type, | ||
| 502 | void *payload, size_t payload_size); | ||
| 503 | |||
| 504 | extern u16 hv_signal_event(void); | ||
| 505 | |||
| 506 | extern void hv_synic_init(void *irqarg); | ||
| 507 | |||
| 508 | extern void hv_synic_cleanup(void *arg); | ||
| 509 | |||
| 510 | |||
| 511 | /* Interface */ | ||
| 512 | |||
| 513 | |||
| 514 | int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, void *buffer, | ||
| 515 | u32 buflen); | ||
| 516 | |||
| 517 | void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info); | ||
| 518 | |||
| 519 | int hv_ringbuffer_write(struct hv_ring_buffer_info *ring_info, | ||
| 520 | struct scatterlist *sglist, | ||
| 521 | u32 sgcount); | ||
| 522 | |||
| 523 | int hv_ringbuffer_peek(struct hv_ring_buffer_info *ring_info, void *buffer, | ||
| 524 | u32 buflen); | ||
| 525 | |||
| 526 | int hv_ringbuffer_read(struct hv_ring_buffer_info *ring_info, | ||
| 527 | void *buffer, | ||
| 528 | u32 buflen, | ||
| 529 | u32 offset); | ||
| 530 | |||
| 531 | u32 hv_get_ringbuffer_interrupt_mask(struct hv_ring_buffer_info *ring_info); | ||
| 532 | |||
| 533 | void hv_dump_ring_info(struct hv_ring_buffer_info *ring_info, char *prefix); | ||
| 534 | |||
| 535 | void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info, | ||
| 536 | struct hv_ring_buffer_debug_info *debug_info); | ||
| 537 | |||
| 538 | /* | ||
| 539 | * Maximum channels is determined by the size of the interrupt page | ||
| 540 | * which is PAGE_SIZE. 1/2 of PAGE_SIZE is for send endpoint interrupt | ||
| 541 | * and the other is receive endpoint interrupt | ||
| 542 | */ | ||
| 543 | #define MAX_NUM_CHANNELS ((PAGE_SIZE >> 1) << 3) /* 16348 channels */ | ||
| 544 | |||
| 545 | /* The value here must be in multiple of 32 */ | ||
| 546 | /* TODO: Need to make this configurable */ | ||
| 547 | #define MAX_NUM_CHANNELS_SUPPORTED 256 | ||
| 548 | |||
| 549 | |||
| 550 | enum vmbus_connect_state { | ||
| 551 | DISCONNECTED, | ||
| 552 | CONNECTING, | ||
| 553 | CONNECTED, | ||
| 554 | DISCONNECTING | ||
| 555 | }; | ||
| 556 | |||
| 557 | #define MAX_SIZE_CHANNEL_MESSAGE HV_MESSAGE_PAYLOAD_BYTE_COUNT | ||
| 558 | |||
| 559 | struct vmbus_connection { | ||
| 560 | enum vmbus_connect_state conn_state; | ||
| 561 | |||
| 562 | atomic_t next_gpadl_handle; | ||
| 563 | |||
| 564 | /* | ||
| 565 | * Represents channel interrupts. Each bit position represents a | ||
| 566 | * channel. When a channel sends an interrupt via VMBUS, it finds its | ||
| 567 | * bit in the sendInterruptPage, set it and calls Hv to generate a port | ||
| 568 | * event. The other end receives the port event and parse the | ||
| 569 | * recvInterruptPage to see which bit is set | ||
| 570 | */ | ||
| 571 | void *int_page; | ||
| 572 | void *send_int_page; | ||
| 573 | void *recv_int_page; | ||
| 574 | |||
| 575 | /* | ||
| 576 | * 2 pages - 1st page for parent->child notification and 2nd | ||
| 577 | * is child->parent notification | ||
| 578 | */ | ||
| 579 | void *monitor_pages; | ||
| 580 | struct list_head chn_msg_list; | ||
| 581 | spinlock_t channelmsg_lock; | ||
| 582 | |||
| 583 | /* List of channels */ | ||
| 584 | struct list_head chn_list; | ||
| 585 | spinlock_t channel_lock; | ||
| 586 | |||
| 587 | struct workqueue_struct *work_queue; | ||
| 588 | }; | ||
| 589 | |||
| 590 | |||
| 591 | struct vmbus_msginfo { | ||
| 592 | /* Bookkeeping stuff */ | ||
| 593 | struct list_head msglist_entry; | ||
| 594 | |||
| 595 | /* The message itself */ | ||
| 596 | unsigned char msg[0]; | ||
| 597 | }; | ||
| 598 | |||
| 599 | |||
| 600 | extern struct vmbus_connection vmbus_connection; | ||
| 601 | |||
| 602 | /* General vmbus interface */ | ||
| 603 | |||
| 604 | struct hv_device *vmbus_child_device_create(struct hv_guid *type, | ||
| 605 | struct hv_guid *instance, | ||
| 606 | struct vmbus_channel *channel); | ||
| 607 | |||
| 608 | int vmbus_child_device_register(struct hv_device *child_device_obj); | ||
| 609 | void vmbus_child_device_unregister(struct hv_device *device_obj); | ||
| 610 | |||
| 611 | /* static void */ | ||
| 612 | /* VmbusChildDeviceDestroy( */ | ||
| 613 | /* struct hv_device *); */ | ||
| 614 | |||
| 615 | struct vmbus_channel *relid2channel(u32 relid); | ||
| 616 | |||
| 617 | |||
| 618 | /* Connection interface */ | ||
| 619 | |||
| 620 | int vmbus_connect(void); | ||
| 621 | |||
| 622 | int vmbus_post_msg(void *buffer, size_t buflen); | ||
| 623 | |||
| 624 | int vmbus_set_event(u32 child_relid); | ||
| 625 | |||
| 626 | void vmbus_on_event(unsigned long data); | ||
| 627 | |||
| 628 | |||
| 629 | #endif /* _HYPERV_VMBUS_H */ | ||
diff --git a/drivers/staging/hv/netvsc.c b/drivers/staging/hv/netvsc.c new file mode 100644 index 00000000000..dc5e5c488e3 --- /dev/null +++ b/drivers/staging/hv/netvsc.c | |||
| @@ -0,0 +1,1015 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2009, Microsoft Corporation. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms and conditions of the GNU General Public License, | ||
| 6 | * version 2, as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License along with | ||
| 14 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
| 15 | * Place - Suite 330, Boston, MA 02111-1307 USA. | ||
| 16 | * | ||
| 17 | * Authors: | ||
| 18 | * Haiyang Zhang <haiyangz@microsoft.com> | ||
| 19 | * Hank Janssen <hjanssen@microsoft.com> | ||
| 20 | */ | ||
| 21 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 22 | |||
| 23 | #include <linux/kernel.h> | ||
| 24 | #include <linux/sched.h> | ||
| 25 | #include <linux/wait.h> | ||
| 26 | #include <linux/mm.h> | ||
| 27 | #include <linux/delay.h> | ||
| 28 | #include <linux/io.h> | ||
| 29 | #include <linux/slab.h> | ||
| 30 | |||
| 31 | #include "hyperv.h" | ||
| 32 | #include "hyperv_net.h" | ||
| 33 | |||
| 34 | |||
| 35 | /* Globals */ | ||
| 36 | static const char *driver_name = "netvsc"; | ||
| 37 | |||
| 38 | /* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */ | ||
| 39 | static const struct hv_guid netvsc_device_type = { | ||
| 40 | .data = { | ||
| 41 | 0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, | ||
| 42 | 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E | ||
| 43 | } | ||
| 44 | }; | ||
| 45 | |||
| 46 | |||
| 47 | static struct netvsc_device *alloc_net_device(struct hv_device *device) | ||
| 48 | { | ||
| 49 | struct netvsc_device *net_device; | ||
| 50 | |||
| 51 | net_device = kzalloc(sizeof(struct netvsc_device), GFP_KERNEL); | ||
| 52 | if (!net_device) | ||
| 53 | return NULL; | ||
| 54 | |||
| 55 | /* Set to 2 to allow both inbound and outbound traffic */ | ||
| 56 | atomic_cmpxchg(&net_device->refcnt, 0, 2); | ||
| 57 | |||
| 58 | net_device->dev = device; | ||
| 59 | device->ext = net_device; | ||
| 60 | |||
| 61 | return net_device; | ||
| 62 | } | ||
| 63 | |||
| 64 | static void free_net_device(struct netvsc_device *device) | ||
| 65 | { | ||
| 66 | WARN_ON(atomic_read(&device->refcnt) != 0); | ||
| 67 | device->dev->ext = NULL; | ||
| 68 | kfree(device); | ||
| 69 | } | ||
| 70 | |||
| 71 | |||
| 72 | /* Get the net device object iff exists and its refcount > 1 */ | ||
| 73 | static struct netvsc_device *get_outbound_net_device(struct hv_device *device) | ||
| 74 | { | ||
| 75 | struct netvsc_device *net_device; | ||
| 76 | |||
| 77 | net_device = device->ext; | ||
| 78 | if (net_device && atomic_read(&net_device->refcnt) > 1) | ||
| 79 | atomic_inc(&net_device->refcnt); | ||
| 80 | else | ||
| 81 | net_device = NULL; | ||
| 82 | |||
| 83 | return net_device; | ||
| 84 | } | ||
| 85 | |||
| 86 | /* Get the net device object iff exists and its refcount > 0 */ | ||
| 87 | static struct netvsc_device *get_inbound_net_device(struct hv_device *device) | ||
| 88 | { | ||
| 89 | struct netvsc_device *net_device; | ||
| 90 | |||
| 91 | net_device = device->ext; | ||
| 92 | if (net_device && atomic_read(&net_device->refcnt)) | ||
| 93 | atomic_inc(&net_device->refcnt); | ||
| 94 | else | ||
| 95 | net_device = NULL; | ||
| 96 | |||
| 97 | return net_device; | ||
| 98 | } | ||
| 99 | |||
| 100 | static void put_net_device(struct hv_device *device) | ||
| 101 | { | ||
| 102 | struct netvsc_device *net_device; | ||
| 103 | |||
| 104 | net_device = device->ext; | ||
| 105 | |||
| 106 | atomic_dec(&net_device->refcnt); | ||
| 107 | } | ||
| 108 | |||
| 109 | static struct netvsc_device *release_outbound_net_device( | ||
| 110 | struct hv_device *device) | ||
| 111 | { | ||
| 112 | struct netvsc_device *net_device; | ||
| 113 | |||
| 114 | net_device = device->ext; | ||
| 115 | if (net_device == NULL) | ||
| 116 | return NULL; | ||
| 117 | |||
| 118 | /* Busy wait until the ref drop to 2, then set it to 1 */ | ||
| 119 | while (atomic_cmpxchg(&net_device->refcnt, 2, 1) != 2) | ||
| 120 | udelay(100); | ||
| 121 | |||
| 122 | return net_device; | ||
| 123 | } | ||
| 124 | |||
| 125 | static struct netvsc_device *release_inbound_net_device( | ||
| 126 | struct hv_device *device) | ||
| 127 | { | ||
| 128 | struct netvsc_device *net_device; | ||
| 129 | |||
| 130 | net_device = device->ext; | ||
| 131 | if (net_device == NULL) | ||
| 132 | return NULL; | ||
| 133 | |||
| 134 | /* Busy wait until the ref drop to 1, then set it to 0 */ | ||
| 135 | while (atomic_cmpxchg(&net_device->refcnt, 1, 0) != 1) | ||
| 136 | udelay(100); | ||
| 137 | |||
| 138 | device->ext = NULL; | ||
| 139 | return net_device; | ||
| 140 | } | ||
| 141 | |||
| 142 | static int netvsc_destroy_recv_buf(struct netvsc_device *net_device) | ||
| 143 | { | ||
| 144 | struct nvsp_message *revoke_packet; | ||
| 145 | int ret = 0; | ||
| 146 | |||
| 147 | /* | ||
| 148 | * If we got a section count, it means we received a | ||
| 149 | * SendReceiveBufferComplete msg (ie sent | ||
| 150 | * NvspMessage1TypeSendReceiveBuffer msg) therefore, we need | ||
| 151 | * to send a revoke msg here | ||
| 152 | */ | ||
| 153 | if (net_device->recv_section_cnt) { | ||
| 154 | /* Send the revoke receive buffer */ | ||
| 155 | revoke_packet = &net_device->revoke_packet; | ||
| 156 | memset(revoke_packet, 0, sizeof(struct nvsp_message)); | ||
| 157 | |||
| 158 | revoke_packet->hdr.msg_type = | ||
| 159 | NVSP_MSG1_TYPE_REVOKE_RECV_BUF; | ||
| 160 | revoke_packet->msg.v1_msg. | ||
| 161 | revoke_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID; | ||
| 162 | |||
| 163 | ret = vmbus_sendpacket(net_device->dev->channel, | ||
| 164 | revoke_packet, | ||
| 165 | sizeof(struct nvsp_message), | ||
| 166 | (unsigned long)revoke_packet, | ||
| 167 | VM_PKT_DATA_INBAND, 0); | ||
| 168 | /* | ||
| 169 | * If we failed here, we might as well return and | ||
| 170 | * have a leak rather than continue and a bugchk | ||
| 171 | */ | ||
| 172 | if (ret != 0) { | ||
| 173 | dev_err(&net_device->dev->device, "unable to send " | ||
| 174 | "revoke receive buffer to netvsp"); | ||
| 175 | return -1; | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | /* Teardown the gpadl on the vsp end */ | ||
| 180 | if (net_device->recv_buf_gpadl_handle) { | ||
| 181 | ret = vmbus_teardown_gpadl(net_device->dev->channel, | ||
| 182 | net_device->recv_buf_gpadl_handle); | ||
| 183 | |||
| 184 | /* If we failed here, we might as well return and have a leak | ||
| 185 | * rather than continue and a bugchk | ||
| 186 | */ | ||
| 187 | if (ret != 0) { | ||
| 188 | dev_err(&net_device->dev->device, | ||
| 189 | "unable to teardown receive buffer's gpadl"); | ||
| 190 | return -1; | ||
| 191 | } | ||
| 192 | net_device->recv_buf_gpadl_handle = 0; | ||
| 193 | } | ||
| 194 | |||
| 195 | if (net_device->recv_buf) { | ||
| 196 | /* Free up the receive buffer */ | ||
| 197 | free_pages((unsigned long)net_device->recv_buf, | ||
| 198 | get_order(net_device->recv_buf_size)); | ||
| 199 | net_device->recv_buf = NULL; | ||
| 200 | } | ||
| 201 | |||
| 202 | if (net_device->recv_section) { | ||
| 203 | net_device->recv_section_cnt = 0; | ||
| 204 | kfree(net_device->recv_section); | ||
| 205 | net_device->recv_section = NULL; | ||
| 206 | } | ||
| 207 | |||
| 208 | return ret; | ||
| 209 | } | ||
| 210 | |||
| 211 | static int netvsc_init_recv_buf(struct hv_device *device) | ||
| 212 | { | ||
| 213 | int ret = 0; | ||
| 214 | int t; | ||
| 215 | struct netvsc_device *net_device; | ||
| 216 | struct nvsp_message *init_packet; | ||
| 217 | |||
| 218 | net_device = get_outbound_net_device(device); | ||
| 219 | if (!net_device) { | ||
| 220 | dev_err(&device->device, "unable to get net device..." | ||
| 221 | "device being destroyed?"); | ||
| 222 | return -1; | ||
| 223 | } | ||
| 224 | |||
| 225 | net_device->recv_buf = | ||
| 226 | (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, | ||
| 227 | get_order(net_device->recv_buf_size)); | ||
| 228 | if (!net_device->recv_buf) { | ||
| 229 | dev_err(&device->device, "unable to allocate receive " | ||
| 230 | "buffer of size %d", net_device->recv_buf_size); | ||
| 231 | ret = -1; | ||
| 232 | goto cleanup; | ||
| 233 | } | ||
| 234 | |||
| 235 | /* | ||
| 236 | * Establish the gpadl handle for this buffer on this | ||
| 237 | * channel. Note: This call uses the vmbus connection rather | ||
| 238 | * than the channel to establish the gpadl handle. | ||
| 239 | */ | ||
| 240 | ret = vmbus_establish_gpadl(device->channel, net_device->recv_buf, | ||
| 241 | net_device->recv_buf_size, | ||
| 242 | &net_device->recv_buf_gpadl_handle); | ||
| 243 | if (ret != 0) { | ||
| 244 | dev_err(&device->device, | ||
| 245 | "unable to establish receive buffer's gpadl"); | ||
| 246 | goto cleanup; | ||
| 247 | } | ||
| 248 | |||
| 249 | |||
| 250 | /* Notify the NetVsp of the gpadl handle */ | ||
| 251 | init_packet = &net_device->channel_init_pkt; | ||
| 252 | |||
| 253 | memset(init_packet, 0, sizeof(struct nvsp_message)); | ||
| 254 | |||
| 255 | init_packet->hdr.msg_type = NVSP_MSG1_TYPE_SEND_RECV_BUF; | ||
| 256 | init_packet->msg.v1_msg.send_recv_buf. | ||
| 257 | gpadl_handle = net_device->recv_buf_gpadl_handle; | ||
| 258 | init_packet->msg.v1_msg. | ||
| 259 | send_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID; | ||
| 260 | |||
| 261 | /* Send the gpadl notification request */ | ||
| 262 | ret = vmbus_sendpacket(device->channel, init_packet, | ||
| 263 | sizeof(struct nvsp_message), | ||
| 264 | (unsigned long)init_packet, | ||
| 265 | VM_PKT_DATA_INBAND, | ||
| 266 | VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); | ||
| 267 | if (ret != 0) { | ||
| 268 | dev_err(&device->device, | ||
| 269 | "unable to send receive buffer's gpadl to netvsp"); | ||
| 270 | goto cleanup; | ||
| 271 | } | ||
| 272 | |||
| 273 | t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ); | ||
| 274 | BUG_ON(t == 0); | ||
| 275 | |||
| 276 | |||
| 277 | /* Check the response */ | ||
| 278 | if (init_packet->msg.v1_msg. | ||
| 279 | send_recv_buf_complete.status != NVSP_STAT_SUCCESS) { | ||
| 280 | dev_err(&device->device, "Unable to complete receive buffer " | ||
| 281 | "initialzation with NetVsp - status %d", | ||
| 282 | init_packet->msg.v1_msg. | ||
| 283 | send_recv_buf_complete.status); | ||
| 284 | ret = -1; | ||
| 285 | goto cleanup; | ||
| 286 | } | ||
| 287 | |||
| 288 | /* Parse the response */ | ||
| 289 | |||
| 290 | net_device->recv_section_cnt = init_packet->msg. | ||
| 291 | v1_msg.send_recv_buf_complete.num_sections; | ||
| 292 | |||
| 293 | net_device->recv_section = kmalloc(net_device->recv_section_cnt | ||
| 294 | * sizeof(struct nvsp_1_receive_buffer_section), GFP_KERNEL); | ||
| 295 | if (net_device->recv_section == NULL) { | ||
| 296 | ret = -1; | ||
| 297 | goto cleanup; | ||
| 298 | } | ||
| 299 | |||
| 300 | memcpy(net_device->recv_section, | ||
| 301 | init_packet->msg.v1_msg. | ||
| 302 | send_recv_buf_complete.sections, | ||
| 303 | net_device->recv_section_cnt * | ||
| 304 | sizeof(struct nvsp_1_receive_buffer_section)); | ||
| 305 | |||
| 306 | /* | ||
| 307 | * For 1st release, there should only be 1 section that represents the | ||
| 308 | * entire receive buffer | ||
| 309 | */ | ||
| 310 | if (net_device->recv_section_cnt != 1 || | ||
| 311 | net_device->recv_section->offset != 0) { | ||
| 312 | ret = -1; | ||
| 313 | goto cleanup; | ||
| 314 | } | ||
| 315 | |||
| 316 | goto exit; | ||
| 317 | |||
| 318 | cleanup: | ||
| 319 | netvsc_destroy_recv_buf(net_device); | ||
| 320 | |||
| 321 | exit: | ||
| 322 | put_net_device(device); | ||
| 323 | return ret; | ||
| 324 | } | ||
| 325 | |||
| 326 | |||
| 327 | static int netvsc_connect_vsp(struct hv_device *device) | ||
| 328 | { | ||
| 329 | int ret, t; | ||
| 330 | struct netvsc_device *net_device; | ||
| 331 | struct nvsp_message *init_packet; | ||
| 332 | int ndis_version; | ||
| 333 | |||
| 334 | net_device = get_outbound_net_device(device); | ||
| 335 | if (!net_device) { | ||
| 336 | dev_err(&device->device, "unable to get net device..." | ||
| 337 | "device being destroyed?"); | ||
| 338 | return -1; | ||
| 339 | } | ||
| 340 | |||
| 341 | init_packet = &net_device->channel_init_pkt; | ||
| 342 | |||
| 343 | memset(init_packet, 0, sizeof(struct nvsp_message)); | ||
| 344 | init_packet->hdr.msg_type = NVSP_MSG_TYPE_INIT; | ||
| 345 | init_packet->msg.init_msg.init.min_protocol_ver = | ||
| 346 | NVSP_MIN_PROTOCOL_VERSION; | ||
| 347 | init_packet->msg.init_msg.init.max_protocol_ver = | ||
| 348 | NVSP_MAX_PROTOCOL_VERSION; | ||
| 349 | |||
| 350 | /* Send the init request */ | ||
| 351 | ret = vmbus_sendpacket(device->channel, init_packet, | ||
| 352 | sizeof(struct nvsp_message), | ||
| 353 | (unsigned long)init_packet, | ||
| 354 | VM_PKT_DATA_INBAND, | ||
| 355 | VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); | ||
| 356 | |||
| 357 | if (ret != 0) | ||
| 358 | goto cleanup; | ||
| 359 | |||
| 360 | t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ); | ||
| 361 | |||
| 362 | if (t == 0) { | ||
| 363 | ret = -ETIMEDOUT; | ||
| 364 | goto cleanup; | ||
| 365 | } | ||
| 366 | |||
| 367 | if (init_packet->msg.init_msg.init_complete.status != | ||
| 368 | NVSP_STAT_SUCCESS) { | ||
| 369 | ret = -1; | ||
| 370 | goto cleanup; | ||
| 371 | } | ||
| 372 | |||
| 373 | if (init_packet->msg.init_msg.init_complete. | ||
| 374 | negotiated_protocol_ver != NVSP_PROTOCOL_VERSION_1) { | ||
| 375 | ret = -1; | ||
| 376 | goto cleanup; | ||
| 377 | } | ||
| 378 | /* Send the ndis version */ | ||
| 379 | memset(init_packet, 0, sizeof(struct nvsp_message)); | ||
| 380 | |||
| 381 | ndis_version = 0x00050000; | ||
| 382 | |||
| 383 | init_packet->hdr.msg_type = NVSP_MSG1_TYPE_SEND_NDIS_VER; | ||
| 384 | init_packet->msg.v1_msg. | ||
| 385 | send_ndis_ver.ndis_major_ver = | ||
| 386 | (ndis_version & 0xFFFF0000) >> 16; | ||
| 387 | init_packet->msg.v1_msg. | ||
| 388 | send_ndis_ver.ndis_minor_ver = | ||
| 389 | ndis_version & 0xFFFF; | ||
| 390 | |||
| 391 | /* Send the init request */ | ||
| 392 | ret = vmbus_sendpacket(device->channel, init_packet, | ||
| 393 | sizeof(struct nvsp_message), | ||
| 394 | (unsigned long)init_packet, | ||
| 395 | VM_PKT_DATA_INBAND, 0); | ||
| 396 | if (ret != 0) { | ||
| 397 | ret = -1; | ||
| 398 | goto cleanup; | ||
| 399 | } | ||
| 400 | |||
| 401 | /* Post the big receive buffer to NetVSP */ | ||
| 402 | ret = netvsc_init_recv_buf(device); | ||
| 403 | |||
| 404 | cleanup: | ||
| 405 | put_net_device(device); | ||
| 406 | return ret; | ||
| 407 | } | ||
| 408 | |||
| 409 | static void netvsc_disconnect_vsp(struct netvsc_device *net_device) | ||
| 410 | { | ||
| 411 | netvsc_destroy_recv_buf(net_device); | ||
| 412 | } | ||
| 413 | |||
| 414 | /* | ||
| 415 | * netvsc_device_remove - Callback when the root bus device is removed | ||
| 416 | */ | ||
| 417 | int netvsc_device_remove(struct hv_device *device) | ||
| 418 | { | ||
| 419 | struct netvsc_device *net_device; | ||
| 420 | struct hv_netvsc_packet *netvsc_packet, *pos; | ||
| 421 | |||
| 422 | /* Stop outbound traffic ie sends and receives completions */ | ||
| 423 | net_device = release_outbound_net_device(device); | ||
| 424 | if (!net_device) { | ||
| 425 | dev_err(&device->device, "No net device present!!"); | ||
| 426 | return -1; | ||
| 427 | } | ||
| 428 | |||
| 429 | /* Wait for all send completions */ | ||
| 430 | while (atomic_read(&net_device->num_outstanding_sends)) { | ||
| 431 | dev_err(&device->device, | ||
| 432 | "waiting for %d requests to complete...", | ||
| 433 | atomic_read(&net_device->num_outstanding_sends)); | ||
| 434 | udelay(100); | ||
| 435 | } | ||
| 436 | |||
| 437 | netvsc_disconnect_vsp(net_device); | ||
| 438 | |||
| 439 | /* Stop inbound traffic ie receives and sends completions */ | ||
| 440 | net_device = release_inbound_net_device(device); | ||
| 441 | |||
| 442 | /* At this point, no one should be accessing netDevice except in here */ | ||
| 443 | dev_notice(&device->device, "net device safe to remove"); | ||
| 444 | |||
| 445 | /* Now, we can close the channel safely */ | ||
| 446 | vmbus_close(device->channel); | ||
| 447 | |||
| 448 | /* Release all resources */ | ||
| 449 | list_for_each_entry_safe(netvsc_packet, pos, | ||
| 450 | &net_device->recv_pkt_list, list_ent) { | ||
| 451 | list_del(&netvsc_packet->list_ent); | ||
| 452 | kfree(netvsc_packet); | ||
| 453 | } | ||
| 454 | |||
| 455 | free_net_device(net_device); | ||
| 456 | return 0; | ||
| 457 | } | ||
| 458 | |||
| 459 | static void netvsc_send_completion(struct hv_device *device, | ||
| 460 | struct vmpacket_descriptor *packet) | ||
| 461 | { | ||
| 462 | struct netvsc_device *net_device; | ||
| 463 | struct nvsp_message *nvsp_packet; | ||
| 464 | struct hv_netvsc_packet *nvsc_packet; | ||
| 465 | |||
| 466 | net_device = get_inbound_net_device(device); | ||
| 467 | if (!net_device) { | ||
| 468 | dev_err(&device->device, "unable to get net device..." | ||
| 469 | "device being destroyed?"); | ||
| 470 | return; | ||
| 471 | } | ||
| 472 | |||
| 473 | nvsp_packet = (struct nvsp_message *)((unsigned long)packet + | ||
| 474 | (packet->offset8 << 3)); | ||
| 475 | |||
| 476 | if ((nvsp_packet->hdr.msg_type == NVSP_MSG_TYPE_INIT_COMPLETE) || | ||
| 477 | (nvsp_packet->hdr.msg_type == | ||
| 478 | NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE) || | ||
| 479 | (nvsp_packet->hdr.msg_type == | ||
| 480 | NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE)) { | ||
| 481 | /* Copy the response back */ | ||
| 482 | memcpy(&net_device->channel_init_pkt, nvsp_packet, | ||
| 483 | sizeof(struct nvsp_message)); | ||
| 484 | complete(&net_device->channel_init_wait); | ||
| 485 | } else if (nvsp_packet->hdr.msg_type == | ||
| 486 | NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE) { | ||
| 487 | /* Get the send context */ | ||
| 488 | nvsc_packet = (struct hv_netvsc_packet *)(unsigned long) | ||
| 489 | packet->trans_id; | ||
| 490 | |||
| 491 | /* Notify the layer above us */ | ||
| 492 | nvsc_packet->completion.send.send_completion( | ||
| 493 | nvsc_packet->completion.send.send_completion_ctx); | ||
| 494 | |||
| 495 | atomic_dec(&net_device->num_outstanding_sends); | ||
| 496 | } else { | ||
| 497 | dev_err(&device->device, "Unknown send completion packet type- " | ||
| 498 | "%d received!!", nvsp_packet->hdr.msg_type); | ||
| 499 | } | ||
| 500 | |||
| 501 | put_net_device(device); | ||
| 502 | } | ||
| 503 | |||
| 504 | int netvsc_send(struct hv_device *device, | ||
| 505 | struct hv_netvsc_packet *packet) | ||
| 506 | { | ||
| 507 | struct netvsc_device *net_device; | ||
| 508 | int ret = 0; | ||
| 509 | |||
| 510 | struct nvsp_message sendMessage; | ||
| 511 | |||
| 512 | net_device = get_outbound_net_device(device); | ||
| 513 | if (!net_device) { | ||
| 514 | dev_err(&device->device, "net device (%p) shutting down..." | ||
| 515 | "ignoring outbound packets", net_device); | ||
| 516 | return -2; | ||
| 517 | } | ||
| 518 | |||
| 519 | sendMessage.hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT; | ||
| 520 | if (packet->is_data_pkt) { | ||
| 521 | /* 0 is RMC_DATA; */ | ||
| 522 | sendMessage.msg.v1_msg.send_rndis_pkt.channel_type = 0; | ||
| 523 | } else { | ||
| 524 | /* 1 is RMC_CONTROL; */ | ||
| 525 | sendMessage.msg.v1_msg.send_rndis_pkt.channel_type = 1; | ||
| 526 | } | ||
| 527 | |||
| 528 | /* Not using send buffer section */ | ||
| 529 | sendMessage.msg.v1_msg.send_rndis_pkt.send_buf_section_index = | ||
| 530 | 0xFFFFFFFF; | ||
| 531 | sendMessage.msg.v1_msg.send_rndis_pkt.send_buf_section_size = 0; | ||
| 532 | |||
| 533 | if (packet->page_buf_cnt) { | ||
| 534 | ret = vmbus_sendpacket_pagebuffer(device->channel, | ||
| 535 | packet->page_buf, | ||
| 536 | packet->page_buf_cnt, | ||
| 537 | &sendMessage, | ||
| 538 | sizeof(struct nvsp_message), | ||
| 539 | (unsigned long)packet); | ||
| 540 | } else { | ||
| 541 | ret = vmbus_sendpacket(device->channel, &sendMessage, | ||
| 542 | sizeof(struct nvsp_message), | ||
| 543 | (unsigned long)packet, | ||
| 544 | VM_PKT_DATA_INBAND, | ||
| 545 | VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); | ||
| 546 | |||
| 547 | } | ||
| 548 | |||
| 549 | if (ret != 0) | ||
| 550 | dev_err(&device->device, "Unable to send packet %p ret %d", | ||
| 551 | packet, ret); | ||
| 552 | |||
| 553 | atomic_inc(&net_device->num_outstanding_sends); | ||
| 554 | put_net_device(device); | ||
| 555 | return ret; | ||
| 556 | } | ||
| 557 | |||
| 558 | static void netvsc_send_recv_completion(struct hv_device *device, | ||
| 559 | u64 transaction_id) | ||
| 560 | { | ||
| 561 | struct nvsp_message recvcompMessage; | ||
| 562 | int retries = 0; | ||
| 563 | int ret; | ||
| 564 | |||
| 565 | recvcompMessage.hdr.msg_type = | ||
| 566 | NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE; | ||
| 567 | |||
| 568 | /* FIXME: Pass in the status */ | ||
| 569 | recvcompMessage.msg.v1_msg.send_rndis_pkt_complete.status = | ||
| 570 | NVSP_STAT_SUCCESS; | ||
| 571 | |||
| 572 | retry_send_cmplt: | ||
| 573 | /* Send the completion */ | ||
| 574 | ret = vmbus_sendpacket(device->channel, &recvcompMessage, | ||
| 575 | sizeof(struct nvsp_message), transaction_id, | ||
| 576 | VM_PKT_COMP, 0); | ||
| 577 | if (ret == 0) { | ||
| 578 | /* success */ | ||
| 579 | /* no-op */ | ||
| 580 | } else if (ret == -1) { | ||
| 581 | /* no more room...wait a bit and attempt to retry 3 times */ | ||
| 582 | retries++; | ||
| 583 | dev_err(&device->device, "unable to send receive completion pkt" | ||
| 584 | " (tid %llx)...retrying %d", transaction_id, retries); | ||
| 585 | |||
| 586 | if (retries < 4) { | ||
| 587 | udelay(100); | ||
| 588 | goto retry_send_cmplt; | ||
| 589 | } else { | ||
| 590 | dev_err(&device->device, "unable to send receive " | ||
| 591 | "completion pkt (tid %llx)...give up retrying", | ||
| 592 | transaction_id); | ||
| 593 | } | ||
| 594 | } else { | ||
| 595 | dev_err(&device->device, "unable to send receive " | ||
| 596 | "completion pkt - %llx", transaction_id); | ||
| 597 | } | ||
| 598 | } | ||
| 599 | |||
| 600 | /* Send a receive completion packet to RNDIS device (ie NetVsp) */ | ||
| 601 | static void netvsc_receive_completion(void *context) | ||
| 602 | { | ||
| 603 | struct hv_netvsc_packet *packet = context; | ||
| 604 | struct hv_device *device = (struct hv_device *)packet->device; | ||
| 605 | struct netvsc_device *net_device; | ||
| 606 | u64 transaction_id = 0; | ||
| 607 | bool fsend_receive_comp = false; | ||
| 608 | unsigned long flags; | ||
| 609 | |||
| 610 | /* | ||
| 611 | * Even though it seems logical to do a GetOutboundNetDevice() here to | ||
| 612 | * send out receive completion, we are using GetInboundNetDevice() | ||
| 613 | * since we may have disable outbound traffic already. | ||
| 614 | */ | ||
| 615 | net_device = get_inbound_net_device(device); | ||
| 616 | if (!net_device) { | ||
| 617 | dev_err(&device->device, "unable to get net device..." | ||
| 618 | "device being destroyed?"); | ||
| 619 | return; | ||
| 620 | } | ||
| 621 | |||
| 622 | /* Overloading use of the lock. */ | ||
| 623 | spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags); | ||
| 624 | |||
| 625 | packet->xfer_page_pkt->count--; | ||
| 626 | |||
| 627 | /* | ||
| 628 | * Last one in the line that represent 1 xfer page packet. | ||
| 629 | * Return the xfer page packet itself to the freelist | ||
| 630 | */ | ||
| 631 | if (packet->xfer_page_pkt->count == 0) { | ||
| 632 | fsend_receive_comp = true; | ||
| 633 | transaction_id = packet->completion.recv.recv_completion_tid; | ||
| 634 | list_add_tail(&packet->xfer_page_pkt->list_ent, | ||
| 635 | &net_device->recv_pkt_list); | ||
| 636 | |||
| 637 | } | ||
| 638 | |||
| 639 | /* Put the packet back */ | ||
| 640 | list_add_tail(&packet->list_ent, &net_device->recv_pkt_list); | ||
| 641 | spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, flags); | ||
| 642 | |||
| 643 | /* Send a receive completion for the xfer page packet */ | ||
| 644 | if (fsend_receive_comp) | ||
| 645 | netvsc_send_recv_completion(device, transaction_id); | ||
| 646 | |||
| 647 | put_net_device(device); | ||
| 648 | } | ||
| 649 | |||
| 650 | static void netvsc_receive(struct hv_device *device, | ||
| 651 | struct vmpacket_descriptor *packet) | ||
| 652 | { | ||
| 653 | struct netvsc_device *net_device; | ||
| 654 | struct vmtransfer_page_packet_header *vmxferpage_packet; | ||
| 655 | struct nvsp_message *nvsp_packet; | ||
| 656 | struct hv_netvsc_packet *netvsc_packet = NULL; | ||
| 657 | unsigned long start; | ||
| 658 | unsigned long end, end_virtual; | ||
| 659 | /* struct netvsc_driver *netvscDriver; */ | ||
| 660 | struct xferpage_packet *xferpage_packet = NULL; | ||
| 661 | int i, j; | ||
| 662 | int count = 0, bytes_remain = 0; | ||
| 663 | unsigned long flags; | ||
| 664 | |||
| 665 | LIST_HEAD(listHead); | ||
| 666 | |||
| 667 | net_device = get_inbound_net_device(device); | ||
| 668 | if (!net_device) { | ||
| 669 | dev_err(&device->device, "unable to get net device..." | ||
| 670 | "device being destroyed?"); | ||
| 671 | return; | ||
| 672 | } | ||
| 673 | |||
| 674 | /* | ||
| 675 | * All inbound packets other than send completion should be xfer page | ||
| 676 | * packet | ||
| 677 | */ | ||
| 678 | if (packet->type != VM_PKT_DATA_USING_XFER_PAGES) { | ||
| 679 | dev_err(&device->device, "Unknown packet type received - %d", | ||
| 680 | packet->type); | ||
| 681 | put_net_device(device); | ||
| 682 | return; | ||
| 683 | } | ||
| 684 | |||
| 685 | nvsp_packet = (struct nvsp_message *)((unsigned long)packet + | ||
| 686 | (packet->offset8 << 3)); | ||
| 687 | |||
| 688 | /* Make sure this is a valid nvsp packet */ | ||
| 689 | if (nvsp_packet->hdr.msg_type != | ||
| 690 | NVSP_MSG1_TYPE_SEND_RNDIS_PKT) { | ||
| 691 | dev_err(&device->device, "Unknown nvsp packet type received-" | ||
| 692 | " %d", nvsp_packet->hdr.msg_type); | ||
| 693 | put_net_device(device); | ||
| 694 | return; | ||
| 695 | } | ||
| 696 | |||
| 697 | vmxferpage_packet = (struct vmtransfer_page_packet_header *)packet; | ||
| 698 | |||
| 699 | if (vmxferpage_packet->xfer_pageset_id != NETVSC_RECEIVE_BUFFER_ID) { | ||
| 700 | dev_err(&device->device, "Invalid xfer page set id - " | ||
| 701 | "expecting %x got %x", NETVSC_RECEIVE_BUFFER_ID, | ||
| 702 | vmxferpage_packet->xfer_pageset_id); | ||
| 703 | put_net_device(device); | ||
| 704 | return; | ||
| 705 | } | ||
| 706 | |||
| 707 | /* | ||
| 708 | * Grab free packets (range count + 1) to represent this xfer | ||
| 709 | * page packet. +1 to represent the xfer page packet itself. | ||
| 710 | * We grab it here so that we know exactly how many we can | ||
| 711 | * fulfil | ||
| 712 | */ | ||
| 713 | spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags); | ||
| 714 | while (!list_empty(&net_device->recv_pkt_list)) { | ||
| 715 | list_move_tail(net_device->recv_pkt_list.next, &listHead); | ||
| 716 | if (++count == vmxferpage_packet->range_cnt + 1) | ||
| 717 | break; | ||
| 718 | } | ||
| 719 | spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, flags); | ||
| 720 | |||
| 721 | /* | ||
| 722 | * We need at least 2 netvsc pkts (1 to represent the xfer | ||
| 723 | * page and at least 1 for the range) i.e. we can handled | ||
| 724 | * some of the xfer page packet ranges... | ||
| 725 | */ | ||
| 726 | if (count < 2) { | ||
| 727 | dev_err(&device->device, "Got only %d netvsc pkt...needed " | ||
| 728 | "%d pkts. Dropping this xfer page packet completely!", | ||
| 729 | count, vmxferpage_packet->range_cnt + 1); | ||
| 730 | |||
| 731 | /* Return it to the freelist */ | ||
| 732 | spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags); | ||
| 733 | for (i = count; i != 0; i--) { | ||
| 734 | list_move_tail(listHead.next, | ||
| 735 | &net_device->recv_pkt_list); | ||
| 736 | } | ||
| 737 | spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, | ||
| 738 | flags); | ||
| 739 | |||
| 740 | netvsc_send_recv_completion(device, | ||
| 741 | vmxferpage_packet->d.trans_id); | ||
| 742 | |||
| 743 | put_net_device(device); | ||
| 744 | return; | ||
| 745 | } | ||
| 746 | |||
| 747 | /* Remove the 1st packet to represent the xfer page packet itself */ | ||
| 748 | xferpage_packet = (struct xferpage_packet *)listHead.next; | ||
| 749 | list_del(&xferpage_packet->list_ent); | ||
| 750 | |||
| 751 | /* This is how much we can satisfy */ | ||
| 752 | xferpage_packet->count = count - 1; | ||
| 753 | |||
| 754 | if (xferpage_packet->count != vmxferpage_packet->range_cnt) { | ||
| 755 | dev_err(&device->device, "Needed %d netvsc pkts to satisy " | ||
| 756 | "this xfer page...got %d", | ||
| 757 | vmxferpage_packet->range_cnt, xferpage_packet->count); | ||
| 758 | } | ||
| 759 | |||
| 760 | /* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */ | ||
| 761 | for (i = 0; i < (count - 1); i++) { | ||
| 762 | netvsc_packet = (struct hv_netvsc_packet *)listHead.next; | ||
| 763 | list_del(&netvsc_packet->list_ent); | ||
| 764 | |||
| 765 | /* Initialize the netvsc packet */ | ||
| 766 | netvsc_packet->xfer_page_pkt = xferpage_packet; | ||
| 767 | netvsc_packet->completion.recv.recv_completion = | ||
| 768 | netvsc_receive_completion; | ||
| 769 | netvsc_packet->completion.recv.recv_completion_ctx = | ||
| 770 | netvsc_packet; | ||
| 771 | netvsc_packet->device = device; | ||
| 772 | /* Save this so that we can send it back */ | ||
| 773 | netvsc_packet->completion.recv.recv_completion_tid = | ||
| 774 | vmxferpage_packet->d.trans_id; | ||
| 775 | |||
| 776 | netvsc_packet->total_data_buflen = | ||
| 777 | vmxferpage_packet->ranges[i].byte_count; | ||
| 778 | netvsc_packet->page_buf_cnt = 1; | ||
| 779 | |||
| 780 | netvsc_packet->page_buf[0].len = | ||
| 781 | vmxferpage_packet->ranges[i].byte_count; | ||
| 782 | |||
| 783 | start = virt_to_phys((void *)((unsigned long)net_device-> | ||
| 784 | recv_buf + vmxferpage_packet->ranges[i].byte_offset)); | ||
| 785 | |||
| 786 | netvsc_packet->page_buf[0].pfn = start >> PAGE_SHIFT; | ||
| 787 | end_virtual = (unsigned long)net_device->recv_buf | ||
| 788 | + vmxferpage_packet->ranges[i].byte_offset | ||
| 789 | + vmxferpage_packet->ranges[i].byte_count - 1; | ||
| 790 | end = virt_to_phys((void *)end_virtual); | ||
| 791 | |||
| 792 | /* Calculate the page relative offset */ | ||
| 793 | netvsc_packet->page_buf[0].offset = | ||
| 794 | vmxferpage_packet->ranges[i].byte_offset & | ||
| 795 | (PAGE_SIZE - 1); | ||
| 796 | if ((end >> PAGE_SHIFT) != (start >> PAGE_SHIFT)) { | ||
| 797 | /* Handle frame across multiple pages: */ | ||
| 798 | netvsc_packet->page_buf[0].len = | ||
| 799 | (netvsc_packet->page_buf[0].pfn << | ||
| 800 | PAGE_SHIFT) | ||
| 801 | + PAGE_SIZE - start; | ||
| 802 | bytes_remain = netvsc_packet->total_data_buflen - | ||
| 803 | netvsc_packet->page_buf[0].len; | ||
| 804 | for (j = 1; j < NETVSC_PACKET_MAXPAGE; j++) { | ||
| 805 | netvsc_packet->page_buf[j].offset = 0; | ||
| 806 | if (bytes_remain <= PAGE_SIZE) { | ||
| 807 | netvsc_packet->page_buf[j].len = | ||
| 808 | bytes_remain; | ||
| 809 | bytes_remain = 0; | ||
| 810 | } else { | ||
| 811 | netvsc_packet->page_buf[j].len = | ||
| 812 | PAGE_SIZE; | ||
| 813 | bytes_remain -= PAGE_SIZE; | ||
| 814 | } | ||
| 815 | netvsc_packet->page_buf[j].pfn = | ||
| 816 | virt_to_phys((void *)(end_virtual - | ||
| 817 | bytes_remain)) >> PAGE_SHIFT; | ||
| 818 | netvsc_packet->page_buf_cnt++; | ||
| 819 | if (bytes_remain == 0) | ||
| 820 | break; | ||
| 821 | } | ||
| 822 | } | ||
| 823 | |||
| 824 | /* Pass it to the upper layer */ | ||
| 825 | rndis_filter_receive(device, netvsc_packet); | ||
| 826 | |||
| 827 | netvsc_receive_completion(netvsc_packet-> | ||
| 828 | completion.recv.recv_completion_ctx); | ||
| 829 | } | ||
| 830 | |||
| 831 | put_net_device(device); | ||
| 832 | } | ||
| 833 | |||
| 834 | static void netvsc_channel_cb(void *context) | ||
| 835 | { | ||
| 836 | int ret; | ||
| 837 | struct hv_device *device = context; | ||
| 838 | struct netvsc_device *net_device; | ||
| 839 | u32 bytes_recvd; | ||
| 840 | u64 request_id; | ||
| 841 | unsigned char *packet; | ||
| 842 | struct vmpacket_descriptor *desc; | ||
| 843 | unsigned char *buffer; | ||
| 844 | int bufferlen = NETVSC_PACKET_SIZE; | ||
| 845 | |||
| 846 | packet = kzalloc(NETVSC_PACKET_SIZE * sizeof(unsigned char), | ||
| 847 | GFP_ATOMIC); | ||
| 848 | if (!packet) | ||
| 849 | return; | ||
| 850 | buffer = packet; | ||
| 851 | |||
| 852 | net_device = get_inbound_net_device(device); | ||
| 853 | if (!net_device) { | ||
| 854 | dev_err(&device->device, "net device (%p) shutting down..." | ||
| 855 | "ignoring inbound packets", net_device); | ||
| 856 | goto out; | ||
| 857 | } | ||
| 858 | |||
| 859 | do { | ||
| 860 | ret = vmbus_recvpacket_raw(device->channel, buffer, bufferlen, | ||
| 861 | &bytes_recvd, &request_id); | ||
| 862 | if (ret == 0) { | ||
| 863 | if (bytes_recvd > 0) { | ||
| 864 | desc = (struct vmpacket_descriptor *)buffer; | ||
| 865 | switch (desc->type) { | ||
| 866 | case VM_PKT_COMP: | ||
| 867 | netvsc_send_completion(device, desc); | ||
| 868 | break; | ||
| 869 | |||
| 870 | case VM_PKT_DATA_USING_XFER_PAGES: | ||
| 871 | netvsc_receive(device, desc); | ||
| 872 | break; | ||
| 873 | |||
| 874 | default: | ||
| 875 | dev_err(&device->device, | ||
| 876 | "unhandled packet type %d, " | ||
| 877 | "tid %llx len %d\n", | ||
| 878 | desc->type, request_id, | ||
| 879 | bytes_recvd); | ||
| 880 | break; | ||
| 881 | } | ||
| 882 | |||
| 883 | /* reset */ | ||
| 884 | if (bufferlen > NETVSC_PACKET_SIZE) { | ||
| 885 | kfree(buffer); | ||
| 886 | buffer = packet; | ||
| 887 | bufferlen = NETVSC_PACKET_SIZE; | ||
| 888 | } | ||
| 889 | } else { | ||
| 890 | /* reset */ | ||
| 891 | if (bufferlen > NETVSC_PACKET_SIZE) { | ||
| 892 | kfree(buffer); | ||
| 893 | buffer = packet; | ||
| 894 | bufferlen = NETVSC_PACKET_SIZE; | ||
| 895 | } | ||
| 896 | |||
| 897 | break; | ||
| 898 | } | ||
| 899 | } else if (ret == -2) { | ||
| 900 | /* Handle large packet */ | ||
| 901 | buffer = kmalloc(bytes_recvd, GFP_ATOMIC); | ||
| 902 | if (buffer == NULL) { | ||
| 903 | /* Try again next time around */ | ||
| 904 | dev_err(&device->device, | ||
| 905 | "unable to allocate buffer of size " | ||
| 906 | "(%d)!!", bytes_recvd); | ||
| 907 | break; | ||
| 908 | } | ||
| 909 | |||
| 910 | bufferlen = bytes_recvd; | ||
| 911 | } | ||
| 912 | } while (1); | ||
| 913 | |||
| 914 | put_net_device(device); | ||
| 915 | out: | ||
| 916 | kfree(buffer); | ||
| 917 | return; | ||
| 918 | } | ||
| 919 | |||
| 920 | /* | ||
| 921 | * netvsc_device_add - Callback when the device belonging to this | ||
| 922 | * driver is added | ||
| 923 | */ | ||
| 924 | int netvsc_device_add(struct hv_device *device, void *additional_info) | ||
| 925 | { | ||
| 926 | int ret = 0; | ||
| 927 | int i; | ||
| 928 | int ring_size = | ||
| 929 | ((struct netvsc_device_info *)additional_info)->ring_size; | ||
| 930 | struct netvsc_device *net_device; | ||
| 931 | struct hv_netvsc_packet *packet, *pos; | ||
| 932 | |||
| 933 | net_device = alloc_net_device(device); | ||
| 934 | if (!net_device) { | ||
| 935 | ret = -1; | ||
| 936 | goto cleanup; | ||
| 937 | } | ||
| 938 | |||
| 939 | /* Initialize the NetVSC channel extension */ | ||
| 940 | net_device->recv_buf_size = NETVSC_RECEIVE_BUFFER_SIZE; | ||
| 941 | spin_lock_init(&net_device->recv_pkt_list_lock); | ||
| 942 | |||
| 943 | INIT_LIST_HEAD(&net_device->recv_pkt_list); | ||
| 944 | |||
| 945 | for (i = 0; i < NETVSC_RECEIVE_PACKETLIST_COUNT; i++) { | ||
| 946 | packet = kzalloc(sizeof(struct hv_netvsc_packet) + | ||
| 947 | (NETVSC_RECEIVE_SG_COUNT * | ||
| 948 | sizeof(struct hv_page_buffer)), GFP_KERNEL); | ||
| 949 | if (!packet) | ||
| 950 | break; | ||
| 951 | |||
| 952 | list_add_tail(&packet->list_ent, | ||
| 953 | &net_device->recv_pkt_list); | ||
| 954 | } | ||
| 955 | init_completion(&net_device->channel_init_wait); | ||
| 956 | |||
| 957 | /* Open the channel */ | ||
| 958 | ret = vmbus_open(device->channel, ring_size * PAGE_SIZE, | ||
| 959 | ring_size * PAGE_SIZE, NULL, 0, | ||
| 960 | netvsc_channel_cb, device); | ||
| 961 | |||
| 962 | if (ret != 0) { | ||
| 963 | dev_err(&device->device, "unable to open channel: %d", ret); | ||
| 964 | ret = -1; | ||
| 965 | goto cleanup; | ||
| 966 | } | ||
| 967 | |||
| 968 | /* Channel is opened */ | ||
| 969 | pr_info("hv_netvsc channel opened successfully"); | ||
| 970 | |||
| 971 | /* Connect with the NetVsp */ | ||
| 972 | ret = netvsc_connect_vsp(device); | ||
| 973 | if (ret != 0) { | ||
| 974 | dev_err(&device->device, | ||
| 975 | "unable to connect to NetVSP - %d", ret); | ||
| 976 | ret = -1; | ||
| 977 | goto close; | ||
| 978 | } | ||
| 979 | |||
| 980 | return ret; | ||
| 981 | |||
| 982 | close: | ||
| 983 | /* Now, we can close the channel safely */ | ||
| 984 | vmbus_close(device->channel); | ||
| 985 | |||
| 986 | cleanup: | ||
| 987 | |||
| 988 | if (net_device) { | ||
| 989 | list_for_each_entry_safe(packet, pos, | ||
| 990 | &net_device->recv_pkt_list, | ||
| 991 | list_ent) { | ||
| 992 | list_del(&packet->list_ent); | ||
| 993 | kfree(packet); | ||
| 994 | } | ||
| 995 | |||
| 996 | release_outbound_net_device(device); | ||
| 997 | release_inbound_net_device(device); | ||
| 998 | |||
| 999 | free_net_device(net_device); | ||
| 1000 | } | ||
| 1001 | |||
| 1002 | return ret; | ||
| 1003 | } | ||
| 1004 | |||
| 1005 | /* | ||
| 1006 | * netvsc_initialize - Main entry point | ||
| 1007 | */ | ||
| 1008 | int netvsc_initialize(struct hv_driver *drv) | ||
| 1009 | { | ||
| 1010 | |||
| 1011 | drv->name = driver_name; | ||
| 1012 | memcpy(&drv->dev_type, &netvsc_device_type, sizeof(struct hv_guid)); | ||
| 1013 | |||
| 1014 | return 0; | ||
| 1015 | } | ||
diff --git a/drivers/staging/hv/netvsc_drv.c b/drivers/staging/hv/netvsc_drv.c new file mode 100644 index 00000000000..88d519359e6 --- /dev/null +++ b/drivers/staging/hv/netvsc_drv.c | |||
| @@ -0,0 +1,476 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2009, Microsoft Corporation. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms and conditions of the GNU General Public License, | ||
| 6 | * version 2, as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License along with | ||
| 14 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
| 15 | * Place - Suite 330, Boston, MA 02111-1307 USA. | ||
| 16 | * | ||
| 17 | * Authors: | ||
| 18 | * Haiyang Zhang <haiyangz@microsoft.com> | ||
| 19 | * Hank Janssen <hjanssen@microsoft.com> | ||
| 20 | */ | ||
| 21 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 22 | |||
| 23 | #include <linux/init.h> | ||
| 24 | #include <linux/atomic.h> | ||
| 25 | #include <linux/module.h> | ||
| 26 | #include <linux/highmem.h> | ||
| 27 | #include <linux/device.h> | ||
| 28 | #include <linux/io.h> | ||
| 29 | #include <linux/delay.h> | ||
| 30 | #include <linux/netdevice.h> | ||
| 31 | #include <linux/inetdevice.h> | ||
| 32 | #include <linux/etherdevice.h> | ||
| 33 | #include <linux/skbuff.h> | ||
| 34 | #include <linux/in.h> | ||
| 35 | #include <linux/slab.h> | ||
| 36 | #include <linux/dmi.h> | ||
| 37 | #include <linux/pci.h> | ||
| 38 | #include <net/arp.h> | ||
| 39 | #include <net/route.h> | ||
| 40 | #include <net/sock.h> | ||
| 41 | #include <net/pkt_sched.h> | ||
| 42 | |||
| 43 | #include "hyperv.h" | ||
| 44 | #include "hyperv_net.h" | ||
| 45 | |||
| 46 | struct net_device_context { | ||
| 47 | /* point back to our device context */ | ||
| 48 | struct hv_device *device_ctx; | ||
| 49 | atomic_t avail; | ||
| 50 | struct delayed_work dwork; | ||
| 51 | }; | ||
| 52 | |||
| 53 | |||
| 54 | #define PACKET_PAGES_LOWATER 8 | ||
| 55 | /* Need this many pages to handle worst case fragmented packet */ | ||
| 56 | #define PACKET_PAGES_HIWATER (MAX_SKB_FRAGS + 2) | ||
| 57 | |||
| 58 | static int ring_size = 128; | ||
| 59 | module_param(ring_size, int, S_IRUGO); | ||
| 60 | MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)"); | ||
| 61 | |||
| 62 | /* no-op so the netdev core doesn't return -EINVAL when modifying the the | ||
| 63 | * multicast address list in SIOCADDMULTI. hv is setup to get all multicast | ||
| 64 | * when it calls RndisFilterOnOpen() */ | ||
| 65 | static void netvsc_set_multicast_list(struct net_device *net) | ||
| 66 | { | ||
| 67 | } | ||
| 68 | |||
| 69 | static int netvsc_open(struct net_device *net) | ||
| 70 | { | ||
| 71 | struct net_device_context *net_device_ctx = netdev_priv(net); | ||
| 72 | struct hv_device *device_obj = net_device_ctx->device_ctx; | ||
| 73 | int ret = 0; | ||
| 74 | |||
| 75 | if (netif_carrier_ok(net)) { | ||
| 76 | /* Open up the device */ | ||
| 77 | ret = rndis_filter_open(device_obj); | ||
| 78 | if (ret != 0) { | ||
| 79 | netdev_err(net, "unable to open device (ret %d).\n", | ||
| 80 | ret); | ||
| 81 | return ret; | ||
| 82 | } | ||
| 83 | |||
| 84 | netif_start_queue(net); | ||
| 85 | } else { | ||
| 86 | netdev_err(net, "unable to open device...link is down.\n"); | ||
| 87 | } | ||
| 88 | |||
| 89 | return ret; | ||
| 90 | } | ||
| 91 | |||
| 92 | static int netvsc_close(struct net_device *net) | ||
| 93 | { | ||
| 94 | struct net_device_context *net_device_ctx = netdev_priv(net); | ||
| 95 | struct hv_device *device_obj = net_device_ctx->device_ctx; | ||
| 96 | int ret; | ||
| 97 | |||
| 98 | netif_stop_queue(net); | ||
| 99 | |||
| 100 | ret = rndis_filter_close(device_obj); | ||
| 101 | if (ret != 0) | ||
| 102 | netdev_err(net, "unable to close device (ret %d).\n", ret); | ||
| 103 | |||
| 104 | return ret; | ||
| 105 | } | ||
| 106 | |||
| 107 | static void netvsc_xmit_completion(void *context) | ||
| 108 | { | ||
| 109 | struct hv_netvsc_packet *packet = (struct hv_netvsc_packet *)context; | ||
| 110 | struct sk_buff *skb = (struct sk_buff *) | ||
| 111 | (unsigned long)packet->completion.send.send_completion_tid; | ||
| 112 | |||
| 113 | kfree(packet); | ||
| 114 | |||
| 115 | if (skb) { | ||
| 116 | struct net_device *net = skb->dev; | ||
| 117 | struct net_device_context *net_device_ctx = netdev_priv(net); | ||
| 118 | unsigned int num_pages = skb_shinfo(skb)->nr_frags + 2; | ||
| 119 | |||
| 120 | dev_kfree_skb_any(skb); | ||
| 121 | |||
| 122 | atomic_add(num_pages, &net_device_ctx->avail); | ||
| 123 | if (atomic_read(&net_device_ctx->avail) >= | ||
| 124 | PACKET_PAGES_HIWATER) | ||
| 125 | netif_wake_queue(net); | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) | ||
| 130 | { | ||
| 131 | struct net_device_context *net_device_ctx = netdev_priv(net); | ||
| 132 | struct hv_netvsc_packet *packet; | ||
| 133 | int ret; | ||
| 134 | unsigned int i, num_pages; | ||
| 135 | |||
| 136 | /* Add 1 for skb->data and additional one for RNDIS */ | ||
| 137 | num_pages = skb_shinfo(skb)->nr_frags + 1 + 1; | ||
| 138 | if (num_pages > atomic_read(&net_device_ctx->avail)) | ||
| 139 | return NETDEV_TX_BUSY; | ||
| 140 | |||
| 141 | /* Allocate a netvsc packet based on # of frags. */ | ||
| 142 | packet = kzalloc(sizeof(struct hv_netvsc_packet) + | ||
| 143 | (num_pages * sizeof(struct hv_page_buffer)) + | ||
| 144 | sizeof(struct rndis_filter_packet), GFP_ATOMIC); | ||
| 145 | if (!packet) { | ||
| 146 | /* out of memory, silently drop packet */ | ||
| 147 | netdev_err(net, "unable to allocate hv_netvsc_packet\n"); | ||
| 148 | |||
| 149 | dev_kfree_skb(skb); | ||
| 150 | net->stats.tx_dropped++; | ||
| 151 | return NETDEV_TX_OK; | ||
| 152 | } | ||
| 153 | |||
| 154 | packet->extension = (void *)(unsigned long)packet + | ||
| 155 | sizeof(struct hv_netvsc_packet) + | ||
| 156 | (num_pages * sizeof(struct hv_page_buffer)); | ||
| 157 | |||
| 158 | /* Setup the rndis header */ | ||
| 159 | packet->page_buf_cnt = num_pages; | ||
| 160 | |||
| 161 | /* Initialize it from the skb */ | ||
| 162 | packet->total_data_buflen = skb->len; | ||
| 163 | |||
| 164 | /* Start filling in the page buffers starting after RNDIS buffer. */ | ||
| 165 | packet->page_buf[1].pfn = virt_to_phys(skb->data) >> PAGE_SHIFT; | ||
| 166 | packet->page_buf[1].offset | ||
| 167 | = (unsigned long)skb->data & (PAGE_SIZE - 1); | ||
| 168 | packet->page_buf[1].len = skb_headlen(skb); | ||
| 169 | |||
| 170 | /* Additional fragments are after SKB data */ | ||
| 171 | for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { | ||
| 172 | skb_frag_t *f = &skb_shinfo(skb)->frags[i]; | ||
| 173 | |||
| 174 | packet->page_buf[i+2].pfn = page_to_pfn(f->page); | ||
| 175 | packet->page_buf[i+2].offset = f->page_offset; | ||
| 176 | packet->page_buf[i+2].len = f->size; | ||
| 177 | } | ||
| 178 | |||
| 179 | /* Set the completion routine */ | ||
| 180 | packet->completion.send.send_completion = netvsc_xmit_completion; | ||
| 181 | packet->completion.send.send_completion_ctx = packet; | ||
| 182 | packet->completion.send.send_completion_tid = (unsigned long)skb; | ||
| 183 | |||
| 184 | ret = rndis_filter_send(net_device_ctx->device_ctx, | ||
| 185 | packet); | ||
| 186 | if (ret == 0) { | ||
| 187 | net->stats.tx_bytes += skb->len; | ||
| 188 | net->stats.tx_packets++; | ||
| 189 | |||
| 190 | atomic_sub(num_pages, &net_device_ctx->avail); | ||
| 191 | if (atomic_read(&net_device_ctx->avail) < PACKET_PAGES_LOWATER) | ||
| 192 | netif_stop_queue(net); | ||
| 193 | } else { | ||
| 194 | /* we are shutting down or bus overloaded, just drop packet */ | ||
| 195 | net->stats.tx_dropped++; | ||
| 196 | netvsc_xmit_completion(packet); | ||
| 197 | } | ||
| 198 | |||
| 199 | return NETDEV_TX_OK; | ||
| 200 | } | ||
| 201 | |||
| 202 | /* | ||
| 203 | * netvsc_linkstatus_callback - Link up/down notification | ||
| 204 | */ | ||
| 205 | void netvsc_linkstatus_callback(struct hv_device *device_obj, | ||
| 206 | unsigned int status) | ||
| 207 | { | ||
| 208 | struct net_device *net = dev_get_drvdata(&device_obj->device); | ||
| 209 | struct net_device_context *ndev_ctx; | ||
| 210 | |||
| 211 | if (!net) { | ||
| 212 | netdev_err(net, "got link status but net device " | ||
| 213 | "not initialized yet\n"); | ||
| 214 | return; | ||
| 215 | } | ||
| 216 | |||
| 217 | if (status == 1) { | ||
| 218 | netif_carrier_on(net); | ||
| 219 | netif_wake_queue(net); | ||
| 220 | ndev_ctx = netdev_priv(net); | ||
| 221 | schedule_delayed_work(&ndev_ctx->dwork, 0); | ||
| 222 | schedule_delayed_work(&ndev_ctx->dwork, msecs_to_jiffies(20)); | ||
| 223 | } else { | ||
| 224 | netif_carrier_off(net); | ||
| 225 | netif_stop_queue(net); | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | /* | ||
| 230 | * netvsc_recv_callback - Callback when we receive a packet from the | ||
| 231 | * "wire" on the specified device. | ||
| 232 | */ | ||
| 233 | int netvsc_recv_callback(struct hv_device *device_obj, | ||
| 234 | struct hv_netvsc_packet *packet) | ||
| 235 | { | ||
| 236 | struct net_device *net = dev_get_drvdata(&device_obj->device); | ||
| 237 | struct sk_buff *skb; | ||
| 238 | void *data; | ||
| 239 | int i; | ||
| 240 | unsigned long flags; | ||
| 241 | |||
| 242 | if (!net) { | ||
| 243 | netdev_err(net, "got receive callback but net device" | ||
| 244 | " not initialized yet\n"); | ||
| 245 | return 0; | ||
| 246 | } | ||
| 247 | |||
| 248 | /* Allocate a skb - TODO direct I/O to pages? */ | ||
| 249 | skb = netdev_alloc_skb_ip_align(net, packet->total_data_buflen); | ||
| 250 | if (unlikely(!skb)) { | ||
| 251 | ++net->stats.rx_dropped; | ||
| 252 | return 0; | ||
| 253 | } | ||
| 254 | |||
| 255 | /* for kmap_atomic */ | ||
| 256 | local_irq_save(flags); | ||
| 257 | |||
| 258 | /* | ||
| 259 | * Copy to skb. This copy is needed here since the memory pointed by | ||
| 260 | * hv_netvsc_packet cannot be deallocated | ||
| 261 | */ | ||
| 262 | for (i = 0; i < packet->page_buf_cnt; i++) { | ||
| 263 | data = kmap_atomic(pfn_to_page(packet->page_buf[i].pfn), | ||
| 264 | KM_IRQ1); | ||
| 265 | data = (void *)(unsigned long)data + | ||
| 266 | packet->page_buf[i].offset; | ||
| 267 | |||
| 268 | memcpy(skb_put(skb, packet->page_buf[i].len), data, | ||
| 269 | packet->page_buf[i].len); | ||
| 270 | |||
| 271 | kunmap_atomic((void *)((unsigned long)data - | ||
| 272 | packet->page_buf[i].offset), KM_IRQ1); | ||
| 273 | } | ||
| 274 | |||
| 275 | local_irq_restore(flags); | ||
| 276 | |||
| 277 | skb->protocol = eth_type_trans(skb, net); | ||
| 278 | skb->ip_summed = CHECKSUM_NONE; | ||
| 279 | |||
| 280 | net->stats.rx_packets++; | ||
| 281 | net->stats.rx_bytes += skb->len; | ||
| 282 | |||
| 283 | /* | ||
| 284 | * Pass the skb back up. Network stack will deallocate the skb when it | ||
| 285 | * is done. | ||
| 286 | * TODO - use NAPI? | ||
| 287 | */ | ||
| 288 | netif_rx(skb); | ||
| 289 | |||
| 290 | return 0; | ||
| 291 | } | ||
| 292 | |||
| 293 | static void netvsc_get_drvinfo(struct net_device *net, | ||
| 294 | struct ethtool_drvinfo *info) | ||
| 295 | { | ||
| 296 | strcpy(info->driver, "hv_netvsc"); | ||
| 297 | strcpy(info->version, HV_DRV_VERSION); | ||
| 298 | strcpy(info->fw_version, "N/A"); | ||
| 299 | } | ||
| 300 | |||
| 301 | static const struct ethtool_ops ethtool_ops = { | ||
| 302 | .get_drvinfo = netvsc_get_drvinfo, | ||
| 303 | .get_link = ethtool_op_get_link, | ||
| 304 | }; | ||
| 305 | |||
| 306 | static const struct net_device_ops device_ops = { | ||
| 307 | .ndo_open = netvsc_open, | ||
| 308 | .ndo_stop = netvsc_close, | ||
| 309 | .ndo_start_xmit = netvsc_start_xmit, | ||
| 310 | .ndo_set_multicast_list = netvsc_set_multicast_list, | ||
| 311 | .ndo_change_mtu = eth_change_mtu, | ||
| 312 | .ndo_validate_addr = eth_validate_addr, | ||
| 313 | .ndo_set_mac_address = eth_mac_addr, | ||
| 314 | }; | ||
| 315 | |||
| 316 | /* | ||
| 317 | * Send GARP packet to network peers after migrations. | ||
| 318 | * After Quick Migration, the network is not immediately operational in the | ||
| 319 | * current context when receiving RNDIS_STATUS_MEDIA_CONNECT event. So, add | ||
| 320 | * another netif_notify_peers() into a delayed work, otherwise GARP packet | ||
| 321 | * will not be sent after quick migration, and cause network disconnection. | ||
| 322 | */ | ||
| 323 | static void netvsc_send_garp(struct work_struct *w) | ||
| 324 | { | ||
| 325 | struct net_device_context *ndev_ctx; | ||
| 326 | struct net_device *net; | ||
| 327 | |||
| 328 | ndev_ctx = container_of(w, struct net_device_context, dwork.work); | ||
| 329 | net = dev_get_drvdata(&ndev_ctx->device_ctx->device); | ||
| 330 | netif_notify_peers(net); | ||
| 331 | } | ||
| 332 | |||
| 333 | |||
| 334 | static int netvsc_probe(struct hv_device *dev) | ||
| 335 | { | ||
| 336 | struct net_device *net = NULL; | ||
| 337 | struct net_device_context *net_device_ctx; | ||
| 338 | struct netvsc_device_info device_info; | ||
| 339 | int ret; | ||
| 340 | |||
| 341 | net = alloc_etherdev(sizeof(struct net_device_context)); | ||
| 342 | if (!net) | ||
| 343 | return -1; | ||
| 344 | |||
| 345 | /* Set initial state */ | ||
| 346 | netif_carrier_off(net); | ||
| 347 | |||
| 348 | net_device_ctx = netdev_priv(net); | ||
| 349 | net_device_ctx->device_ctx = dev; | ||
| 350 | atomic_set(&net_device_ctx->avail, ring_size); | ||
| 351 | dev_set_drvdata(&dev->device, net); | ||
| 352 | INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_send_garp); | ||
| 353 | |||
| 354 | /* Notify the netvsc driver of the new device */ | ||
| 355 | device_info.ring_size = ring_size; | ||
| 356 | ret = rndis_filter_device_add(dev, &device_info); | ||
| 357 | if (ret != 0) { | ||
| 358 | free_netdev(net); | ||
| 359 | dev_set_drvdata(&dev->device, NULL); | ||
| 360 | |||
| 361 | netdev_err(net, "unable to add netvsc device (ret %d)\n", ret); | ||
| 362 | return ret; | ||
| 363 | } | ||
| 364 | |||
| 365 | netif_carrier_on(net); | ||
| 366 | |||
| 367 | memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN); | ||
| 368 | |||
| 369 | net->netdev_ops = &device_ops; | ||
| 370 | |||
| 371 | /* TODO: Add GSO and Checksum offload */ | ||
| 372 | net->hw_features = NETIF_F_SG; | ||
| 373 | net->features = NETIF_F_SG; | ||
| 374 | |||
| 375 | SET_ETHTOOL_OPS(net, ðtool_ops); | ||
| 376 | SET_NETDEV_DEV(net, &dev->device); | ||
| 377 | |||
| 378 | ret = register_netdev(net); | ||
| 379 | if (ret != 0) { | ||
| 380 | /* Remove the device and release the resource */ | ||
| 381 | rndis_filter_device_remove(dev); | ||
| 382 | free_netdev(net); | ||
| 383 | } | ||
| 384 | |||
| 385 | return ret; | ||
| 386 | } | ||
| 387 | |||
| 388 | static int netvsc_remove(struct hv_device *dev) | ||
| 389 | { | ||
| 390 | struct net_device *net = dev_get_drvdata(&dev->device); | ||
| 391 | struct net_device_context *ndev_ctx; | ||
| 392 | |||
| 393 | if (net == NULL) { | ||
| 394 | dev_err(&dev->device, "No net device to remove\n"); | ||
| 395 | return 0; | ||
| 396 | } | ||
| 397 | |||
| 398 | ndev_ctx = netdev_priv(net); | ||
| 399 | cancel_delayed_work_sync(&ndev_ctx->dwork); | ||
| 400 | |||
| 401 | /* Stop outbound asap */ | ||
| 402 | netif_stop_queue(net); | ||
| 403 | |||
| 404 | unregister_netdev(net); | ||
| 405 | |||
| 406 | /* | ||
| 407 | * Call to the vsc driver to let it know that the device is being | ||
| 408 | * removed | ||
| 409 | */ | ||
| 410 | rndis_filter_device_remove(dev); | ||
| 411 | |||
| 412 | free_netdev(net); | ||
| 413 | return 0; | ||
| 414 | } | ||
| 415 | |||
| 416 | /* The one and only one */ | ||
| 417 | static struct hv_driver netvsc_drv = { | ||
| 418 | .probe = netvsc_probe, | ||
| 419 | .remove = netvsc_remove, | ||
| 420 | }; | ||
| 421 | |||
| 422 | static void __exit netvsc_drv_exit(void) | ||
| 423 | { | ||
| 424 | vmbus_child_driver_unregister(&netvsc_drv.driver); | ||
| 425 | } | ||
| 426 | |||
| 427 | |||
| 428 | static const struct dmi_system_id __initconst | ||
| 429 | hv_netvsc_dmi_table[] __maybe_unused = { | ||
| 430 | { | ||
| 431 | .ident = "Hyper-V", | ||
| 432 | .matches = { | ||
| 433 | DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), | ||
| 434 | DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"), | ||
| 435 | DMI_MATCH(DMI_BOARD_NAME, "Virtual Machine"), | ||
| 436 | }, | ||
| 437 | }, | ||
| 438 | { }, | ||
| 439 | }; | ||
| 440 | MODULE_DEVICE_TABLE(dmi, hv_netvsc_dmi_table); | ||
| 441 | |||
| 442 | static int __init netvsc_drv_init(void) | ||
| 443 | { | ||
| 444 | struct hv_driver *drv = &netvsc_drv; | ||
| 445 | int ret; | ||
| 446 | |||
| 447 | pr_info("initializing...."); | ||
| 448 | |||
| 449 | if (!dmi_check_system(hv_netvsc_dmi_table)) | ||
| 450 | return -ENODEV; | ||
| 451 | |||
| 452 | |||
| 453 | /* Callback to client driver to complete the initialization */ | ||
| 454 | netvsc_initialize(drv); | ||
| 455 | |||
| 456 | drv->driver.name = drv->name; | ||
| 457 | |||
| 458 | /* The driver belongs to vmbus */ | ||
| 459 | ret = vmbus_child_driver_register(&drv->driver); | ||
| 460 | |||
| 461 | return ret; | ||
| 462 | } | ||
| 463 | |||
| 464 | static const struct pci_device_id __initconst | ||
| 465 | hv_netvsc_pci_table[] __maybe_unused = { | ||
| 466 | { PCI_DEVICE(0x1414, 0x5353) }, /* VGA compatible controller */ | ||
| 467 | { 0 } | ||
| 468 | }; | ||
| 469 | MODULE_DEVICE_TABLE(pci, hv_netvsc_pci_table); | ||
| 470 | |||
| 471 | MODULE_LICENSE("GPL"); | ||
| 472 | MODULE_VERSION(HV_DRV_VERSION); | ||
| 473 | MODULE_DESCRIPTION("Microsoft Hyper-V network driver"); | ||
| 474 | |||
| 475 | module_init(netvsc_drv_init); | ||
| 476 | module_exit(netvsc_drv_exit); | ||
diff --git a/drivers/staging/hv/ring_buffer.c b/drivers/staging/hv/ring_buffer.c new file mode 100644 index 00000000000..42f76728429 --- /dev/null +++ b/drivers/staging/hv/ring_buffer.c | |||
| @@ -0,0 +1,526 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * Copyright (c) 2009, Microsoft Corporation. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms and conditions of the GNU General Public License, | ||
| 7 | * version 2, as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | * more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License along with | ||
| 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
| 16 | * Place - Suite 330, Boston, MA 02111-1307 USA. | ||
| 17 | * | ||
| 18 | * Authors: | ||
| 19 | * Haiyang Zhang <haiyangz@microsoft.com> | ||
| 20 | * Hank Janssen <hjanssen@microsoft.com> | ||
| 21 | * K. Y. Srinivasan <kys@microsoft.com> | ||
| 22 | * | ||
| 23 | */ | ||
| 24 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 25 | |||
| 26 | #include <linux/kernel.h> | ||
| 27 | #include <linux/mm.h> | ||
| 28 | |||
| 29 | #include "hyperv.h" | ||
| 30 | #include "hyperv_vmbus.h" | ||
| 31 | |||
| 32 | |||
| 33 | /* #defines */ | ||
| 34 | |||
| 35 | |||
| 36 | /* Amount of space to write to */ | ||
| 37 | #define BYTES_AVAIL_TO_WRITE(r, w, z) ((w) >= (r)) ? ((z) - ((w) - (r))) : ((r) - (w)) | ||
| 38 | |||
| 39 | |||
| 40 | /* | ||
| 41 | * | ||
| 42 | * hv_get_ringbuffer_availbytes() | ||
| 43 | * | ||
| 44 | * Get number of bytes available to read and to write to | ||
| 45 | * for the specified ring buffer | ||
| 46 | */ | ||
| 47 | static inline void | ||
| 48 | hv_get_ringbuffer_availbytes(struct hv_ring_buffer_info *rbi, | ||
| 49 | u32 *read, u32 *write) | ||
| 50 | { | ||
| 51 | u32 read_loc, write_loc; | ||
| 52 | |||
| 53 | smp_read_barrier_depends(); | ||
| 54 | |||
| 55 | /* Capture the read/write indices before they changed */ | ||
| 56 | read_loc = rbi->ring_buffer->read_index; | ||
| 57 | write_loc = rbi->ring_buffer->write_index; | ||
| 58 | |||
| 59 | *write = BYTES_AVAIL_TO_WRITE(read_loc, write_loc, rbi->ring_datasize); | ||
| 60 | *read = rbi->ring_datasize - *write; | ||
| 61 | } | ||
| 62 | |||
| 63 | /* | ||
| 64 | * hv_get_next_write_location() | ||
| 65 | * | ||
| 66 | * Get the next write location for the specified ring buffer | ||
| 67 | * | ||
| 68 | */ | ||
| 69 | static inline u32 | ||
| 70 | hv_get_next_write_location(struct hv_ring_buffer_info *ring_info) | ||
| 71 | { | ||
| 72 | u32 next = ring_info->ring_buffer->write_index; | ||
| 73 | |||
| 74 | return next; | ||
| 75 | } | ||
| 76 | |||
| 77 | /* | ||
| 78 | * hv_set_next_write_location() | ||
| 79 | * | ||
| 80 | * Set the next write location for the specified ring buffer | ||
| 81 | * | ||
| 82 | */ | ||
| 83 | static inline void | ||
| 84 | hv_set_next_write_location(struct hv_ring_buffer_info *ring_info, | ||
| 85 | u32 next_write_location) | ||
| 86 | { | ||
| 87 | ring_info->ring_buffer->write_index = next_write_location; | ||
| 88 | } | ||
| 89 | |||
| 90 | /* | ||
| 91 | * hv_get_next_read_location() | ||
| 92 | * | ||
| 93 | * Get the next read location for the specified ring buffer | ||
| 94 | */ | ||
| 95 | static inline u32 | ||
| 96 | hv_get_next_read_location(struct hv_ring_buffer_info *ring_info) | ||
| 97 | { | ||
| 98 | u32 next = ring_info->ring_buffer->read_index; | ||
| 99 | |||
| 100 | return next; | ||
| 101 | } | ||
| 102 | |||
| 103 | /* | ||
| 104 | * hv_get_next_readlocation_withoffset() | ||
| 105 | * | ||
| 106 | * Get the next read location + offset for the specified ring buffer. | ||
| 107 | * This allows the caller to skip | ||
| 108 | */ | ||
| 109 | static inline u32 | ||
| 110 | hv_get_next_readlocation_withoffset(struct hv_ring_buffer_info *ring_info, | ||
| 111 | u32 offset) | ||
| 112 | { | ||
| 113 | u32 next = ring_info->ring_buffer->read_index; | ||
| 114 | |||
| 115 | next += offset; | ||
| 116 | next %= ring_info->ring_datasize; | ||
| 117 | |||
| 118 | return next; | ||
| 119 | } | ||
| 120 | |||
| 121 | /* | ||
| 122 | * | ||
| 123 | * hv_set_next_read_location() | ||
| 124 | * | ||
| 125 | * Set the next read location for the specified ring buffer | ||
| 126 | * | ||
| 127 | */ | ||
| 128 | static inline void | ||
| 129 | hv_set_next_read_location(struct hv_ring_buffer_info *ring_info, | ||
| 130 | u32 next_read_location) | ||
| 131 | { | ||
| 132 | ring_info->ring_buffer->read_index = next_read_location; | ||
| 133 | } | ||
| 134 | |||
| 135 | |||
| 136 | /* | ||
| 137 | * | ||
| 138 | * hv_get_ring_buffer() | ||
| 139 | * | ||
| 140 | * Get the start of the ring buffer | ||
| 141 | */ | ||
| 142 | static inline void * | ||
| 143 | hv_get_ring_buffer(struct hv_ring_buffer_info *ring_info) | ||
| 144 | { | ||
| 145 | return (void *)ring_info->ring_buffer->buffer; | ||
| 146 | } | ||
| 147 | |||
| 148 | |||
| 149 | /* | ||
| 150 | * | ||
| 151 | * hv_get_ring_buffersize() | ||
| 152 | * | ||
| 153 | * Get the size of the ring buffer | ||
| 154 | */ | ||
| 155 | static inline u32 | ||
| 156 | hv_get_ring_buffersize(struct hv_ring_buffer_info *ring_info) | ||
| 157 | { | ||
| 158 | return ring_info->ring_datasize; | ||
| 159 | } | ||
| 160 | |||
| 161 | /* | ||
| 162 | * | ||
| 163 | * hv_get_ring_bufferindices() | ||
| 164 | * | ||
| 165 | * Get the read and write indices as u64 of the specified ring buffer | ||
| 166 | * | ||
| 167 | */ | ||
| 168 | static inline u64 | ||
| 169 | hv_get_ring_bufferindices(struct hv_ring_buffer_info *ring_info) | ||
| 170 | { | ||
| 171 | return (u64)ring_info->ring_buffer->write_index << 32; | ||
| 172 | } | ||
| 173 | |||
| 174 | |||
| 175 | /* | ||
| 176 | * | ||
| 177 | * hv_dump_ring_info() | ||
| 178 | * | ||
| 179 | * Dump out to console the ring buffer info | ||
| 180 | * | ||
| 181 | */ | ||
| 182 | void hv_dump_ring_info(struct hv_ring_buffer_info *ring_info, char *prefix) | ||
| 183 | { | ||
| 184 | u32 bytes_avail_towrite; | ||
| 185 | u32 bytes_avail_toread; | ||
| 186 | |||
| 187 | hv_get_ringbuffer_availbytes(ring_info, | ||
| 188 | &bytes_avail_toread, | ||
| 189 | &bytes_avail_towrite); | ||
| 190 | |||
| 191 | DPRINT(VMBUS, | ||
| 192 | DEBUG_RING_LVL, | ||
| 193 | "%s <<ringinfo %p buffer %p avail write %u " | ||
| 194 | "avail read %u read idx %u write idx %u>>", | ||
| 195 | prefix, | ||
| 196 | ring_info, | ||
| 197 | ring_info->ring_buffer->buffer, | ||
| 198 | bytes_avail_towrite, | ||
| 199 | bytes_avail_toread, | ||
| 200 | ring_info->ring_buffer->read_index, | ||
| 201 | ring_info->ring_buffer->write_index); | ||
| 202 | } | ||
| 203 | |||
| 204 | |||
| 205 | /* | ||
| 206 | * | ||
| 207 | * hv_copyfrom_ringbuffer() | ||
| 208 | * | ||
| 209 | * Helper routine to copy to source from ring buffer. | ||
| 210 | * Assume there is enough room. Handles wrap-around in src case only!! | ||
| 211 | * | ||
| 212 | */ | ||
| 213 | static u32 hv_copyfrom_ringbuffer( | ||
| 214 | struct hv_ring_buffer_info *ring_info, | ||
| 215 | void *dest, | ||
| 216 | u32 destlen, | ||
| 217 | u32 start_read_offset) | ||
| 218 | { | ||
| 219 | void *ring_buffer = hv_get_ring_buffer(ring_info); | ||
| 220 | u32 ring_buffer_size = hv_get_ring_buffersize(ring_info); | ||
| 221 | |||
| 222 | u32 frag_len; | ||
| 223 | |||
| 224 | /* wrap-around detected at the src */ | ||
| 225 | if (destlen > ring_buffer_size - start_read_offset) { | ||
| 226 | frag_len = ring_buffer_size - start_read_offset; | ||
| 227 | |||
| 228 | memcpy(dest, ring_buffer + start_read_offset, frag_len); | ||
| 229 | memcpy(dest + frag_len, ring_buffer, destlen - frag_len); | ||
| 230 | } else | ||
| 231 | |||
| 232 | memcpy(dest, ring_buffer + start_read_offset, destlen); | ||
| 233 | |||
| 234 | |||
| 235 | start_read_offset += destlen; | ||
| 236 | start_read_offset %= ring_buffer_size; | ||
| 237 | |||
| 238 | return start_read_offset; | ||
| 239 | } | ||
| 240 | |||
| 241 | |||
| 242 | /* | ||
| 243 | * | ||
| 244 | * hv_copyto_ringbuffer() | ||
| 245 | * | ||
| 246 | * Helper routine to copy from source to ring buffer. | ||
| 247 | * Assume there is enough room. Handles wrap-around in dest case only!! | ||
| 248 | * | ||
| 249 | */ | ||
| 250 | static u32 hv_copyto_ringbuffer( | ||
| 251 | struct hv_ring_buffer_info *ring_info, | ||
| 252 | u32 start_write_offset, | ||
| 253 | void *src, | ||
| 254 | u32 srclen) | ||
| 255 | { | ||
| 256 | void *ring_buffer = hv_get_ring_buffer(ring_info); | ||
| 257 | u32 ring_buffer_size = hv_get_ring_buffersize(ring_info); | ||
| 258 | u32 frag_len; | ||
| 259 | |||
| 260 | /* wrap-around detected! */ | ||
| 261 | if (srclen > ring_buffer_size - start_write_offset) { | ||
| 262 | frag_len = ring_buffer_size - start_write_offset; | ||
| 263 | memcpy(ring_buffer + start_write_offset, src, frag_len); | ||
| 264 | memcpy(ring_buffer, src + frag_len, srclen - frag_len); | ||
| 265 | } else | ||
| 266 | memcpy(ring_buffer + start_write_offset, src, srclen); | ||
| 267 | |||
| 268 | start_write_offset += srclen; | ||
| 269 | start_write_offset %= ring_buffer_size; | ||
| 270 | |||
| 271 | return start_write_offset; | ||
| 272 | } | ||
| 273 | |||
| 274 | /* | ||
| 275 | * | ||
| 276 | * hv_ringbuffer_get_debuginfo() | ||
| 277 | * | ||
| 278 | * Get various debug metrics for the specified ring buffer | ||
| 279 | * | ||
| 280 | */ | ||
| 281 | void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info, | ||
| 282 | struct hv_ring_buffer_debug_info *debug_info) | ||
| 283 | { | ||
| 284 | u32 bytes_avail_towrite; | ||
| 285 | u32 bytes_avail_toread; | ||
| 286 | |||
| 287 | if (ring_info->ring_buffer) { | ||
| 288 | hv_get_ringbuffer_availbytes(ring_info, | ||
| 289 | &bytes_avail_toread, | ||
| 290 | &bytes_avail_towrite); | ||
| 291 | |||
| 292 | debug_info->bytes_avail_toread = bytes_avail_toread; | ||
| 293 | debug_info->bytes_avail_towrite = bytes_avail_towrite; | ||
| 294 | debug_info->current_read_index = | ||
| 295 | ring_info->ring_buffer->read_index; | ||
| 296 | debug_info->current_write_index = | ||
| 297 | ring_info->ring_buffer->write_index; | ||
| 298 | debug_info->current_interrupt_mask = | ||
| 299 | ring_info->ring_buffer->interrupt_mask; | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | |||
| 304 | /* | ||
| 305 | * | ||
| 306 | * hv_get_ringbuffer_interrupt_mask() | ||
| 307 | * | ||
| 308 | * Get the interrupt mask for the specified ring buffer | ||
| 309 | * | ||
| 310 | */ | ||
| 311 | u32 hv_get_ringbuffer_interrupt_mask(struct hv_ring_buffer_info *rbi) | ||
| 312 | { | ||
| 313 | return rbi->ring_buffer->interrupt_mask; | ||
| 314 | } | ||
| 315 | |||
| 316 | /* | ||
| 317 | * | ||
| 318 | * hv_ringbuffer_init() | ||
| 319 | * | ||
| 320 | *Initialize the ring buffer | ||
| 321 | * | ||
| 322 | */ | ||
| 323 | int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, | ||
| 324 | void *buffer, u32 buflen) | ||
| 325 | { | ||
| 326 | if (sizeof(struct hv_ring_buffer) != PAGE_SIZE) | ||
| 327 | return -EINVAL; | ||
| 328 | |||
| 329 | memset(ring_info, 0, sizeof(struct hv_ring_buffer_info)); | ||
| 330 | |||
| 331 | ring_info->ring_buffer = (struct hv_ring_buffer *)buffer; | ||
| 332 | ring_info->ring_buffer->read_index = | ||
| 333 | ring_info->ring_buffer->write_index = 0; | ||
| 334 | |||
| 335 | ring_info->ring_size = buflen; | ||
| 336 | ring_info->ring_datasize = buflen - sizeof(struct hv_ring_buffer); | ||
| 337 | |||
| 338 | spin_lock_init(&ring_info->ring_lock); | ||
| 339 | |||
| 340 | return 0; | ||
| 341 | } | ||
| 342 | |||
| 343 | /* | ||
| 344 | * | ||
| 345 | * hv_ringbuffer_cleanup() | ||
| 346 | * | ||
| 347 | * Cleanup the ring buffer | ||
| 348 | * | ||
| 349 | */ | ||
| 350 | void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info) | ||
| 351 | { | ||
| 352 | } | ||
| 353 | |||
| 354 | /* | ||
| 355 | * | ||
| 356 | * hv_ringbuffer_write() | ||
| 357 | * | ||
| 358 | * Write to the ring buffer | ||
| 359 | * | ||
| 360 | */ | ||
| 361 | int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info, | ||
| 362 | struct scatterlist *sglist, u32 sgcount) | ||
| 363 | { | ||
| 364 | int i = 0; | ||
| 365 | u32 bytes_avail_towrite; | ||
| 366 | u32 bytes_avail_toread; | ||
| 367 | u32 totalbytes_towrite = 0; | ||
| 368 | |||
| 369 | struct scatterlist *sg; | ||
| 370 | u32 next_write_location; | ||
| 371 | u64 prev_indices = 0; | ||
| 372 | unsigned long flags; | ||
| 373 | |||
| 374 | for_each_sg(sglist, sg, sgcount, i) | ||
| 375 | { | ||
| 376 | totalbytes_towrite += sg->length; | ||
| 377 | } | ||
| 378 | |||
| 379 | totalbytes_towrite += sizeof(u64); | ||
| 380 | |||
| 381 | spin_lock_irqsave(&outring_info->ring_lock, flags); | ||
| 382 | |||
| 383 | hv_get_ringbuffer_availbytes(outring_info, | ||
| 384 | &bytes_avail_toread, | ||
| 385 | &bytes_avail_towrite); | ||
| 386 | |||
| 387 | |||
| 388 | /* If there is only room for the packet, assume it is full. */ | ||
| 389 | /* Otherwise, the next time around, we think the ring buffer */ | ||
| 390 | /* is empty since the read index == write index */ | ||
| 391 | if (bytes_avail_towrite <= totalbytes_towrite) { | ||
| 392 | spin_unlock_irqrestore(&outring_info->ring_lock, flags); | ||
| 393 | return -1; | ||
| 394 | } | ||
| 395 | |||
| 396 | /* Write to the ring buffer */ | ||
| 397 | next_write_location = hv_get_next_write_location(outring_info); | ||
| 398 | |||
| 399 | for_each_sg(sglist, sg, sgcount, i) | ||
| 400 | { | ||
| 401 | next_write_location = hv_copyto_ringbuffer(outring_info, | ||
| 402 | next_write_location, | ||
| 403 | sg_virt(sg), | ||
| 404 | sg->length); | ||
| 405 | } | ||
| 406 | |||
| 407 | /* Set previous packet start */ | ||
| 408 | prev_indices = hv_get_ring_bufferindices(outring_info); | ||
| 409 | |||
| 410 | next_write_location = hv_copyto_ringbuffer(outring_info, | ||
| 411 | next_write_location, | ||
| 412 | &prev_indices, | ||
| 413 | sizeof(u64)); | ||
| 414 | |||
| 415 | /* Make sure we flush all writes before updating the writeIndex */ | ||
| 416 | smp_wmb(); | ||
| 417 | |||
| 418 | /* Now, update the write location */ | ||
| 419 | hv_set_next_write_location(outring_info, next_write_location); | ||
| 420 | |||
| 421 | |||
| 422 | spin_unlock_irqrestore(&outring_info->ring_lock, flags); | ||
| 423 | return 0; | ||
| 424 | } | ||
| 425 | |||
| 426 | |||
| 427 | /* | ||
| 428 | * | ||
| 429 | * hv_ringbuffer_peek() | ||
| 430 | * | ||
| 431 | * Read without advancing the read index | ||
| 432 | * | ||
| 433 | */ | ||
| 434 | int hv_ringbuffer_peek(struct hv_ring_buffer_info *Inring_info, | ||
| 435 | void *Buffer, u32 buflen) | ||
| 436 | { | ||
| 437 | u32 bytes_avail_towrite; | ||
| 438 | u32 bytes_avail_toread; | ||
| 439 | u32 next_read_location = 0; | ||
| 440 | unsigned long flags; | ||
| 441 | |||
| 442 | spin_lock_irqsave(&Inring_info->ring_lock, flags); | ||
| 443 | |||
| 444 | hv_get_ringbuffer_availbytes(Inring_info, | ||
| 445 | &bytes_avail_toread, | ||
| 446 | &bytes_avail_towrite); | ||
| 447 | |||
| 448 | /* Make sure there is something to read */ | ||
| 449 | if (bytes_avail_toread < buflen) { | ||
| 450 | |||
| 451 | spin_unlock_irqrestore(&Inring_info->ring_lock, flags); | ||
| 452 | |||
| 453 | return -1; | ||
| 454 | } | ||
| 455 | |||
| 456 | /* Convert to byte offset */ | ||
| 457 | next_read_location = hv_get_next_read_location(Inring_info); | ||
| 458 | |||
| 459 | next_read_location = hv_copyfrom_ringbuffer(Inring_info, | ||
| 460 | Buffer, | ||
| 461 | buflen, | ||
| 462 | next_read_location); | ||
| 463 | |||
| 464 | spin_unlock_irqrestore(&Inring_info->ring_lock, flags); | ||
| 465 | |||
| 466 | return 0; | ||
| 467 | } | ||
| 468 | |||
| 469 | |||
| 470 | /* | ||
| 471 | * | ||
| 472 | * hv_ringbuffer_read() | ||
| 473 | * | ||
| 474 | * Read and advance the read index | ||
| 475 | * | ||
| 476 | */ | ||
| 477 | int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer, | ||
| 478 | u32 buflen, u32 offset) | ||
| 479 | { | ||
| 480 | u32 bytes_avail_towrite; | ||
| 481 | u32 bytes_avail_toread; | ||
| 482 | u32 next_read_location = 0; | ||
| 483 | u64 prev_indices = 0; | ||
| 484 | unsigned long flags; | ||
| 485 | |||
| 486 | if (buflen <= 0) | ||
| 487 | return -EINVAL; | ||
| 488 | |||
| 489 | spin_lock_irqsave(&inring_info->ring_lock, flags); | ||
| 490 | |||
| 491 | hv_get_ringbuffer_availbytes(inring_info, | ||
| 492 | &bytes_avail_toread, | ||
| 493 | &bytes_avail_towrite); | ||
| 494 | |||
| 495 | /* Make sure there is something to read */ | ||
| 496 | if (bytes_avail_toread < buflen) { | ||
| 497 | spin_unlock_irqrestore(&inring_info->ring_lock, flags); | ||
| 498 | |||
| 499 | return -1; | ||
| 500 | } | ||
| 501 | |||
| 502 | next_read_location = | ||
| 503 | hv_get_next_readlocation_withoffset(inring_info, offset); | ||
| 504 | |||
| 505 | next_read_location = hv_copyfrom_ringbuffer(inring_info, | ||
| 506 | buffer, | ||
| 507 | buflen, | ||
| 508 | next_read_location); | ||
| 509 | |||
| 510 | next_read_location = hv_copyfrom_ringbuffer(inring_info, | ||
| 511 | &prev_indices, | ||
| 512 | sizeof(u64), | ||
| 513 | next_read_location); | ||
| 514 | |||
| 515 | /* Make sure all reads are done before we update the read index since */ | ||
| 516 | /* the writer may start writing to the read area once the read index */ | ||
| 517 | /*is updated */ | ||
| 518 | smp_mb(); | ||
| 519 | |||
| 520 | /* Update the read index */ | ||
| 521 | hv_set_next_read_location(inring_info, next_read_location); | ||
| 522 | |||
| 523 | spin_unlock_irqrestore(&inring_info->ring_lock, flags); | ||
| 524 | |||
| 525 | return 0; | ||
| 526 | } | ||
diff --git a/drivers/staging/hv/rndis_filter.c b/drivers/staging/hv/rndis_filter.c new file mode 100644 index 00000000000..dbb52019975 --- /dev/null +++ b/drivers/staging/hv/rndis_filter.c | |||
| @@ -0,0 +1,831 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2009, Microsoft Corporation. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms and conditions of the GNU General Public License, | ||
| 6 | * version 2, as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License along with | ||
| 14 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
| 15 | * Place - Suite 330, Boston, MA 02111-1307 USA. | ||
| 16 | * | ||
| 17 | * Authors: | ||
| 18 | * Haiyang Zhang <haiyangz@microsoft.com> | ||
| 19 | * Hank Janssen <hjanssen@microsoft.com> | ||
| 20 | */ | ||
| 21 | #include <linux/kernel.h> | ||
| 22 | #include <linux/sched.h> | ||
| 23 | #include <linux/wait.h> | ||
| 24 | #include <linux/highmem.h> | ||
| 25 | #include <linux/slab.h> | ||
| 26 | #include <linux/io.h> | ||
| 27 | #include <linux/if_ether.h> | ||
| 28 | #include <linux/netdevice.h> | ||
| 29 | |||
| 30 | #include "hyperv.h" | ||
| 31 | #include "hyperv_net.h" | ||
| 32 | |||
| 33 | |||
| 34 | enum rndis_device_state { | ||
| 35 | RNDIS_DEV_UNINITIALIZED = 0, | ||
| 36 | RNDIS_DEV_INITIALIZING, | ||
| 37 | RNDIS_DEV_INITIALIZED, | ||
| 38 | RNDIS_DEV_DATAINITIALIZED, | ||
| 39 | }; | ||
| 40 | |||
| 41 | struct rndis_device { | ||
| 42 | struct netvsc_device *net_dev; | ||
| 43 | |||
| 44 | enum rndis_device_state state; | ||
| 45 | u32 link_stat; | ||
| 46 | atomic_t new_req_id; | ||
| 47 | |||
| 48 | spinlock_t request_lock; | ||
| 49 | struct list_head req_list; | ||
| 50 | |||
| 51 | unsigned char hw_mac_adr[ETH_ALEN]; | ||
| 52 | }; | ||
| 53 | |||
| 54 | struct rndis_request { | ||
| 55 | struct list_head list_ent; | ||
| 56 | struct completion wait_event; | ||
| 57 | |||
| 58 | /* | ||
| 59 | * FIXME: We assumed a fixed size response here. If we do ever need to | ||
| 60 | * handle a bigger response, we can either define a max response | ||
| 61 | * message or add a response buffer variable above this field | ||
| 62 | */ | ||
| 63 | struct rndis_message response_msg; | ||
| 64 | |||
| 65 | /* Simplify allocation by having a netvsc packet inline */ | ||
| 66 | struct hv_netvsc_packet pkt; | ||
| 67 | struct hv_page_buffer buf; | ||
| 68 | /* FIXME: We assumed a fixed size request here. */ | ||
| 69 | struct rndis_message request_msg; | ||
| 70 | }; | ||
| 71 | |||
| 72 | static void rndis_filter_send_completion(void *ctx); | ||
| 73 | |||
| 74 | static void rndis_filter_send_request_completion(void *ctx); | ||
| 75 | |||
| 76 | |||
| 77 | |||
| 78 | static struct rndis_device *get_rndis_device(void) | ||
| 79 | { | ||
| 80 | struct rndis_device *device; | ||
| 81 | |||
| 82 | device = kzalloc(sizeof(struct rndis_device), GFP_KERNEL); | ||
| 83 | if (!device) | ||
| 84 | return NULL; | ||
| 85 | |||
| 86 | spin_lock_init(&device->request_lock); | ||
| 87 | |||
| 88 | INIT_LIST_HEAD(&device->req_list); | ||
| 89 | |||
| 90 | device->state = RNDIS_DEV_UNINITIALIZED; | ||
| 91 | |||
| 92 | return device; | ||
| 93 | } | ||
| 94 | |||
| 95 | static struct rndis_request *get_rndis_request(struct rndis_device *dev, | ||
| 96 | u32 msg_type, | ||
| 97 | u32 msg_len) | ||
| 98 | { | ||
| 99 | struct rndis_request *request; | ||
| 100 | struct rndis_message *rndis_msg; | ||
| 101 | struct rndis_set_request *set; | ||
| 102 | unsigned long flags; | ||
| 103 | |||
| 104 | request = kzalloc(sizeof(struct rndis_request), GFP_KERNEL); | ||
| 105 | if (!request) | ||
| 106 | return NULL; | ||
| 107 | |||
| 108 | init_completion(&request->wait_event); | ||
| 109 | |||
| 110 | rndis_msg = &request->request_msg; | ||
| 111 | rndis_msg->ndis_msg_type = msg_type; | ||
| 112 | rndis_msg->msg_len = msg_len; | ||
| 113 | |||
| 114 | /* | ||
| 115 | * Set the request id. This field is always after the rndis header for | ||
| 116 | * request/response packet types so we just used the SetRequest as a | ||
| 117 | * template | ||
| 118 | */ | ||
| 119 | set = &rndis_msg->msg.set_req; | ||
| 120 | set->req_id = atomic_inc_return(&dev->new_req_id); | ||
| 121 | |||
| 122 | /* Add to the request list */ | ||
| 123 | spin_lock_irqsave(&dev->request_lock, flags); | ||
| 124 | list_add_tail(&request->list_ent, &dev->req_list); | ||
| 125 | spin_unlock_irqrestore(&dev->request_lock, flags); | ||
| 126 | |||
| 127 | return request; | ||
| 128 | } | ||
| 129 | |||
| 130 | static void put_rndis_request(struct rndis_device *dev, | ||
| 131 | struct rndis_request *req) | ||
| 132 | { | ||
| 133 | unsigned long flags; | ||
| 134 | |||
| 135 | spin_lock_irqsave(&dev->request_lock, flags); | ||
| 136 | list_del(&req->list_ent); | ||
| 137 | spin_unlock_irqrestore(&dev->request_lock, flags); | ||
| 138 | |||
| 139 | kfree(req); | ||
| 140 | } | ||
| 141 | |||
| 142 | static void dump_rndis_message(struct hv_device *hv_dev, | ||
| 143 | struct rndis_message *rndis_msg) | ||
| 144 | { | ||
| 145 | struct net_device *netdev = dev_get_drvdata(&hv_dev->device); | ||
| 146 | |||
| 147 | switch (rndis_msg->ndis_msg_type) { | ||
| 148 | case REMOTE_NDIS_PACKET_MSG: | ||
| 149 | netdev_dbg(netdev, "REMOTE_NDIS_PACKET_MSG (len %u, " | ||
| 150 | "data offset %u data len %u, # oob %u, " | ||
| 151 | "oob offset %u, oob len %u, pkt offset %u, " | ||
| 152 | "pkt len %u\n", | ||
| 153 | rndis_msg->msg_len, | ||
| 154 | rndis_msg->msg.pkt.data_offset, | ||
| 155 | rndis_msg->msg.pkt.data_len, | ||
| 156 | rndis_msg->msg.pkt.num_oob_data_elements, | ||
| 157 | rndis_msg->msg.pkt.oob_data_offset, | ||
| 158 | rndis_msg->msg.pkt.oob_data_len, | ||
| 159 | rndis_msg->msg.pkt.per_pkt_info_offset, | ||
| 160 | rndis_msg->msg.pkt.per_pkt_info_len); | ||
| 161 | break; | ||
| 162 | |||
| 163 | case REMOTE_NDIS_INITIALIZE_CMPLT: | ||
| 164 | netdev_dbg(netdev, "REMOTE_NDIS_INITIALIZE_CMPLT " | ||
| 165 | "(len %u, id 0x%x, status 0x%x, major %d, minor %d, " | ||
| 166 | "device flags %d, max xfer size 0x%x, max pkts %u, " | ||
| 167 | "pkt aligned %u)\n", | ||
| 168 | rndis_msg->msg_len, | ||
| 169 | rndis_msg->msg.init_complete.req_id, | ||
| 170 | rndis_msg->msg.init_complete.status, | ||
| 171 | rndis_msg->msg.init_complete.major_ver, | ||
| 172 | rndis_msg->msg.init_complete.minor_ver, | ||
| 173 | rndis_msg->msg.init_complete.dev_flags, | ||
| 174 | rndis_msg->msg.init_complete.max_xfer_size, | ||
| 175 | rndis_msg->msg.init_complete. | ||
| 176 | max_pkt_per_msg, | ||
| 177 | rndis_msg->msg.init_complete. | ||
| 178 | pkt_alignment_factor); | ||
| 179 | break; | ||
| 180 | |||
| 181 | case REMOTE_NDIS_QUERY_CMPLT: | ||
| 182 | netdev_dbg(netdev, "REMOTE_NDIS_QUERY_CMPLT " | ||
| 183 | "(len %u, id 0x%x, status 0x%x, buf len %u, " | ||
| 184 | "buf offset %u)\n", | ||
| 185 | rndis_msg->msg_len, | ||
| 186 | rndis_msg->msg.query_complete.req_id, | ||
| 187 | rndis_msg->msg.query_complete.status, | ||
| 188 | rndis_msg->msg.query_complete. | ||
| 189 | info_buflen, | ||
| 190 | rndis_msg->msg.query_complete. | ||
| 191 | info_buf_offset); | ||
| 192 | break; | ||
| 193 | |||
| 194 | case REMOTE_NDIS_SET_CMPLT: | ||
| 195 | netdev_dbg(netdev, | ||
| 196 | "REMOTE_NDIS_SET_CMPLT (len %u, id 0x%x, status 0x%x)\n", | ||
| 197 | rndis_msg->msg_len, | ||
| 198 | rndis_msg->msg.set_complete.req_id, | ||
| 199 | rndis_msg->msg.set_complete.status); | ||
| 200 | break; | ||
| 201 | |||
| 202 | case REMOTE_NDIS_INDICATE_STATUS_MSG: | ||
| 203 | netdev_dbg(netdev, "REMOTE_NDIS_INDICATE_STATUS_MSG " | ||
| 204 | "(len %u, status 0x%x, buf len %u, buf offset %u)\n", | ||
| 205 | rndis_msg->msg_len, | ||
| 206 | rndis_msg->msg.indicate_status.status, | ||
| 207 | rndis_msg->msg.indicate_status.status_buflen, | ||
| 208 | rndis_msg->msg.indicate_status.status_buf_offset); | ||
| 209 | break; | ||
| 210 | |||
| 211 | default: | ||
| 212 | netdev_dbg(netdev, "0x%x (len %u)\n", | ||
| 213 | rndis_msg->ndis_msg_type, | ||
| 214 | rndis_msg->msg_len); | ||
| 215 | break; | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | static int rndis_filter_send_request(struct rndis_device *dev, | ||
| 220 | struct rndis_request *req) | ||
| 221 | { | ||
| 222 | int ret; | ||
| 223 | struct hv_netvsc_packet *packet; | ||
| 224 | |||
| 225 | /* Setup the packet to send it */ | ||
| 226 | packet = &req->pkt; | ||
| 227 | |||
| 228 | packet->is_data_pkt = false; | ||
| 229 | packet->total_data_buflen = req->request_msg.msg_len; | ||
| 230 | packet->page_buf_cnt = 1; | ||
| 231 | |||
| 232 | packet->page_buf[0].pfn = virt_to_phys(&req->request_msg) >> | ||
| 233 | PAGE_SHIFT; | ||
| 234 | packet->page_buf[0].len = req->request_msg.msg_len; | ||
| 235 | packet->page_buf[0].offset = | ||
| 236 | (unsigned long)&req->request_msg & (PAGE_SIZE - 1); | ||
| 237 | |||
| 238 | packet->completion.send.send_completion_ctx = req;/* packet; */ | ||
| 239 | packet->completion.send.send_completion = | ||
| 240 | rndis_filter_send_request_completion; | ||
| 241 | packet->completion.send.send_completion_tid = (unsigned long)dev; | ||
| 242 | |||
| 243 | ret = netvsc_send(dev->net_dev->dev, packet); | ||
| 244 | return ret; | ||
| 245 | } | ||
| 246 | |||
| 247 | static void rndis_filter_receive_response(struct rndis_device *dev, | ||
| 248 | struct rndis_message *resp) | ||
| 249 | { | ||
| 250 | struct rndis_request *request = NULL; | ||
| 251 | bool found = false; | ||
| 252 | unsigned long flags; | ||
| 253 | |||
| 254 | spin_lock_irqsave(&dev->request_lock, flags); | ||
| 255 | list_for_each_entry(request, &dev->req_list, list_ent) { | ||
| 256 | /* | ||
| 257 | * All request/response message contains RequestId as the 1st | ||
| 258 | * field | ||
| 259 | */ | ||
| 260 | if (request->request_msg.msg.init_req.req_id | ||
| 261 | == resp->msg.init_complete.req_id) { | ||
| 262 | found = true; | ||
| 263 | break; | ||
| 264 | } | ||
| 265 | } | ||
| 266 | spin_unlock_irqrestore(&dev->request_lock, flags); | ||
| 267 | |||
| 268 | if (found) { | ||
| 269 | if (resp->msg_len <= sizeof(struct rndis_message)) { | ||
| 270 | memcpy(&request->response_msg, resp, | ||
| 271 | resp->msg_len); | ||
| 272 | } else { | ||
| 273 | dev_err(&dev->net_dev->dev->device, | ||
| 274 | "rndis response buffer overflow " | ||
| 275 | "detected (size %u max %zu)\n", | ||
| 276 | resp->msg_len, | ||
| 277 | sizeof(struct rndis_filter_packet)); | ||
| 278 | |||
| 279 | if (resp->ndis_msg_type == | ||
| 280 | REMOTE_NDIS_RESET_CMPLT) { | ||
| 281 | /* does not have a request id field */ | ||
| 282 | request->response_msg.msg.reset_complete. | ||
| 283 | status = STATUS_BUFFER_OVERFLOW; | ||
| 284 | } else { | ||
| 285 | request->response_msg.msg. | ||
| 286 | init_complete.status = | ||
| 287 | STATUS_BUFFER_OVERFLOW; | ||
| 288 | } | ||
| 289 | } | ||
| 290 | |||
| 291 | complete(&request->wait_event); | ||
| 292 | } else { | ||
| 293 | dev_err(&dev->net_dev->dev->device, | ||
| 294 | "no rndis request found for this response " | ||
| 295 | "(id 0x%x res type 0x%x)\n", | ||
| 296 | resp->msg.init_complete.req_id, | ||
| 297 | resp->ndis_msg_type); | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | static void rndis_filter_receive_indicate_status(struct rndis_device *dev, | ||
| 302 | struct rndis_message *resp) | ||
| 303 | { | ||
| 304 | struct rndis_indicate_status *indicate = | ||
| 305 | &resp->msg.indicate_status; | ||
| 306 | |||
| 307 | if (indicate->status == RNDIS_STATUS_MEDIA_CONNECT) { | ||
| 308 | netvsc_linkstatus_callback( | ||
| 309 | dev->net_dev->dev, 1); | ||
| 310 | } else if (indicate->status == RNDIS_STATUS_MEDIA_DISCONNECT) { | ||
| 311 | netvsc_linkstatus_callback( | ||
| 312 | dev->net_dev->dev, 0); | ||
| 313 | } else { | ||
| 314 | /* | ||
| 315 | * TODO: | ||
| 316 | */ | ||
| 317 | } | ||
| 318 | } | ||
| 319 | |||
| 320 | static void rndis_filter_receive_data(struct rndis_device *dev, | ||
| 321 | struct rndis_message *msg, | ||
| 322 | struct hv_netvsc_packet *pkt) | ||
| 323 | { | ||
| 324 | struct rndis_packet *rndis_pkt; | ||
| 325 | u32 data_offset; | ||
| 326 | |||
| 327 | rndis_pkt = &msg->msg.pkt; | ||
| 328 | |||
| 329 | /* | ||
| 330 | * FIXME: Handle multiple rndis pkt msgs that maybe enclosed in this | ||
| 331 | * netvsc packet (ie TotalDataBufferLength != MessageLength) | ||
| 332 | */ | ||
| 333 | |||
| 334 | /* Remove the rndis header and pass it back up the stack */ | ||
| 335 | data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset; | ||
| 336 | |||
| 337 | pkt->total_data_buflen -= data_offset; | ||
| 338 | pkt->page_buf[0].offset += data_offset; | ||
| 339 | pkt->page_buf[0].len -= data_offset; | ||
| 340 | |||
| 341 | pkt->is_data_pkt = true; | ||
| 342 | |||
| 343 | netvsc_recv_callback(dev->net_dev->dev, pkt); | ||
| 344 | } | ||
| 345 | |||
| 346 | int rndis_filter_receive(struct hv_device *dev, | ||
| 347 | struct hv_netvsc_packet *pkt) | ||
| 348 | { | ||
| 349 | struct netvsc_device *net_dev = dev->ext; | ||
| 350 | struct rndis_device *rndis_dev; | ||
| 351 | struct rndis_message rndis_msg; | ||
| 352 | struct rndis_message *rndis_hdr; | ||
| 353 | |||
| 354 | if (!net_dev) | ||
| 355 | return -EINVAL; | ||
| 356 | |||
| 357 | /* Make sure the rndis device state is initialized */ | ||
| 358 | if (!net_dev->extension) { | ||
| 359 | dev_err(&dev->device, "got rndis message but no rndis device - " | ||
| 360 | "dropping this message!\n"); | ||
| 361 | return -1; | ||
| 362 | } | ||
| 363 | |||
| 364 | rndis_dev = (struct rndis_device *)net_dev->extension; | ||
| 365 | if (rndis_dev->state == RNDIS_DEV_UNINITIALIZED) { | ||
| 366 | dev_err(&dev->device, "got rndis message but rndis device " | ||
| 367 | "uninitialized...dropping this message!\n"); | ||
| 368 | return -1; | ||
| 369 | } | ||
| 370 | |||
| 371 | rndis_hdr = (struct rndis_message *)kmap_atomic( | ||
| 372 | pfn_to_page(pkt->page_buf[0].pfn), KM_IRQ0); | ||
| 373 | |||
| 374 | rndis_hdr = (void *)((unsigned long)rndis_hdr + | ||
| 375 | pkt->page_buf[0].offset); | ||
| 376 | |||
| 377 | /* Make sure we got a valid rndis message */ | ||
| 378 | if ((rndis_hdr->ndis_msg_type != REMOTE_NDIS_PACKET_MSG) && | ||
| 379 | (rndis_hdr->msg_len > sizeof(struct rndis_message))) { | ||
| 380 | dev_err(&dev->device, "incoming rndis message buffer overflow " | ||
| 381 | "detected (got %u, max %zu)..marking it an error!\n", | ||
| 382 | rndis_hdr->msg_len, | ||
| 383 | sizeof(struct rndis_message)); | ||
| 384 | } | ||
| 385 | |||
| 386 | memcpy(&rndis_msg, rndis_hdr, | ||
| 387 | (rndis_hdr->msg_len > sizeof(struct rndis_message)) ? | ||
| 388 | sizeof(struct rndis_message) : | ||
| 389 | rndis_hdr->msg_len); | ||
| 390 | |||
| 391 | kunmap_atomic(rndis_hdr - pkt->page_buf[0].offset, KM_IRQ0); | ||
| 392 | |||
| 393 | dump_rndis_message(dev, &rndis_msg); | ||
| 394 | |||
| 395 | switch (rndis_msg.ndis_msg_type) { | ||
| 396 | case REMOTE_NDIS_PACKET_MSG: | ||
| 397 | /* data msg */ | ||
| 398 | rndis_filter_receive_data(rndis_dev, &rndis_msg, pkt); | ||
| 399 | break; | ||
| 400 | |||
| 401 | case REMOTE_NDIS_INITIALIZE_CMPLT: | ||
| 402 | case REMOTE_NDIS_QUERY_CMPLT: | ||
| 403 | case REMOTE_NDIS_SET_CMPLT: | ||
| 404 | /* completion msgs */ | ||
| 405 | rndis_filter_receive_response(rndis_dev, &rndis_msg); | ||
| 406 | break; | ||
| 407 | |||
| 408 | case REMOTE_NDIS_INDICATE_STATUS_MSG: | ||
| 409 | /* notification msgs */ | ||
| 410 | rndis_filter_receive_indicate_status(rndis_dev, &rndis_msg); | ||
| 411 | break; | ||
| 412 | default: | ||
| 413 | dev_err(&dev->device, | ||
| 414 | "unhandled rndis message (type %u len %u)\n", | ||
| 415 | rndis_msg.ndis_msg_type, | ||
| 416 | rndis_msg.msg_len); | ||
| 417 | break; | ||
| 418 | } | ||
| 419 | |||
| 420 | return 0; | ||
| 421 | } | ||
| 422 | |||
| 423 | static int rndis_filter_query_device(struct rndis_device *dev, u32 oid, | ||
| 424 | void *result, u32 *result_size) | ||
| 425 | { | ||
| 426 | struct rndis_request *request; | ||
| 427 | u32 inresult_size = *result_size; | ||
| 428 | struct rndis_query_request *query; | ||
| 429 | struct rndis_query_complete *query_complete; | ||
| 430 | int ret = 0; | ||
| 431 | int t; | ||
| 432 | |||
| 433 | if (!result) | ||
| 434 | return -EINVAL; | ||
| 435 | |||
| 436 | *result_size = 0; | ||
| 437 | request = get_rndis_request(dev, REMOTE_NDIS_QUERY_MSG, | ||
| 438 | RNDIS_MESSAGE_SIZE(struct rndis_query_request)); | ||
| 439 | if (!request) { | ||
| 440 | ret = -1; | ||
| 441 | goto Cleanup; | ||
| 442 | } | ||
| 443 | |||
| 444 | /* Setup the rndis query */ | ||
| 445 | query = &request->request_msg.msg.query_req; | ||
| 446 | query->oid = oid; | ||
| 447 | query->info_buf_offset = sizeof(struct rndis_query_request); | ||
| 448 | query->info_buflen = 0; | ||
| 449 | query->dev_vc_handle = 0; | ||
| 450 | |||
| 451 | ret = rndis_filter_send_request(dev, request); | ||
| 452 | if (ret != 0) | ||
| 453 | goto Cleanup; | ||
| 454 | |||
| 455 | t = wait_for_completion_timeout(&request->wait_event, 5*HZ); | ||
| 456 | if (t == 0) { | ||
| 457 | ret = -ETIMEDOUT; | ||
| 458 | goto Cleanup; | ||
| 459 | } | ||
| 460 | |||
| 461 | /* Copy the response back */ | ||
| 462 | query_complete = &request->response_msg.msg.query_complete; | ||
| 463 | |||
| 464 | if (query_complete->info_buflen > inresult_size) { | ||
| 465 | ret = -1; | ||
| 466 | goto Cleanup; | ||
| 467 | } | ||
| 468 | |||
| 469 | memcpy(result, | ||
| 470 | (void *)((unsigned long)query_complete + | ||
| 471 | query_complete->info_buf_offset), | ||
| 472 | query_complete->info_buflen); | ||
| 473 | |||
| 474 | *result_size = query_complete->info_buflen; | ||
| 475 | |||
| 476 | Cleanup: | ||
| 477 | if (request) | ||
| 478 | put_rndis_request(dev, request); | ||
| 479 | |||
| 480 | return ret; | ||
| 481 | } | ||
| 482 | |||
| 483 | static int rndis_filter_query_device_mac(struct rndis_device *dev) | ||
| 484 | { | ||
| 485 | u32 size = ETH_ALEN; | ||
| 486 | |||
| 487 | return rndis_filter_query_device(dev, | ||
| 488 | RNDIS_OID_802_3_PERMANENT_ADDRESS, | ||
| 489 | dev->hw_mac_adr, &size); | ||
| 490 | } | ||
| 491 | |||
| 492 | static int rndis_filter_query_device_link_status(struct rndis_device *dev) | ||
| 493 | { | ||
| 494 | u32 size = sizeof(u32); | ||
| 495 | |||
| 496 | return rndis_filter_query_device(dev, | ||
| 497 | RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, | ||
| 498 | &dev->link_stat, &size); | ||
| 499 | } | ||
| 500 | |||
| 501 | static int rndis_filter_set_packet_filter(struct rndis_device *dev, | ||
| 502 | u32 new_filter) | ||
| 503 | { | ||
| 504 | struct rndis_request *request; | ||
| 505 | struct rndis_set_request *set; | ||
| 506 | struct rndis_set_complete *set_complete; | ||
| 507 | u32 status; | ||
| 508 | int ret, t; | ||
| 509 | |||
| 510 | request = get_rndis_request(dev, REMOTE_NDIS_SET_MSG, | ||
| 511 | RNDIS_MESSAGE_SIZE(struct rndis_set_request) + | ||
| 512 | sizeof(u32)); | ||
| 513 | if (!request) { | ||
| 514 | ret = -1; | ||
| 515 | goto Cleanup; | ||
| 516 | } | ||
| 517 | |||
| 518 | /* Setup the rndis set */ | ||
| 519 | set = &request->request_msg.msg.set_req; | ||
| 520 | set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER; | ||
| 521 | set->info_buflen = sizeof(u32); | ||
| 522 | set->info_buf_offset = sizeof(struct rndis_set_request); | ||
| 523 | |||
| 524 | memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request), | ||
| 525 | &new_filter, sizeof(u32)); | ||
| 526 | |||
| 527 | ret = rndis_filter_send_request(dev, request); | ||
| 528 | if (ret != 0) | ||
| 529 | goto Cleanup; | ||
| 530 | |||
| 531 | t = wait_for_completion_timeout(&request->wait_event, 5*HZ); | ||
| 532 | |||
| 533 | if (t == 0) { | ||
| 534 | ret = -1; | ||
| 535 | dev_err(&dev->net_dev->dev->device, | ||
| 536 | "timeout before we got a set response...\n"); | ||
| 537 | /* | ||
| 538 | * We can't deallocate the request since we may still receive a | ||
| 539 | * send completion for it. | ||
| 540 | */ | ||
| 541 | goto Exit; | ||
| 542 | } else { | ||
| 543 | if (ret > 0) | ||
| 544 | ret = 0; | ||
| 545 | set_complete = &request->response_msg.msg.set_complete; | ||
| 546 | status = set_complete->status; | ||
| 547 | } | ||
| 548 | |||
| 549 | Cleanup: | ||
| 550 | if (request) | ||
| 551 | put_rndis_request(dev, request); | ||
| 552 | Exit: | ||
| 553 | return ret; | ||
| 554 | } | ||
| 555 | |||
| 556 | |||
| 557 | static int rndis_filter_init_device(struct rndis_device *dev) | ||
| 558 | { | ||
| 559 | struct rndis_request *request; | ||
| 560 | struct rndis_initialize_request *init; | ||
| 561 | struct rndis_initialize_complete *init_complete; | ||
| 562 | u32 status; | ||
| 563 | int ret, t; | ||
| 564 | |||
| 565 | request = get_rndis_request(dev, REMOTE_NDIS_INITIALIZE_MSG, | ||
| 566 | RNDIS_MESSAGE_SIZE(struct rndis_initialize_request)); | ||
| 567 | if (!request) { | ||
| 568 | ret = -1; | ||
| 569 | goto Cleanup; | ||
| 570 | } | ||
| 571 | |||
| 572 | /* Setup the rndis set */ | ||
| 573 | init = &request->request_msg.msg.init_req; | ||
| 574 | init->major_ver = RNDIS_MAJOR_VERSION; | ||
| 575 | init->minor_ver = RNDIS_MINOR_VERSION; | ||
| 576 | /* FIXME: Use 1536 - rounded ethernet frame size */ | ||
| 577 | init->max_xfer_size = 2048; | ||
| 578 | |||
| 579 | dev->state = RNDIS_DEV_INITIALIZING; | ||
| 580 | |||
| 581 | ret = rndis_filter_send_request(dev, request); | ||
| 582 | if (ret != 0) { | ||
| 583 | dev->state = RNDIS_DEV_UNINITIALIZED; | ||
| 584 | goto Cleanup; | ||
| 585 | } | ||
| 586 | |||
| 587 | |||
| 588 | t = wait_for_completion_timeout(&request->wait_event, 5*HZ); | ||
| 589 | |||
| 590 | if (t == 0) { | ||
| 591 | ret = -ETIMEDOUT; | ||
| 592 | goto Cleanup; | ||
| 593 | } | ||
| 594 | |||
| 595 | init_complete = &request->response_msg.msg.init_complete; | ||
| 596 | status = init_complete->status; | ||
| 597 | if (status == RNDIS_STATUS_SUCCESS) { | ||
| 598 | dev->state = RNDIS_DEV_INITIALIZED; | ||
| 599 | ret = 0; | ||
| 600 | } else { | ||
| 601 | dev->state = RNDIS_DEV_UNINITIALIZED; | ||
| 602 | ret = -1; | ||
| 603 | } | ||
| 604 | |||
| 605 | Cleanup: | ||
| 606 | if (request) | ||
| 607 | put_rndis_request(dev, request); | ||
| 608 | |||
| 609 | return ret; | ||
| 610 | } | ||
| 611 | |||
| 612 | static void rndis_filter_halt_device(struct rndis_device *dev) | ||
| 613 | { | ||
| 614 | struct rndis_request *request; | ||
| 615 | struct rndis_halt_request *halt; | ||
| 616 | |||
| 617 | /* Attempt to do a rndis device halt */ | ||
| 618 | request = get_rndis_request(dev, REMOTE_NDIS_HALT_MSG, | ||
| 619 | RNDIS_MESSAGE_SIZE(struct rndis_halt_request)); | ||
| 620 | if (!request) | ||
| 621 | goto Cleanup; | ||
| 622 | |||
| 623 | /* Setup the rndis set */ | ||
| 624 | halt = &request->request_msg.msg.halt_req; | ||
| 625 | halt->req_id = atomic_inc_return(&dev->new_req_id); | ||
| 626 | |||
| 627 | /* Ignore return since this msg is optional. */ | ||
| 628 | rndis_filter_send_request(dev, request); | ||
| 629 | |||
| 630 | dev->state = RNDIS_DEV_UNINITIALIZED; | ||
| 631 | |||
| 632 | Cleanup: | ||
| 633 | if (request) | ||
| 634 | put_rndis_request(dev, request); | ||
| 635 | return; | ||
| 636 | } | ||
| 637 | |||
| 638 | static int rndis_filter_open_device(struct rndis_device *dev) | ||
| 639 | { | ||
| 640 | int ret; | ||
| 641 | |||
| 642 | if (dev->state != RNDIS_DEV_INITIALIZED) | ||
| 643 | return 0; | ||
| 644 | |||
| 645 | ret = rndis_filter_set_packet_filter(dev, | ||
| 646 | NDIS_PACKET_TYPE_BROADCAST | | ||
| 647 | NDIS_PACKET_TYPE_ALL_MULTICAST | | ||
| 648 | NDIS_PACKET_TYPE_DIRECTED); | ||
| 649 | if (ret == 0) | ||
| 650 | dev->state = RNDIS_DEV_DATAINITIALIZED; | ||
| 651 | |||
| 652 | return ret; | ||
| 653 | } | ||
| 654 | |||
| 655 | static int rndis_filter_close_device(struct rndis_device *dev) | ||
| 656 | { | ||
| 657 | int ret; | ||
| 658 | |||
| 659 | if (dev->state != RNDIS_DEV_DATAINITIALIZED) | ||
| 660 | return 0; | ||
| 661 | |||
| 662 | ret = rndis_filter_set_packet_filter(dev, 0); | ||
| 663 | if (ret == 0) | ||
| 664 | dev->state = RNDIS_DEV_INITIALIZED; | ||
| 665 | |||
| 666 | return ret; | ||
| 667 | } | ||
| 668 | |||
| 669 | int rndis_filter_device_add(struct hv_device *dev, | ||
| 670 | void *additional_info) | ||
| 671 | { | ||
| 672 | int ret; | ||
| 673 | struct netvsc_device *netDevice; | ||
| 674 | struct rndis_device *rndisDevice; | ||
| 675 | struct netvsc_device_info *deviceInfo = additional_info; | ||
| 676 | |||
| 677 | rndisDevice = get_rndis_device(); | ||
| 678 | if (!rndisDevice) | ||
| 679 | return -1; | ||
| 680 | |||
| 681 | /* | ||
| 682 | * Let the inner driver handle this first to create the netvsc channel | ||
| 683 | * NOTE! Once the channel is created, we may get a receive callback | ||
| 684 | * (RndisFilterOnReceive()) before this call is completed | ||
| 685 | */ | ||
| 686 | ret = netvsc_device_add(dev, additional_info); | ||
| 687 | if (ret != 0) { | ||
| 688 | kfree(rndisDevice); | ||
| 689 | return ret; | ||
| 690 | } | ||
| 691 | |||
| 692 | |||
| 693 | /* Initialize the rndis device */ | ||
| 694 | netDevice = dev->ext; | ||
| 695 | |||
| 696 | netDevice->extension = rndisDevice; | ||
| 697 | rndisDevice->net_dev = netDevice; | ||
| 698 | |||
| 699 | /* Send the rndis initialization message */ | ||
| 700 | ret = rndis_filter_init_device(rndisDevice); | ||
| 701 | if (ret != 0) { | ||
| 702 | /* | ||
| 703 | * TODO: If rndis init failed, we will need to shut down the | ||
| 704 | * channel | ||
| 705 | */ | ||
| 706 | } | ||
| 707 | |||
| 708 | /* Get the mac address */ | ||
| 709 | ret = rndis_filter_query_device_mac(rndisDevice); | ||
| 710 | if (ret != 0) { | ||
| 711 | /* | ||
| 712 | * TODO: shutdown rndis device and the channel | ||
| 713 | */ | ||
| 714 | } | ||
| 715 | |||
| 716 | memcpy(deviceInfo->mac_adr, rndisDevice->hw_mac_adr, ETH_ALEN); | ||
| 717 | |||
| 718 | rndis_filter_query_device_link_status(rndisDevice); | ||
| 719 | |||
| 720 | deviceInfo->link_state = rndisDevice->link_stat; | ||
| 721 | |||
| 722 | dev_info(&dev->device, "Device MAC %pM link state %s", | ||
| 723 | rndisDevice->hw_mac_adr, | ||
| 724 | ((deviceInfo->link_state) ? ("down\n") : ("up\n"))); | ||
| 725 | |||
| 726 | return ret; | ||
| 727 | } | ||
| 728 | |||
| 729 | void rndis_filter_device_remove(struct hv_device *dev) | ||
| 730 | { | ||
| 731 | struct netvsc_device *net_dev = dev->ext; | ||
| 732 | struct rndis_device *rndis_dev = net_dev->extension; | ||
| 733 | |||
| 734 | /* Halt and release the rndis device */ | ||
| 735 | rndis_filter_halt_device(rndis_dev); | ||
| 736 | |||
| 737 | kfree(rndis_dev); | ||
| 738 | net_dev->extension = NULL; | ||
| 739 | |||
| 740 | netvsc_device_remove(dev); | ||
| 741 | } | ||
| 742 | |||
| 743 | |||
| 744 | int rndis_filter_open(struct hv_device *dev) | ||
| 745 | { | ||
| 746 | struct netvsc_device *netDevice = dev->ext; | ||
| 747 | |||
| 748 | if (!netDevice) | ||
| 749 | return -EINVAL; | ||
| 750 | |||
| 751 | return rndis_filter_open_device(netDevice->extension); | ||
| 752 | } | ||
| 753 | |||
| 754 | int rndis_filter_close(struct hv_device *dev) | ||
| 755 | { | ||
| 756 | struct netvsc_device *netDevice = dev->ext; | ||
| 757 | |||
| 758 | if (!netDevice) | ||
| 759 | return -EINVAL; | ||
| 760 | |||
| 761 | return rndis_filter_close_device(netDevice->extension); | ||
| 762 | } | ||
| 763 | |||
| 764 | int rndis_filter_send(struct hv_device *dev, | ||
| 765 | struct hv_netvsc_packet *pkt) | ||
| 766 | { | ||
| 767 | int ret; | ||
| 768 | struct rndis_filter_packet *filterPacket; | ||
| 769 | struct rndis_message *rndisMessage; | ||
| 770 | struct rndis_packet *rndisPacket; | ||
| 771 | u32 rndisMessageSize; | ||
| 772 | |||
| 773 | /* Add the rndis header */ | ||
| 774 | filterPacket = (struct rndis_filter_packet *)pkt->extension; | ||
| 775 | |||
| 776 | memset(filterPacket, 0, sizeof(struct rndis_filter_packet)); | ||
| 777 | |||
| 778 | rndisMessage = &filterPacket->msg; | ||
| 779 | rndisMessageSize = RNDIS_MESSAGE_SIZE(struct rndis_packet); | ||
| 780 | |||
| 781 | rndisMessage->ndis_msg_type = REMOTE_NDIS_PACKET_MSG; | ||
| 782 | rndisMessage->msg_len = pkt->total_data_buflen + | ||
| 783 | rndisMessageSize; | ||
| 784 | |||
| 785 | rndisPacket = &rndisMessage->msg.pkt; | ||
| 786 | rndisPacket->data_offset = sizeof(struct rndis_packet); | ||
| 787 | rndisPacket->data_len = pkt->total_data_buflen; | ||
| 788 | |||
| 789 | pkt->is_data_pkt = true; | ||
| 790 | pkt->page_buf[0].pfn = virt_to_phys(rndisMessage) >> PAGE_SHIFT; | ||
| 791 | pkt->page_buf[0].offset = | ||
| 792 | (unsigned long)rndisMessage & (PAGE_SIZE-1); | ||
| 793 | pkt->page_buf[0].len = rndisMessageSize; | ||
| 794 | |||
| 795 | /* Save the packet send completion and context */ | ||
| 796 | filterPacket->completion = pkt->completion.send.send_completion; | ||
| 797 | filterPacket->completion_ctx = | ||
| 798 | pkt->completion.send.send_completion_ctx; | ||
| 799 | |||
| 800 | /* Use ours */ | ||
| 801 | pkt->completion.send.send_completion = rndis_filter_send_completion; | ||
| 802 | pkt->completion.send.send_completion_ctx = filterPacket; | ||
| 803 | |||
| 804 | ret = netvsc_send(dev, pkt); | ||
| 805 | if (ret != 0) { | ||
| 806 | /* | ||
| 807 | * Reset the completion to originals to allow retries from | ||
| 808 | * above | ||
| 809 | */ | ||
| 810 | pkt->completion.send.send_completion = | ||
| 811 | filterPacket->completion; | ||
| 812 | pkt->completion.send.send_completion_ctx = | ||
| 813 | filterPacket->completion_ctx; | ||
| 814 | } | ||
| 815 | |||
| 816 | return ret; | ||
| 817 | } | ||
| 818 | |||
| 819 | static void rndis_filter_send_completion(void *ctx) | ||
| 820 | { | ||
| 821 | struct rndis_filter_packet *filterPacket = ctx; | ||
| 822 | |||
| 823 | /* Pass it back to the original handler */ | ||
| 824 | filterPacket->completion(filterPacket->completion_ctx); | ||
| 825 | } | ||
| 826 | |||
| 827 | |||
| 828 | static void rndis_filter_send_request_completion(void *ctx) | ||
| 829 | { | ||
| 830 | /* Noop */ | ||
| 831 | } | ||
diff --git a/drivers/staging/hv/storvsc.c b/drivers/staging/hv/storvsc.c new file mode 100644 index 00000000000..30297861194 --- /dev/null +++ b/drivers/staging/hv/storvsc.c | |||
| @@ -0,0 +1,564 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2009, Microsoft Corporation. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms and conditions of the GNU General Public License, | ||
| 6 | * version 2, as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License along with | ||
| 14 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
| 15 | * Place - Suite 330, Boston, MA 02111-1307 USA. | ||
| 16 | * | ||
| 17 | * Authors: | ||
| 18 | * Haiyang Zhang <haiyangz@microsoft.com> | ||
| 19 | * Hank Janssen <hjanssen@microsoft.com> | ||
| 20 | * K. Y. Srinivasan <kys@microsoft.com> | ||
| 21 | * | ||
| 22 | */ | ||
| 23 | #include <linux/kernel.h> | ||
| 24 | #include <linux/sched.h> | ||
| 25 | #include <linux/completion.h> | ||
| 26 | #include <linux/string.h> | ||
| 27 | #include <linux/slab.h> | ||
| 28 | #include <linux/mm.h> | ||
| 29 | #include <linux/delay.h> | ||
| 30 | |||
| 31 | #include "hyperv.h" | ||
| 32 | #include "hyperv_storage.h" | ||
| 33 | |||
| 34 | |||
| 35 | static inline struct storvsc_device *alloc_stor_device(struct hv_device *device) | ||
| 36 | { | ||
| 37 | struct storvsc_device *stor_device; | ||
| 38 | |||
| 39 | stor_device = kzalloc(sizeof(struct storvsc_device), GFP_KERNEL); | ||
| 40 | if (!stor_device) | ||
| 41 | return NULL; | ||
| 42 | |||
| 43 | /* Set to 2 to allow both inbound and outbound traffics */ | ||
| 44 | /* (ie get_stor_device() and must_get_stor_device()) to proceed. */ | ||
| 45 | atomic_cmpxchg(&stor_device->ref_count, 0, 2); | ||
| 46 | |||
| 47 | init_waitqueue_head(&stor_device->waiting_to_drain); | ||
| 48 | stor_device->device = device; | ||
| 49 | device->ext = stor_device; | ||
| 50 | |||
| 51 | return stor_device; | ||
| 52 | } | ||
| 53 | |||
| 54 | static inline void free_stor_device(struct storvsc_device *device) | ||
| 55 | { | ||
| 56 | kfree(device); | ||
| 57 | } | ||
| 58 | |||
| 59 | /* Get the stordevice object iff exists and its refcount > 0 */ | ||
| 60 | static inline struct storvsc_device *must_get_stor_device( | ||
| 61 | struct hv_device *device) | ||
| 62 | { | ||
| 63 | struct storvsc_device *stor_device; | ||
| 64 | |||
| 65 | stor_device = (struct storvsc_device *)device->ext; | ||
| 66 | if (stor_device && atomic_read(&stor_device->ref_count)) | ||
| 67 | atomic_inc(&stor_device->ref_count); | ||
| 68 | else | ||
| 69 | stor_device = NULL; | ||
| 70 | |||
| 71 | return stor_device; | ||
| 72 | } | ||
| 73 | |||
| 74 | /* Drop ref count to 1 to effectively disable get_stor_device() */ | ||
| 75 | static inline struct storvsc_device *release_stor_device( | ||
| 76 | struct hv_device *device) | ||
| 77 | { | ||
| 78 | struct storvsc_device *stor_device; | ||
| 79 | |||
| 80 | stor_device = (struct storvsc_device *)device->ext; | ||
| 81 | |||
| 82 | /* Busy wait until the ref drop to 2, then set it to 1 */ | ||
| 83 | while (atomic_cmpxchg(&stor_device->ref_count, 2, 1) != 2) | ||
| 84 | udelay(100); | ||
| 85 | |||
| 86 | return stor_device; | ||
| 87 | } | ||
| 88 | |||
| 89 | /* Drop ref count to 0. No one can use stor_device object. */ | ||
| 90 | static inline struct storvsc_device *final_release_stor_device( | ||
| 91 | struct hv_device *device) | ||
| 92 | { | ||
| 93 | struct storvsc_device *stor_device; | ||
| 94 | |||
| 95 | stor_device = (struct storvsc_device *)device->ext; | ||
| 96 | |||
| 97 | /* Busy wait until the ref drop to 1, then set it to 0 */ | ||
| 98 | while (atomic_cmpxchg(&stor_device->ref_count, 1, 0) != 1) | ||
| 99 | udelay(100); | ||
| 100 | |||
| 101 | device->ext = NULL; | ||
| 102 | return stor_device; | ||
| 103 | } | ||
| 104 | |||
| 105 | static int storvsc_channel_init(struct hv_device *device) | ||
| 106 | { | ||
| 107 | struct storvsc_device *stor_device; | ||
| 108 | struct hv_storvsc_request *request; | ||
| 109 | struct vstor_packet *vstor_packet; | ||
| 110 | int ret, t; | ||
| 111 | |||
| 112 | stor_device = get_stor_device(device); | ||
| 113 | if (!stor_device) | ||
| 114 | return -1; | ||
| 115 | |||
| 116 | request = &stor_device->init_request; | ||
| 117 | vstor_packet = &request->vstor_packet; | ||
| 118 | |||
| 119 | /* | ||
| 120 | * Now, initiate the vsc/vsp initialization protocol on the open | ||
| 121 | * channel | ||
| 122 | */ | ||
| 123 | memset(request, 0, sizeof(struct hv_storvsc_request)); | ||
| 124 | init_completion(&request->wait_event); | ||
| 125 | vstor_packet->operation = VSTOR_OPERATION_BEGIN_INITIALIZATION; | ||
| 126 | vstor_packet->flags = REQUEST_COMPLETION_FLAG; | ||
| 127 | |||
| 128 | DPRINT_INFO(STORVSC, "BEGIN_INITIALIZATION_OPERATION..."); | ||
| 129 | |||
| 130 | ret = vmbus_sendpacket(device->channel, vstor_packet, | ||
| 131 | sizeof(struct vstor_packet), | ||
| 132 | (unsigned long)request, | ||
| 133 | VM_PKT_DATA_INBAND, | ||
| 134 | VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); | ||
| 135 | if (ret != 0) | ||
| 136 | goto cleanup; | ||
| 137 | |||
| 138 | t = wait_for_completion_timeout(&request->wait_event, 5*HZ); | ||
| 139 | if (t == 0) { | ||
| 140 | ret = -ETIMEDOUT; | ||
| 141 | goto cleanup; | ||
| 142 | } | ||
| 143 | |||
| 144 | if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || | ||
| 145 | vstor_packet->status != 0) | ||
| 146 | goto cleanup; | ||
| 147 | |||
| 148 | DPRINT_INFO(STORVSC, "QUERY_PROTOCOL_VERSION_OPERATION..."); | ||
| 149 | |||
| 150 | /* reuse the packet for version range supported */ | ||
| 151 | memset(vstor_packet, 0, sizeof(struct vstor_packet)); | ||
| 152 | vstor_packet->operation = VSTOR_OPERATION_QUERY_PROTOCOL_VERSION; | ||
| 153 | vstor_packet->flags = REQUEST_COMPLETION_FLAG; | ||
| 154 | |||
| 155 | vstor_packet->version.major_minor = VMSTOR_PROTOCOL_VERSION_CURRENT; | ||
| 156 | FILL_VMSTOR_REVISION(vstor_packet->version.revision); | ||
| 157 | |||
| 158 | ret = vmbus_sendpacket(device->channel, vstor_packet, | ||
| 159 | sizeof(struct vstor_packet), | ||
| 160 | (unsigned long)request, | ||
| 161 | VM_PKT_DATA_INBAND, | ||
| 162 | VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); | ||
| 163 | if (ret != 0) | ||
| 164 | goto cleanup; | ||
| 165 | |||
| 166 | t = wait_for_completion_timeout(&request->wait_event, 5*HZ); | ||
| 167 | if (t == 0) { | ||
| 168 | ret = -ETIMEDOUT; | ||
| 169 | goto cleanup; | ||
| 170 | } | ||
| 171 | |||
| 172 | /* TODO: Check returned version */ | ||
| 173 | if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || | ||
| 174 | vstor_packet->status != 0) | ||
| 175 | goto cleanup; | ||
| 176 | |||
| 177 | /* Query channel properties */ | ||
| 178 | DPRINT_INFO(STORVSC, "QUERY_PROPERTIES_OPERATION..."); | ||
| 179 | |||
| 180 | memset(vstor_packet, 0, sizeof(struct vstor_packet)); | ||
| 181 | vstor_packet->operation = VSTOR_OPERATION_QUERY_PROPERTIES; | ||
| 182 | vstor_packet->flags = REQUEST_COMPLETION_FLAG; | ||
| 183 | vstor_packet->storage_channel_properties.port_number = | ||
| 184 | stor_device->port_number; | ||
| 185 | |||
| 186 | ret = vmbus_sendpacket(device->channel, vstor_packet, | ||
| 187 | sizeof(struct vstor_packet), | ||
| 188 | (unsigned long)request, | ||
| 189 | VM_PKT_DATA_INBAND, | ||
| 190 | VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); | ||
| 191 | |||
| 192 | if (ret != 0) | ||
| 193 | goto cleanup; | ||
| 194 | |||
| 195 | t = wait_for_completion_timeout(&request->wait_event, 5*HZ); | ||
| 196 | if (t == 0) { | ||
| 197 | ret = -ETIMEDOUT; | ||
| 198 | goto cleanup; | ||
| 199 | } | ||
| 200 | |||
| 201 | /* TODO: Check returned version */ | ||
| 202 | if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || | ||
| 203 | vstor_packet->status != 0) | ||
| 204 | goto cleanup; | ||
| 205 | |||
| 206 | stor_device->path_id = vstor_packet->storage_channel_properties.path_id; | ||
| 207 | stor_device->target_id | ||
| 208 | = vstor_packet->storage_channel_properties.target_id; | ||
| 209 | |||
| 210 | DPRINT_INFO(STORVSC, "END_INITIALIZATION_OPERATION..."); | ||
| 211 | |||
| 212 | memset(vstor_packet, 0, sizeof(struct vstor_packet)); | ||
| 213 | vstor_packet->operation = VSTOR_OPERATION_END_INITIALIZATION; | ||
| 214 | vstor_packet->flags = REQUEST_COMPLETION_FLAG; | ||
| 215 | |||
| 216 | ret = vmbus_sendpacket(device->channel, vstor_packet, | ||
| 217 | sizeof(struct vstor_packet), | ||
| 218 | (unsigned long)request, | ||
| 219 | VM_PKT_DATA_INBAND, | ||
| 220 | VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); | ||
| 221 | |||
| 222 | if (ret != 0) | ||
| 223 | goto cleanup; | ||
| 224 | |||
| 225 | t = wait_for_completion_timeout(&request->wait_event, 5*HZ); | ||
| 226 | if (t == 0) { | ||
| 227 | ret = -ETIMEDOUT; | ||
| 228 | goto cleanup; | ||
| 229 | } | ||
| 230 | |||
| 231 | if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || | ||
| 232 | vstor_packet->status != 0) | ||
| 233 | goto cleanup; | ||
| 234 | |||
| 235 | DPRINT_INFO(STORVSC, "**** storage channel up and running!! ****"); | ||
| 236 | |||
| 237 | cleanup: | ||
| 238 | put_stor_device(device); | ||
| 239 | return ret; | ||
| 240 | } | ||
| 241 | |||
| 242 | static void storvsc_on_io_completion(struct hv_device *device, | ||
| 243 | struct vstor_packet *vstor_packet, | ||
| 244 | struct hv_storvsc_request *request) | ||
| 245 | { | ||
| 246 | struct storvsc_device *stor_device; | ||
| 247 | struct vstor_packet *stor_pkt; | ||
| 248 | |||
| 249 | stor_device = must_get_stor_device(device); | ||
| 250 | if (!stor_device) | ||
| 251 | return; | ||
| 252 | |||
| 253 | stor_pkt = &request->vstor_packet; | ||
| 254 | |||
| 255 | |||
| 256 | /* Copy over the status...etc */ | ||
| 257 | stor_pkt->vm_srb.scsi_status = vstor_packet->vm_srb.scsi_status; | ||
| 258 | stor_pkt->vm_srb.srb_status = vstor_packet->vm_srb.srb_status; | ||
| 259 | stor_pkt->vm_srb.sense_info_length = | ||
| 260 | vstor_packet->vm_srb.sense_info_length; | ||
| 261 | |||
| 262 | if (vstor_packet->vm_srb.scsi_status != 0 || | ||
| 263 | vstor_packet->vm_srb.srb_status != 1){ | ||
| 264 | DPRINT_WARN(STORVSC, | ||
| 265 | "cmd 0x%x scsi status 0x%x srb status 0x%x\n", | ||
| 266 | stor_pkt->vm_srb.cdb[0], | ||
| 267 | vstor_packet->vm_srb.scsi_status, | ||
| 268 | vstor_packet->vm_srb.srb_status); | ||
| 269 | } | ||
| 270 | |||
| 271 | if ((vstor_packet->vm_srb.scsi_status & 0xFF) == 0x02) { | ||
| 272 | /* CHECK_CONDITION */ | ||
| 273 | if (vstor_packet->vm_srb.srb_status & 0x80) { | ||
| 274 | /* autosense data available */ | ||
| 275 | DPRINT_WARN(STORVSC, "storvsc pkt %p autosense data " | ||
| 276 | "valid - len %d\n", request, | ||
| 277 | vstor_packet->vm_srb.sense_info_length); | ||
| 278 | |||
| 279 | memcpy(request->sense_buffer, | ||
| 280 | vstor_packet->vm_srb.sense_data, | ||
| 281 | vstor_packet->vm_srb.sense_info_length); | ||
| 282 | |||
| 283 | } | ||
| 284 | } | ||
| 285 | |||
| 286 | stor_pkt->vm_srb.data_transfer_length = | ||
| 287 | vstor_packet->vm_srb.data_transfer_length; | ||
| 288 | |||
| 289 | request->on_io_completion(request); | ||
| 290 | |||
| 291 | if (atomic_dec_and_test(&stor_device->num_outstanding_req) && | ||
| 292 | stor_device->drain_notify) | ||
| 293 | wake_up(&stor_device->waiting_to_drain); | ||
| 294 | |||
| 295 | |||
| 296 | put_stor_device(device); | ||
| 297 | } | ||
| 298 | |||
| 299 | static void storvsc_on_receive(struct hv_device *device, | ||
| 300 | struct vstor_packet *vstor_packet, | ||
| 301 | struct hv_storvsc_request *request) | ||
| 302 | { | ||
| 303 | switch (vstor_packet->operation) { | ||
| 304 | case VSTOR_OPERATION_COMPLETE_IO: | ||
| 305 | storvsc_on_io_completion(device, vstor_packet, request); | ||
| 306 | break; | ||
| 307 | case VSTOR_OPERATION_REMOVE_DEVICE: | ||
| 308 | DPRINT_INFO(STORVSC, "REMOVE_DEVICE_OPERATION"); | ||
| 309 | /* TODO: */ | ||
| 310 | break; | ||
| 311 | |||
| 312 | default: | ||
| 313 | DPRINT_INFO(STORVSC, "Unknown operation received - %d", | ||
| 314 | vstor_packet->operation); | ||
| 315 | break; | ||
| 316 | } | ||
| 317 | } | ||
| 318 | |||
| 319 | static void storvsc_on_channel_callback(void *context) | ||
| 320 | { | ||
| 321 | struct hv_device *device = (struct hv_device *)context; | ||
| 322 | struct storvsc_device *stor_device; | ||
| 323 | u32 bytes_recvd; | ||
| 324 | u64 request_id; | ||
| 325 | unsigned char packet[ALIGN(sizeof(struct vstor_packet), 8)]; | ||
| 326 | struct hv_storvsc_request *request; | ||
| 327 | int ret; | ||
| 328 | |||
| 329 | |||
| 330 | stor_device = must_get_stor_device(device); | ||
| 331 | if (!stor_device) | ||
| 332 | return; | ||
| 333 | |||
| 334 | do { | ||
| 335 | ret = vmbus_recvpacket(device->channel, packet, | ||
| 336 | ALIGN(sizeof(struct vstor_packet), 8), | ||
| 337 | &bytes_recvd, &request_id); | ||
| 338 | if (ret == 0 && bytes_recvd > 0) { | ||
| 339 | |||
| 340 | request = (struct hv_storvsc_request *) | ||
| 341 | (unsigned long)request_id; | ||
| 342 | |||
| 343 | if ((request == &stor_device->init_request) || | ||
| 344 | (request == &stor_device->reset_request)) { | ||
| 345 | |||
| 346 | memcpy(&request->vstor_packet, packet, | ||
| 347 | sizeof(struct vstor_packet)); | ||
| 348 | complete(&request->wait_event); | ||
| 349 | } else { | ||
| 350 | storvsc_on_receive(device, | ||
| 351 | (struct vstor_packet *)packet, | ||
| 352 | request); | ||
| 353 | } | ||
| 354 | } else { | ||
| 355 | break; | ||
| 356 | } | ||
| 357 | } while (1); | ||
| 358 | |||
| 359 | put_stor_device(device); | ||
| 360 | return; | ||
| 361 | } | ||
| 362 | |||
| 363 | static int storvsc_connect_to_vsp(struct hv_device *device, u32 ring_size) | ||
| 364 | { | ||
| 365 | struct vmstorage_channel_properties props; | ||
| 366 | int ret; | ||
| 367 | |||
| 368 | memset(&props, 0, sizeof(struct vmstorage_channel_properties)); | ||
| 369 | |||
| 370 | /* Open the channel */ | ||
| 371 | ret = vmbus_open(device->channel, | ||
| 372 | ring_size, | ||
| 373 | ring_size, | ||
| 374 | (void *)&props, | ||
| 375 | sizeof(struct vmstorage_channel_properties), | ||
| 376 | storvsc_on_channel_callback, device); | ||
| 377 | |||
| 378 | if (ret != 0) | ||
| 379 | return -1; | ||
| 380 | |||
| 381 | ret = storvsc_channel_init(device); | ||
| 382 | |||
| 383 | return ret; | ||
| 384 | } | ||
| 385 | |||
| 386 | int storvsc_dev_add(struct hv_device *device, | ||
| 387 | void *additional_info) | ||
| 388 | { | ||
| 389 | struct storvsc_device *stor_device; | ||
| 390 | struct storvsc_device_info *device_info; | ||
| 391 | int ret = 0; | ||
| 392 | |||
| 393 | device_info = (struct storvsc_device_info *)additional_info; | ||
| 394 | stor_device = alloc_stor_device(device); | ||
| 395 | if (!stor_device) { | ||
| 396 | ret = -1; | ||
| 397 | goto cleanup; | ||
| 398 | } | ||
| 399 | |||
| 400 | /* Save the channel properties to our storvsc channel */ | ||
| 401 | |||
| 402 | /* FIXME: */ | ||
| 403 | /* | ||
| 404 | * If we support more than 1 scsi channel, we need to set the | ||
| 405 | * port number here to the scsi channel but how do we get the | ||
| 406 | * scsi channel prior to the bus scan | ||
| 407 | */ | ||
| 408 | |||
| 409 | stor_device->port_number = device_info->port_number; | ||
| 410 | /* Send it back up */ | ||
| 411 | ret = storvsc_connect_to_vsp(device, device_info->ring_buffer_size); | ||
| 412 | |||
| 413 | device_info->path_id = stor_device->path_id; | ||
| 414 | device_info->target_id = stor_device->target_id; | ||
| 415 | |||
| 416 | cleanup: | ||
| 417 | return ret; | ||
| 418 | } | ||
| 419 | |||
| 420 | int storvsc_dev_remove(struct hv_device *device) | ||
| 421 | { | ||
| 422 | struct storvsc_device *stor_device; | ||
| 423 | |||
| 424 | DPRINT_INFO(STORVSC, "disabling storage device (%p)...", | ||
| 425 | device->ext); | ||
| 426 | |||
| 427 | stor_device = release_stor_device(device); | ||
| 428 | |||
| 429 | /* | ||
| 430 | * At this point, all outbound traffic should be disable. We | ||
| 431 | * only allow inbound traffic (responses) to proceed so that | ||
| 432 | * outstanding requests can be completed. | ||
| 433 | */ | ||
| 434 | |||
| 435 | storvsc_wait_to_drain(stor_device); | ||
| 436 | |||
| 437 | stor_device = final_release_stor_device(device); | ||
| 438 | |||
| 439 | /* Close the channel */ | ||
| 440 | vmbus_close(device->channel); | ||
| 441 | |||
| 442 | free_stor_device(stor_device); | ||
| 443 | return 0; | ||
| 444 | } | ||
| 445 | |||
| 446 | int storvsc_do_io(struct hv_device *device, | ||
| 447 | struct hv_storvsc_request *request) | ||
| 448 | { | ||
| 449 | struct storvsc_device *stor_device; | ||
| 450 | struct vstor_packet *vstor_packet; | ||
| 451 | int ret = 0; | ||
| 452 | |||
| 453 | vstor_packet = &request->vstor_packet; | ||
| 454 | stor_device = get_stor_device(device); | ||
| 455 | |||
| 456 | if (!stor_device) | ||
| 457 | return -2; | ||
| 458 | |||
| 459 | |||
| 460 | request->device = device; | ||
| 461 | |||
| 462 | |||
| 463 | vstor_packet->flags |= REQUEST_COMPLETION_FLAG; | ||
| 464 | |||
| 465 | vstor_packet->vm_srb.length = sizeof(struct vmscsi_request); | ||
| 466 | |||
| 467 | |||
| 468 | vstor_packet->vm_srb.sense_info_length = SENSE_BUFFER_SIZE; | ||
| 469 | |||
| 470 | |||
| 471 | vstor_packet->vm_srb.data_transfer_length = | ||
| 472 | request->data_buffer.len; | ||
| 473 | |||
| 474 | vstor_packet->operation = VSTOR_OPERATION_EXECUTE_SRB; | ||
| 475 | |||
| 476 | if (request->data_buffer.len) { | ||
| 477 | ret = vmbus_sendpacket_multipagebuffer(device->channel, | ||
| 478 | &request->data_buffer, | ||
| 479 | vstor_packet, | ||
| 480 | sizeof(struct vstor_packet), | ||
| 481 | (unsigned long)request); | ||
| 482 | } else { | ||
| 483 | ret = vmbus_sendpacket(device->channel, vstor_packet, | ||
| 484 | sizeof(struct vstor_packet), | ||
| 485 | (unsigned long)request, | ||
| 486 | VM_PKT_DATA_INBAND, | ||
| 487 | VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); | ||
| 488 | } | ||
| 489 | |||
| 490 | if (ret != 0) | ||
| 491 | return ret; | ||
| 492 | |||
| 493 | atomic_inc(&stor_device->num_outstanding_req); | ||
| 494 | |||
| 495 | put_stor_device(device); | ||
| 496 | return ret; | ||
| 497 | } | ||
| 498 | |||
| 499 | /* | ||
| 500 | * The channel properties uniquely specify how the device is to be | ||
| 501 | * presented to the guest. Map this information for use by the block | ||
| 502 | * driver. For Linux guests on Hyper-V, we emulate a scsi HBA in the guest | ||
| 503 | * (storvsc_drv) and so scsi devices in the guest are handled by | ||
| 504 | * native upper level Linux drivers. Consequently, Hyper-V | ||
| 505 | * block driver, while being a generic block driver, presently does not | ||
| 506 | * deal with anything other than devices that would need to be presented | ||
| 507 | * to the guest as an IDE disk. | ||
| 508 | * | ||
| 509 | * This function maps the channel properties as embedded in the input | ||
| 510 | * parameter device_info onto information necessary to register the | ||
| 511 | * corresponding block device. | ||
| 512 | * | ||
| 513 | * Currently, there is no way to stop the emulation of the block device | ||
| 514 | * on the host side. And so, to prevent the native IDE drivers in Linux | ||
| 515 | * from taking over these devices (to be managedby Hyper-V block | ||
| 516 | * driver), we will take over if need be the major of the IDE controllers. | ||
| 517 | * | ||
| 518 | */ | ||
| 519 | |||
| 520 | int storvsc_get_major_info(struct storvsc_device_info *device_info, | ||
| 521 | struct storvsc_major_info *major_info) | ||
| 522 | { | ||
| 523 | static bool ide0_registered; | ||
| 524 | static bool ide1_registered; | ||
| 525 | |||
| 526 | /* | ||
| 527 | * For now we only support IDE disks. | ||
| 528 | */ | ||
| 529 | major_info->devname = "ide"; | ||
| 530 | major_info->diskname = "hd"; | ||
| 531 | |||
| 532 | if (device_info->path_id) { | ||
| 533 | major_info->major = 22; | ||
| 534 | if (!ide1_registered) { | ||
| 535 | major_info->do_register = true; | ||
| 536 | ide1_registered = true; | ||
| 537 | } else | ||
| 538 | major_info->do_register = false; | ||
| 539 | |||
| 540 | if (device_info->target_id) | ||
| 541 | major_info->index = 3; | ||
| 542 | else | ||
| 543 | major_info->index = 2; | ||
| 544 | |||
| 545 | return 0; | ||
| 546 | } else { | ||
| 547 | major_info->major = 3; | ||
| 548 | if (!ide0_registered) { | ||
| 549 | major_info->do_register = true; | ||
| 550 | ide0_registered = true; | ||
| 551 | } else | ||
| 552 | major_info->do_register = false; | ||
| 553 | |||
| 554 | if (device_info->target_id) | ||
| 555 | major_info->index = 1; | ||
| 556 | else | ||
| 557 | major_info->index = 0; | ||
| 558 | |||
| 559 | return 0; | ||
| 560 | } | ||
| 561 | |||
| 562 | return -ENODEV; | ||
| 563 | } | ||
| 564 | |||
diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c new file mode 100644 index 00000000000..26983ac1ae4 --- /dev/null +++ b/drivers/staging/hv/storvsc_drv.c | |||
| @@ -0,0 +1,794 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2009, Microsoft Corporation. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms and conditions of the GNU General Public License, | ||
| 6 | * version 2, as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License along with | ||
| 14 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
| 15 | * Place - Suite 330, Boston, MA 02111-1307 USA. | ||
| 16 | * | ||
| 17 | * Authors: | ||
| 18 | * Haiyang Zhang <haiyangz@microsoft.com> | ||
| 19 | * Hank Janssen <hjanssen@microsoft.com> | ||
| 20 | * K. Y. Srinivasan <kys@microsoft.com> | ||
| 21 | */ | ||
| 22 | #include <linux/init.h> | ||
| 23 | #include <linux/slab.h> | ||
| 24 | #include <linux/module.h> | ||
| 25 | #include <linux/device.h> | ||
| 26 | #include <linux/blkdev.h> | ||
| 27 | #include <linux/dmi.h> | ||
| 28 | #include <scsi/scsi.h> | ||
| 29 | #include <scsi/scsi_cmnd.h> | ||
| 30 | #include <scsi/scsi_host.h> | ||
| 31 | #include <scsi/scsi_device.h> | ||
| 32 | #include <scsi/scsi_tcq.h> | ||
| 33 | #include <scsi/scsi_eh.h> | ||
| 34 | #include <scsi/scsi_devinfo.h> | ||
| 35 | #include <scsi/scsi_dbg.h> | ||
| 36 | |||
| 37 | #include "hyperv.h" | ||
| 38 | #include "hyperv_storage.h" | ||
| 39 | |||
| 40 | static int storvsc_ringbuffer_size = STORVSC_RING_BUFFER_SIZE; | ||
| 41 | |||
| 42 | module_param(storvsc_ringbuffer_size, int, S_IRUGO); | ||
| 43 | MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)"); | ||
| 44 | |||
| 45 | static const char *driver_name = "storvsc"; | ||
| 46 | |||
| 47 | /* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */ | ||
| 48 | static const struct hv_guid stor_vsci_device_type = { | ||
| 49 | .data = { | ||
| 50 | 0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, | ||
| 51 | 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f | ||
| 52 | } | ||
| 53 | }; | ||
| 54 | |||
| 55 | struct hv_host_device { | ||
| 56 | struct hv_device *dev; | ||
| 57 | struct kmem_cache *request_pool; | ||
| 58 | unsigned int port; | ||
| 59 | unsigned char path; | ||
| 60 | unsigned char target; | ||
| 61 | }; | ||
| 62 | |||
| 63 | struct storvsc_cmd_request { | ||
| 64 | struct list_head entry; | ||
| 65 | struct scsi_cmnd *cmd; | ||
| 66 | |||
| 67 | unsigned int bounce_sgl_count; | ||
| 68 | struct scatterlist *bounce_sgl; | ||
| 69 | |||
| 70 | struct hv_storvsc_request request; | ||
| 71 | }; | ||
| 72 | |||
| 73 | |||
| 74 | static int storvsc_device_alloc(struct scsi_device *sdevice) | ||
| 75 | { | ||
| 76 | /* | ||
| 77 | * This enables luns to be located sparsely. Otherwise, we may not | ||
| 78 | * discovered them. | ||
| 79 | */ | ||
| 80 | sdevice->sdev_bflags |= BLIST_SPARSELUN | BLIST_LARGELUN; | ||
| 81 | return 0; | ||
| 82 | } | ||
| 83 | |||
| 84 | static int storvsc_merge_bvec(struct request_queue *q, | ||
| 85 | struct bvec_merge_data *bmd, struct bio_vec *bvec) | ||
| 86 | { | ||
| 87 | /* checking done by caller. */ | ||
| 88 | return bvec->bv_len; | ||
| 89 | } | ||
| 90 | |||
| 91 | static int storvsc_device_configure(struct scsi_device *sdevice) | ||
| 92 | { | ||
| 93 | scsi_adjust_queue_depth(sdevice, MSG_SIMPLE_TAG, | ||
| 94 | STORVSC_MAX_IO_REQUESTS); | ||
| 95 | |||
| 96 | blk_queue_max_segment_size(sdevice->request_queue, PAGE_SIZE); | ||
| 97 | |||
| 98 | blk_queue_merge_bvec(sdevice->request_queue, storvsc_merge_bvec); | ||
| 99 | |||
| 100 | blk_queue_bounce_limit(sdevice->request_queue, BLK_BOUNCE_ANY); | ||
| 101 | |||
| 102 | return 0; | ||
| 103 | } | ||
| 104 | |||
| 105 | static void destroy_bounce_buffer(struct scatterlist *sgl, | ||
| 106 | unsigned int sg_count) | ||
| 107 | { | ||
| 108 | int i; | ||
| 109 | struct page *page_buf; | ||
| 110 | |||
| 111 | for (i = 0; i < sg_count; i++) { | ||
| 112 | page_buf = sg_page((&sgl[i])); | ||
| 113 | if (page_buf != NULL) | ||
| 114 | __free_page(page_buf); | ||
| 115 | } | ||
| 116 | |||
| 117 | kfree(sgl); | ||
| 118 | } | ||
| 119 | |||
| 120 | static int do_bounce_buffer(struct scatterlist *sgl, unsigned int sg_count) | ||
| 121 | { | ||
| 122 | int i; | ||
| 123 | |||
| 124 | /* No need to check */ | ||
| 125 | if (sg_count < 2) | ||
| 126 | return -1; | ||
| 127 | |||
| 128 | /* We have at least 2 sg entries */ | ||
| 129 | for (i = 0; i < sg_count; i++) { | ||
| 130 | if (i == 0) { | ||
| 131 | /* make sure 1st one does not have hole */ | ||
| 132 | if (sgl[i].offset + sgl[i].length != PAGE_SIZE) | ||
| 133 | return i; | ||
| 134 | } else if (i == sg_count - 1) { | ||
| 135 | /* make sure last one does not have hole */ | ||
| 136 | if (sgl[i].offset != 0) | ||
| 137 | return i; | ||
| 138 | } else { | ||
| 139 | /* make sure no hole in the middle */ | ||
| 140 | if (sgl[i].length != PAGE_SIZE || sgl[i].offset != 0) | ||
| 141 | return i; | ||
| 142 | } | ||
| 143 | } | ||
| 144 | return -1; | ||
| 145 | } | ||
| 146 | |||
| 147 | static struct scatterlist *create_bounce_buffer(struct scatterlist *sgl, | ||
| 148 | unsigned int sg_count, | ||
| 149 | unsigned int len) | ||
| 150 | { | ||
| 151 | int i; | ||
| 152 | int num_pages; | ||
| 153 | struct scatterlist *bounce_sgl; | ||
| 154 | struct page *page_buf; | ||
| 155 | |||
| 156 | num_pages = ALIGN(len, PAGE_SIZE) >> PAGE_SHIFT; | ||
| 157 | |||
| 158 | bounce_sgl = kcalloc(num_pages, sizeof(struct scatterlist), GFP_ATOMIC); | ||
| 159 | if (!bounce_sgl) | ||
| 160 | return NULL; | ||
| 161 | |||
| 162 | for (i = 0; i < num_pages; i++) { | ||
| 163 | page_buf = alloc_page(GFP_ATOMIC); | ||
| 164 | if (!page_buf) | ||
| 165 | goto cleanup; | ||
| 166 | sg_set_page(&bounce_sgl[i], page_buf, 0, 0); | ||
| 167 | } | ||
| 168 | |||
| 169 | return bounce_sgl; | ||
| 170 | |||
| 171 | cleanup: | ||
| 172 | destroy_bounce_buffer(bounce_sgl, num_pages); | ||
| 173 | return NULL; | ||
| 174 | } | ||
| 175 | |||
| 176 | |||
| 177 | /* Assume the original sgl has enough room */ | ||
| 178 | static unsigned int copy_from_bounce_buffer(struct scatterlist *orig_sgl, | ||
| 179 | struct scatterlist *bounce_sgl, | ||
| 180 | unsigned int orig_sgl_count) | ||
| 181 | { | ||
| 182 | int i; | ||
| 183 | int j = 0; | ||
| 184 | unsigned long src, dest; | ||
| 185 | unsigned int srclen, destlen, copylen; | ||
| 186 | unsigned int total_copied = 0; | ||
| 187 | unsigned long bounce_addr = 0; | ||
| 188 | unsigned long dest_addr = 0; | ||
| 189 | unsigned long flags; | ||
| 190 | |||
| 191 | local_irq_save(flags); | ||
| 192 | |||
| 193 | for (i = 0; i < orig_sgl_count; i++) { | ||
| 194 | dest_addr = (unsigned long)kmap_atomic(sg_page((&orig_sgl[i])), | ||
| 195 | KM_IRQ0) + orig_sgl[i].offset; | ||
| 196 | dest = dest_addr; | ||
| 197 | destlen = orig_sgl[i].length; | ||
| 198 | |||
| 199 | if (bounce_addr == 0) | ||
| 200 | bounce_addr = | ||
| 201 | (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), | ||
| 202 | KM_IRQ0); | ||
| 203 | |||
| 204 | while (destlen) { | ||
| 205 | src = bounce_addr + bounce_sgl[j].offset; | ||
| 206 | srclen = bounce_sgl[j].length - bounce_sgl[j].offset; | ||
| 207 | |||
| 208 | copylen = min(srclen, destlen); | ||
| 209 | memcpy((void *)dest, (void *)src, copylen); | ||
| 210 | |||
| 211 | total_copied += copylen; | ||
| 212 | bounce_sgl[j].offset += copylen; | ||
| 213 | destlen -= copylen; | ||
| 214 | dest += copylen; | ||
| 215 | |||
| 216 | if (bounce_sgl[j].offset == bounce_sgl[j].length) { | ||
| 217 | /* full */ | ||
| 218 | kunmap_atomic((void *)bounce_addr, KM_IRQ0); | ||
| 219 | j++; | ||
| 220 | |||
| 221 | /* if we need to use another bounce buffer */ | ||
| 222 | if (destlen || i != orig_sgl_count - 1) | ||
| 223 | bounce_addr = | ||
| 224 | (unsigned long)kmap_atomic( | ||
| 225 | sg_page((&bounce_sgl[j])), KM_IRQ0); | ||
| 226 | } else if (destlen == 0 && i == orig_sgl_count - 1) { | ||
| 227 | /* unmap the last bounce that is < PAGE_SIZE */ | ||
| 228 | kunmap_atomic((void *)bounce_addr, KM_IRQ0); | ||
| 229 | } | ||
| 230 | } | ||
| 231 | |||
| 232 | kunmap_atomic((void *)(dest_addr - orig_sgl[i].offset), | ||
| 233 | KM_IRQ0); | ||
| 234 | } | ||
| 235 | |||
| 236 | local_irq_restore(flags); | ||
| 237 | |||
| 238 | return total_copied; | ||
| 239 | } | ||
| 240 | |||
| 241 | |||
| 242 | /* Assume the bounce_sgl has enough room ie using the create_bounce_buffer() */ | ||
| 243 | static unsigned int copy_to_bounce_buffer(struct scatterlist *orig_sgl, | ||
| 244 | struct scatterlist *bounce_sgl, | ||
| 245 | unsigned int orig_sgl_count) | ||
| 246 | { | ||
| 247 | int i; | ||
| 248 | int j = 0; | ||
| 249 | unsigned long src, dest; | ||
| 250 | unsigned int srclen, destlen, copylen; | ||
| 251 | unsigned int total_copied = 0; | ||
| 252 | unsigned long bounce_addr = 0; | ||
| 253 | unsigned long src_addr = 0; | ||
| 254 | unsigned long flags; | ||
| 255 | |||
| 256 | local_irq_save(flags); | ||
| 257 | |||
| 258 | for (i = 0; i < orig_sgl_count; i++) { | ||
| 259 | src_addr = (unsigned long)kmap_atomic(sg_page((&orig_sgl[i])), | ||
| 260 | KM_IRQ0) + orig_sgl[i].offset; | ||
| 261 | src = src_addr; | ||
| 262 | srclen = orig_sgl[i].length; | ||
| 263 | |||
| 264 | if (bounce_addr == 0) | ||
| 265 | bounce_addr = | ||
| 266 | (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), | ||
| 267 | KM_IRQ0); | ||
| 268 | |||
| 269 | while (srclen) { | ||
| 270 | /* assume bounce offset always == 0 */ | ||
| 271 | dest = bounce_addr + bounce_sgl[j].length; | ||
| 272 | destlen = PAGE_SIZE - bounce_sgl[j].length; | ||
| 273 | |||
| 274 | copylen = min(srclen, destlen); | ||
| 275 | memcpy((void *)dest, (void *)src, copylen); | ||
| 276 | |||
| 277 | total_copied += copylen; | ||
| 278 | bounce_sgl[j].length += copylen; | ||
| 279 | srclen -= copylen; | ||
| 280 | src += copylen; | ||
| 281 | |||
| 282 | if (bounce_sgl[j].length == PAGE_SIZE) { | ||
| 283 | /* full..move to next entry */ | ||
| 284 | kunmap_atomic((void *)bounce_addr, KM_IRQ0); | ||
| 285 | j++; | ||
| 286 | |||
| 287 | /* if we need to use another bounce buffer */ | ||
| 288 | if (srclen || i != orig_sgl_count - 1) | ||
| 289 | bounce_addr = | ||
| 290 | (unsigned long)kmap_atomic( | ||
| 291 | sg_page((&bounce_sgl[j])), KM_IRQ0); | ||
| 292 | |||
| 293 | } else if (srclen == 0 && i == orig_sgl_count - 1) { | ||
| 294 | /* unmap the last bounce that is < PAGE_SIZE */ | ||
| 295 | kunmap_atomic((void *)bounce_addr, KM_IRQ0); | ||
| 296 | } | ||
| 297 | } | ||
| 298 | |||
| 299 | kunmap_atomic((void *)(src_addr - orig_sgl[i].offset), KM_IRQ0); | ||
| 300 | } | ||
| 301 | |||
| 302 | local_irq_restore(flags); | ||
| 303 | |||
| 304 | return total_copied; | ||
| 305 | } | ||
| 306 | |||
| 307 | |||
| 308 | static int storvsc_remove(struct hv_device *dev) | ||
| 309 | { | ||
| 310 | struct Scsi_Host *host = dev_get_drvdata(&dev->device); | ||
| 311 | struct hv_host_device *host_dev = | ||
| 312 | (struct hv_host_device *)host->hostdata; | ||
| 313 | |||
| 314 | scsi_remove_host(host); | ||
| 315 | |||
| 316 | scsi_host_put(host); | ||
| 317 | |||
| 318 | storvsc_dev_remove(dev); | ||
| 319 | if (host_dev->request_pool) { | ||
| 320 | kmem_cache_destroy(host_dev->request_pool); | ||
| 321 | host_dev->request_pool = NULL; | ||
| 322 | } | ||
| 323 | return 0; | ||
| 324 | } | ||
| 325 | |||
| 326 | |||
| 327 | static int storvsc_get_chs(struct scsi_device *sdev, struct block_device * bdev, | ||
| 328 | sector_t capacity, int *info) | ||
| 329 | { | ||
| 330 | sector_t nsect = capacity; | ||
| 331 | sector_t cylinders = nsect; | ||
| 332 | int heads, sectors_pt; | ||
| 333 | |||
| 334 | /* | ||
| 335 | * We are making up these values; let us keep it simple. | ||
| 336 | */ | ||
| 337 | heads = 0xff; | ||
| 338 | sectors_pt = 0x3f; /* Sectors per track */ | ||
| 339 | sector_div(cylinders, heads * sectors_pt); | ||
| 340 | if ((sector_t)(cylinders + 1) * heads * sectors_pt < nsect) | ||
| 341 | cylinders = 0xffff; | ||
| 342 | |||
| 343 | info[0] = heads; | ||
| 344 | info[1] = sectors_pt; | ||
| 345 | info[2] = (int)cylinders; | ||
| 346 | |||
| 347 | return 0; | ||
| 348 | } | ||
| 349 | |||
| 350 | static int storvsc_host_reset(struct hv_device *device) | ||
| 351 | { | ||
| 352 | struct storvsc_device *stor_device; | ||
| 353 | struct hv_storvsc_request *request; | ||
| 354 | struct vstor_packet *vstor_packet; | ||
| 355 | int ret, t; | ||
| 356 | |||
| 357 | |||
| 358 | stor_device = get_stor_device(device); | ||
| 359 | if (!stor_device) | ||
| 360 | return -1; | ||
| 361 | |||
| 362 | request = &stor_device->reset_request; | ||
| 363 | vstor_packet = &request->vstor_packet; | ||
| 364 | |||
| 365 | init_completion(&request->wait_event); | ||
| 366 | |||
| 367 | vstor_packet->operation = VSTOR_OPERATION_RESET_BUS; | ||
| 368 | vstor_packet->flags = REQUEST_COMPLETION_FLAG; | ||
| 369 | vstor_packet->vm_srb.path_id = stor_device->path_id; | ||
| 370 | |||
| 371 | ret = vmbus_sendpacket(device->channel, vstor_packet, | ||
| 372 | sizeof(struct vstor_packet), | ||
| 373 | (unsigned long)&stor_device->reset_request, | ||
| 374 | VM_PKT_DATA_INBAND, | ||
| 375 | VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); | ||
| 376 | if (ret != 0) | ||
| 377 | goto cleanup; | ||
| 378 | |||
| 379 | t = wait_for_completion_timeout(&request->wait_event, 5*HZ); | ||
| 380 | if (t == 0) { | ||
| 381 | ret = -ETIMEDOUT; | ||
| 382 | goto cleanup; | ||
| 383 | } | ||
| 384 | |||
| 385 | |||
| 386 | /* | ||
| 387 | * At this point, all outstanding requests in the adapter | ||
| 388 | * should have been flushed out and return to us | ||
| 389 | */ | ||
| 390 | |||
| 391 | cleanup: | ||
| 392 | put_stor_device(device); | ||
| 393 | return ret; | ||
| 394 | } | ||
| 395 | |||
| 396 | |||
| 397 | /* | ||
| 398 | * storvsc_host_reset_handler - Reset the scsi HBA | ||
| 399 | */ | ||
| 400 | static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd) | ||
| 401 | { | ||
| 402 | int ret; | ||
| 403 | struct hv_host_device *host_dev = | ||
| 404 | (struct hv_host_device *)scmnd->device->host->hostdata; | ||
| 405 | struct hv_device *dev = host_dev->dev; | ||
| 406 | |||
| 407 | ret = storvsc_host_reset(dev); | ||
| 408 | if (ret != 0) | ||
| 409 | return ret; | ||
| 410 | |||
| 411 | return ret; | ||
| 412 | } | ||
| 413 | |||
| 414 | |||
| 415 | /* | ||
| 416 | * storvsc_commmand_completion - Command completion processing | ||
| 417 | */ | ||
| 418 | static void storvsc_commmand_completion(struct hv_storvsc_request *request) | ||
| 419 | { | ||
| 420 | struct storvsc_cmd_request *cmd_request = | ||
| 421 | (struct storvsc_cmd_request *)request->context; | ||
| 422 | struct scsi_cmnd *scmnd = cmd_request->cmd; | ||
| 423 | struct hv_host_device *host_dev = | ||
| 424 | (struct hv_host_device *)scmnd->device->host->hostdata; | ||
| 425 | void (*scsi_done_fn)(struct scsi_cmnd *); | ||
| 426 | struct scsi_sense_hdr sense_hdr; | ||
| 427 | struct vmscsi_request *vm_srb; | ||
| 428 | |||
| 429 | if (cmd_request->bounce_sgl_count) { | ||
| 430 | |||
| 431 | /* FIXME: We can optimize on writes by just skipping this */ | ||
| 432 | copy_from_bounce_buffer(scsi_sglist(scmnd), | ||
| 433 | cmd_request->bounce_sgl, | ||
| 434 | scsi_sg_count(scmnd)); | ||
| 435 | destroy_bounce_buffer(cmd_request->bounce_sgl, | ||
| 436 | cmd_request->bounce_sgl_count); | ||
| 437 | } | ||
| 438 | |||
| 439 | vm_srb = &request->vstor_packet.vm_srb; | ||
| 440 | scmnd->result = vm_srb->scsi_status; | ||
| 441 | |||
| 442 | if (scmnd->result) { | ||
| 443 | if (scsi_normalize_sense(scmnd->sense_buffer, | ||
| 444 | SCSI_SENSE_BUFFERSIZE, &sense_hdr)) | ||
| 445 | scsi_print_sense_hdr("storvsc", &sense_hdr); | ||
| 446 | } | ||
| 447 | |||
| 448 | scsi_set_resid(scmnd, | ||
| 449 | request->data_buffer.len - | ||
| 450 | vm_srb->data_transfer_length); | ||
| 451 | |||
| 452 | scsi_done_fn = scmnd->scsi_done; | ||
| 453 | |||
| 454 | scmnd->host_scribble = NULL; | ||
| 455 | scmnd->scsi_done = NULL; | ||
| 456 | |||
| 457 | scsi_done_fn(scmnd); | ||
| 458 | |||
| 459 | kmem_cache_free(host_dev->request_pool, cmd_request); | ||
| 460 | } | ||
| 461 | |||
| 462 | |||
| 463 | /* | ||
| 464 | * storvsc_queuecommand - Initiate command processing | ||
| 465 | */ | ||
| 466 | static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, | ||
| 467 | void (*done)(struct scsi_cmnd *)) | ||
| 468 | { | ||
| 469 | int ret; | ||
| 470 | struct hv_host_device *host_dev = | ||
| 471 | (struct hv_host_device *)scmnd->device->host->hostdata; | ||
| 472 | struct hv_device *dev = host_dev->dev; | ||
| 473 | struct hv_storvsc_request *request; | ||
| 474 | struct storvsc_cmd_request *cmd_request; | ||
| 475 | unsigned int request_size = 0; | ||
| 476 | int i; | ||
| 477 | struct scatterlist *sgl; | ||
| 478 | unsigned int sg_count = 0; | ||
| 479 | struct vmscsi_request *vm_srb; | ||
| 480 | |||
| 481 | |||
| 482 | /* If retrying, no need to prep the cmd */ | ||
| 483 | if (scmnd->host_scribble) { | ||
| 484 | |||
| 485 | cmd_request = | ||
| 486 | (struct storvsc_cmd_request *)scmnd->host_scribble; | ||
| 487 | |||
| 488 | goto retry_request; | ||
| 489 | } | ||
| 490 | |||
| 491 | scmnd->scsi_done = done; | ||
| 492 | |||
| 493 | request_size = sizeof(struct storvsc_cmd_request); | ||
| 494 | |||
| 495 | cmd_request = kmem_cache_zalloc(host_dev->request_pool, | ||
| 496 | GFP_ATOMIC); | ||
| 497 | if (!cmd_request) { | ||
| 498 | scmnd->scsi_done = NULL; | ||
| 499 | return SCSI_MLQUEUE_DEVICE_BUSY; | ||
| 500 | } | ||
| 501 | |||
| 502 | /* Setup the cmd request */ | ||
| 503 | cmd_request->bounce_sgl_count = 0; | ||
| 504 | cmd_request->bounce_sgl = NULL; | ||
| 505 | cmd_request->cmd = scmnd; | ||
| 506 | |||
| 507 | scmnd->host_scribble = (unsigned char *)cmd_request; | ||
| 508 | |||
| 509 | request = &cmd_request->request; | ||
| 510 | vm_srb = &request->vstor_packet.vm_srb; | ||
| 511 | |||
| 512 | |||
| 513 | /* Build the SRB */ | ||
| 514 | switch (scmnd->sc_data_direction) { | ||
| 515 | case DMA_TO_DEVICE: | ||
| 516 | vm_srb->data_in = WRITE_TYPE; | ||
| 517 | break; | ||
| 518 | case DMA_FROM_DEVICE: | ||
| 519 | vm_srb->data_in = READ_TYPE; | ||
| 520 | break; | ||
| 521 | default: | ||
| 522 | vm_srb->data_in = UNKNOWN_TYPE; | ||
| 523 | break; | ||
| 524 | } | ||
| 525 | |||
| 526 | request->on_io_completion = storvsc_commmand_completion; | ||
| 527 | request->context = cmd_request;/* scmnd; */ | ||
| 528 | |||
| 529 | vm_srb->port_number = host_dev->port; | ||
| 530 | vm_srb->path_id = scmnd->device->channel; | ||
| 531 | vm_srb->target_id = scmnd->device->id; | ||
| 532 | vm_srb->lun = scmnd->device->lun; | ||
| 533 | |||
| 534 | vm_srb->cdb_length = scmnd->cmd_len; | ||
| 535 | |||
| 536 | memcpy(vm_srb->cdb, scmnd->cmnd, vm_srb->cdb_length); | ||
| 537 | |||
| 538 | request->sense_buffer = scmnd->sense_buffer; | ||
| 539 | |||
| 540 | |||
| 541 | request->data_buffer.len = scsi_bufflen(scmnd); | ||
| 542 | if (scsi_sg_count(scmnd)) { | ||
| 543 | sgl = (struct scatterlist *)scsi_sglist(scmnd); | ||
| 544 | sg_count = scsi_sg_count(scmnd); | ||
| 545 | |||
| 546 | /* check if we need to bounce the sgl */ | ||
| 547 | if (do_bounce_buffer(sgl, scsi_sg_count(scmnd)) != -1) { | ||
| 548 | cmd_request->bounce_sgl = | ||
| 549 | create_bounce_buffer(sgl, scsi_sg_count(scmnd), | ||
| 550 | scsi_bufflen(scmnd)); | ||
| 551 | if (!cmd_request->bounce_sgl) { | ||
| 552 | scmnd->scsi_done = NULL; | ||
| 553 | scmnd->host_scribble = NULL; | ||
| 554 | kmem_cache_free(host_dev->request_pool, | ||
| 555 | cmd_request); | ||
| 556 | |||
| 557 | return SCSI_MLQUEUE_HOST_BUSY; | ||
| 558 | } | ||
| 559 | |||
| 560 | cmd_request->bounce_sgl_count = | ||
| 561 | ALIGN(scsi_bufflen(scmnd), PAGE_SIZE) >> | ||
| 562 | PAGE_SHIFT; | ||
| 563 | |||
| 564 | /* | ||
| 565 | * FIXME: We can optimize on reads by just skipping | ||
| 566 | * this | ||
| 567 | */ | ||
| 568 | copy_to_bounce_buffer(sgl, cmd_request->bounce_sgl, | ||
| 569 | scsi_sg_count(scmnd)); | ||
| 570 | |||
| 571 | sgl = cmd_request->bounce_sgl; | ||
| 572 | sg_count = cmd_request->bounce_sgl_count; | ||
| 573 | } | ||
| 574 | |||
| 575 | request->data_buffer.offset = sgl[0].offset; | ||
| 576 | |||
| 577 | for (i = 0; i < sg_count; i++) | ||
| 578 | request->data_buffer.pfn_array[i] = | ||
| 579 | page_to_pfn(sg_page((&sgl[i]))); | ||
| 580 | |||
| 581 | } else if (scsi_sglist(scmnd)) { | ||
| 582 | request->data_buffer.offset = | ||
| 583 | virt_to_phys(scsi_sglist(scmnd)) & (PAGE_SIZE-1); | ||
| 584 | request->data_buffer.pfn_array[0] = | ||
| 585 | virt_to_phys(scsi_sglist(scmnd)) >> PAGE_SHIFT; | ||
| 586 | } | ||
| 587 | |||
| 588 | retry_request: | ||
| 589 | /* Invokes the vsc to start an IO */ | ||
| 590 | ret = storvsc_do_io(dev, &cmd_request->request); | ||
| 591 | |||
| 592 | if (ret == -1) { | ||
| 593 | /* no more space */ | ||
| 594 | |||
| 595 | if (cmd_request->bounce_sgl_count) { | ||
| 596 | /* | ||
| 597 | * FIXME: We can optimize on writes by just skipping | ||
| 598 | * this | ||
| 599 | */ | ||
| 600 | copy_from_bounce_buffer(scsi_sglist(scmnd), | ||
| 601 | cmd_request->bounce_sgl, | ||
| 602 | scsi_sg_count(scmnd)); | ||
| 603 | destroy_bounce_buffer(cmd_request->bounce_sgl, | ||
| 604 | cmd_request->bounce_sgl_count); | ||
| 605 | } | ||
| 606 | |||
| 607 | kmem_cache_free(host_dev->request_pool, cmd_request); | ||
| 608 | |||
| 609 | scmnd->scsi_done = NULL; | ||
| 610 | scmnd->host_scribble = NULL; | ||
| 611 | |||
| 612 | ret = SCSI_MLQUEUE_DEVICE_BUSY; | ||
| 613 | } | ||
| 614 | |||
| 615 | return ret; | ||
| 616 | } | ||
| 617 | |||
| 618 | static DEF_SCSI_QCMD(storvsc_queuecommand) | ||
| 619 | |||
| 620 | |||
| 621 | /* Scsi driver */ | ||
| 622 | static struct scsi_host_template scsi_driver = { | ||
| 623 | .module = THIS_MODULE, | ||
| 624 | .name = "storvsc_host_t", | ||
| 625 | .bios_param = storvsc_get_chs, | ||
| 626 | .queuecommand = storvsc_queuecommand, | ||
| 627 | .eh_host_reset_handler = storvsc_host_reset_handler, | ||
| 628 | .slave_alloc = storvsc_device_alloc, | ||
| 629 | .slave_configure = storvsc_device_configure, | ||
| 630 | .cmd_per_lun = 1, | ||
| 631 | /* 64 max_queue * 1 target */ | ||
| 632 | .can_queue = STORVSC_MAX_IO_REQUESTS*STORVSC_MAX_TARGETS, | ||
| 633 | .this_id = -1, | ||
| 634 | /* no use setting to 0 since ll_blk_rw reset it to 1 */ | ||
| 635 | /* currently 32 */ | ||
| 636 | .sg_tablesize = MAX_MULTIPAGE_BUFFER_COUNT, | ||
| 637 | /* | ||
| 638 | * ENABLE_CLUSTERING allows mutiple physically contig bio_vecs to merge | ||
| 639 | * into 1 sg element. If set, we must limit the max_segment_size to | ||
| 640 | * PAGE_SIZE, otherwise we may get 1 sg element that represents | ||
| 641 | * multiple | ||
| 642 | */ | ||
| 643 | /* physically contig pfns (ie sg[x].length > PAGE_SIZE). */ | ||
| 644 | .use_clustering = ENABLE_CLUSTERING, | ||
| 645 | /* Make sure we dont get a sg segment crosses a page boundary */ | ||
| 646 | .dma_boundary = PAGE_SIZE-1, | ||
| 647 | }; | ||
| 648 | |||
| 649 | |||
| 650 | /* | ||
| 651 | * storvsc_probe - Add a new device for this driver | ||
| 652 | */ | ||
| 653 | |||
| 654 | static int storvsc_probe(struct hv_device *device) | ||
| 655 | { | ||
| 656 | int ret; | ||
| 657 | struct Scsi_Host *host; | ||
| 658 | struct hv_host_device *host_dev; | ||
| 659 | struct storvsc_device_info device_info; | ||
| 660 | |||
| 661 | host = scsi_host_alloc(&scsi_driver, | ||
| 662 | sizeof(struct hv_host_device)); | ||
| 663 | if (!host) | ||
| 664 | return -ENOMEM; | ||
| 665 | |||
| 666 | dev_set_drvdata(&device->device, host); | ||
| 667 | |||
| 668 | host_dev = (struct hv_host_device *)host->hostdata; | ||
| 669 | memset(host_dev, 0, sizeof(struct hv_host_device)); | ||
| 670 | |||
| 671 | host_dev->port = host->host_no; | ||
| 672 | host_dev->dev = device; | ||
| 673 | |||
| 674 | host_dev->request_pool = | ||
| 675 | kmem_cache_create(dev_name(&device->device), | ||
| 676 | sizeof(struct storvsc_cmd_request), 0, | ||
| 677 | SLAB_HWCACHE_ALIGN, NULL); | ||
| 678 | |||
| 679 | if (!host_dev->request_pool) { | ||
| 680 | scsi_host_put(host); | ||
| 681 | return -ENOMEM; | ||
| 682 | } | ||
| 683 | |||
| 684 | device_info.port_number = host->host_no; | ||
| 685 | device_info.ring_buffer_size = storvsc_ringbuffer_size; | ||
| 686 | /* Call to the vsc driver to add the device */ | ||
| 687 | ret = storvsc_dev_add(device, (void *)&device_info); | ||
| 688 | |||
| 689 | if (ret != 0) { | ||
| 690 | kmem_cache_destroy(host_dev->request_pool); | ||
| 691 | scsi_host_put(host); | ||
| 692 | return -1; | ||
| 693 | } | ||
| 694 | |||
| 695 | host_dev->path = device_info.path_id; | ||
| 696 | host_dev->target = device_info.target_id; | ||
| 697 | |||
| 698 | /* max # of devices per target */ | ||
| 699 | host->max_lun = STORVSC_MAX_LUNS_PER_TARGET; | ||
| 700 | /* max # of targets per channel */ | ||
| 701 | host->max_id = STORVSC_MAX_TARGETS; | ||
| 702 | /* max # of channels */ | ||
| 703 | host->max_channel = STORVSC_MAX_CHANNELS - 1; | ||
| 704 | /* max cmd length */ | ||
| 705 | host->max_cmd_len = STORVSC_MAX_CMD_LEN; | ||
| 706 | |||
| 707 | /* Register the HBA and start the scsi bus scan */ | ||
| 708 | ret = scsi_add_host(host, &device->device); | ||
| 709 | if (ret != 0) { | ||
| 710 | |||
| 711 | storvsc_dev_remove(device); | ||
| 712 | |||
| 713 | kmem_cache_destroy(host_dev->request_pool); | ||
| 714 | scsi_host_put(host); | ||
| 715 | return -1; | ||
| 716 | } | ||
| 717 | |||
| 718 | scsi_scan_host(host); | ||
| 719 | return ret; | ||
| 720 | } | ||
| 721 | |||
| 722 | /* The one and only one */ | ||
| 723 | |||
| 724 | static struct hv_driver storvsc_drv = { | ||
| 725 | .probe = storvsc_probe, | ||
| 726 | .remove = storvsc_remove, | ||
| 727 | }; | ||
| 728 | |||
| 729 | /* | ||
| 730 | * We use a DMI table to determine if we should autoload this driver This is | ||
| 731 | * needed by distro tools to determine if the hyperv drivers should be | ||
| 732 | * installed and/or configured. We don't do anything else with the table, but | ||
| 733 | * it needs to be present. | ||
| 734 | */ | ||
| 735 | |||
| 736 | static const struct dmi_system_id __initconst | ||
| 737 | hv_stor_dmi_table[] __maybe_unused = { | ||
| 738 | { | ||
| 739 | .ident = "Hyper-V", | ||
| 740 | .matches = { | ||
| 741 | DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), | ||
| 742 | DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"), | ||
| 743 | DMI_MATCH(DMI_BOARD_NAME, "Virtual Machine"), | ||
| 744 | }, | ||
| 745 | }, | ||
| 746 | { }, | ||
| 747 | }; | ||
| 748 | MODULE_DEVICE_TABLE(dmi, hv_stor_dmi_table); | ||
| 749 | |||
| 750 | static int __init storvsc_drv_init(void) | ||
| 751 | { | ||
| 752 | int ret; | ||
| 753 | struct hv_driver *drv = &storvsc_drv; | ||
| 754 | u32 max_outstanding_req_per_channel; | ||
| 755 | |||
| 756 | /* | ||
| 757 | * Divide the ring buffer data size (which is 1 page less | ||
| 758 | * than the ring buffer size since that page is reserved for | ||
| 759 | * the ring buffer indices) by the max request size (which is | ||
| 760 | * vmbus_channel_packet_multipage_buffer + struct vstor_packet + u64) | ||
| 761 | */ | ||
| 762 | |||
| 763 | max_outstanding_req_per_channel = | ||
| 764 | ((storvsc_ringbuffer_size - PAGE_SIZE) / | ||
| 765 | ALIGN(MAX_MULTIPAGE_BUFFER_PACKET + | ||
| 766 | sizeof(struct vstor_packet) + sizeof(u64), | ||
| 767 | sizeof(u64))); | ||
| 768 | |||
| 769 | memcpy(&drv->dev_type, &stor_vsci_device_type, | ||
| 770 | sizeof(struct hv_guid)); | ||
| 771 | |||
| 772 | if (max_outstanding_req_per_channel < | ||
| 773 | STORVSC_MAX_IO_REQUESTS) | ||
| 774 | return -1; | ||
| 775 | |||
| 776 | drv->driver.name = driver_name; | ||
| 777 | |||
| 778 | |||
| 779 | /* The driver belongs to vmbus */ | ||
| 780 | ret = vmbus_child_driver_register(&drv->driver); | ||
| 781 | |||
| 782 | return ret; | ||
| 783 | } | ||
| 784 | |||
| 785 | static void __exit storvsc_drv_exit(void) | ||
| 786 | { | ||
| 787 | vmbus_child_driver_unregister(&storvsc_drv.driver); | ||
| 788 | } | ||
| 789 | |||
| 790 | MODULE_LICENSE("GPL"); | ||
| 791 | MODULE_VERSION(HV_DRV_VERSION); | ||
| 792 | MODULE_DESCRIPTION("Microsoft Hyper-V virtual storage driver"); | ||
| 793 | module_init(storvsc_drv_init); | ||
| 794 | module_exit(storvsc_drv_exit); | ||
diff --git a/drivers/staging/hv/tools/hv_kvp_daemon.c b/drivers/staging/hv/tools/hv_kvp_daemon.c new file mode 100644 index 00000000000..a4a407f7052 --- /dev/null +++ b/drivers/staging/hv/tools/hv_kvp_daemon.c | |||
| @@ -0,0 +1,493 @@ | |||
| 1 | /* | ||
| 2 | * An implementation of key value pair (KVP) functionality for Linux. | ||
| 3 | * | ||
| 4 | * | ||
| 5 | * Copyright (C) 2010, Novell, Inc. | ||
| 6 | * Author : K. Y. Srinivasan <ksrinivasan@novell.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify it | ||
| 9 | * under the terms of the GNU General Public License version 2 as published | ||
| 10 | * by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, but | ||
| 13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
| 15 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
| 16 | * details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License | ||
| 19 | * along with this program; if not, write to the Free Software | ||
| 20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 21 | * | ||
| 22 | */ | ||
| 23 | |||
| 24 | |||
| 25 | #include <sys/types.h> | ||
| 26 | #include <sys/socket.h> | ||
| 27 | #include <sys/poll.h> | ||
| 28 | #include <sys/utsname.h> | ||
| 29 | #include <linux/types.h> | ||
| 30 | #include <stdio.h> | ||
| 31 | #include <stdlib.h> | ||
| 32 | #include <unistd.h> | ||
| 33 | #include <string.h> | ||
| 34 | #include <errno.h> | ||
| 35 | #include <arpa/inet.h> | ||
| 36 | #include <linux/connector.h> | ||
| 37 | #include <linux/netlink.h> | ||
| 38 | #include <ifaddrs.h> | ||
| 39 | #include <netdb.h> | ||
| 40 | #include <syslog.h> | ||
| 41 | |||
| 42 | /* | ||
| 43 | * KYS: TODO. Need to register these in the kernel. | ||
| 44 | * | ||
| 45 | * The following definitions are shared with the in-kernel component; do not | ||
| 46 | * change any of this without making the corresponding changes in | ||
| 47 | * the KVP kernel component. | ||
| 48 | */ | ||
| 49 | #define CN_KVP_IDX 0x9 /* MSFT KVP functionality */ | ||
| 50 | #define CN_KVP_VAL 0x1 /* This supports queries from the kernel */ | ||
| 51 | #define CN_KVP_USER_VAL 0x2 /* This supports queries from the user */ | ||
| 52 | |||
| 53 | /* | ||
| 54 | * KVP protocol: The user mode component first registers with the | ||
| 55 | * the kernel component. Subsequently, the kernel component requests, data | ||
| 56 | * for the specified keys. In response to this message the user mode component | ||
| 57 | * fills in the value corresponding to the specified key. We overload the | ||
| 58 | * sequence field in the cn_msg header to define our KVP message types. | ||
| 59 | * | ||
| 60 | * We use this infrastructure for also supporting queries from user mode | ||
| 61 | * application for state that may be maintained in the KVP kernel component. | ||
| 62 | * | ||
| 63 | * XXXKYS: Have a shared header file between the user and kernel (TODO) | ||
| 64 | */ | ||
| 65 | |||
| 66 | enum kvp_op { | ||
| 67 | KVP_REGISTER = 0, /* Register the user mode component*/ | ||
| 68 | KVP_KERNEL_GET, /*Kernel is requesting the value for the specified key*/ | ||
| 69 | KVP_KERNEL_SET, /*Kernel is providing the value for the specified key*/ | ||
| 70 | KVP_USER_GET, /*User is requesting the value for the specified key*/ | ||
| 71 | KVP_USER_SET /*User is providing the value for the specified key*/ | ||
| 72 | }; | ||
| 73 | |||
| 74 | #define HV_KVP_EXCHANGE_MAX_KEY_SIZE 512 | ||
| 75 | #define HV_KVP_EXCHANGE_MAX_VALUE_SIZE 2048 | ||
| 76 | |||
| 77 | struct hv_ku_msg { | ||
| 78 | __u32 kvp_index; | ||
| 79 | __u8 kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */ | ||
| 80 | __u8 kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key value */ | ||
| 81 | }; | ||
| 82 | |||
| 83 | enum key_index { | ||
| 84 | FullyQualifiedDomainName = 0, | ||
| 85 | IntegrationServicesVersion, /*This key is serviced in the kernel*/ | ||
| 86 | NetworkAddressIPv4, | ||
| 87 | NetworkAddressIPv6, | ||
| 88 | OSBuildNumber, | ||
| 89 | OSName, | ||
| 90 | OSMajorVersion, | ||
| 91 | OSMinorVersion, | ||
| 92 | OSVersion, | ||
| 93 | ProcessorArchitecture | ||
| 94 | }; | ||
| 95 | |||
| 96 | /* | ||
| 97 | * End of shared definitions. | ||
| 98 | */ | ||
| 99 | |||
| 100 | static char kvp_send_buffer[4096]; | ||
| 101 | static char kvp_recv_buffer[4096]; | ||
| 102 | static struct sockaddr_nl addr; | ||
| 103 | |||
| 104 | static char *os_name = ""; | ||
| 105 | static char *os_major = ""; | ||
| 106 | static char *os_minor = ""; | ||
| 107 | static char *processor_arch; | ||
| 108 | static char *os_build; | ||
| 109 | static char *lic_version; | ||
| 110 | static struct utsname uts_buf; | ||
| 111 | |||
| 112 | void kvp_get_os_info(void) | ||
| 113 | { | ||
| 114 | FILE *file; | ||
| 115 | char *p, buf[512]; | ||
| 116 | |||
| 117 | uname(&uts_buf); | ||
| 118 | os_build = uts_buf.release; | ||
| 119 | processor_arch= uts_buf.machine; | ||
| 120 | |||
| 121 | file = fopen("/etc/SuSE-release", "r"); | ||
| 122 | if (file != NULL) | ||
| 123 | goto kvp_osinfo_found; | ||
| 124 | file = fopen("/etc/redhat-release", "r"); | ||
| 125 | if (file != NULL) | ||
| 126 | goto kvp_osinfo_found; | ||
| 127 | /* | ||
| 128 | * Add code for other supported platforms. | ||
| 129 | */ | ||
| 130 | |||
| 131 | /* | ||
| 132 | * We don't have information about the os. | ||
| 133 | */ | ||
| 134 | os_name = uts_buf.sysname; | ||
| 135 | return; | ||
| 136 | |||
| 137 | kvp_osinfo_found: | ||
| 138 | /* up to three lines */ | ||
| 139 | p = fgets(buf, sizeof(buf), file); | ||
| 140 | if (p) { | ||
| 141 | p = strchr(buf, '\n'); | ||
| 142 | if (p) | ||
| 143 | *p = '\0'; | ||
| 144 | p = strdup(buf); | ||
| 145 | if (!p) | ||
| 146 | goto done; | ||
| 147 | os_name = p; | ||
| 148 | |||
| 149 | /* second line */ | ||
| 150 | p = fgets(buf, sizeof(buf), file); | ||
| 151 | if (p) { | ||
| 152 | p = strchr(buf, '\n'); | ||
| 153 | if (p) | ||
| 154 | *p = '\0'; | ||
| 155 | p = strdup(buf); | ||
| 156 | if (!p) | ||
| 157 | goto done; | ||
| 158 | os_major = p; | ||
| 159 | |||
| 160 | /* third line */ | ||
| 161 | p = fgets(buf, sizeof(buf), file); | ||
| 162 | if (p) { | ||
| 163 | p = strchr(buf, '\n'); | ||
| 164 | if (p) | ||
| 165 | *p = '\0'; | ||
| 166 | p = strdup(buf); | ||
| 167 | if (p) | ||
| 168 | os_minor = p; | ||
| 169 | } | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | done: | ||
| 174 | fclose(file); | ||
| 175 | return; | ||
| 176 | } | ||
| 177 | |||
| 178 | static int | ||
| 179 | kvp_get_ip_address(int family, char *buffer, int length) | ||
| 180 | { | ||
| 181 | struct ifaddrs *ifap; | ||
| 182 | struct ifaddrs *curp; | ||
| 183 | int ipv4_len = strlen("255.255.255.255") + 1; | ||
| 184 | int ipv6_len = strlen("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")+1; | ||
| 185 | int offset = 0; | ||
| 186 | const char *str; | ||
| 187 | char tmp[50]; | ||
| 188 | int error = 0; | ||
| 189 | |||
| 190 | /* | ||
| 191 | * On entry into this function, the buffer is capable of holding the | ||
| 192 | * maximum key value (2048 bytes). | ||
| 193 | */ | ||
| 194 | |||
| 195 | if (getifaddrs(&ifap)) { | ||
| 196 | strcpy(buffer, "getifaddrs failed\n"); | ||
| 197 | return 1; | ||
| 198 | } | ||
| 199 | |||
| 200 | curp = ifap; | ||
| 201 | while (curp != NULL) { | ||
| 202 | if ((curp->ifa_addr != NULL) && | ||
| 203 | (curp->ifa_addr->sa_family == family)) { | ||
| 204 | if (family == AF_INET) { | ||
| 205 | struct sockaddr_in *addr = | ||
| 206 | (struct sockaddr_in *) curp->ifa_addr; | ||
| 207 | |||
| 208 | str = inet_ntop(family, &addr->sin_addr, | ||
| 209 | tmp, 50); | ||
| 210 | if (str == NULL) { | ||
| 211 | strcpy(buffer, "inet_ntop failed\n"); | ||
| 212 | error = 1; | ||
| 213 | goto getaddr_done; | ||
| 214 | } | ||
| 215 | if (offset == 0) | ||
| 216 | strcpy(buffer, tmp); | ||
| 217 | else | ||
| 218 | strcat(buffer, tmp); | ||
| 219 | strcat(buffer, ";"); | ||
| 220 | |||
| 221 | offset += strlen(str) + 1; | ||
| 222 | if ((length - offset) < (ipv4_len + 1)) | ||
| 223 | goto getaddr_done; | ||
| 224 | |||
| 225 | } else { | ||
| 226 | |||
| 227 | /* | ||
| 228 | * We only support AF_INET and AF_INET6 | ||
| 229 | * and the list of addresses is separated by a ";". | ||
| 230 | */ | ||
| 231 | struct sockaddr_in6 *addr = | ||
| 232 | (struct sockaddr_in6 *) curp->ifa_addr; | ||
| 233 | |||
| 234 | str = inet_ntop(family, | ||
| 235 | &addr->sin6_addr.s6_addr, | ||
| 236 | tmp, 50); | ||
| 237 | if (str == NULL) { | ||
| 238 | strcpy(buffer, "inet_ntop failed\n"); | ||
| 239 | error = 1; | ||
| 240 | goto getaddr_done; | ||
| 241 | } | ||
| 242 | if (offset == 0) | ||
| 243 | strcpy(buffer, tmp); | ||
| 244 | else | ||
| 245 | strcat(buffer, tmp); | ||
| 246 | strcat(buffer, ";"); | ||
| 247 | offset += strlen(str) + 1; | ||
| 248 | if ((length - offset) < (ipv6_len + 1)) | ||
| 249 | goto getaddr_done; | ||
| 250 | |||
| 251 | } | ||
| 252 | |||
| 253 | } | ||
| 254 | curp = curp->ifa_next; | ||
| 255 | } | ||
| 256 | |||
| 257 | getaddr_done: | ||
| 258 | freeifaddrs(ifap); | ||
| 259 | return error; | ||
| 260 | } | ||
| 261 | |||
| 262 | |||
| 263 | static int | ||
| 264 | kvp_get_domain_name(char *buffer, int length) | ||
| 265 | { | ||
| 266 | struct addrinfo hints, *info ; | ||
| 267 | gethostname(buffer, length); | ||
| 268 | int error = 0; | ||
| 269 | |||
| 270 | memset(&hints, 0, sizeof(hints)); | ||
| 271 | hints.ai_family = AF_INET; /*Get only ipv4 addrinfo. */ | ||
| 272 | hints.ai_socktype = SOCK_STREAM; | ||
| 273 | hints.ai_flags = AI_CANONNAME; | ||
| 274 | |||
| 275 | error = getaddrinfo(buffer, "http", &hints, &info); | ||
| 276 | if (error != 0) { | ||
| 277 | strcpy(buffer, "getaddrinfo failed\n"); | ||
| 278 | error = 1; | ||
| 279 | goto get_domain_done; | ||
| 280 | } | ||
| 281 | strcpy(buffer, info->ai_canonname); | ||
| 282 | get_domain_done: | ||
| 283 | freeaddrinfo(info); | ||
| 284 | return error; | ||
| 285 | } | ||
| 286 | |||
| 287 | static int | ||
| 288 | netlink_send(int fd, struct cn_msg *msg) | ||
| 289 | { | ||
| 290 | struct nlmsghdr *nlh; | ||
| 291 | unsigned int size; | ||
| 292 | struct msghdr message; | ||
| 293 | char buffer[64]; | ||
| 294 | struct iovec iov[2]; | ||
| 295 | |||
| 296 | size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len); | ||
| 297 | |||
| 298 | nlh = (struct nlmsghdr *)buffer; | ||
| 299 | nlh->nlmsg_seq = 0; | ||
| 300 | nlh->nlmsg_pid = getpid(); | ||
| 301 | nlh->nlmsg_type = NLMSG_DONE; | ||
| 302 | nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh)); | ||
| 303 | nlh->nlmsg_flags = 0; | ||
| 304 | |||
| 305 | iov[0].iov_base = nlh; | ||
| 306 | iov[0].iov_len = sizeof(*nlh); | ||
| 307 | |||
| 308 | iov[1].iov_base = msg; | ||
| 309 | iov[1].iov_len = size; | ||
| 310 | |||
| 311 | memset(&message, 0, sizeof(message)); | ||
| 312 | message.msg_name = &addr; | ||
| 313 | message.msg_namelen = sizeof(addr); | ||
| 314 | message.msg_iov = iov; | ||
| 315 | message.msg_iovlen = 2; | ||
| 316 | |||
| 317 | return sendmsg(fd, &message, 0); | ||
| 318 | } | ||
| 319 | |||
| 320 | int main(void) | ||
| 321 | { | ||
| 322 | int fd, len, sock_opt; | ||
| 323 | int error; | ||
| 324 | struct cn_msg *message; | ||
| 325 | struct pollfd pfd; | ||
| 326 | struct nlmsghdr *incoming_msg; | ||
| 327 | struct cn_msg *incoming_cn_msg; | ||
| 328 | struct hv_ku_msg *hv_msg; | ||
| 329 | char *p; | ||
| 330 | char *key_value; | ||
| 331 | char *key_name; | ||
| 332 | |||
| 333 | daemon(1, 0); | ||
| 334 | openlog("KVP", 0, LOG_USER); | ||
| 335 | syslog(LOG_INFO, "KVP starting; pid is:%d", getpid()); | ||
| 336 | /* | ||
| 337 | * Retrieve OS release information. | ||
| 338 | */ | ||
| 339 | kvp_get_os_info(); | ||
| 340 | |||
| 341 | fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); | ||
| 342 | if (fd < 0) { | ||
| 343 | syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd); | ||
| 344 | exit(-1); | ||
| 345 | } | ||
| 346 | addr.nl_family = AF_NETLINK; | ||
| 347 | addr.nl_pad = 0; | ||
| 348 | addr.nl_pid = 0; | ||
| 349 | addr.nl_groups = CN_KVP_IDX; | ||
| 350 | |||
| 351 | |||
| 352 | error = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); | ||
| 353 | if (error < 0) { | ||
| 354 | syslog(LOG_ERR, "bind failed; error:%d", error); | ||
| 355 | close(fd); | ||
| 356 | exit(-1); | ||
| 357 | } | ||
| 358 | sock_opt = addr.nl_groups; | ||
| 359 | setsockopt(fd, 270, 1, &sock_opt, sizeof(sock_opt)); | ||
| 360 | /* | ||
| 361 | * Register ourselves with the kernel. | ||
| 362 | */ | ||
| 363 | message = (struct cn_msg *)kvp_send_buffer; | ||
| 364 | message->id.idx = CN_KVP_IDX; | ||
| 365 | message->id.val = CN_KVP_VAL; | ||
| 366 | message->seq = KVP_REGISTER; | ||
| 367 | message->ack = 0; | ||
| 368 | message->len = 0; | ||
| 369 | |||
| 370 | len = netlink_send(fd, message); | ||
| 371 | if (len < 0) { | ||
| 372 | syslog(LOG_ERR, "netlink_send failed; error:%d", len); | ||
| 373 | close(fd); | ||
| 374 | exit(-1); | ||
| 375 | } | ||
| 376 | |||
| 377 | pfd.fd = fd; | ||
| 378 | |||
| 379 | while (1) { | ||
| 380 | pfd.events = POLLIN; | ||
| 381 | pfd.revents = 0; | ||
| 382 | poll(&pfd, 1, -1); | ||
| 383 | |||
| 384 | len = recv(fd, kvp_recv_buffer, sizeof(kvp_recv_buffer), 0); | ||
| 385 | |||
| 386 | if (len < 0) { | ||
| 387 | syslog(LOG_ERR, "recv failed; error:%d", len); | ||
| 388 | close(fd); | ||
| 389 | return -1; | ||
| 390 | } | ||
| 391 | |||
| 392 | incoming_msg = (struct nlmsghdr *)kvp_recv_buffer; | ||
| 393 | incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg); | ||
| 394 | |||
| 395 | switch (incoming_cn_msg->seq) { | ||
| 396 | case KVP_REGISTER: | ||
| 397 | /* | ||
| 398 | * Driver is registering with us; stash away the version | ||
| 399 | * information. | ||
| 400 | */ | ||
| 401 | p = (char *)incoming_cn_msg->data; | ||
| 402 | lic_version = malloc(strlen(p) + 1); | ||
| 403 | if (lic_version) { | ||
| 404 | strcpy(lic_version, p); | ||
| 405 | syslog(LOG_INFO, "KVP LIC Version: %s", | ||
| 406 | lic_version); | ||
| 407 | } else { | ||
| 408 | syslog(LOG_ERR, "malloc failed"); | ||
| 409 | } | ||
| 410 | continue; | ||
| 411 | |||
| 412 | case KVP_KERNEL_GET: | ||
| 413 | break; | ||
| 414 | default: | ||
| 415 | continue; | ||
| 416 | } | ||
| 417 | |||
| 418 | hv_msg = (struct hv_ku_msg *)incoming_cn_msg->data; | ||
| 419 | key_name = (char *)hv_msg->kvp_key; | ||
| 420 | key_value = (char *)hv_msg->kvp_value; | ||
| 421 | |||
| 422 | switch (hv_msg->kvp_index) { | ||
| 423 | case FullyQualifiedDomainName: | ||
| 424 | kvp_get_domain_name(key_value, | ||
| 425 | HV_KVP_EXCHANGE_MAX_VALUE_SIZE); | ||
| 426 | strcpy(key_name, "FullyQualifiedDomainName"); | ||
| 427 | break; | ||
| 428 | case IntegrationServicesVersion: | ||
| 429 | strcpy(key_name, "IntegrationServicesVersion"); | ||
| 430 | strcpy(key_value, lic_version); | ||
| 431 | break; | ||
| 432 | case NetworkAddressIPv4: | ||
| 433 | kvp_get_ip_address(AF_INET, key_value, | ||
| 434 | HV_KVP_EXCHANGE_MAX_VALUE_SIZE); | ||
| 435 | strcpy(key_name, "NetworkAddressIPv4"); | ||
| 436 | break; | ||
| 437 | case NetworkAddressIPv6: | ||
| 438 | kvp_get_ip_address(AF_INET6, key_value, | ||
| 439 | HV_KVP_EXCHANGE_MAX_VALUE_SIZE); | ||
| 440 | strcpy(key_name, "NetworkAddressIPv6"); | ||
| 441 | break; | ||
| 442 | case OSBuildNumber: | ||
| 443 | strcpy(key_value, os_build); | ||
| 444 | strcpy(key_name, "OSBuildNumber"); | ||
| 445 | break; | ||
| 446 | case OSName: | ||
| 447 | strcpy(key_value, os_name); | ||
| 448 | strcpy(key_name, "OSName"); | ||
| 449 | break; | ||
| 450 | case OSMajorVersion: | ||
| 451 | strcpy(key_value, os_major); | ||
| 452 | strcpy(key_name, "OSMajorVersion"); | ||
| 453 | break; | ||
| 454 | case OSMinorVersion: | ||
| 455 | strcpy(key_value, os_minor); | ||
| 456 | strcpy(key_name, "OSMinorVersion"); | ||
| 457 | break; | ||
| 458 | case OSVersion: | ||
| 459 | strcpy(key_value, os_build); | ||
| 460 | strcpy(key_name, "OSVersion"); | ||
| 461 | break; | ||
| 462 | case ProcessorArchitecture: | ||
| 463 | strcpy(key_value, processor_arch); | ||
| 464 | strcpy(key_name, "ProcessorArchitecture"); | ||
| 465 | break; | ||
| 466 | default: | ||
| 467 | strcpy(key_value, "Unknown Key"); | ||
| 468 | /* | ||
| 469 | * We use a null key name to terminate enumeration. | ||
| 470 | */ | ||
| 471 | strcpy(key_name, ""); | ||
| 472 | break; | ||
| 473 | } | ||
| 474 | /* | ||
| 475 | * Send the value back to the kernel. The response is | ||
| 476 | * already in the receive buffer. Update the cn_msg header to | ||
| 477 | * reflect the key value that has been added to the message | ||
| 478 | */ | ||
| 479 | |||
| 480 | incoming_cn_msg->id.idx = CN_KVP_IDX; | ||
| 481 | incoming_cn_msg->id.val = CN_KVP_VAL; | ||
| 482 | incoming_cn_msg->seq = KVP_USER_SET; | ||
| 483 | incoming_cn_msg->ack = 0; | ||
| 484 | incoming_cn_msg->len = sizeof(struct hv_ku_msg); | ||
| 485 | |||
| 486 | len = netlink_send(fd, incoming_cn_msg); | ||
| 487 | if (len < 0) { | ||
| 488 | syslog(LOG_ERR, "net_link send failed; error:%d", len); | ||
| 489 | exit(-1); | ||
| 490 | } | ||
| 491 | } | ||
| 492 | |||
| 493 | } | ||
diff --git a/drivers/staging/hv/vmbus_drv.c b/drivers/staging/hv/vmbus_drv.c new file mode 100644 index 00000000000..1c949f5fb71 --- /dev/null +++ b/drivers/staging/hv/vmbus_drv.c | |||
| @@ -0,0 +1,802 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2009, Microsoft Corporation. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms and conditions of the GNU General Public License, | ||
| 6 | * version 2, as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License along with | ||
| 14 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
| 15 | * Place - Suite 330, Boston, MA 02111-1307 USA. | ||
| 16 | * | ||
| 17 | * Authors: | ||
| 18 | * Haiyang Zhang <haiyangz@microsoft.com> | ||
| 19 | * Hank Janssen <hjanssen@microsoft.com> | ||
| 20 | * K. Y. Srinivasan <kys@microsoft.com> | ||
| 21 | * | ||
| 22 | */ | ||
| 23 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 24 | |||
| 25 | #include <linux/init.h> | ||
| 26 | #include <linux/module.h> | ||
| 27 | #include <linux/device.h> | ||
| 28 | #include <linux/irq.h> | ||
| 29 | #include <linux/interrupt.h> | ||
| 30 | #include <linux/sysctl.h> | ||
| 31 | #include <linux/pci.h> | ||
| 32 | #include <linux/dmi.h> | ||
| 33 | #include <linux/slab.h> | ||
| 34 | #include <linux/acpi.h> | ||
| 35 | #include <acpi/acpi_bus.h> | ||
| 36 | #include <linux/completion.h> | ||
| 37 | |||
| 38 | #include "hyperv.h" | ||
| 39 | #include "hyperv_vmbus.h" | ||
| 40 | |||
| 41 | |||
| 42 | static struct acpi_device *hv_acpi_dev; | ||
| 43 | |||
| 44 | static struct tasklet_struct msg_dpc; | ||
| 45 | static struct tasklet_struct event_dpc; | ||
| 46 | |||
| 47 | unsigned int vmbus_loglevel = (ALL_MODULES << 16 | INFO_LVL); | ||
| 48 | EXPORT_SYMBOL(vmbus_loglevel); | ||
| 49 | /* (ALL_MODULES << 16 | DEBUG_LVL_ENTEREXIT); */ | ||
| 50 | /* (((VMBUS | VMBUS_DRV)<<16) | DEBUG_LVL_ENTEREXIT); */ | ||
| 51 | |||
| 52 | static struct completion probe_event; | ||
| 53 | static int irq; | ||
| 54 | |||
| 55 | static void get_channel_info(struct hv_device *device, | ||
| 56 | struct hv_device_info *info) | ||
| 57 | { | ||
| 58 | struct vmbus_channel_debug_info debug_info; | ||
| 59 | |||
| 60 | if (!device->channel) | ||
| 61 | return; | ||
| 62 | |||
| 63 | vmbus_get_debug_info(device->channel, &debug_info); | ||
| 64 | |||
| 65 | info->chn_id = debug_info.relid; | ||
| 66 | info->chn_state = debug_info.state; | ||
| 67 | memcpy(&info->chn_type, &debug_info.interfacetype, | ||
| 68 | sizeof(struct hv_guid)); | ||
| 69 | memcpy(&info->chn_instance, &debug_info.interface_instance, | ||
| 70 | sizeof(struct hv_guid)); | ||
| 71 | |||
| 72 | info->monitor_id = debug_info.monitorid; | ||
| 73 | |||
| 74 | info->server_monitor_pending = debug_info.servermonitor_pending; | ||
| 75 | info->server_monitor_latency = debug_info.servermonitor_latency; | ||
| 76 | info->server_monitor_conn_id = debug_info.servermonitor_connectionid; | ||
| 77 | |||
| 78 | info->client_monitor_pending = debug_info.clientmonitor_pending; | ||
| 79 | info->client_monitor_latency = debug_info.clientmonitor_latency; | ||
| 80 | info->client_monitor_conn_id = debug_info.clientmonitor_connectionid; | ||
| 81 | |||
| 82 | info->inbound.int_mask = debug_info.inbound.current_interrupt_mask; | ||
| 83 | info->inbound.read_idx = debug_info.inbound.current_read_index; | ||
| 84 | info->inbound.write_idx = debug_info.inbound.current_write_index; | ||
| 85 | info->inbound.bytes_avail_toread = | ||
| 86 | debug_info.inbound.bytes_avail_toread; | ||
| 87 | info->inbound.bytes_avail_towrite = | ||
| 88 | debug_info.inbound.bytes_avail_towrite; | ||
| 89 | |||
| 90 | info->outbound.int_mask = | ||
| 91 | debug_info.outbound.current_interrupt_mask; | ||
| 92 | info->outbound.read_idx = debug_info.outbound.current_read_index; | ||
| 93 | info->outbound.write_idx = debug_info.outbound.current_write_index; | ||
| 94 | info->outbound.bytes_avail_toread = | ||
| 95 | debug_info.outbound.bytes_avail_toread; | ||
| 96 | info->outbound.bytes_avail_towrite = | ||
| 97 | debug_info.outbound.bytes_avail_towrite; | ||
| 98 | } | ||
| 99 | |||
| 100 | /* | ||
| 101 | * vmbus_show_device_attr - Show the device attribute in sysfs. | ||
| 102 | * | ||
| 103 | * This is invoked when user does a | ||
| 104 | * "cat /sys/bus/vmbus/devices/<busdevice>/<attr name>" | ||
| 105 | */ | ||
| 106 | static ssize_t vmbus_show_device_attr(struct device *dev, | ||
| 107 | struct device_attribute *dev_attr, | ||
| 108 | char *buf) | ||
| 109 | { | ||
| 110 | struct hv_device *hv_dev = device_to_hv_device(dev); | ||
| 111 | struct hv_device_info device_info; | ||
| 112 | |||
| 113 | memset(&device_info, 0, sizeof(struct hv_device_info)); | ||
| 114 | |||
| 115 | get_channel_info(hv_dev, &device_info); | ||
| 116 | |||
| 117 | if (!strcmp(dev_attr->attr.name, "class_id")) { | ||
| 118 | return sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-" | ||
| 119 | "%02x%02x%02x%02x%02x%02x%02x%02x}\n", | ||
| 120 | device_info.chn_type.data[3], | ||
| 121 | device_info.chn_type.data[2], | ||
| 122 | device_info.chn_type.data[1], | ||
| 123 | device_info.chn_type.data[0], | ||
| 124 | device_info.chn_type.data[5], | ||
| 125 | device_info.chn_type.data[4], | ||
| 126 | device_info.chn_type.data[7], | ||
| 127 | device_info.chn_type.data[6], | ||
| 128 | device_info.chn_type.data[8], | ||
| 129 | device_info.chn_type.data[9], | ||
| 130 | device_info.chn_type.data[10], | ||
| 131 | device_info.chn_type.data[11], | ||
| 132 | device_info.chn_type.data[12], | ||
| 133 | device_info.chn_type.data[13], | ||
| 134 | device_info.chn_type.data[14], | ||
| 135 | device_info.chn_type.data[15]); | ||
| 136 | } else if (!strcmp(dev_attr->attr.name, "device_id")) { | ||
| 137 | return sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-" | ||
| 138 | "%02x%02x%02x%02x%02x%02x%02x%02x}\n", | ||
| 139 | device_info.chn_instance.data[3], | ||
| 140 | device_info.chn_instance.data[2], | ||
| 141 | device_info.chn_instance.data[1], | ||
| 142 | device_info.chn_instance.data[0], | ||
| 143 | device_info.chn_instance.data[5], | ||
| 144 | device_info.chn_instance.data[4], | ||
| 145 | device_info.chn_instance.data[7], | ||
| 146 | device_info.chn_instance.data[6], | ||
| 147 | device_info.chn_instance.data[8], | ||
| 148 | device_info.chn_instance.data[9], | ||
| 149 | device_info.chn_instance.data[10], | ||
| 150 | device_info.chn_instance.data[11], | ||
| 151 | device_info.chn_instance.data[12], | ||
| 152 | device_info.chn_instance.data[13], | ||
| 153 | device_info.chn_instance.data[14], | ||
| 154 | device_info.chn_instance.data[15]); | ||
| 155 | } else if (!strcmp(dev_attr->attr.name, "state")) { | ||
| 156 | return sprintf(buf, "%d\n", device_info.chn_state); | ||
| 157 | } else if (!strcmp(dev_attr->attr.name, "id")) { | ||
| 158 | return sprintf(buf, "%d\n", device_info.chn_id); | ||
| 159 | } else if (!strcmp(dev_attr->attr.name, "out_intr_mask")) { | ||
| 160 | return sprintf(buf, "%d\n", device_info.outbound.int_mask); | ||
| 161 | } else if (!strcmp(dev_attr->attr.name, "out_read_index")) { | ||
| 162 | return sprintf(buf, "%d\n", device_info.outbound.read_idx); | ||
| 163 | } else if (!strcmp(dev_attr->attr.name, "out_write_index")) { | ||
| 164 | return sprintf(buf, "%d\n", device_info.outbound.write_idx); | ||
| 165 | } else if (!strcmp(dev_attr->attr.name, "out_read_bytes_avail")) { | ||
| 166 | return sprintf(buf, "%d\n", | ||
| 167 | device_info.outbound.bytes_avail_toread); | ||
| 168 | } else if (!strcmp(dev_attr->attr.name, "out_write_bytes_avail")) { | ||
| 169 | return sprintf(buf, "%d\n", | ||
| 170 | device_info.outbound.bytes_avail_towrite); | ||
| 171 | } else if (!strcmp(dev_attr->attr.name, "in_intr_mask")) { | ||
| 172 | return sprintf(buf, "%d\n", device_info.inbound.int_mask); | ||
| 173 | } else if (!strcmp(dev_attr->attr.name, "in_read_index")) { | ||
| 174 | return sprintf(buf, "%d\n", device_info.inbound.read_idx); | ||
| 175 | } else if (!strcmp(dev_attr->attr.name, "in_write_index")) { | ||
| 176 | return sprintf(buf, "%d\n", device_info.inbound.write_idx); | ||
| 177 | } else if (!strcmp(dev_attr->attr.name, "in_read_bytes_avail")) { | ||
| 178 | return sprintf(buf, "%d\n", | ||
| 179 | device_info.inbound.bytes_avail_toread); | ||
| 180 | } else if (!strcmp(dev_attr->attr.name, "in_write_bytes_avail")) { | ||
| 181 | return sprintf(buf, "%d\n", | ||
| 182 | device_info.inbound.bytes_avail_towrite); | ||
| 183 | } else if (!strcmp(dev_attr->attr.name, "monitor_id")) { | ||
| 184 | return sprintf(buf, "%d\n", device_info.monitor_id); | ||
| 185 | } else if (!strcmp(dev_attr->attr.name, "server_monitor_pending")) { | ||
| 186 | return sprintf(buf, "%d\n", device_info.server_monitor_pending); | ||
| 187 | } else if (!strcmp(dev_attr->attr.name, "server_monitor_latency")) { | ||
| 188 | return sprintf(buf, "%d\n", device_info.server_monitor_latency); | ||
| 189 | } else if (!strcmp(dev_attr->attr.name, "server_monitor_conn_id")) { | ||
| 190 | return sprintf(buf, "%d\n", | ||
| 191 | device_info.server_monitor_conn_id); | ||
| 192 | } else if (!strcmp(dev_attr->attr.name, "client_monitor_pending")) { | ||
| 193 | return sprintf(buf, "%d\n", device_info.client_monitor_pending); | ||
| 194 | } else if (!strcmp(dev_attr->attr.name, "client_monitor_latency")) { | ||
| 195 | return sprintf(buf, "%d\n", device_info.client_monitor_latency); | ||
| 196 | } else if (!strcmp(dev_attr->attr.name, "client_monitor_conn_id")) { | ||
| 197 | return sprintf(buf, "%d\n", | ||
| 198 | device_info.client_monitor_conn_id); | ||
| 199 | } else { | ||
| 200 | return 0; | ||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 | /* Set up per device attributes in /sys/bus/vmbus/devices/<bus device> */ | ||
| 205 | static struct device_attribute vmbus_device_attrs[] = { | ||
| 206 | __ATTR(id, S_IRUGO, vmbus_show_device_attr, NULL), | ||
| 207 | __ATTR(state, S_IRUGO, vmbus_show_device_attr, NULL), | ||
| 208 | __ATTR(class_id, S_IRUGO, vmbus_show_device_attr, NULL), | ||
| 209 | __ATTR(device_id, S_IRUGO, vmbus_show_device_attr, NULL), | ||
| 210 | __ATTR(monitor_id, S_IRUGO, vmbus_show_device_attr, NULL), | ||
| 211 | |||
| 212 | __ATTR(server_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL), | ||
| 213 | __ATTR(server_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL), | ||
| 214 | __ATTR(server_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL), | ||
| 215 | |||
| 216 | __ATTR(client_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL), | ||
| 217 | __ATTR(client_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL), | ||
| 218 | __ATTR(client_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL), | ||
| 219 | |||
| 220 | __ATTR(out_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL), | ||
| 221 | __ATTR(out_read_index, S_IRUGO, vmbus_show_device_attr, NULL), | ||
| 222 | __ATTR(out_write_index, S_IRUGO, vmbus_show_device_attr, NULL), | ||
| 223 | __ATTR(out_read_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL), | ||
| 224 | __ATTR(out_write_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL), | ||
| 225 | |||
| 226 | __ATTR(in_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL), | ||
| 227 | __ATTR(in_read_index, S_IRUGO, vmbus_show_device_attr, NULL), | ||
| 228 | __ATTR(in_write_index, S_IRUGO, vmbus_show_device_attr, NULL), | ||
| 229 | __ATTR(in_read_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL), | ||
| 230 | __ATTR(in_write_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL), | ||
| 231 | __ATTR_NULL | ||
| 232 | }; | ||
| 233 | |||
| 234 | |||
| 235 | /* | ||
| 236 | * vmbus_uevent - add uevent for our device | ||
| 237 | * | ||
| 238 | * This routine is invoked when a device is added or removed on the vmbus to | ||
| 239 | * generate a uevent to udev in the userspace. The udev will then look at its | ||
| 240 | * rule and the uevent generated here to load the appropriate driver | ||
| 241 | */ | ||
| 242 | static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env) | ||
| 243 | { | ||
| 244 | struct hv_device *dev = device_to_hv_device(device); | ||
| 245 | int ret; | ||
| 246 | |||
| 247 | ret = add_uevent_var(env, "VMBUS_DEVICE_CLASS_GUID={" | ||
| 248 | "%02x%02x%02x%02x-%02x%02x-%02x%02x-" | ||
| 249 | "%02x%02x%02x%02x%02x%02x%02x%02x}", | ||
| 250 | dev->dev_type.data[3], | ||
| 251 | dev->dev_type.data[2], | ||
| 252 | dev->dev_type.data[1], | ||
| 253 | dev->dev_type.data[0], | ||
| 254 | dev->dev_type.data[5], | ||
| 255 | dev->dev_type.data[4], | ||
| 256 | dev->dev_type.data[7], | ||
| 257 | dev->dev_type.data[6], | ||
| 258 | dev->dev_type.data[8], | ||
| 259 | dev->dev_type.data[9], | ||
| 260 | dev->dev_type.data[10], | ||
| 261 | dev->dev_type.data[11], | ||
| 262 | dev->dev_type.data[12], | ||
| 263 | dev->dev_type.data[13], | ||
| 264 | dev->dev_type.data[14], | ||
| 265 | dev->dev_type.data[15]); | ||
| 266 | |||
| 267 | if (ret) | ||
| 268 | return ret; | ||
| 269 | |||
| 270 | ret = add_uevent_var(env, "VMBUS_DEVICE_DEVICE_GUID={" | ||
| 271 | "%02x%02x%02x%02x-%02x%02x-%02x%02x-" | ||
| 272 | "%02x%02x%02x%02x%02x%02x%02x%02x}", | ||
| 273 | dev->dev_instance.data[3], | ||
| 274 | dev->dev_instance.data[2], | ||
| 275 | dev->dev_instance.data[1], | ||
| 276 | dev->dev_instance.data[0], | ||
| 277 | dev->dev_instance.data[5], | ||
| 278 | dev->dev_instance.data[4], | ||
| 279 | dev->dev_instance.data[7], | ||
| 280 | dev->dev_instance.data[6], | ||
| 281 | dev->dev_instance.data[8], | ||
| 282 | dev->dev_instance.data[9], | ||
| 283 | dev->dev_instance.data[10], | ||
| 284 | dev->dev_instance.data[11], | ||
| 285 | dev->dev_instance.data[12], | ||
| 286 | dev->dev_instance.data[13], | ||
| 287 | dev->dev_instance.data[14], | ||
| 288 | dev->dev_instance.data[15]); | ||
| 289 | if (ret) | ||
| 290 | return ret; | ||
| 291 | |||
| 292 | return 0; | ||
| 293 | } | ||
| 294 | |||
| 295 | |||
| 296 | /* | ||
| 297 | * vmbus_match - Attempt to match the specified device to the specified driver | ||
| 298 | */ | ||
| 299 | static int vmbus_match(struct device *device, struct device_driver *driver) | ||
| 300 | { | ||
| 301 | int match = 0; | ||
| 302 | struct hv_driver *drv = drv_to_hv_drv(driver); | ||
| 303 | struct hv_device *hv_dev = device_to_hv_device(device); | ||
| 304 | |||
| 305 | /* We found our driver ? */ | ||
| 306 | if (memcmp(&hv_dev->dev_type, &drv->dev_type, | ||
| 307 | sizeof(struct hv_guid)) == 0) | ||
| 308 | match = 1; | ||
| 309 | |||
| 310 | return match; | ||
| 311 | } | ||
| 312 | |||
| 313 | /* | ||
| 314 | * vmbus_probe - Add the new vmbus's child device | ||
| 315 | */ | ||
| 316 | static int vmbus_probe(struct device *child_device) | ||
| 317 | { | ||
| 318 | int ret = 0; | ||
| 319 | struct hv_driver *drv = | ||
| 320 | drv_to_hv_drv(child_device->driver); | ||
| 321 | struct hv_device *dev = device_to_hv_device(child_device); | ||
| 322 | |||
| 323 | if (drv->probe) { | ||
| 324 | ret = drv->probe(dev); | ||
| 325 | if (ret != 0) | ||
| 326 | pr_err("probe failed for device %s (%d)\n", | ||
| 327 | dev_name(child_device), ret); | ||
| 328 | |||
| 329 | } else { | ||
| 330 | pr_err("probe not set for driver %s\n", | ||
| 331 | dev_name(child_device)); | ||
| 332 | ret = -ENODEV; | ||
| 333 | } | ||
| 334 | return ret; | ||
| 335 | } | ||
| 336 | |||
| 337 | /* | ||
| 338 | * vmbus_remove - Remove a vmbus device | ||
| 339 | */ | ||
| 340 | static int vmbus_remove(struct device *child_device) | ||
| 341 | { | ||
| 342 | int ret; | ||
| 343 | struct hv_driver *drv; | ||
| 344 | |||
| 345 | struct hv_device *dev = device_to_hv_device(child_device); | ||
| 346 | |||
| 347 | if (child_device->driver) { | ||
| 348 | drv = drv_to_hv_drv(child_device->driver); | ||
| 349 | |||
| 350 | if (drv->remove) { | ||
| 351 | ret = drv->remove(dev); | ||
| 352 | } else { | ||
| 353 | pr_err("remove not set for driver %s\n", | ||
| 354 | dev_name(child_device)); | ||
| 355 | ret = -ENODEV; | ||
| 356 | } | ||
| 357 | } | ||
| 358 | |||
| 359 | return 0; | ||
| 360 | } | ||
| 361 | |||
| 362 | |||
| 363 | /* | ||
| 364 | * vmbus_shutdown - Shutdown a vmbus device | ||
| 365 | */ | ||
| 366 | static void vmbus_shutdown(struct device *child_device) | ||
| 367 | { | ||
| 368 | struct hv_driver *drv; | ||
| 369 | struct hv_device *dev = device_to_hv_device(child_device); | ||
| 370 | |||
| 371 | |||
| 372 | /* The device may not be attached yet */ | ||
| 373 | if (!child_device->driver) | ||
| 374 | return; | ||
| 375 | |||
| 376 | drv = drv_to_hv_drv(child_device->driver); | ||
| 377 | |||
| 378 | if (drv->shutdown) | ||
| 379 | drv->shutdown(dev); | ||
| 380 | |||
| 381 | return; | ||
| 382 | } | ||
| 383 | |||
| 384 | |||
| 385 | /* | ||
| 386 | * vmbus_device_release - Final callback release of the vmbus child device | ||
| 387 | */ | ||
| 388 | static void vmbus_device_release(struct device *device) | ||
| 389 | { | ||
| 390 | struct hv_device *hv_dev = device_to_hv_device(device); | ||
| 391 | |||
| 392 | kfree(hv_dev); | ||
| 393 | |||
| 394 | } | ||
| 395 | |||
| 396 | /* The one and only one */ | ||
| 397 | static struct bus_type hv_bus = { | ||
| 398 | .name = "vmbus", | ||
| 399 | .match = vmbus_match, | ||
| 400 | .shutdown = vmbus_shutdown, | ||
| 401 | .remove = vmbus_remove, | ||
| 402 | .probe = vmbus_probe, | ||
| 403 | .uevent = vmbus_uevent, | ||
| 404 | .dev_attrs = vmbus_device_attrs, | ||
| 405 | }; | ||
| 406 | |||
| 407 | static const char *driver_name = "hyperv"; | ||
| 408 | |||
| 409 | |||
| 410 | struct onmessage_work_context { | ||
| 411 | struct work_struct work; | ||
| 412 | struct hv_message msg; | ||
| 413 | }; | ||
| 414 | |||
| 415 | static void vmbus_onmessage_work(struct work_struct *work) | ||
| 416 | { | ||
| 417 | struct onmessage_work_context *ctx; | ||
| 418 | |||
| 419 | ctx = container_of(work, struct onmessage_work_context, | ||
| 420 | work); | ||
| 421 | vmbus_onmessage(&ctx->msg); | ||
| 422 | kfree(ctx); | ||
| 423 | } | ||
| 424 | |||
| 425 | /* | ||
| 426 | * vmbus_on_msg_dpc - DPC routine to handle messages from the hypervisior | ||
| 427 | */ | ||
| 428 | static void vmbus_on_msg_dpc(unsigned long data) | ||
| 429 | { | ||
| 430 | int cpu = smp_processor_id(); | ||
| 431 | void *page_addr = hv_context.synic_message_page[cpu]; | ||
| 432 | struct hv_message *msg = (struct hv_message *)page_addr + | ||
| 433 | VMBUS_MESSAGE_SINT; | ||
| 434 | struct onmessage_work_context *ctx; | ||
| 435 | |||
| 436 | while (1) { | ||
| 437 | if (msg->header.message_type == HVMSG_NONE) { | ||
| 438 | /* no msg */ | ||
| 439 | break; | ||
| 440 | } else { | ||
| 441 | ctx = kmalloc(sizeof(*ctx), GFP_ATOMIC); | ||
| 442 | if (ctx == NULL) | ||
| 443 | continue; | ||
| 444 | INIT_WORK(&ctx->work, vmbus_onmessage_work); | ||
| 445 | memcpy(&ctx->msg, msg, sizeof(*msg)); | ||
| 446 | queue_work(vmbus_connection.work_queue, &ctx->work); | ||
| 447 | } | ||
| 448 | |||
| 449 | msg->header.message_type = HVMSG_NONE; | ||
| 450 | |||
| 451 | /* | ||
| 452 | * Make sure the write to MessageType (ie set to | ||
| 453 | * HVMSG_NONE) happens before we read the | ||
| 454 | * MessagePending and EOMing. Otherwise, the EOMing | ||
| 455 | * will not deliver any more messages since there is | ||
| 456 | * no empty slot | ||
| 457 | */ | ||
| 458 | smp_mb(); | ||
| 459 | |||
| 460 | if (msg->header.message_flags.msg_pending) { | ||
| 461 | /* | ||
| 462 | * This will cause message queue rescan to | ||
| 463 | * possibly deliver another msg from the | ||
| 464 | * hypervisor | ||
| 465 | */ | ||
| 466 | wrmsrl(HV_X64_MSR_EOM, 0); | ||
| 467 | } | ||
| 468 | } | ||
| 469 | } | ||
| 470 | |||
| 471 | /* | ||
| 472 | * vmbus_on_isr - ISR routine | ||
| 473 | */ | ||
| 474 | static int vmbus_on_isr(void) | ||
| 475 | { | ||
| 476 | int ret = 0; | ||
| 477 | int cpu = smp_processor_id(); | ||
| 478 | void *page_addr; | ||
| 479 | struct hv_message *msg; | ||
| 480 | union hv_synic_event_flags *event; | ||
| 481 | |||
| 482 | page_addr = hv_context.synic_message_page[cpu]; | ||
| 483 | msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; | ||
| 484 | |||
| 485 | /* Check if there are actual msgs to be process */ | ||
| 486 | if (msg->header.message_type != HVMSG_NONE) | ||
| 487 | ret |= 0x1; | ||
| 488 | |||
| 489 | page_addr = hv_context.synic_event_page[cpu]; | ||
| 490 | event = (union hv_synic_event_flags *)page_addr + VMBUS_MESSAGE_SINT; | ||
| 491 | |||
| 492 | /* Since we are a child, we only need to check bit 0 */ | ||
| 493 | if (sync_test_and_clear_bit(0, (unsigned long *) &event->flags32[0])) | ||
| 494 | ret |= 0x2; | ||
| 495 | |||
| 496 | return ret; | ||
| 497 | } | ||
| 498 | |||
| 499 | |||
| 500 | static irqreturn_t vmbus_isr(int irq, void *dev_id) | ||
| 501 | { | ||
| 502 | int ret; | ||
| 503 | |||
| 504 | ret = vmbus_on_isr(); | ||
| 505 | |||
| 506 | /* Schedules a dpc if necessary */ | ||
| 507 | if (ret > 0) { | ||
| 508 | if (test_bit(0, (unsigned long *)&ret)) | ||
| 509 | tasklet_schedule(&msg_dpc); | ||
| 510 | |||
| 511 | if (test_bit(1, (unsigned long *)&ret)) | ||
| 512 | tasklet_schedule(&event_dpc); | ||
| 513 | |||
| 514 | return IRQ_HANDLED; | ||
| 515 | } else { | ||
| 516 | return IRQ_NONE; | ||
| 517 | } | ||
| 518 | } | ||
| 519 | |||
| 520 | /* | ||
| 521 | * vmbus_bus_init -Main vmbus driver initialization routine. | ||
| 522 | * | ||
| 523 | * Here, we | ||
| 524 | * - initialize the vmbus driver context | ||
| 525 | * - invoke the vmbus hv main init routine | ||
| 526 | * - get the irq resource | ||
| 527 | * - retrieve the channel offers | ||
| 528 | */ | ||
| 529 | static int vmbus_bus_init(int irq) | ||
| 530 | { | ||
| 531 | int ret; | ||
| 532 | unsigned int vector; | ||
| 533 | |||
| 534 | /* Hypervisor initialization...setup hypercall page..etc */ | ||
| 535 | ret = hv_init(); | ||
| 536 | if (ret != 0) { | ||
| 537 | pr_err("Unable to initialize the hypervisor - 0x%x\n", ret); | ||
| 538 | return ret; | ||
| 539 | } | ||
| 540 | |||
| 541 | /* Initialize the bus context */ | ||
| 542 | tasklet_init(&msg_dpc, vmbus_on_msg_dpc, 0); | ||
| 543 | tasklet_init(&event_dpc, vmbus_on_event, 0); | ||
| 544 | |||
| 545 | /* Now, register the bus with LDM */ | ||
| 546 | ret = bus_register(&hv_bus); | ||
| 547 | if (ret) | ||
| 548 | return ret; | ||
| 549 | |||
| 550 | /* Get the interrupt resource */ | ||
| 551 | ret = request_irq(irq, vmbus_isr, IRQF_SAMPLE_RANDOM, | ||
| 552 | driver_name, hv_acpi_dev); | ||
| 553 | |||
| 554 | if (ret != 0) { | ||
| 555 | pr_err("Unable to request IRQ %d\n", | ||
| 556 | irq); | ||
| 557 | |||
| 558 | bus_unregister(&hv_bus); | ||
| 559 | |||
| 560 | return ret; | ||
| 561 | } | ||
| 562 | |||
| 563 | vector = IRQ0_VECTOR + irq; | ||
| 564 | |||
| 565 | /* | ||
| 566 | * Notify the hypervisor of our irq and | ||
| 567 | * connect to the host. | ||
| 568 | */ | ||
| 569 | on_each_cpu(hv_synic_init, (void *)&vector, 1); | ||
| 570 | ret = vmbus_connect(); | ||
| 571 | if (ret) { | ||
| 572 | free_irq(irq, hv_acpi_dev); | ||
| 573 | bus_unregister(&hv_bus); | ||
| 574 | return ret; | ||
| 575 | } | ||
| 576 | |||
| 577 | |||
| 578 | vmbus_request_offers(); | ||
| 579 | |||
| 580 | return 0; | ||
| 581 | } | ||
| 582 | |||
| 583 | /** | ||
| 584 | * vmbus_child_driver_register() - Register a vmbus's child driver | ||
| 585 | * @drv: Pointer to driver structure you want to register | ||
| 586 | * | ||
| 587 | * | ||
| 588 | * Registers the given driver with Linux through the 'driver_register()' call | ||
| 589 | * And sets up the hyper-v vmbus handling for this driver. | ||
| 590 | * It will return the state of the 'driver_register()' call. | ||
| 591 | * | ||
| 592 | * Mainly used by Hyper-V drivers. | ||
| 593 | */ | ||
| 594 | int vmbus_child_driver_register(struct device_driver *drv) | ||
| 595 | { | ||
| 596 | int ret; | ||
| 597 | |||
| 598 | pr_info("child driver registering - name %s\n", drv->name); | ||
| 599 | |||
| 600 | /* The child driver on this vmbus */ | ||
| 601 | drv->bus = &hv_bus; | ||
| 602 | |||
| 603 | ret = driver_register(drv); | ||
| 604 | |||
| 605 | vmbus_request_offers(); | ||
| 606 | |||
| 607 | return ret; | ||
| 608 | } | ||
| 609 | EXPORT_SYMBOL(vmbus_child_driver_register); | ||
| 610 | |||
| 611 | /** | ||
| 612 | * vmbus_child_driver_unregister() - Unregister a vmbus's child driver | ||
| 613 | * @drv: Pointer to driver structure you want to un-register | ||
| 614 | * | ||
| 615 | * | ||
| 616 | * Un-register the given driver with Linux through the 'driver_unregister()' | ||
| 617 | * call. And ungegisters the driver from the Hyper-V vmbus handler. | ||
| 618 | * | ||
| 619 | * Mainly used by Hyper-V drivers. | ||
| 620 | */ | ||
| 621 | void vmbus_child_driver_unregister(struct device_driver *drv) | ||
| 622 | { | ||
| 623 | pr_info("child driver unregistering - name %s\n", drv->name); | ||
| 624 | |||
| 625 | driver_unregister(drv); | ||
| 626 | |||
| 627 | } | ||
| 628 | EXPORT_SYMBOL(vmbus_child_driver_unregister); | ||
| 629 | |||
| 630 | /* | ||
| 631 | * vmbus_child_device_create - Creates and registers a new child device | ||
| 632 | * on the vmbus. | ||
| 633 | */ | ||
| 634 | struct hv_device *vmbus_child_device_create(struct hv_guid *type, | ||
| 635 | struct hv_guid *instance, | ||
| 636 | struct vmbus_channel *channel) | ||
| 637 | { | ||
| 638 | struct hv_device *child_device_obj; | ||
| 639 | |||
| 640 | /* Allocate the new child device */ | ||
| 641 | child_device_obj = kzalloc(sizeof(struct hv_device), GFP_KERNEL); | ||
| 642 | if (!child_device_obj) { | ||
| 643 | pr_err("Unable to allocate device object for child device\n"); | ||
| 644 | return NULL; | ||
| 645 | } | ||
| 646 | |||
| 647 | child_device_obj->channel = channel; | ||
| 648 | memcpy(&child_device_obj->dev_type, type, sizeof(struct hv_guid)); | ||
| 649 | memcpy(&child_device_obj->dev_instance, instance, | ||
| 650 | sizeof(struct hv_guid)); | ||
| 651 | |||
| 652 | |||
| 653 | return child_device_obj; | ||
| 654 | } | ||
| 655 | |||
| 656 | /* | ||
| 657 | * vmbus_child_device_register - Register the child device | ||
| 658 | */ | ||
| 659 | int vmbus_child_device_register(struct hv_device *child_device_obj) | ||
| 660 | { | ||
| 661 | int ret = 0; | ||
| 662 | |||
| 663 | static atomic_t device_num = ATOMIC_INIT(0); | ||
| 664 | |||
| 665 | /* Set the device name. Otherwise, device_register() will fail. */ | ||
| 666 | dev_set_name(&child_device_obj->device, "vmbus_0_%d", | ||
| 667 | atomic_inc_return(&device_num)); | ||
| 668 | |||
| 669 | /* The new device belongs to this bus */ | ||
| 670 | child_device_obj->device.bus = &hv_bus; /* device->dev.bus; */ | ||
| 671 | child_device_obj->device.parent = &hv_acpi_dev->dev; | ||
| 672 | child_device_obj->device.release = vmbus_device_release; | ||
| 673 | |||
| 674 | /* | ||
| 675 | * Register with the LDM. This will kick off the driver/device | ||
| 676 | * binding...which will eventually call vmbus_match() and vmbus_probe() | ||
| 677 | */ | ||
| 678 | ret = device_register(&child_device_obj->device); | ||
| 679 | |||
| 680 | if (ret) | ||
| 681 | pr_err("Unable to register child device\n"); | ||
| 682 | else | ||
| 683 | pr_info("child device %s registered\n", | ||
| 684 | dev_name(&child_device_obj->device)); | ||
| 685 | |||
| 686 | return ret; | ||
| 687 | } | ||
| 688 | |||
| 689 | /* | ||
| 690 | * vmbus_child_device_unregister - Remove the specified child device | ||
| 691 | * from the vmbus. | ||
| 692 | */ | ||
| 693 | void vmbus_child_device_unregister(struct hv_device *device_obj) | ||
| 694 | { | ||
| 695 | /* | ||
| 696 | * Kick off the process of unregistering the device. | ||
| 697 | * This will call vmbus_remove() and eventually vmbus_device_release() | ||
| 698 | */ | ||
| 699 | device_unregister(&device_obj->device); | ||
| 700 | |||
| 701 | pr_info("child device %s unregistered\n", | ||
| 702 | dev_name(&device_obj->device)); | ||
| 703 | } | ||
| 704 | |||
| 705 | |||
| 706 | /* | ||
| 707 | * VMBUS is an acpi enumerated device. Get the the IRQ information | ||
| 708 | * from DSDT. | ||
| 709 | */ | ||
| 710 | |||
| 711 | static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *irq) | ||
| 712 | { | ||
| 713 | |||
| 714 | if (res->type == ACPI_RESOURCE_TYPE_IRQ) { | ||
| 715 | struct acpi_resource_irq *irqp; | ||
| 716 | irqp = &res->data.irq; | ||
| 717 | |||
| 718 | *((unsigned int *)irq) = irqp->interrupts[0]; | ||
| 719 | } | ||
| 720 | |||
| 721 | return AE_OK; | ||
| 722 | } | ||
| 723 | |||
| 724 | static int vmbus_acpi_add(struct acpi_device *device) | ||
| 725 | { | ||
| 726 | acpi_status result; | ||
| 727 | |||
| 728 | hv_acpi_dev = device; | ||
| 729 | |||
| 730 | result = | ||
| 731 | acpi_walk_resources(device->handle, METHOD_NAME__CRS, | ||
| 732 | vmbus_walk_resources, &irq); | ||
| 733 | |||
| 734 | if (ACPI_FAILURE(result)) { | ||
| 735 | complete(&probe_event); | ||
| 736 | return -ENODEV; | ||
| 737 | } | ||
| 738 | complete(&probe_event); | ||
| 739 | return 0; | ||
| 740 | } | ||
| 741 | |||
| 742 | static const struct acpi_device_id vmbus_acpi_device_ids[] = { | ||
| 743 | {"VMBUS", 0}, | ||
| 744 | {"VMBus", 0}, | ||
| 745 | {"", 0}, | ||
| 746 | }; | ||
| 747 | MODULE_DEVICE_TABLE(acpi, vmbus_acpi_device_ids); | ||
| 748 | |||
| 749 | static struct acpi_driver vmbus_acpi_driver = { | ||
| 750 | .name = "vmbus", | ||
| 751 | .ids = vmbus_acpi_device_ids, | ||
| 752 | .ops = { | ||
| 753 | .add = vmbus_acpi_add, | ||
| 754 | }, | ||
| 755 | }; | ||
| 756 | |||
| 757 | /* | ||
| 758 | * We use a PCI table to determine if we should autoload this driver This is | ||
| 759 | * needed by distro tools to determine if the hyperv drivers should be | ||
| 760 | * installed and/or configured. We don't do anything else with the table, but | ||
| 761 | * it needs to be present. | ||
| 762 | */ | ||
| 763 | static const struct pci_device_id microsoft_hv_pci_table[] = { | ||
| 764 | { PCI_DEVICE(0x1414, 0x5353) }, /* VGA compatible controller */ | ||
| 765 | { 0 } | ||
| 766 | }; | ||
| 767 | MODULE_DEVICE_TABLE(pci, microsoft_hv_pci_table); | ||
| 768 | |||
| 769 | static int __init hv_acpi_init(void) | ||
| 770 | { | ||
| 771 | int ret; | ||
| 772 | |||
| 773 | init_completion(&probe_event); | ||
| 774 | |||
| 775 | /* | ||
| 776 | * Get irq resources first. | ||
| 777 | */ | ||
| 778 | |||
| 779 | ret = acpi_bus_register_driver(&vmbus_acpi_driver); | ||
| 780 | |||
| 781 | if (ret) | ||
| 782 | return ret; | ||
| 783 | |||
| 784 | wait_for_completion(&probe_event); | ||
| 785 | |||
| 786 | if (irq <= 0) { | ||
| 787 | acpi_bus_unregister_driver(&vmbus_acpi_driver); | ||
| 788 | return -ENODEV; | ||
| 789 | } | ||
| 790 | |||
| 791 | ret = vmbus_bus_init(irq); | ||
| 792 | if (ret) | ||
| 793 | acpi_bus_unregister_driver(&vmbus_acpi_driver); | ||
| 794 | return ret; | ||
| 795 | } | ||
| 796 | |||
| 797 | |||
| 798 | MODULE_LICENSE("GPL"); | ||
| 799 | MODULE_VERSION(HV_DRV_VERSION); | ||
| 800 | module_param(vmbus_loglevel, int, S_IRUGO|S_IWUSR); | ||
| 801 | |||
| 802 | module_init(hv_acpi_init); | ||
