diff options
Diffstat (limited to 'drivers/hv/vmbus_drv.c')
-rw-r--r-- | drivers/hv/vmbus_drv.c | 772 |
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 | |||
40 | static struct acpi_device *hv_acpi_dev; | ||
41 | |||
42 | static struct tasklet_struct msg_dpc; | ||
43 | static struct tasklet_struct event_dpc; | ||
44 | |||
45 | unsigned int vmbus_loglevel = (ALL_MODULES << 16 | INFO_LVL); | ||
46 | EXPORT_SYMBOL(vmbus_loglevel); | ||
47 | |||
48 | static struct completion probe_event; | ||
49 | static int irq; | ||
50 | |||
51 | static 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) | ||
97 | static 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 | */ | ||
110 | static 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> */ | ||
217 | static 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 | */ | ||
259 | static 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 | |||
270 | static uuid_le null_guid; | ||
271 | |||
272 | static 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 | */ | ||
283 | static 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 | */ | ||
299 | static 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 | */ | ||
313 | static 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 | */ | ||
339 | static 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 | */ | ||
357 | static 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 | */ | ||
379 | static 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 */ | ||
388 | static 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 | |||
398 | static const char *driver_name = "hyperv"; | ||
399 | |||
400 | |||
401 | struct onmessage_work_context { | ||
402 | struct work_struct work; | ||
403 | struct hv_message msg; | ||
404 | }; | ||
405 | |||
406 | static 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 | |||
416 | static 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 | |||
459 | static 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 | */ | ||
506 | static 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 | |||
549 | err_irq: | ||
550 | free_irq(irq, hv_acpi_dev); | ||
551 | |||
552 | err_unregister: | ||
553 | bus_unregister(&hv_bus); | ||
554 | |||
555 | err_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 | */ | ||
572 | int __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 | } | ||
589 | EXPORT_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 | */ | ||
598 | void 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 | } | ||
605 | EXPORT_SYMBOL_GPL(vmbus_driver_unregister); | ||
606 | |||
607 | /* | ||
608 | * vmbus_device_create - Creates and registers a new child device | ||
609 | * on the vmbus. | ||
610 | */ | ||
611 | struct 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 | */ | ||
635 | int 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 | */ | ||
667 | void 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 | |||
685 | static 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 | |||
698 | static 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 | |||
715 | static const struct acpi_device_id vmbus_acpi_device_ids[] = { | ||
716 | {"VMBUS", 0}, | ||
717 | {"VMBus", 0}, | ||
718 | {"", 0}, | ||
719 | }; | ||
720 | MODULE_DEVICE_TABLE(acpi, vmbus_acpi_device_ids); | ||
721 | |||
722 | static 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 | |||
730 | static 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 | |||
762 | cleanup: | ||
763 | acpi_bus_unregister_driver(&vmbus_acpi_driver); | ||
764 | return ret; | ||
765 | } | ||
766 | |||
767 | |||
768 | MODULE_LICENSE("GPL"); | ||
769 | MODULE_VERSION(HV_DRV_VERSION); | ||
770 | module_param(vmbus_loglevel, int, S_IRUGO|S_IWUSR); | ||
771 | |||
772 | module_init(hv_acpi_init); | ||