aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hv/vmbus_drv.c
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@suse.de>2011-10-04 15:29:52 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-10-11 00:52:55 -0400
commit46a971913611a23478283931460a95be962ce329 (patch)
tree7452d0f07ee9f1f5270a8da6c1387f35c439843d /drivers/hv/vmbus_drv.c
parent715a4801e734ea9c8e528265ce3ff6aead85bce1 (diff)
Staging: hv: move hyperv code out of staging directory
After many years wandering the desert, it is finally time for the Microsoft HyperV code to move out of the staging directory. Or at least the core hyperv bus code, and the utility driver, the rest still have some review to get through by the various subsystem maintainers. Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Diffstat (limited to 'drivers/hv/vmbus_drv.c')
-rw-r--r--drivers/hv/vmbus_drv.c772
1 files changed, 772 insertions, 0 deletions
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
new file mode 100644
index 000000000000..b0d08f980de1
--- /dev/null
+++ b/drivers/hv/vmbus_drv.c
@@ -0,0 +1,772 @@
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/slab.h>
32#include <linux/acpi.h>
33#include <acpi/acpi_bus.h>
34#include <linux/completion.h>
35#include <linux/hyperv.h>
36
37#include "hyperv_vmbus.h"
38
39
40static struct acpi_device *hv_acpi_dev;
41
42static struct tasklet_struct msg_dpc;
43static struct tasklet_struct event_dpc;
44
45unsigned int vmbus_loglevel = (ALL_MODULES << 16 | INFO_LVL);
46EXPORT_SYMBOL(vmbus_loglevel);
47
48static struct completion probe_event;
49static int irq;
50
51static void get_channel_info(struct hv_device *device,
52 struct hv_device_info *info)
53{
54 struct vmbus_channel_debug_info debug_info;
55
56 if (!device->channel)
57 return;
58
59 vmbus_get_debug_info(device->channel, &debug_info);
60
61 info->chn_id = debug_info.relid;
62 info->chn_state = debug_info.state;
63 memcpy(&info->chn_type, &debug_info.interfacetype,
64 sizeof(uuid_le));
65 memcpy(&info->chn_instance, &debug_info.interface_instance,
66 sizeof(uuid_le));
67
68 info->monitor_id = debug_info.monitorid;
69
70 info->server_monitor_pending = debug_info.servermonitor_pending;
71 info->server_monitor_latency = debug_info.servermonitor_latency;
72 info->server_monitor_conn_id = debug_info.servermonitor_connectionid;
73
74 info->client_monitor_pending = debug_info.clientmonitor_pending;
75 info->client_monitor_latency = debug_info.clientmonitor_latency;
76 info->client_monitor_conn_id = debug_info.clientmonitor_connectionid;
77
78 info->inbound.int_mask = debug_info.inbound.current_interrupt_mask;
79 info->inbound.read_idx = debug_info.inbound.current_read_index;
80 info->inbound.write_idx = debug_info.inbound.current_write_index;
81 info->inbound.bytes_avail_toread =
82 debug_info.inbound.bytes_avail_toread;
83 info->inbound.bytes_avail_towrite =
84 debug_info.inbound.bytes_avail_towrite;
85
86 info->outbound.int_mask =
87 debug_info.outbound.current_interrupt_mask;
88 info->outbound.read_idx = debug_info.outbound.current_read_index;
89 info->outbound.write_idx = debug_info.outbound.current_write_index;
90 info->outbound.bytes_avail_toread =
91 debug_info.outbound.bytes_avail_toread;
92 info->outbound.bytes_avail_towrite =
93 debug_info.outbound.bytes_avail_towrite;
94}
95
96#define VMBUS_ALIAS_LEN ((sizeof((struct hv_vmbus_device_id *)0)->guid) * 2)
97static void print_alias_name(struct hv_device *hv_dev, char *alias_name)
98{
99 int i;
100 for (i = 0; i < VMBUS_ALIAS_LEN; i += 2)
101 sprintf(&alias_name[i], "%02x", hv_dev->dev_type.b[i/2]);
102}
103
104/*
105 * vmbus_show_device_attr - Show the device attribute in sysfs.
106 *
107 * This is invoked when user does a
108 * "cat /sys/bus/vmbus/devices/<busdevice>/<attr name>"
109 */
110static ssize_t vmbus_show_device_attr(struct device *dev,
111 struct device_attribute *dev_attr,
112 char *buf)
113{
114 struct hv_device *hv_dev = device_to_hv_device(dev);
115 struct hv_device_info *device_info;
116 char alias_name[VMBUS_ALIAS_LEN + 1];
117 int ret = 0;
118
119 device_info = kzalloc(sizeof(struct hv_device_info), GFP_KERNEL);
120 if (!device_info)
121 return ret;
122
123 get_channel_info(hv_dev, device_info);
124
125 if (!strcmp(dev_attr->attr.name, "class_id")) {
126 ret = sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-"
127 "%02x%02x%02x%02x%02x%02x%02x%02x}\n",
128 device_info->chn_type.b[3],
129 device_info->chn_type.b[2],
130 device_info->chn_type.b[1],
131 device_info->chn_type.b[0],
132 device_info->chn_type.b[5],
133 device_info->chn_type.b[4],
134 device_info->chn_type.b[7],
135 device_info->chn_type.b[6],
136 device_info->chn_type.b[8],
137 device_info->chn_type.b[9],
138 device_info->chn_type.b[10],
139 device_info->chn_type.b[11],
140 device_info->chn_type.b[12],
141 device_info->chn_type.b[13],
142 device_info->chn_type.b[14],
143 device_info->chn_type.b[15]);
144 } else if (!strcmp(dev_attr->attr.name, "device_id")) {
145 ret = sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-"
146 "%02x%02x%02x%02x%02x%02x%02x%02x}\n",
147 device_info->chn_instance.b[3],
148 device_info->chn_instance.b[2],
149 device_info->chn_instance.b[1],
150 device_info->chn_instance.b[0],
151 device_info->chn_instance.b[5],
152 device_info->chn_instance.b[4],
153 device_info->chn_instance.b[7],
154 device_info->chn_instance.b[6],
155 device_info->chn_instance.b[8],
156 device_info->chn_instance.b[9],
157 device_info->chn_instance.b[10],
158 device_info->chn_instance.b[11],
159 device_info->chn_instance.b[12],
160 device_info->chn_instance.b[13],
161 device_info->chn_instance.b[14],
162 device_info->chn_instance.b[15]);
163 } else if (!strcmp(dev_attr->attr.name, "modalias")) {
164 print_alias_name(hv_dev, alias_name);
165 ret = sprintf(buf, "vmbus:%s\n", alias_name);
166 } else if (!strcmp(dev_attr->attr.name, "state")) {
167 ret = sprintf(buf, "%d\n", device_info->chn_state);
168 } else if (!strcmp(dev_attr->attr.name, "id")) {
169 ret = sprintf(buf, "%d\n", device_info->chn_id);
170 } else if (!strcmp(dev_attr->attr.name, "out_intr_mask")) {
171 ret = sprintf(buf, "%d\n", device_info->outbound.int_mask);
172 } else if (!strcmp(dev_attr->attr.name, "out_read_index")) {
173 ret = sprintf(buf, "%d\n", device_info->outbound.read_idx);
174 } else if (!strcmp(dev_attr->attr.name, "out_write_index")) {
175 ret = sprintf(buf, "%d\n", device_info->outbound.write_idx);
176 } else if (!strcmp(dev_attr->attr.name, "out_read_bytes_avail")) {
177 ret = sprintf(buf, "%d\n",
178 device_info->outbound.bytes_avail_toread);
179 } else if (!strcmp(dev_attr->attr.name, "out_write_bytes_avail")) {
180 ret = sprintf(buf, "%d\n",
181 device_info->outbound.bytes_avail_towrite);
182 } else if (!strcmp(dev_attr->attr.name, "in_intr_mask")) {
183 ret = sprintf(buf, "%d\n", device_info->inbound.int_mask);
184 } else if (!strcmp(dev_attr->attr.name, "in_read_index")) {
185 ret = sprintf(buf, "%d\n", device_info->inbound.read_idx);
186 } else if (!strcmp(dev_attr->attr.name, "in_write_index")) {
187 ret = sprintf(buf, "%d\n", device_info->inbound.write_idx);
188 } else if (!strcmp(dev_attr->attr.name, "in_read_bytes_avail")) {
189 ret = sprintf(buf, "%d\n",
190 device_info->inbound.bytes_avail_toread);
191 } else if (!strcmp(dev_attr->attr.name, "in_write_bytes_avail")) {
192 ret = sprintf(buf, "%d\n",
193 device_info->inbound.bytes_avail_towrite);
194 } else if (!strcmp(dev_attr->attr.name, "monitor_id")) {
195 ret = sprintf(buf, "%d\n", device_info->monitor_id);
196 } else if (!strcmp(dev_attr->attr.name, "server_monitor_pending")) {
197 ret = sprintf(buf, "%d\n", device_info->server_monitor_pending);
198 } else if (!strcmp(dev_attr->attr.name, "server_monitor_latency")) {
199 ret = sprintf(buf, "%d\n", device_info->server_monitor_latency);
200 } else if (!strcmp(dev_attr->attr.name, "server_monitor_conn_id")) {
201 ret = sprintf(buf, "%d\n",
202 device_info->server_monitor_conn_id);
203 } else if (!strcmp(dev_attr->attr.name, "client_monitor_pending")) {
204 ret = sprintf(buf, "%d\n", device_info->client_monitor_pending);
205 } else if (!strcmp(dev_attr->attr.name, "client_monitor_latency")) {
206 ret = sprintf(buf, "%d\n", device_info->client_monitor_latency);
207 } else if (!strcmp(dev_attr->attr.name, "client_monitor_conn_id")) {
208 ret = sprintf(buf, "%d\n",
209 device_info->client_monitor_conn_id);
210 }
211
212 kfree(device_info);
213 return ret;
214}
215
216/* Set up per device attributes in /sys/bus/vmbus/devices/<bus device> */
217static struct device_attribute vmbus_device_attrs[] = {
218 __ATTR(id, S_IRUGO, vmbus_show_device_attr, NULL),
219 __ATTR(state, S_IRUGO, vmbus_show_device_attr, NULL),
220 __ATTR(class_id, S_IRUGO, vmbus_show_device_attr, NULL),
221 __ATTR(device_id, S_IRUGO, vmbus_show_device_attr, NULL),
222 __ATTR(monitor_id, S_IRUGO, vmbus_show_device_attr, NULL),
223 __ATTR(modalias, S_IRUGO, vmbus_show_device_attr, NULL),
224
225 __ATTR(server_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL),
226 __ATTR(server_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL),
227 __ATTR(server_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL),
228
229 __ATTR(client_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL),
230 __ATTR(client_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL),
231 __ATTR(client_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL),
232
233 __ATTR(out_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL),
234 __ATTR(out_read_index, S_IRUGO, vmbus_show_device_attr, NULL),
235 __ATTR(out_write_index, S_IRUGO, vmbus_show_device_attr, NULL),
236 __ATTR(out_read_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
237 __ATTR(out_write_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
238
239 __ATTR(in_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL),
240 __ATTR(in_read_index, S_IRUGO, vmbus_show_device_attr, NULL),
241 __ATTR(in_write_index, S_IRUGO, vmbus_show_device_attr, NULL),
242 __ATTR(in_read_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
243 __ATTR(in_write_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
244 __ATTR_NULL
245};
246
247
248/*
249 * vmbus_uevent - add uevent for our device
250 *
251 * This routine is invoked when a device is added or removed on the vmbus to
252 * generate a uevent to udev in the userspace. The udev will then look at its
253 * rule and the uevent generated here to load the appropriate driver
254 *
255 * The alias string will be of the form vmbus:guid where guid is the string
256 * representation of the device guid (each byte of the guid will be
257 * represented with two hex characters.
258 */
259static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env)
260{
261 struct hv_device *dev = device_to_hv_device(device);
262 int ret;
263 char alias_name[VMBUS_ALIAS_LEN + 1];
264
265 print_alias_name(dev, alias_name);
266 ret = add_uevent_var(env, "MODALIAS=vmbus:%s", alias_name);
267 return ret;
268}
269
270static uuid_le null_guid;
271
272static inline bool is_null_guid(const __u8 *guid)
273{
274 if (memcmp(guid, &null_guid, sizeof(uuid_le)))
275 return false;
276 return true;
277}
278
279/*
280 * Return a matching hv_vmbus_device_id pointer.
281 * If there is no match, return NULL.
282 */
283static const struct hv_vmbus_device_id *hv_vmbus_get_id(
284 const struct hv_vmbus_device_id *id,
285 __u8 *guid)
286{
287 for (; !is_null_guid(id->guid); id++)
288 if (!memcmp(&id->guid, guid, sizeof(uuid_le)))
289 return id;
290
291 return NULL;
292}
293
294
295
296/*
297 * vmbus_match - Attempt to match the specified device to the specified driver
298 */
299static int vmbus_match(struct device *device, struct device_driver *driver)
300{
301 struct hv_driver *drv = drv_to_hv_drv(driver);
302 struct hv_device *hv_dev = device_to_hv_device(device);
303
304 if (hv_vmbus_get_id(drv->id_table, hv_dev->dev_type.b))
305 return 1;
306
307 return 0;
308}
309
310/*
311 * vmbus_probe - Add the new vmbus's child device
312 */
313static int vmbus_probe(struct device *child_device)
314{
315 int ret = 0;
316 struct hv_driver *drv =
317 drv_to_hv_drv(child_device->driver);
318 struct hv_device *dev = device_to_hv_device(child_device);
319 const struct hv_vmbus_device_id *dev_id;
320
321 dev_id = hv_vmbus_get_id(drv->id_table, dev->dev_type.b);
322 if (drv->probe) {
323 ret = drv->probe(dev, dev_id);
324 if (ret != 0)
325 pr_err("probe failed for device %s (%d)\n",
326 dev_name(child_device), ret);
327
328 } else {
329 pr_err("probe not set for driver %s\n",
330 dev_name(child_device));
331 ret = -ENODEV;
332 }
333 return ret;
334}
335
336/*
337 * vmbus_remove - Remove a vmbus device
338 */
339static int vmbus_remove(struct device *child_device)
340{
341 struct hv_driver *drv = drv_to_hv_drv(child_device->driver);
342 struct hv_device *dev = device_to_hv_device(child_device);
343
344 if (drv->remove)
345 drv->remove(dev);
346 else
347 pr_err("remove not set for driver %s\n",
348 dev_name(child_device));
349
350 return 0;
351}
352
353
354/*
355 * vmbus_shutdown - Shutdown a vmbus device
356 */
357static void vmbus_shutdown(struct device *child_device)
358{
359 struct hv_driver *drv;
360 struct hv_device *dev = device_to_hv_device(child_device);
361
362
363 /* The device may not be attached yet */
364 if (!child_device->driver)
365 return;
366
367 drv = drv_to_hv_drv(child_device->driver);
368
369 if (drv->shutdown)
370 drv->shutdown(dev);
371
372 return;
373}
374
375
376/*
377 * vmbus_device_release - Final callback release of the vmbus child device
378 */
379static void vmbus_device_release(struct device *device)
380{
381 struct hv_device *hv_dev = device_to_hv_device(device);
382
383 kfree(hv_dev);
384
385}
386
387/* The one and only one */
388static struct bus_type hv_bus = {
389 .name = "vmbus",
390 .match = vmbus_match,
391 .shutdown = vmbus_shutdown,
392 .remove = vmbus_remove,
393 .probe = vmbus_probe,
394 .uevent = vmbus_uevent,
395 .dev_attrs = vmbus_device_attrs,
396};
397
398static const char *driver_name = "hyperv";
399
400
401struct onmessage_work_context {
402 struct work_struct work;
403 struct hv_message msg;
404};
405
406static void vmbus_onmessage_work(struct work_struct *work)
407{
408 struct onmessage_work_context *ctx;
409
410 ctx = container_of(work, struct onmessage_work_context,
411 work);
412 vmbus_onmessage(&ctx->msg);
413 kfree(ctx);
414}
415
416static void vmbus_on_msg_dpc(unsigned long data)
417{
418 int cpu = smp_processor_id();
419 void *page_addr = hv_context.synic_message_page[cpu];
420 struct hv_message *msg = (struct hv_message *)page_addr +
421 VMBUS_MESSAGE_SINT;
422 struct onmessage_work_context *ctx;
423
424 while (1) {
425 if (msg->header.message_type == HVMSG_NONE) {
426 /* no msg */
427 break;
428 } else {
429 ctx = kmalloc(sizeof(*ctx), GFP_ATOMIC);
430 if (ctx == NULL)
431 continue;
432 INIT_WORK(&ctx->work, vmbus_onmessage_work);
433 memcpy(&ctx->msg, msg, sizeof(*msg));
434 queue_work(vmbus_connection.work_queue, &ctx->work);
435 }
436
437 msg->header.message_type = HVMSG_NONE;
438
439 /*
440 * Make sure the write to MessageType (ie set to
441 * HVMSG_NONE) happens before we read the
442 * MessagePending and EOMing. Otherwise, the EOMing
443 * will not deliver any more messages since there is
444 * no empty slot
445 */
446 smp_mb();
447
448 if (msg->header.message_flags.msg_pending) {
449 /*
450 * This will cause message queue rescan to
451 * possibly deliver another msg from the
452 * hypervisor
453 */
454 wrmsrl(HV_X64_MSR_EOM, 0);
455 }
456 }
457}
458
459static irqreturn_t vmbus_isr(int irq, void *dev_id)
460{
461 int cpu = smp_processor_id();
462 void *page_addr;
463 struct hv_message *msg;
464 union hv_synic_event_flags *event;
465 bool handled = false;
466
467 /*
468 * Check for events before checking for messages. This is the order
469 * in which events and messages are checked in Windows guests on
470 * Hyper-V, and the Windows team suggested we do the same.
471 */
472
473 page_addr = hv_context.synic_event_page[cpu];
474 event = (union hv_synic_event_flags *)page_addr + VMBUS_MESSAGE_SINT;
475
476 /* Since we are a child, we only need to check bit 0 */
477 if (sync_test_and_clear_bit(0, (unsigned long *) &event->flags32[0])) {
478 handled = true;
479 tasklet_schedule(&event_dpc);
480 }
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 processed */
486 if (msg->header.message_type != HVMSG_NONE) {
487 handled = true;
488 tasklet_schedule(&msg_dpc);
489 }
490
491 if (handled)
492 return IRQ_HANDLED;
493 else
494 return IRQ_NONE;
495}
496
497/*
498 * vmbus_bus_init -Main vmbus driver initialization routine.
499 *
500 * Here, we
501 * - initialize the vmbus driver context
502 * - invoke the vmbus hv main init routine
503 * - get the irq resource
504 * - retrieve the channel offers
505 */
506static int vmbus_bus_init(int irq)
507{
508 int ret;
509 unsigned int vector;
510
511 /* Hypervisor initialization...setup hypercall page..etc */
512 ret = hv_init();
513 if (ret != 0) {
514 pr_err("Unable to initialize the hypervisor - 0x%x\n", ret);
515 return ret;
516 }
517
518 tasklet_init(&msg_dpc, vmbus_on_msg_dpc, 0);
519 tasklet_init(&event_dpc, vmbus_on_event, 0);
520
521 ret = bus_register(&hv_bus);
522 if (ret)
523 goto err_cleanup;
524
525 ret = request_irq(irq, vmbus_isr, IRQF_SAMPLE_RANDOM,
526 driver_name, hv_acpi_dev);
527
528 if (ret != 0) {
529 pr_err("Unable to request IRQ %d\n",
530 irq);
531 goto err_unregister;
532 }
533
534 vector = IRQ0_VECTOR + irq;
535
536 /*
537 * Notify the hypervisor of our irq and
538 * connect to the host.
539 */
540 on_each_cpu(hv_synic_init, (void *)&vector, 1);
541 ret = vmbus_connect();
542 if (ret)
543 goto err_irq;
544
545 vmbus_request_offers();
546
547 return 0;
548
549err_irq:
550 free_irq(irq, hv_acpi_dev);
551
552err_unregister:
553 bus_unregister(&hv_bus);
554
555err_cleanup:
556 hv_cleanup();
557
558 return ret;
559}
560
561/**
562 * __vmbus_child_driver_register - Register a vmbus's driver
563 * @drv: Pointer to driver structure you want to register
564 * @owner: owner module of the drv
565 * @mod_name: module name string
566 *
567 * Registers the given driver with Linux through the 'driver_register()' call
568 * and sets up the hyper-v vmbus handling for this driver.
569 * It will return the state of the 'driver_register()' call.
570 *
571 */
572int __vmbus_driver_register(struct hv_driver *hv_driver, struct module *owner, const char *mod_name)
573{
574 int ret;
575
576 pr_info("registering driver %s\n", hv_driver->name);
577
578 hv_driver->driver.name = hv_driver->name;
579 hv_driver->driver.owner = owner;
580 hv_driver->driver.mod_name = mod_name;
581 hv_driver->driver.bus = &hv_bus;
582
583 ret = driver_register(&hv_driver->driver);
584
585 vmbus_request_offers();
586
587 return ret;
588}
589EXPORT_SYMBOL_GPL(__vmbus_driver_register);
590
591/**
592 * vmbus_driver_unregister() - Unregister a vmbus's driver
593 * @drv: Pointer to driver structure you want to un-register
594 *
595 * Un-register the given driver that was previous registered with a call to
596 * vmbus_driver_register()
597 */
598void vmbus_driver_unregister(struct hv_driver *hv_driver)
599{
600 pr_info("unregistering driver %s\n", hv_driver->name);
601
602 driver_unregister(&hv_driver->driver);
603
604}
605EXPORT_SYMBOL_GPL(vmbus_driver_unregister);
606
607/*
608 * vmbus_device_create - Creates and registers a new child device
609 * on the vmbus.
610 */
611struct hv_device *vmbus_device_create(uuid_le *type,
612 uuid_le *instance,
613 struct vmbus_channel *channel)
614{
615 struct hv_device *child_device_obj;
616
617 child_device_obj = kzalloc(sizeof(struct hv_device), GFP_KERNEL);
618 if (!child_device_obj) {
619 pr_err("Unable to allocate device object for child device\n");
620 return NULL;
621 }
622
623 child_device_obj->channel = channel;
624 memcpy(&child_device_obj->dev_type, type, sizeof(uuid_le));
625 memcpy(&child_device_obj->dev_instance, instance,
626 sizeof(uuid_le));
627
628
629 return child_device_obj;
630}
631
632/*
633 * vmbus_device_register - Register the child device
634 */
635int vmbus_device_register(struct hv_device *child_device_obj)
636{
637 int ret = 0;
638
639 static atomic_t device_num = ATOMIC_INIT(0);
640
641 dev_set_name(&child_device_obj->device, "vmbus_0_%d",
642 atomic_inc_return(&device_num));
643
644 child_device_obj->device.bus = &hv_bus;
645 child_device_obj->device.parent = &hv_acpi_dev->dev;
646 child_device_obj->device.release = vmbus_device_release;
647
648 /*
649 * Register with the LDM. This will kick off the driver/device
650 * binding...which will eventually call vmbus_match() and vmbus_probe()
651 */
652 ret = device_register(&child_device_obj->device);
653
654 if (ret)
655 pr_err("Unable to register child device\n");
656 else
657 pr_info("child device %s registered\n",
658 dev_name(&child_device_obj->device));
659
660 return ret;
661}
662
663/*
664 * vmbus_device_unregister - Remove the specified child device
665 * from the vmbus.
666 */
667void vmbus_device_unregister(struct hv_device *device_obj)
668{
669 /*
670 * Kick off the process of unregistering the device.
671 * This will call vmbus_remove() and eventually vmbus_device_release()
672 */
673 device_unregister(&device_obj->device);
674
675 pr_info("child device %s unregistered\n",
676 dev_name(&device_obj->device));
677}
678
679
680/*
681 * VMBUS is an acpi enumerated device. Get the the IRQ information
682 * from DSDT.
683 */
684
685static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *irq)
686{
687
688 if (res->type == ACPI_RESOURCE_TYPE_IRQ) {
689 struct acpi_resource_irq *irqp;
690 irqp = &res->data.irq;
691
692 *((unsigned int *)irq) = irqp->interrupts[0];
693 }
694
695 return AE_OK;
696}
697
698static int vmbus_acpi_add(struct acpi_device *device)
699{
700 acpi_status result;
701
702 hv_acpi_dev = device;
703
704 result = acpi_walk_resources(device->handle, METHOD_NAME__CRS,
705 vmbus_walk_resources, &irq);
706
707 if (ACPI_FAILURE(result)) {
708 complete(&probe_event);
709 return -ENODEV;
710 }
711 complete(&probe_event);
712 return 0;
713}
714
715static const struct acpi_device_id vmbus_acpi_device_ids[] = {
716 {"VMBUS", 0},
717 {"VMBus", 0},
718 {"", 0},
719};
720MODULE_DEVICE_TABLE(acpi, vmbus_acpi_device_ids);
721
722static struct acpi_driver vmbus_acpi_driver = {
723 .name = "vmbus",
724 .ids = vmbus_acpi_device_ids,
725 .ops = {
726 .add = vmbus_acpi_add,
727 },
728};
729
730static int __init hv_acpi_init(void)
731{
732 int ret, t;
733
734 init_completion(&probe_event);
735
736 /*
737 * Get irq resources first.
738 */
739
740 ret = acpi_bus_register_driver(&vmbus_acpi_driver);
741
742 if (ret)
743 return ret;
744
745 t = wait_for_completion_timeout(&probe_event, 5*HZ);
746 if (t == 0) {
747 ret = -ETIMEDOUT;
748 goto cleanup;
749 }
750
751 if (irq <= 0) {
752 ret = -ENODEV;
753 goto cleanup;
754 }
755
756 ret = vmbus_bus_init(irq);
757 if (ret)
758 goto cleanup;
759
760 return 0;
761
762cleanup:
763 acpi_bus_unregister_driver(&vmbus_acpi_driver);
764 return ret;
765}
766
767
768MODULE_LICENSE("GPL");
769MODULE_VERSION(HV_DRV_VERSION);
770module_param(vmbus_loglevel, int, S_IRUGO|S_IWUSR);
771
772module_init(hv_acpi_init);