diff options
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); | ||