146a97191SGreg Kroah-Hartman /* 246a97191SGreg Kroah-Hartman * Copyright (c) 2009, Microsoft Corporation. 346a97191SGreg Kroah-Hartman * 446a97191SGreg Kroah-Hartman * This program is free software; you can redistribute it and/or modify it 546a97191SGreg Kroah-Hartman * under the terms and conditions of the GNU General Public License, 646a97191SGreg Kroah-Hartman * version 2, as published by the Free Software Foundation. 746a97191SGreg Kroah-Hartman * 846a97191SGreg Kroah-Hartman * This program is distributed in the hope it will be useful, but WITHOUT 946a97191SGreg Kroah-Hartman * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1046a97191SGreg Kroah-Hartman * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 1146a97191SGreg Kroah-Hartman * more details. 1246a97191SGreg Kroah-Hartman * 1346a97191SGreg Kroah-Hartman * You should have received a copy of the GNU General Public License along with 1446a97191SGreg Kroah-Hartman * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 1546a97191SGreg Kroah-Hartman * Place - Suite 330, Boston, MA 02111-1307 USA. 1646a97191SGreg Kroah-Hartman * 1746a97191SGreg Kroah-Hartman * Authors: 1846a97191SGreg Kroah-Hartman * Haiyang Zhang <haiyangz@microsoft.com> 1946a97191SGreg Kroah-Hartman * Hank Janssen <hjanssen@microsoft.com> 2046a97191SGreg Kroah-Hartman * K. Y. Srinivasan <kys@microsoft.com> 2146a97191SGreg Kroah-Hartman * 2246a97191SGreg Kroah-Hartman */ 2346a97191SGreg Kroah-Hartman #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 2446a97191SGreg Kroah-Hartman 2546a97191SGreg Kroah-Hartman #include <linux/init.h> 2646a97191SGreg Kroah-Hartman #include <linux/module.h> 2746a97191SGreg Kroah-Hartman #include <linux/device.h> 2846a97191SGreg Kroah-Hartman #include <linux/interrupt.h> 2946a97191SGreg Kroah-Hartman #include <linux/sysctl.h> 3046a97191SGreg Kroah-Hartman #include <linux/slab.h> 3146a97191SGreg Kroah-Hartman #include <linux/acpi.h> 3246a97191SGreg Kroah-Hartman #include <linux/completion.h> 3346a97191SGreg Kroah-Hartman #include <linux/hyperv.h> 34b0209501SK. Y. Srinivasan #include <linux/kernel_stat.h> 354061ed9eSK. Y. Srinivasan #include <linux/clockchips.h> 36e513229bSVitaly Kuznetsov #include <linux/cpu.h> 37407dd164SGreg Kroah-Hartman #include <asm/hyperv.h> 381f94ea81SJason Wang #include <asm/hypervisor.h> 39302a3c0fSK. Y. Srinivasan #include <asm/mshyperv.h> 4096c1d058SNick Meier #include <linux/notifier.h> 4196c1d058SNick Meier #include <linux/ptrace.h> 4235464483SJake Oshins #include <linux/screen_info.h> 43510f7aefSVitaly Kuznetsov #include <linux/kdebug.h> 446d146aefSJake Oshins #include <linux/efi.h> 454b44f2d1SStephan Mueller #include <linux/random.h> 4646a97191SGreg Kroah-Hartman #include "hyperv_vmbus.h" 4746a97191SGreg Kroah-Hartman 4846a97191SGreg Kroah-Hartman static struct acpi_device *hv_acpi_dev; 4946a97191SGreg Kroah-Hartman 5046a97191SGreg Kroah-Hartman static struct completion probe_event; 5146a97191SGreg Kroah-Hartman 5296c1d058SNick Meier 53510f7aefSVitaly Kuznetsov static void hyperv_report_panic(struct pt_regs *regs) 5496c1d058SNick Meier { 55510f7aefSVitaly Kuznetsov static bool panic_reported; 5696c1d058SNick Meier 57510f7aefSVitaly Kuznetsov /* 58510f7aefSVitaly Kuznetsov * We prefer to report panic on 'die' chain as we have proper 59510f7aefSVitaly Kuznetsov * registers to report, but if we miss it (e.g. on BUG()) we need 60510f7aefSVitaly Kuznetsov * to report it on 'panic'. 61510f7aefSVitaly Kuznetsov */ 62510f7aefSVitaly Kuznetsov if (panic_reported) 63510f7aefSVitaly Kuznetsov return; 64510f7aefSVitaly Kuznetsov panic_reported = true; 6596c1d058SNick Meier 6696c1d058SNick Meier wrmsrl(HV_X64_MSR_CRASH_P0, regs->ip); 6796c1d058SNick Meier wrmsrl(HV_X64_MSR_CRASH_P1, regs->ax); 6896c1d058SNick Meier wrmsrl(HV_X64_MSR_CRASH_P2, regs->bx); 6996c1d058SNick Meier wrmsrl(HV_X64_MSR_CRASH_P3, regs->cx); 7096c1d058SNick Meier wrmsrl(HV_X64_MSR_CRASH_P4, regs->dx); 7196c1d058SNick Meier 7296c1d058SNick Meier /* 7396c1d058SNick Meier * Let Hyper-V know there is crash data available 7496c1d058SNick Meier */ 7596c1d058SNick Meier wrmsrl(HV_X64_MSR_CRASH_CTL, HV_CRASH_CTL_CRASH_NOTIFY); 76510f7aefSVitaly Kuznetsov } 77510f7aefSVitaly Kuznetsov 78510f7aefSVitaly Kuznetsov static int hyperv_panic_event(struct notifier_block *nb, unsigned long val, 79510f7aefSVitaly Kuznetsov void *args) 80510f7aefSVitaly Kuznetsov { 81510f7aefSVitaly Kuznetsov struct pt_regs *regs; 82510f7aefSVitaly Kuznetsov 83510f7aefSVitaly Kuznetsov regs = current_pt_regs(); 84510f7aefSVitaly Kuznetsov 85510f7aefSVitaly Kuznetsov hyperv_report_panic(regs); 8696c1d058SNick Meier return NOTIFY_DONE; 8796c1d058SNick Meier } 8896c1d058SNick Meier 89510f7aefSVitaly Kuznetsov static int hyperv_die_event(struct notifier_block *nb, unsigned long val, 90510f7aefSVitaly Kuznetsov void *args) 91510f7aefSVitaly Kuznetsov { 92510f7aefSVitaly Kuznetsov struct die_args *die = (struct die_args *)args; 93510f7aefSVitaly Kuznetsov struct pt_regs *regs = die->regs; 94510f7aefSVitaly Kuznetsov 95510f7aefSVitaly Kuznetsov hyperv_report_panic(regs); 96510f7aefSVitaly Kuznetsov return NOTIFY_DONE; 97510f7aefSVitaly Kuznetsov } 98510f7aefSVitaly Kuznetsov 99510f7aefSVitaly Kuznetsov static struct notifier_block hyperv_die_block = { 100510f7aefSVitaly Kuznetsov .notifier_call = hyperv_die_event, 101510f7aefSVitaly Kuznetsov }; 10296c1d058SNick Meier static struct notifier_block hyperv_panic_block = { 10396c1d058SNick Meier .notifier_call = hyperv_panic_event, 10496c1d058SNick Meier }; 10596c1d058SNick Meier 1066d146aefSJake Oshins static const char *fb_mmio_name = "fb_range"; 1076d146aefSJake Oshins static struct resource *fb_mmio; 108e2e80841SStephen Hemminger static struct resource *hyperv_mmio; 109e2e80841SStephen Hemminger static DEFINE_SEMAPHORE(hyperv_mmio_lock); 11046a97191SGreg Kroah-Hartman 111cf6a2eacSK. Y. Srinivasan static int vmbus_exists(void) 112cf6a2eacSK. Y. Srinivasan { 113cf6a2eacSK. Y. Srinivasan if (hv_acpi_dev == NULL) 114cf6a2eacSK. Y. Srinivasan return -ENODEV; 115cf6a2eacSK. Y. Srinivasan 116cf6a2eacSK. Y. Srinivasan return 0; 117cf6a2eacSK. Y. Srinivasan } 118cf6a2eacSK. Y. Srinivasan 11946a97191SGreg Kroah-Hartman #define VMBUS_ALIAS_LEN ((sizeof((struct hv_vmbus_device_id *)0)->guid) * 2) 12046a97191SGreg Kroah-Hartman static void print_alias_name(struct hv_device *hv_dev, char *alias_name) 12146a97191SGreg Kroah-Hartman { 12246a97191SGreg Kroah-Hartman int i; 12346a97191SGreg Kroah-Hartman for (i = 0; i < VMBUS_ALIAS_LEN; i += 2) 12446a97191SGreg Kroah-Hartman sprintf(&alias_name[i], "%02x", hv_dev->dev_type.b[i/2]); 12546a97191SGreg Kroah-Hartman } 12646a97191SGreg Kroah-Hartman 12776c52bbeSGreg Kroah-Hartman static u8 channel_monitor_group(struct vmbus_channel *channel) 12876c52bbeSGreg Kroah-Hartman { 12976c52bbeSGreg Kroah-Hartman return (u8)channel->offermsg.monitorid / 32; 13076c52bbeSGreg Kroah-Hartman } 13176c52bbeSGreg Kroah-Hartman 13276c52bbeSGreg Kroah-Hartman static u8 channel_monitor_offset(struct vmbus_channel *channel) 13376c52bbeSGreg Kroah-Hartman { 13476c52bbeSGreg Kroah-Hartman return (u8)channel->offermsg.monitorid % 32; 13576c52bbeSGreg Kroah-Hartman } 13676c52bbeSGreg Kroah-Hartman 13776c52bbeSGreg Kroah-Hartman static u32 channel_pending(struct vmbus_channel *channel, 13876c52bbeSGreg Kroah-Hartman struct hv_monitor_page *monitor_page) 13976c52bbeSGreg Kroah-Hartman { 14076c52bbeSGreg Kroah-Hartman u8 monitor_group = channel_monitor_group(channel); 14176c52bbeSGreg Kroah-Hartman return monitor_page->trigger_group[monitor_group].pending; 14276c52bbeSGreg Kroah-Hartman } 14376c52bbeSGreg Kroah-Hartman 1441cee272bSGreg Kroah-Hartman static u32 channel_latency(struct vmbus_channel *channel, 1451cee272bSGreg Kroah-Hartman struct hv_monitor_page *monitor_page) 1461cee272bSGreg Kroah-Hartman { 1471cee272bSGreg Kroah-Hartman u8 monitor_group = channel_monitor_group(channel); 1481cee272bSGreg Kroah-Hartman u8 monitor_offset = channel_monitor_offset(channel); 1491cee272bSGreg Kroah-Hartman return monitor_page->latency[monitor_group][monitor_offset]; 1501cee272bSGreg Kroah-Hartman } 1511cee272bSGreg Kroah-Hartman 1524947c745SGreg Kroah-Hartman static u32 channel_conn_id(struct vmbus_channel *channel, 1534947c745SGreg Kroah-Hartman struct hv_monitor_page *monitor_page) 1544947c745SGreg Kroah-Hartman { 1554947c745SGreg Kroah-Hartman u8 monitor_group = channel_monitor_group(channel); 1564947c745SGreg Kroah-Hartman u8 monitor_offset = channel_monitor_offset(channel); 1574947c745SGreg Kroah-Hartman return monitor_page->parameter[monitor_group][monitor_offset].connectionid.u.id; 1584947c745SGreg Kroah-Hartman } 1594947c745SGreg Kroah-Hartman 16003f3a910SGreg Kroah-Hartman static ssize_t id_show(struct device *dev, struct device_attribute *dev_attr, 16103f3a910SGreg Kroah-Hartman char *buf) 16203f3a910SGreg Kroah-Hartman { 16303f3a910SGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(dev); 16403f3a910SGreg Kroah-Hartman 16503f3a910SGreg Kroah-Hartman if (!hv_dev->channel) 16603f3a910SGreg Kroah-Hartman return -ENODEV; 16703f3a910SGreg Kroah-Hartman return sprintf(buf, "%d\n", hv_dev->channel->offermsg.child_relid); 16803f3a910SGreg Kroah-Hartman } 16903f3a910SGreg Kroah-Hartman static DEVICE_ATTR_RO(id); 17003f3a910SGreg Kroah-Hartman 171a8fb5f3dSGreg Kroah-Hartman static ssize_t state_show(struct device *dev, struct device_attribute *dev_attr, 172a8fb5f3dSGreg Kroah-Hartman char *buf) 173a8fb5f3dSGreg Kroah-Hartman { 174a8fb5f3dSGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(dev); 175a8fb5f3dSGreg Kroah-Hartman 176a8fb5f3dSGreg Kroah-Hartman if (!hv_dev->channel) 177a8fb5f3dSGreg Kroah-Hartman return -ENODEV; 178a8fb5f3dSGreg Kroah-Hartman return sprintf(buf, "%d\n", hv_dev->channel->state); 179a8fb5f3dSGreg Kroah-Hartman } 180a8fb5f3dSGreg Kroah-Hartman static DEVICE_ATTR_RO(state); 181a8fb5f3dSGreg Kroah-Hartman 1825ffd00e2SGreg Kroah-Hartman static ssize_t monitor_id_show(struct device *dev, 1835ffd00e2SGreg Kroah-Hartman struct device_attribute *dev_attr, char *buf) 1845ffd00e2SGreg Kroah-Hartman { 1855ffd00e2SGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(dev); 1865ffd00e2SGreg Kroah-Hartman 1875ffd00e2SGreg Kroah-Hartman if (!hv_dev->channel) 1885ffd00e2SGreg Kroah-Hartman return -ENODEV; 1895ffd00e2SGreg Kroah-Hartman return sprintf(buf, "%d\n", hv_dev->channel->offermsg.monitorid); 1905ffd00e2SGreg Kroah-Hartman } 1915ffd00e2SGreg Kroah-Hartman static DEVICE_ATTR_RO(monitor_id); 1925ffd00e2SGreg Kroah-Hartman 19368234c04SGreg Kroah-Hartman static ssize_t class_id_show(struct device *dev, 19468234c04SGreg Kroah-Hartman struct device_attribute *dev_attr, char *buf) 19568234c04SGreg Kroah-Hartman { 19668234c04SGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(dev); 19768234c04SGreg Kroah-Hartman 19868234c04SGreg Kroah-Hartman if (!hv_dev->channel) 19968234c04SGreg Kroah-Hartman return -ENODEV; 20068234c04SGreg Kroah-Hartman return sprintf(buf, "{%pUl}\n", 20168234c04SGreg Kroah-Hartman hv_dev->channel->offermsg.offer.if_type.b); 20268234c04SGreg Kroah-Hartman } 20368234c04SGreg Kroah-Hartman static DEVICE_ATTR_RO(class_id); 20468234c04SGreg Kroah-Hartman 2057c55e1d0SGreg Kroah-Hartman static ssize_t device_id_show(struct device *dev, 2067c55e1d0SGreg Kroah-Hartman struct device_attribute *dev_attr, char *buf) 2077c55e1d0SGreg Kroah-Hartman { 2087c55e1d0SGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(dev); 2097c55e1d0SGreg Kroah-Hartman 2107c55e1d0SGreg Kroah-Hartman if (!hv_dev->channel) 2117c55e1d0SGreg Kroah-Hartman return -ENODEV; 2127c55e1d0SGreg Kroah-Hartman return sprintf(buf, "{%pUl}\n", 2137c55e1d0SGreg Kroah-Hartman hv_dev->channel->offermsg.offer.if_instance.b); 2147c55e1d0SGreg Kroah-Hartman } 2157c55e1d0SGreg Kroah-Hartman static DEVICE_ATTR_RO(device_id); 2167c55e1d0SGreg Kroah-Hartman 217647fa371SGreg Kroah-Hartman static ssize_t modalias_show(struct device *dev, 218647fa371SGreg Kroah-Hartman struct device_attribute *dev_attr, char *buf) 219647fa371SGreg Kroah-Hartman { 220647fa371SGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(dev); 221647fa371SGreg Kroah-Hartman char alias_name[VMBUS_ALIAS_LEN + 1]; 222647fa371SGreg Kroah-Hartman 223647fa371SGreg Kroah-Hartman print_alias_name(hv_dev, alias_name); 224647fa371SGreg Kroah-Hartman return sprintf(buf, "vmbus:%s\n", alias_name); 225647fa371SGreg Kroah-Hartman } 226647fa371SGreg Kroah-Hartman static DEVICE_ATTR_RO(modalias); 227647fa371SGreg Kroah-Hartman 22876c52bbeSGreg Kroah-Hartman static ssize_t server_monitor_pending_show(struct device *dev, 22976c52bbeSGreg Kroah-Hartman struct device_attribute *dev_attr, 23076c52bbeSGreg Kroah-Hartman char *buf) 23176c52bbeSGreg Kroah-Hartman { 23276c52bbeSGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(dev); 23376c52bbeSGreg Kroah-Hartman 23476c52bbeSGreg Kroah-Hartman if (!hv_dev->channel) 23576c52bbeSGreg Kroah-Hartman return -ENODEV; 23676c52bbeSGreg Kroah-Hartman return sprintf(buf, "%d\n", 23776c52bbeSGreg Kroah-Hartman channel_pending(hv_dev->channel, 23876c52bbeSGreg Kroah-Hartman vmbus_connection.monitor_pages[1])); 23976c52bbeSGreg Kroah-Hartman } 24076c52bbeSGreg Kroah-Hartman static DEVICE_ATTR_RO(server_monitor_pending); 24176c52bbeSGreg Kroah-Hartman 24276c52bbeSGreg Kroah-Hartman static ssize_t client_monitor_pending_show(struct device *dev, 24376c52bbeSGreg Kroah-Hartman struct device_attribute *dev_attr, 24476c52bbeSGreg Kroah-Hartman char *buf) 24576c52bbeSGreg Kroah-Hartman { 24676c52bbeSGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(dev); 24776c52bbeSGreg Kroah-Hartman 24876c52bbeSGreg Kroah-Hartman if (!hv_dev->channel) 24976c52bbeSGreg Kroah-Hartman return -ENODEV; 25076c52bbeSGreg Kroah-Hartman return sprintf(buf, "%d\n", 25176c52bbeSGreg Kroah-Hartman channel_pending(hv_dev->channel, 25276c52bbeSGreg Kroah-Hartman vmbus_connection.monitor_pages[1])); 25376c52bbeSGreg Kroah-Hartman } 25476c52bbeSGreg Kroah-Hartman static DEVICE_ATTR_RO(client_monitor_pending); 25568234c04SGreg Kroah-Hartman 2561cee272bSGreg Kroah-Hartman static ssize_t server_monitor_latency_show(struct device *dev, 2571cee272bSGreg Kroah-Hartman struct device_attribute *dev_attr, 2581cee272bSGreg Kroah-Hartman char *buf) 2591cee272bSGreg Kroah-Hartman { 2601cee272bSGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(dev); 2611cee272bSGreg Kroah-Hartman 2621cee272bSGreg Kroah-Hartman if (!hv_dev->channel) 2631cee272bSGreg Kroah-Hartman return -ENODEV; 2641cee272bSGreg Kroah-Hartman return sprintf(buf, "%d\n", 2651cee272bSGreg Kroah-Hartman channel_latency(hv_dev->channel, 2661cee272bSGreg Kroah-Hartman vmbus_connection.monitor_pages[0])); 2671cee272bSGreg Kroah-Hartman } 2681cee272bSGreg Kroah-Hartman static DEVICE_ATTR_RO(server_monitor_latency); 2691cee272bSGreg Kroah-Hartman 2701cee272bSGreg Kroah-Hartman static ssize_t client_monitor_latency_show(struct device *dev, 2711cee272bSGreg Kroah-Hartman struct device_attribute *dev_attr, 2721cee272bSGreg Kroah-Hartman char *buf) 2731cee272bSGreg Kroah-Hartman { 2741cee272bSGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(dev); 2751cee272bSGreg Kroah-Hartman 2761cee272bSGreg Kroah-Hartman if (!hv_dev->channel) 2771cee272bSGreg Kroah-Hartman return -ENODEV; 2781cee272bSGreg Kroah-Hartman return sprintf(buf, "%d\n", 2791cee272bSGreg Kroah-Hartman channel_latency(hv_dev->channel, 2801cee272bSGreg Kroah-Hartman vmbus_connection.monitor_pages[1])); 2811cee272bSGreg Kroah-Hartman } 2821cee272bSGreg Kroah-Hartman static DEVICE_ATTR_RO(client_monitor_latency); 2831cee272bSGreg Kroah-Hartman 2844947c745SGreg Kroah-Hartman static ssize_t server_monitor_conn_id_show(struct device *dev, 2854947c745SGreg Kroah-Hartman struct device_attribute *dev_attr, 2864947c745SGreg Kroah-Hartman char *buf) 2874947c745SGreg Kroah-Hartman { 2884947c745SGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(dev); 2894947c745SGreg Kroah-Hartman 2904947c745SGreg Kroah-Hartman if (!hv_dev->channel) 2914947c745SGreg Kroah-Hartman return -ENODEV; 2924947c745SGreg Kroah-Hartman return sprintf(buf, "%d\n", 2934947c745SGreg Kroah-Hartman channel_conn_id(hv_dev->channel, 2944947c745SGreg Kroah-Hartman vmbus_connection.monitor_pages[0])); 2954947c745SGreg Kroah-Hartman } 2964947c745SGreg Kroah-Hartman static DEVICE_ATTR_RO(server_monitor_conn_id); 2974947c745SGreg Kroah-Hartman 2984947c745SGreg Kroah-Hartman static ssize_t client_monitor_conn_id_show(struct device *dev, 2994947c745SGreg Kroah-Hartman struct device_attribute *dev_attr, 3004947c745SGreg Kroah-Hartman char *buf) 3014947c745SGreg Kroah-Hartman { 3024947c745SGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(dev); 3034947c745SGreg Kroah-Hartman 3044947c745SGreg Kroah-Hartman if (!hv_dev->channel) 3054947c745SGreg Kroah-Hartman return -ENODEV; 3064947c745SGreg Kroah-Hartman return sprintf(buf, "%d\n", 3074947c745SGreg Kroah-Hartman channel_conn_id(hv_dev->channel, 3084947c745SGreg Kroah-Hartman vmbus_connection.monitor_pages[1])); 3094947c745SGreg Kroah-Hartman } 3104947c745SGreg Kroah-Hartman static DEVICE_ATTR_RO(client_monitor_conn_id); 3114947c745SGreg Kroah-Hartman 31298f4c651SGreg Kroah-Hartman static ssize_t out_intr_mask_show(struct device *dev, 31398f4c651SGreg Kroah-Hartman struct device_attribute *dev_attr, char *buf) 31498f4c651SGreg Kroah-Hartman { 31598f4c651SGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(dev); 31698f4c651SGreg Kroah-Hartman struct hv_ring_buffer_debug_info outbound; 31798f4c651SGreg Kroah-Hartman 31898f4c651SGreg Kroah-Hartman if (!hv_dev->channel) 31998f4c651SGreg Kroah-Hartman return -ENODEV; 32098f4c651SGreg Kroah-Hartman hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound); 32198f4c651SGreg Kroah-Hartman return sprintf(buf, "%d\n", outbound.current_interrupt_mask); 32298f4c651SGreg Kroah-Hartman } 32398f4c651SGreg Kroah-Hartman static DEVICE_ATTR_RO(out_intr_mask); 32498f4c651SGreg Kroah-Hartman 32598f4c651SGreg Kroah-Hartman static ssize_t out_read_index_show(struct device *dev, 32698f4c651SGreg Kroah-Hartman struct device_attribute *dev_attr, char *buf) 32798f4c651SGreg Kroah-Hartman { 32898f4c651SGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(dev); 32998f4c651SGreg Kroah-Hartman struct hv_ring_buffer_debug_info outbound; 33098f4c651SGreg Kroah-Hartman 33198f4c651SGreg Kroah-Hartman if (!hv_dev->channel) 33298f4c651SGreg Kroah-Hartman return -ENODEV; 33398f4c651SGreg Kroah-Hartman hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound); 33498f4c651SGreg Kroah-Hartman return sprintf(buf, "%d\n", outbound.current_read_index); 33598f4c651SGreg Kroah-Hartman } 33698f4c651SGreg Kroah-Hartman static DEVICE_ATTR_RO(out_read_index); 33798f4c651SGreg Kroah-Hartman 33898f4c651SGreg Kroah-Hartman static ssize_t out_write_index_show(struct device *dev, 33998f4c651SGreg Kroah-Hartman struct device_attribute *dev_attr, 34098f4c651SGreg Kroah-Hartman char *buf) 34198f4c651SGreg Kroah-Hartman { 34298f4c651SGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(dev); 34398f4c651SGreg Kroah-Hartman struct hv_ring_buffer_debug_info outbound; 34498f4c651SGreg Kroah-Hartman 34598f4c651SGreg Kroah-Hartman if (!hv_dev->channel) 34698f4c651SGreg Kroah-Hartman return -ENODEV; 34798f4c651SGreg Kroah-Hartman hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound); 34898f4c651SGreg Kroah-Hartman return sprintf(buf, "%d\n", outbound.current_write_index); 34998f4c651SGreg Kroah-Hartman } 35098f4c651SGreg Kroah-Hartman static DEVICE_ATTR_RO(out_write_index); 35198f4c651SGreg Kroah-Hartman 35298f4c651SGreg Kroah-Hartman static ssize_t out_read_bytes_avail_show(struct device *dev, 35398f4c651SGreg Kroah-Hartman struct device_attribute *dev_attr, 35498f4c651SGreg Kroah-Hartman char *buf) 35598f4c651SGreg Kroah-Hartman { 35698f4c651SGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(dev); 35798f4c651SGreg Kroah-Hartman struct hv_ring_buffer_debug_info outbound; 35898f4c651SGreg Kroah-Hartman 35998f4c651SGreg Kroah-Hartman if (!hv_dev->channel) 36098f4c651SGreg Kroah-Hartman return -ENODEV; 36198f4c651SGreg Kroah-Hartman hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound); 36298f4c651SGreg Kroah-Hartman return sprintf(buf, "%d\n", outbound.bytes_avail_toread); 36398f4c651SGreg Kroah-Hartman } 36498f4c651SGreg Kroah-Hartman static DEVICE_ATTR_RO(out_read_bytes_avail); 36598f4c651SGreg Kroah-Hartman 36698f4c651SGreg Kroah-Hartman static ssize_t out_write_bytes_avail_show(struct device *dev, 36798f4c651SGreg Kroah-Hartman struct device_attribute *dev_attr, 36898f4c651SGreg Kroah-Hartman char *buf) 36998f4c651SGreg Kroah-Hartman { 37098f4c651SGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(dev); 37198f4c651SGreg Kroah-Hartman struct hv_ring_buffer_debug_info outbound; 37298f4c651SGreg Kroah-Hartman 37398f4c651SGreg Kroah-Hartman if (!hv_dev->channel) 37498f4c651SGreg Kroah-Hartman return -ENODEV; 37598f4c651SGreg Kroah-Hartman hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound); 37698f4c651SGreg Kroah-Hartman return sprintf(buf, "%d\n", outbound.bytes_avail_towrite); 37798f4c651SGreg Kroah-Hartman } 37898f4c651SGreg Kroah-Hartman static DEVICE_ATTR_RO(out_write_bytes_avail); 37998f4c651SGreg Kroah-Hartman 38098f4c651SGreg Kroah-Hartman static ssize_t in_intr_mask_show(struct device *dev, 38198f4c651SGreg Kroah-Hartman struct device_attribute *dev_attr, char *buf) 38298f4c651SGreg Kroah-Hartman { 38398f4c651SGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(dev); 38498f4c651SGreg Kroah-Hartman struct hv_ring_buffer_debug_info inbound; 38598f4c651SGreg Kroah-Hartman 38698f4c651SGreg Kroah-Hartman if (!hv_dev->channel) 38798f4c651SGreg Kroah-Hartman return -ENODEV; 38898f4c651SGreg Kroah-Hartman hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound); 38998f4c651SGreg Kroah-Hartman return sprintf(buf, "%d\n", inbound.current_interrupt_mask); 39098f4c651SGreg Kroah-Hartman } 39198f4c651SGreg Kroah-Hartman static DEVICE_ATTR_RO(in_intr_mask); 39298f4c651SGreg Kroah-Hartman 39398f4c651SGreg Kroah-Hartman static ssize_t in_read_index_show(struct device *dev, 39498f4c651SGreg Kroah-Hartman struct device_attribute *dev_attr, char *buf) 39598f4c651SGreg Kroah-Hartman { 39698f4c651SGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(dev); 39798f4c651SGreg Kroah-Hartman struct hv_ring_buffer_debug_info inbound; 39898f4c651SGreg Kroah-Hartman 39998f4c651SGreg Kroah-Hartman if (!hv_dev->channel) 40098f4c651SGreg Kroah-Hartman return -ENODEV; 40198f4c651SGreg Kroah-Hartman hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound); 40298f4c651SGreg Kroah-Hartman return sprintf(buf, "%d\n", inbound.current_read_index); 40398f4c651SGreg Kroah-Hartman } 40498f4c651SGreg Kroah-Hartman static DEVICE_ATTR_RO(in_read_index); 40598f4c651SGreg Kroah-Hartman 40698f4c651SGreg Kroah-Hartman static ssize_t in_write_index_show(struct device *dev, 40798f4c651SGreg Kroah-Hartman struct device_attribute *dev_attr, char *buf) 40898f4c651SGreg Kroah-Hartman { 40998f4c651SGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(dev); 41098f4c651SGreg Kroah-Hartman struct hv_ring_buffer_debug_info inbound; 41198f4c651SGreg Kroah-Hartman 41298f4c651SGreg Kroah-Hartman if (!hv_dev->channel) 41398f4c651SGreg Kroah-Hartman return -ENODEV; 41498f4c651SGreg Kroah-Hartman hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound); 41598f4c651SGreg Kroah-Hartman return sprintf(buf, "%d\n", inbound.current_write_index); 41698f4c651SGreg Kroah-Hartman } 41798f4c651SGreg Kroah-Hartman static DEVICE_ATTR_RO(in_write_index); 41898f4c651SGreg Kroah-Hartman 41998f4c651SGreg Kroah-Hartman static ssize_t in_read_bytes_avail_show(struct device *dev, 42098f4c651SGreg Kroah-Hartman struct device_attribute *dev_attr, 42198f4c651SGreg Kroah-Hartman char *buf) 42298f4c651SGreg Kroah-Hartman { 42398f4c651SGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(dev); 42498f4c651SGreg Kroah-Hartman struct hv_ring_buffer_debug_info inbound; 42598f4c651SGreg Kroah-Hartman 42698f4c651SGreg Kroah-Hartman if (!hv_dev->channel) 42798f4c651SGreg Kroah-Hartman return -ENODEV; 42898f4c651SGreg Kroah-Hartman hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound); 42998f4c651SGreg Kroah-Hartman return sprintf(buf, "%d\n", inbound.bytes_avail_toread); 43098f4c651SGreg Kroah-Hartman } 43198f4c651SGreg Kroah-Hartman static DEVICE_ATTR_RO(in_read_bytes_avail); 43298f4c651SGreg Kroah-Hartman 43398f4c651SGreg Kroah-Hartman static ssize_t in_write_bytes_avail_show(struct device *dev, 43498f4c651SGreg Kroah-Hartman struct device_attribute *dev_attr, 43598f4c651SGreg Kroah-Hartman char *buf) 43698f4c651SGreg Kroah-Hartman { 43798f4c651SGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(dev); 43898f4c651SGreg Kroah-Hartman struct hv_ring_buffer_debug_info inbound; 43998f4c651SGreg Kroah-Hartman 44098f4c651SGreg Kroah-Hartman if (!hv_dev->channel) 44198f4c651SGreg Kroah-Hartman return -ENODEV; 44298f4c651SGreg Kroah-Hartman hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound); 44398f4c651SGreg Kroah-Hartman return sprintf(buf, "%d\n", inbound.bytes_avail_towrite); 44498f4c651SGreg Kroah-Hartman } 44598f4c651SGreg Kroah-Hartman static DEVICE_ATTR_RO(in_write_bytes_avail); 44698f4c651SGreg Kroah-Hartman 447042ab031SDexuan Cui static ssize_t channel_vp_mapping_show(struct device *dev, 448042ab031SDexuan Cui struct device_attribute *dev_attr, 449042ab031SDexuan Cui char *buf) 450042ab031SDexuan Cui { 451042ab031SDexuan Cui struct hv_device *hv_dev = device_to_hv_device(dev); 452042ab031SDexuan Cui struct vmbus_channel *channel = hv_dev->channel, *cur_sc; 453042ab031SDexuan Cui unsigned long flags; 454042ab031SDexuan Cui int buf_size = PAGE_SIZE, n_written, tot_written; 455042ab031SDexuan Cui struct list_head *cur; 456042ab031SDexuan Cui 457042ab031SDexuan Cui if (!channel) 458042ab031SDexuan Cui return -ENODEV; 459042ab031SDexuan Cui 460042ab031SDexuan Cui tot_written = snprintf(buf, buf_size, "%u:%u\n", 461042ab031SDexuan Cui channel->offermsg.child_relid, channel->target_cpu); 462042ab031SDexuan Cui 463042ab031SDexuan Cui spin_lock_irqsave(&channel->lock, flags); 464042ab031SDexuan Cui 465042ab031SDexuan Cui list_for_each(cur, &channel->sc_list) { 466042ab031SDexuan Cui if (tot_written >= buf_size - 1) 467042ab031SDexuan Cui break; 468042ab031SDexuan Cui 469042ab031SDexuan Cui cur_sc = list_entry(cur, struct vmbus_channel, sc_list); 470042ab031SDexuan Cui n_written = scnprintf(buf + tot_written, 471042ab031SDexuan Cui buf_size - tot_written, 472042ab031SDexuan Cui "%u:%u\n", 473042ab031SDexuan Cui cur_sc->offermsg.child_relid, 474042ab031SDexuan Cui cur_sc->target_cpu); 475042ab031SDexuan Cui tot_written += n_written; 476042ab031SDexuan Cui } 477042ab031SDexuan Cui 478042ab031SDexuan Cui spin_unlock_irqrestore(&channel->lock, flags); 479042ab031SDexuan Cui 480042ab031SDexuan Cui return tot_written; 481042ab031SDexuan Cui } 482042ab031SDexuan Cui static DEVICE_ATTR_RO(channel_vp_mapping); 483042ab031SDexuan Cui 4847047f17dSK. Y. Srinivasan static ssize_t vendor_show(struct device *dev, 4857047f17dSK. Y. Srinivasan struct device_attribute *dev_attr, 4867047f17dSK. Y. Srinivasan char *buf) 4877047f17dSK. Y. Srinivasan { 4887047f17dSK. Y. Srinivasan struct hv_device *hv_dev = device_to_hv_device(dev); 4897047f17dSK. Y. Srinivasan return sprintf(buf, "0x%x\n", hv_dev->vendor_id); 4907047f17dSK. Y. Srinivasan } 4917047f17dSK. Y. Srinivasan static DEVICE_ATTR_RO(vendor); 4927047f17dSK. Y. Srinivasan 4937047f17dSK. Y. Srinivasan static ssize_t device_show(struct device *dev, 4947047f17dSK. Y. Srinivasan struct device_attribute *dev_attr, 4957047f17dSK. Y. Srinivasan char *buf) 4967047f17dSK. Y. Srinivasan { 4977047f17dSK. Y. Srinivasan struct hv_device *hv_dev = device_to_hv_device(dev); 4987047f17dSK. Y. Srinivasan return sprintf(buf, "0x%x\n", hv_dev->device_id); 4997047f17dSK. Y. Srinivasan } 5007047f17dSK. Y. Srinivasan static DEVICE_ATTR_RO(device); 5017047f17dSK. Y. Srinivasan 50298f4c651SGreg Kroah-Hartman /* Set up per device attributes in /sys/bus/vmbus/devices/<bus device> */ 50303f3a910SGreg Kroah-Hartman static struct attribute *vmbus_attrs[] = { 50403f3a910SGreg Kroah-Hartman &dev_attr_id.attr, 505a8fb5f3dSGreg Kroah-Hartman &dev_attr_state.attr, 5065ffd00e2SGreg Kroah-Hartman &dev_attr_monitor_id.attr, 50768234c04SGreg Kroah-Hartman &dev_attr_class_id.attr, 5087c55e1d0SGreg Kroah-Hartman &dev_attr_device_id.attr, 509647fa371SGreg Kroah-Hartman &dev_attr_modalias.attr, 51076c52bbeSGreg Kroah-Hartman &dev_attr_server_monitor_pending.attr, 51176c52bbeSGreg Kroah-Hartman &dev_attr_client_monitor_pending.attr, 5121cee272bSGreg Kroah-Hartman &dev_attr_server_monitor_latency.attr, 5131cee272bSGreg Kroah-Hartman &dev_attr_client_monitor_latency.attr, 5144947c745SGreg Kroah-Hartman &dev_attr_server_monitor_conn_id.attr, 5154947c745SGreg Kroah-Hartman &dev_attr_client_monitor_conn_id.attr, 51698f4c651SGreg Kroah-Hartman &dev_attr_out_intr_mask.attr, 51798f4c651SGreg Kroah-Hartman &dev_attr_out_read_index.attr, 51898f4c651SGreg Kroah-Hartman &dev_attr_out_write_index.attr, 51998f4c651SGreg Kroah-Hartman &dev_attr_out_read_bytes_avail.attr, 52098f4c651SGreg Kroah-Hartman &dev_attr_out_write_bytes_avail.attr, 52198f4c651SGreg Kroah-Hartman &dev_attr_in_intr_mask.attr, 52298f4c651SGreg Kroah-Hartman &dev_attr_in_read_index.attr, 52398f4c651SGreg Kroah-Hartman &dev_attr_in_write_index.attr, 52498f4c651SGreg Kroah-Hartman &dev_attr_in_read_bytes_avail.attr, 52598f4c651SGreg Kroah-Hartman &dev_attr_in_write_bytes_avail.attr, 526042ab031SDexuan Cui &dev_attr_channel_vp_mapping.attr, 5277047f17dSK. Y. Srinivasan &dev_attr_vendor.attr, 5287047f17dSK. Y. Srinivasan &dev_attr_device.attr, 52903f3a910SGreg Kroah-Hartman NULL, 53003f3a910SGreg Kroah-Hartman }; 53103f3a910SGreg Kroah-Hartman ATTRIBUTE_GROUPS(vmbus); 53203f3a910SGreg Kroah-Hartman 53346a97191SGreg Kroah-Hartman /* 53446a97191SGreg Kroah-Hartman * vmbus_uevent - add uevent for our device 53546a97191SGreg Kroah-Hartman * 53646a97191SGreg Kroah-Hartman * This routine is invoked when a device is added or removed on the vmbus to 53746a97191SGreg Kroah-Hartman * generate a uevent to udev in the userspace. The udev will then look at its 53846a97191SGreg Kroah-Hartman * rule and the uevent generated here to load the appropriate driver 53946a97191SGreg Kroah-Hartman * 54046a97191SGreg Kroah-Hartman * The alias string will be of the form vmbus:guid where guid is the string 54146a97191SGreg Kroah-Hartman * representation of the device guid (each byte of the guid will be 54246a97191SGreg Kroah-Hartman * represented with two hex characters. 54346a97191SGreg Kroah-Hartman */ 54446a97191SGreg Kroah-Hartman static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env) 54546a97191SGreg Kroah-Hartman { 54646a97191SGreg Kroah-Hartman struct hv_device *dev = device_to_hv_device(device); 54746a97191SGreg Kroah-Hartman int ret; 54846a97191SGreg Kroah-Hartman char alias_name[VMBUS_ALIAS_LEN + 1]; 54946a97191SGreg Kroah-Hartman 55046a97191SGreg Kroah-Hartman print_alias_name(dev, alias_name); 55146a97191SGreg Kroah-Hartman ret = add_uevent_var(env, "MODALIAS=vmbus:%s", alias_name); 55246a97191SGreg Kroah-Hartman return ret; 55346a97191SGreg Kroah-Hartman } 55446a97191SGreg Kroah-Hartman 5551b9d48f2Sstephen hemminger static const uuid_le null_guid; 55646a97191SGreg Kroah-Hartman 557af3ff643SK. Y. Srinivasan static inline bool is_null_guid(const uuid_le *guid) 55846a97191SGreg Kroah-Hartman { 5594ae92508SK. Y. Srinivasan if (uuid_le_cmp(*guid, null_guid)) 56046a97191SGreg Kroah-Hartman return false; 56146a97191SGreg Kroah-Hartman return true; 56246a97191SGreg Kroah-Hartman } 56346a97191SGreg Kroah-Hartman 56446a97191SGreg Kroah-Hartman /* 56546a97191SGreg Kroah-Hartman * Return a matching hv_vmbus_device_id pointer. 56646a97191SGreg Kroah-Hartman * If there is no match, return NULL. 56746a97191SGreg Kroah-Hartman */ 56846a97191SGreg Kroah-Hartman static const struct hv_vmbus_device_id *hv_vmbus_get_id( 56946a97191SGreg Kroah-Hartman const struct hv_vmbus_device_id *id, 570af3ff643SK. Y. Srinivasan const uuid_le *guid) 57146a97191SGreg Kroah-Hartman { 572af3ff643SK. Y. Srinivasan for (; !is_null_guid(&id->guid); id++) 5734ae92508SK. Y. Srinivasan if (!uuid_le_cmp(id->guid, *guid)) 57446a97191SGreg Kroah-Hartman return id; 57546a97191SGreg Kroah-Hartman 57646a97191SGreg Kroah-Hartman return NULL; 57746a97191SGreg Kroah-Hartman } 57846a97191SGreg Kroah-Hartman 57946a97191SGreg Kroah-Hartman 58046a97191SGreg Kroah-Hartman 58146a97191SGreg Kroah-Hartman /* 58246a97191SGreg Kroah-Hartman * vmbus_match - Attempt to match the specified device to the specified driver 58346a97191SGreg Kroah-Hartman */ 58446a97191SGreg Kroah-Hartman static int vmbus_match(struct device *device, struct device_driver *driver) 58546a97191SGreg Kroah-Hartman { 58646a97191SGreg Kroah-Hartman struct hv_driver *drv = drv_to_hv_drv(driver); 58746a97191SGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(device); 58846a97191SGreg Kroah-Hartman 5898981da32SDexuan Cui /* The hv_sock driver handles all hv_sock offers. */ 5908981da32SDexuan Cui if (is_hvsock_channel(hv_dev->channel)) 5918981da32SDexuan Cui return drv->hvsock; 5928981da32SDexuan Cui 593af3ff643SK. Y. Srinivasan if (hv_vmbus_get_id(drv->id_table, &hv_dev->dev_type)) 59446a97191SGreg Kroah-Hartman return 1; 59546a97191SGreg Kroah-Hartman 59646a97191SGreg Kroah-Hartman return 0; 59746a97191SGreg Kroah-Hartman } 59846a97191SGreg Kroah-Hartman 59946a97191SGreg Kroah-Hartman /* 60046a97191SGreg Kroah-Hartman * vmbus_probe - Add the new vmbus's child device 60146a97191SGreg Kroah-Hartman */ 60246a97191SGreg Kroah-Hartman static int vmbus_probe(struct device *child_device) 60346a97191SGreg Kroah-Hartman { 60446a97191SGreg Kroah-Hartman int ret = 0; 60546a97191SGreg Kroah-Hartman struct hv_driver *drv = 60646a97191SGreg Kroah-Hartman drv_to_hv_drv(child_device->driver); 60746a97191SGreg Kroah-Hartman struct hv_device *dev = device_to_hv_device(child_device); 60846a97191SGreg Kroah-Hartman const struct hv_vmbus_device_id *dev_id; 60946a97191SGreg Kroah-Hartman 610af3ff643SK. Y. Srinivasan dev_id = hv_vmbus_get_id(drv->id_table, &dev->dev_type); 61146a97191SGreg Kroah-Hartman if (drv->probe) { 61246a97191SGreg Kroah-Hartman ret = drv->probe(dev, dev_id); 61346a97191SGreg Kroah-Hartman if (ret != 0) 61446a97191SGreg Kroah-Hartman pr_err("probe failed for device %s (%d)\n", 61546a97191SGreg Kroah-Hartman dev_name(child_device), ret); 61646a97191SGreg Kroah-Hartman 61746a97191SGreg Kroah-Hartman } else { 61846a97191SGreg Kroah-Hartman pr_err("probe not set for driver %s\n", 61946a97191SGreg Kroah-Hartman dev_name(child_device)); 62046a97191SGreg Kroah-Hartman ret = -ENODEV; 62146a97191SGreg Kroah-Hartman } 62246a97191SGreg Kroah-Hartman return ret; 62346a97191SGreg Kroah-Hartman } 62446a97191SGreg Kroah-Hartman 62546a97191SGreg Kroah-Hartman /* 62646a97191SGreg Kroah-Hartman * vmbus_remove - Remove a vmbus device 62746a97191SGreg Kroah-Hartman */ 62846a97191SGreg Kroah-Hartman static int vmbus_remove(struct device *child_device) 62946a97191SGreg Kroah-Hartman { 630d15a0301SK. Y. Srinivasan struct hv_driver *drv; 63146a97191SGreg Kroah-Hartman struct hv_device *dev = device_to_hv_device(child_device); 63246a97191SGreg Kroah-Hartman 633d15a0301SK. Y. Srinivasan if (child_device->driver) { 634d15a0301SK. Y. Srinivasan drv = drv_to_hv_drv(child_device->driver); 63546a97191SGreg Kroah-Hartman if (drv->remove) 63646a97191SGreg Kroah-Hartman drv->remove(dev); 637ed6cfcc5SK. Y. Srinivasan } 63846a97191SGreg Kroah-Hartman 63946a97191SGreg Kroah-Hartman return 0; 64046a97191SGreg Kroah-Hartman } 64146a97191SGreg Kroah-Hartman 64246a97191SGreg Kroah-Hartman 64346a97191SGreg Kroah-Hartman /* 64446a97191SGreg Kroah-Hartman * vmbus_shutdown - Shutdown a vmbus device 64546a97191SGreg Kroah-Hartman */ 64646a97191SGreg Kroah-Hartman static void vmbus_shutdown(struct device *child_device) 64746a97191SGreg Kroah-Hartman { 64846a97191SGreg Kroah-Hartman struct hv_driver *drv; 64946a97191SGreg Kroah-Hartman struct hv_device *dev = device_to_hv_device(child_device); 65046a97191SGreg Kroah-Hartman 65146a97191SGreg Kroah-Hartman 65246a97191SGreg Kroah-Hartman /* The device may not be attached yet */ 65346a97191SGreg Kroah-Hartman if (!child_device->driver) 65446a97191SGreg Kroah-Hartman return; 65546a97191SGreg Kroah-Hartman 65646a97191SGreg Kroah-Hartman drv = drv_to_hv_drv(child_device->driver); 65746a97191SGreg Kroah-Hartman 65846a97191SGreg Kroah-Hartman if (drv->shutdown) 65946a97191SGreg Kroah-Hartman drv->shutdown(dev); 66046a97191SGreg Kroah-Hartman 66146a97191SGreg Kroah-Hartman return; 66246a97191SGreg Kroah-Hartman } 66346a97191SGreg Kroah-Hartman 66446a97191SGreg Kroah-Hartman 66546a97191SGreg Kroah-Hartman /* 66646a97191SGreg Kroah-Hartman * vmbus_device_release - Final callback release of the vmbus child device 66746a97191SGreg Kroah-Hartman */ 66846a97191SGreg Kroah-Hartman static void vmbus_device_release(struct device *device) 66946a97191SGreg Kroah-Hartman { 67046a97191SGreg Kroah-Hartman struct hv_device *hv_dev = device_to_hv_device(device); 67134c6801eSDexuan Cui struct vmbus_channel *channel = hv_dev->channel; 67246a97191SGreg Kroah-Hartman 67334c6801eSDexuan Cui hv_process_channel_removal(channel, 67434c6801eSDexuan Cui channel->offermsg.child_relid); 67546a97191SGreg Kroah-Hartman kfree(hv_dev); 67646a97191SGreg Kroah-Hartman 67746a97191SGreg Kroah-Hartman } 67846a97191SGreg Kroah-Hartman 67946a97191SGreg Kroah-Hartman /* The one and only one */ 68046a97191SGreg Kroah-Hartman static struct bus_type hv_bus = { 68146a97191SGreg Kroah-Hartman .name = "vmbus", 68246a97191SGreg Kroah-Hartman .match = vmbus_match, 68346a97191SGreg Kroah-Hartman .shutdown = vmbus_shutdown, 68446a97191SGreg Kroah-Hartman .remove = vmbus_remove, 68546a97191SGreg Kroah-Hartman .probe = vmbus_probe, 68646a97191SGreg Kroah-Hartman .uevent = vmbus_uevent, 68703f3a910SGreg Kroah-Hartman .dev_groups = vmbus_groups, 68846a97191SGreg Kroah-Hartman }; 68946a97191SGreg Kroah-Hartman 69046a97191SGreg Kroah-Hartman struct onmessage_work_context { 69146a97191SGreg Kroah-Hartman struct work_struct work; 69246a97191SGreg Kroah-Hartman struct hv_message msg; 69346a97191SGreg Kroah-Hartman }; 69446a97191SGreg Kroah-Hartman 69546a97191SGreg Kroah-Hartman static void vmbus_onmessage_work(struct work_struct *work) 69646a97191SGreg Kroah-Hartman { 69746a97191SGreg Kroah-Hartman struct onmessage_work_context *ctx; 69846a97191SGreg Kroah-Hartman 69909a19628SVitaly Kuznetsov /* Do not process messages if we're in DISCONNECTED state */ 70009a19628SVitaly Kuznetsov if (vmbus_connection.conn_state == DISCONNECTED) 70109a19628SVitaly Kuznetsov return; 70209a19628SVitaly Kuznetsov 70346a97191SGreg Kroah-Hartman ctx = container_of(work, struct onmessage_work_context, 70446a97191SGreg Kroah-Hartman work); 70546a97191SGreg Kroah-Hartman vmbus_onmessage(&ctx->msg); 70646a97191SGreg Kroah-Hartman kfree(ctx); 70746a97191SGreg Kroah-Hartman } 70846a97191SGreg Kroah-Hartman 709d8a60e00Skbuild test robot static void hv_process_timer_expiration(struct hv_message *msg, int cpu) 7104061ed9eSK. Y. Srinivasan { 7114061ed9eSK. Y. Srinivasan struct clock_event_device *dev = hv_context.clk_evt[cpu]; 7124061ed9eSK. Y. Srinivasan 7134061ed9eSK. Y. Srinivasan if (dev->event_handler) 7144061ed9eSK. Y. Srinivasan dev->event_handler(dev); 7154061ed9eSK. Y. Srinivasan 716cd95aad5SVitaly Kuznetsov vmbus_signal_eom(msg, HVMSG_TIMER_EXPIRED); 7174061ed9eSK. Y. Srinivasan } 7184061ed9eSK. Y. Srinivasan 719d81274aaSK. Y. Srinivasan void vmbus_on_msg_dpc(unsigned long data) 72046a97191SGreg Kroah-Hartman { 72146a97191SGreg Kroah-Hartman int cpu = smp_processor_id(); 72246a97191SGreg Kroah-Hartman void *page_addr = hv_context.synic_message_page[cpu]; 72346a97191SGreg Kroah-Hartman struct hv_message *msg = (struct hv_message *)page_addr + 72446a97191SGreg Kroah-Hartman VMBUS_MESSAGE_SINT; 725652594c7SDexuan Cui struct vmbus_channel_message_header *hdr; 726652594c7SDexuan Cui struct vmbus_channel_message_table_entry *entry; 72746a97191SGreg Kroah-Hartman struct onmessage_work_context *ctx; 728cd95aad5SVitaly Kuznetsov u32 message_type = msg->header.message_type; 72946a97191SGreg Kroah-Hartman 730cd95aad5SVitaly Kuznetsov if (message_type == HVMSG_NONE) 73146a97191SGreg Kroah-Hartman /* no msg */ 7327be3e169SVitaly Kuznetsov return; 733652594c7SDexuan Cui 734652594c7SDexuan Cui hdr = (struct vmbus_channel_message_header *)msg->u.payload; 735652594c7SDexuan Cui 736652594c7SDexuan Cui if (hdr->msgtype >= CHANNELMSG_COUNT) { 737652594c7SDexuan Cui WARN_ONCE(1, "unknown msgtype=%d\n", hdr->msgtype); 738652594c7SDexuan Cui goto msg_handled; 739652594c7SDexuan Cui } 740652594c7SDexuan Cui 741652594c7SDexuan Cui entry = &channel_message_table[hdr->msgtype]; 742652594c7SDexuan Cui if (entry->handler_type == VMHT_BLOCKING) { 74346a97191SGreg Kroah-Hartman ctx = kmalloc(sizeof(*ctx), GFP_ATOMIC); 74446a97191SGreg Kroah-Hartman if (ctx == NULL) 7457be3e169SVitaly Kuznetsov return; 746652594c7SDexuan Cui 74746a97191SGreg Kroah-Hartman INIT_WORK(&ctx->work, vmbus_onmessage_work); 74846a97191SGreg Kroah-Hartman memcpy(&ctx->msg, msg, sizeof(*msg)); 74946a97191SGreg Kroah-Hartman 750652594c7SDexuan Cui queue_work(vmbus_connection.work_queue, &ctx->work); 751652594c7SDexuan Cui } else 752652594c7SDexuan Cui entry->message_handler(hdr); 753652594c7SDexuan Cui 754652594c7SDexuan Cui msg_handled: 755cd95aad5SVitaly Kuznetsov vmbus_signal_eom(msg, message_type); 75646a97191SGreg Kroah-Hartman } 75746a97191SGreg Kroah-Hartman 75876d388cdSThomas Gleixner static void vmbus_isr(void) 75946a97191SGreg Kroah-Hartman { 76046a97191SGreg Kroah-Hartman int cpu = smp_processor_id(); 76146a97191SGreg Kroah-Hartman void *page_addr; 76246a97191SGreg Kroah-Hartman struct hv_message *msg; 76346a97191SGreg Kroah-Hartman union hv_synic_event_flags *event; 76446a97191SGreg Kroah-Hartman bool handled = false; 76546a97191SGreg Kroah-Hartman 7665ab05951SK. Y. Srinivasan page_addr = hv_context.synic_event_page[cpu]; 7675ab05951SK. Y. Srinivasan if (page_addr == NULL) 76876d388cdSThomas Gleixner return; 7695ab05951SK. Y. Srinivasan 7705ab05951SK. Y. Srinivasan event = (union hv_synic_event_flags *)page_addr + 7715ab05951SK. Y. Srinivasan VMBUS_MESSAGE_SINT; 77246a97191SGreg Kroah-Hartman /* 77346a97191SGreg Kroah-Hartman * Check for events before checking for messages. This is the order 77446a97191SGreg Kroah-Hartman * in which events and messages are checked in Windows guests on 77546a97191SGreg Kroah-Hartman * Hyper-V, and the Windows team suggested we do the same. 77646a97191SGreg Kroah-Hartman */ 77746a97191SGreg Kroah-Hartman 7786552ecd7SK. Y. Srinivasan if ((vmbus_proto_version == VERSION_WS2008) || 7796552ecd7SK. Y. Srinivasan (vmbus_proto_version == VERSION_WIN7)) { 7806552ecd7SK. Y. Srinivasan 78146a97191SGreg Kroah-Hartman /* Since we are a child, we only need to check bit 0 */ 7826552ecd7SK. Y. Srinivasan if (sync_test_and_clear_bit(0, 7836552ecd7SK. Y. Srinivasan (unsigned long *) &event->flags32[0])) { 78446a97191SGreg Kroah-Hartman handled = true; 78546a97191SGreg Kroah-Hartman } 7866552ecd7SK. Y. Srinivasan } else { 7876552ecd7SK. Y. Srinivasan /* 7886552ecd7SK. Y. Srinivasan * Our host is win8 or above. The signaling mechanism 7896552ecd7SK. Y. Srinivasan * has changed and we can directly look at the event page. 7906552ecd7SK. Y. Srinivasan * If bit n is set then we have an interrup on the channel 7916552ecd7SK. Y. Srinivasan * whose id is n. 7926552ecd7SK. Y. Srinivasan */ 7936552ecd7SK. Y. Srinivasan handled = true; 7946552ecd7SK. Y. Srinivasan } 7956552ecd7SK. Y. Srinivasan 7966552ecd7SK. Y. Srinivasan if (handled) 797db11f12aSK. Y. Srinivasan tasklet_schedule(hv_context.event_dpc[cpu]); 7986552ecd7SK. Y. Srinivasan 79946a97191SGreg Kroah-Hartman 80046a97191SGreg Kroah-Hartman page_addr = hv_context.synic_message_page[cpu]; 80146a97191SGreg Kroah-Hartman msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; 80246a97191SGreg Kroah-Hartman 80346a97191SGreg Kroah-Hartman /* Check if there are actual msgs to be processed */ 8044061ed9eSK. Y. Srinivasan if (msg->header.message_type != HVMSG_NONE) { 8054061ed9eSK. Y. Srinivasan if (msg->header.message_type == HVMSG_TIMER_EXPIRED) 8064061ed9eSK. Y. Srinivasan hv_process_timer_expiration(msg, cpu); 8074061ed9eSK. Y. Srinivasan else 808d81274aaSK. Y. Srinivasan tasklet_schedule(hv_context.msg_dpc[cpu]); 80946a97191SGreg Kroah-Hartman } 8104b44f2d1SStephan Mueller 8114b44f2d1SStephan Mueller add_interrupt_randomness(HYPERVISOR_CALLBACK_VECTOR, 0); 8124061ed9eSK. Y. Srinivasan } 81346a97191SGreg Kroah-Hartman 814e513229bSVitaly Kuznetsov 81546a97191SGreg Kroah-Hartman /* 81646a97191SGreg Kroah-Hartman * vmbus_bus_init -Main vmbus driver initialization routine. 81746a97191SGreg Kroah-Hartman * 81846a97191SGreg Kroah-Hartman * Here, we 81946a97191SGreg Kroah-Hartman * - initialize the vmbus driver context 82046a97191SGreg Kroah-Hartman * - invoke the vmbus hv main init routine 82146a97191SGreg Kroah-Hartman * - retrieve the channel offers 82246a97191SGreg Kroah-Hartman */ 823efc26722SK. Y. Srinivasan static int vmbus_bus_init(void) 82446a97191SGreg Kroah-Hartman { 82546a97191SGreg Kroah-Hartman int ret; 82646a97191SGreg Kroah-Hartman 82746a97191SGreg Kroah-Hartman /* Hypervisor initialization...setup hypercall page..etc */ 82846a97191SGreg Kroah-Hartman ret = hv_init(); 82946a97191SGreg Kroah-Hartman if (ret != 0) { 83046a97191SGreg Kroah-Hartman pr_err("Unable to initialize the hypervisor - 0x%x\n", ret); 83146a97191SGreg Kroah-Hartman return ret; 83246a97191SGreg Kroah-Hartman } 83346a97191SGreg Kroah-Hartman 83446a97191SGreg Kroah-Hartman ret = bus_register(&hv_bus); 83546a97191SGreg Kroah-Hartman if (ret) 83646a97191SGreg Kroah-Hartman goto err_cleanup; 83746a97191SGreg Kroah-Hartman 83876d388cdSThomas Gleixner hv_setup_vmbus_irq(vmbus_isr); 83946a97191SGreg Kroah-Hartman 8402608fb65SJason Wang ret = hv_synic_alloc(); 8412608fb65SJason Wang if (ret) 8422608fb65SJason Wang goto err_alloc; 84346a97191SGreg Kroah-Hartman /* 844302a3c0fSK. Y. Srinivasan * Initialize the per-cpu interrupt state and 84546a97191SGreg Kroah-Hartman * connect to the host. 84646a97191SGreg Kroah-Hartman */ 847302a3c0fSK. Y. Srinivasan on_each_cpu(hv_synic_init, NULL, 1); 84846a97191SGreg Kroah-Hartman ret = vmbus_connect(); 84946a97191SGreg Kroah-Hartman if (ret) 85017efbee8SAndrey Smetanin goto err_connect; 85146a97191SGreg Kroah-Hartman 852f39c4280SVitaly Kuznetsov if (vmbus_proto_version > VERSION_WIN7) 853f39c4280SVitaly Kuznetsov cpu_hotplug_disable(); 85496c1d058SNick Meier 85596c1d058SNick Meier /* 85696c1d058SNick Meier * Only register if the crash MSRs are available 85796c1d058SNick Meier */ 858cc2dd402SDenis V. Lunev if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) { 859510f7aefSVitaly Kuznetsov register_die_notifier(&hyperv_die_block); 86096c1d058SNick Meier atomic_notifier_chain_register(&panic_notifier_list, 86196c1d058SNick Meier &hyperv_panic_block); 86296c1d058SNick Meier } 86396c1d058SNick Meier 86446a97191SGreg Kroah-Hartman vmbus_request_offers(); 86546a97191SGreg Kroah-Hartman 86646a97191SGreg Kroah-Hartman return 0; 86746a97191SGreg Kroah-Hartman 86817efbee8SAndrey Smetanin err_connect: 86917efbee8SAndrey Smetanin on_each_cpu(hv_synic_cleanup, NULL, 1); 8702608fb65SJason Wang err_alloc: 8712608fb65SJason Wang hv_synic_free(); 87276d388cdSThomas Gleixner hv_remove_vmbus_irq(); 87346a97191SGreg Kroah-Hartman 87446a97191SGreg Kroah-Hartman bus_unregister(&hv_bus); 87546a97191SGreg Kroah-Hartman 87646a97191SGreg Kroah-Hartman err_cleanup: 877a9f61ca7SVitaly Kuznetsov hv_cleanup(false); 87846a97191SGreg Kroah-Hartman 87946a97191SGreg Kroah-Hartman return ret; 88046a97191SGreg Kroah-Hartman } 88146a97191SGreg Kroah-Hartman 88246a97191SGreg Kroah-Hartman /** 88335464483SJake Oshins * __vmbus_child_driver_register() - Register a vmbus's driver 88435464483SJake Oshins * @hv_driver: Pointer to driver structure you want to register 88546a97191SGreg Kroah-Hartman * @owner: owner module of the drv 88646a97191SGreg Kroah-Hartman * @mod_name: module name string 88746a97191SGreg Kroah-Hartman * 88846a97191SGreg Kroah-Hartman * Registers the given driver with Linux through the 'driver_register()' call 88946a97191SGreg Kroah-Hartman * and sets up the hyper-v vmbus handling for this driver. 89046a97191SGreg Kroah-Hartman * It will return the state of the 'driver_register()' call. 89146a97191SGreg Kroah-Hartman * 89246a97191SGreg Kroah-Hartman */ 89346a97191SGreg Kroah-Hartman int __vmbus_driver_register(struct hv_driver *hv_driver, struct module *owner, const char *mod_name) 89446a97191SGreg Kroah-Hartman { 89546a97191SGreg Kroah-Hartman int ret; 89646a97191SGreg Kroah-Hartman 89746a97191SGreg Kroah-Hartman pr_info("registering driver %s\n", hv_driver->name); 89846a97191SGreg Kroah-Hartman 899cf6a2eacSK. Y. Srinivasan ret = vmbus_exists(); 900cf6a2eacSK. Y. Srinivasan if (ret < 0) 901cf6a2eacSK. Y. Srinivasan return ret; 902cf6a2eacSK. Y. Srinivasan 90346a97191SGreg Kroah-Hartman hv_driver->driver.name = hv_driver->name; 90446a97191SGreg Kroah-Hartman hv_driver->driver.owner = owner; 90546a97191SGreg Kroah-Hartman hv_driver->driver.mod_name = mod_name; 90646a97191SGreg Kroah-Hartman hv_driver->driver.bus = &hv_bus; 90746a97191SGreg Kroah-Hartman 90846a97191SGreg Kroah-Hartman ret = driver_register(&hv_driver->driver); 90946a97191SGreg Kroah-Hartman 91046a97191SGreg Kroah-Hartman return ret; 91146a97191SGreg Kroah-Hartman } 91246a97191SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(__vmbus_driver_register); 91346a97191SGreg Kroah-Hartman 91446a97191SGreg Kroah-Hartman /** 91546a97191SGreg Kroah-Hartman * vmbus_driver_unregister() - Unregister a vmbus's driver 91635464483SJake Oshins * @hv_driver: Pointer to driver structure you want to 91735464483SJake Oshins * un-register 91846a97191SGreg Kroah-Hartman * 91946a97191SGreg Kroah-Hartman * Un-register the given driver that was previous registered with a call to 92046a97191SGreg Kroah-Hartman * vmbus_driver_register() 92146a97191SGreg Kroah-Hartman */ 92246a97191SGreg Kroah-Hartman void vmbus_driver_unregister(struct hv_driver *hv_driver) 92346a97191SGreg Kroah-Hartman { 92446a97191SGreg Kroah-Hartman pr_info("unregistering driver %s\n", hv_driver->name); 92546a97191SGreg Kroah-Hartman 926cf6a2eacSK. Y. Srinivasan if (!vmbus_exists()) 92746a97191SGreg Kroah-Hartman driver_unregister(&hv_driver->driver); 92846a97191SGreg Kroah-Hartman } 92946a97191SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(vmbus_driver_unregister); 93046a97191SGreg Kroah-Hartman 93146a97191SGreg Kroah-Hartman /* 93246a97191SGreg Kroah-Hartman * vmbus_device_create - Creates and registers a new child device 93346a97191SGreg Kroah-Hartman * on the vmbus. 93446a97191SGreg Kroah-Hartman */ 9351b9d48f2Sstephen hemminger struct hv_device *vmbus_device_create(const uuid_le *type, 9361b9d48f2Sstephen hemminger const uuid_le *instance, 93746a97191SGreg Kroah-Hartman struct vmbus_channel *channel) 93846a97191SGreg Kroah-Hartman { 93946a97191SGreg Kroah-Hartman struct hv_device *child_device_obj; 94046a97191SGreg Kroah-Hartman 94146a97191SGreg Kroah-Hartman child_device_obj = kzalloc(sizeof(struct hv_device), GFP_KERNEL); 94246a97191SGreg Kroah-Hartman if (!child_device_obj) { 94346a97191SGreg Kroah-Hartman pr_err("Unable to allocate device object for child device\n"); 94446a97191SGreg Kroah-Hartman return NULL; 94546a97191SGreg Kroah-Hartman } 94646a97191SGreg Kroah-Hartman 94746a97191SGreg Kroah-Hartman child_device_obj->channel = channel; 94846a97191SGreg Kroah-Hartman memcpy(&child_device_obj->dev_type, type, sizeof(uuid_le)); 94946a97191SGreg Kroah-Hartman memcpy(&child_device_obj->dev_instance, instance, 95046a97191SGreg Kroah-Hartman sizeof(uuid_le)); 9517047f17dSK. Y. Srinivasan child_device_obj->vendor_id = 0x1414; /* MSFT vendor ID */ 95246a97191SGreg Kroah-Hartman 95346a97191SGreg Kroah-Hartman 95446a97191SGreg Kroah-Hartman return child_device_obj; 95546a97191SGreg Kroah-Hartman } 95646a97191SGreg Kroah-Hartman 95746a97191SGreg Kroah-Hartman /* 95846a97191SGreg Kroah-Hartman * vmbus_device_register - Register the child device 95946a97191SGreg Kroah-Hartman */ 96046a97191SGreg Kroah-Hartman int vmbus_device_register(struct hv_device *child_device_obj) 96146a97191SGreg Kroah-Hartman { 96246a97191SGreg Kroah-Hartman int ret = 0; 96346a97191SGreg Kroah-Hartman 964*f6b2db08SStephen Hemminger dev_set_name(&child_device_obj->device, "%pUl", 965b294809dSVitaly Kuznetsov child_device_obj->channel->offermsg.offer.if_instance.b); 96646a97191SGreg Kroah-Hartman 96746a97191SGreg Kroah-Hartman child_device_obj->device.bus = &hv_bus; 96846a97191SGreg Kroah-Hartman child_device_obj->device.parent = &hv_acpi_dev->dev; 96946a97191SGreg Kroah-Hartman child_device_obj->device.release = vmbus_device_release; 97046a97191SGreg Kroah-Hartman 97146a97191SGreg Kroah-Hartman /* 97246a97191SGreg Kroah-Hartman * Register with the LDM. This will kick off the driver/device 97346a97191SGreg Kroah-Hartman * binding...which will eventually call vmbus_match() and vmbus_probe() 97446a97191SGreg Kroah-Hartman */ 97546a97191SGreg Kroah-Hartman ret = device_register(&child_device_obj->device); 97646a97191SGreg Kroah-Hartman 97746a97191SGreg Kroah-Hartman if (ret) 97846a97191SGreg Kroah-Hartman pr_err("Unable to register child device\n"); 97946a97191SGreg Kroah-Hartman else 98084672369SFernando Soto pr_debug("child device %s registered\n", 98146a97191SGreg Kroah-Hartman dev_name(&child_device_obj->device)); 98246a97191SGreg Kroah-Hartman 98346a97191SGreg Kroah-Hartman return ret; 98446a97191SGreg Kroah-Hartman } 98546a97191SGreg Kroah-Hartman 98646a97191SGreg Kroah-Hartman /* 98746a97191SGreg Kroah-Hartman * vmbus_device_unregister - Remove the specified child device 98846a97191SGreg Kroah-Hartman * from the vmbus. 98946a97191SGreg Kroah-Hartman */ 99046a97191SGreg Kroah-Hartman void vmbus_device_unregister(struct hv_device *device_obj) 99146a97191SGreg Kroah-Hartman { 99284672369SFernando Soto pr_debug("child device %s unregistered\n", 99384672369SFernando Soto dev_name(&device_obj->device)); 99484672369SFernando Soto 99546a97191SGreg Kroah-Hartman /* 99646a97191SGreg Kroah-Hartman * Kick off the process of unregistering the device. 99746a97191SGreg Kroah-Hartman * This will call vmbus_remove() and eventually vmbus_device_release() 99846a97191SGreg Kroah-Hartman */ 99946a97191SGreg Kroah-Hartman device_unregister(&device_obj->device); 100046a97191SGreg Kroah-Hartman } 100146a97191SGreg Kroah-Hartman 100246a97191SGreg Kroah-Hartman 100346a97191SGreg Kroah-Hartman /* 10047f163a6fSJake Oshins * VMBUS is an acpi enumerated device. Get the information we 100590f34535SK. Y. Srinivasan * need from DSDT. 100646a97191SGreg Kroah-Hartman */ 10077f163a6fSJake Oshins #define VTPM_BASE_ADDRESS 0xfed40000 100890f34535SK. Y. Srinivasan static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx) 100946a97191SGreg Kroah-Hartman { 10107f163a6fSJake Oshins resource_size_t start = 0; 10117f163a6fSJake Oshins resource_size_t end = 0; 10127f163a6fSJake Oshins struct resource *new_res; 10137f163a6fSJake Oshins struct resource **old_res = &hyperv_mmio; 10147f163a6fSJake Oshins struct resource **prev_res = NULL; 10157f163a6fSJake Oshins 101690f34535SK. Y. Srinivasan switch (res->type) { 10177f163a6fSJake Oshins 10187f163a6fSJake Oshins /* 10197f163a6fSJake Oshins * "Address" descriptors are for bus windows. Ignore 10207f163a6fSJake Oshins * "memory" descriptors, which are for registers on 10217f163a6fSJake Oshins * devices. 10227f163a6fSJake Oshins */ 10237f163a6fSJake Oshins case ACPI_RESOURCE_TYPE_ADDRESS32: 10247f163a6fSJake Oshins start = res->data.address32.address.minimum; 10257f163a6fSJake Oshins end = res->data.address32.address.maximum; 10264eb923f8SGerd Hoffmann break; 102746a97191SGreg Kroah-Hartman 102890f34535SK. Y. Srinivasan case ACPI_RESOURCE_TYPE_ADDRESS64: 10297f163a6fSJake Oshins start = res->data.address64.address.minimum; 10307f163a6fSJake Oshins end = res->data.address64.address.maximum; 10317f163a6fSJake Oshins break; 10327f163a6fSJake Oshins 10337f163a6fSJake Oshins default: 10347f163a6fSJake Oshins /* Unused resource type */ 10357f163a6fSJake Oshins return AE_OK; 10367f163a6fSJake Oshins 10377f163a6fSJake Oshins } 10387f163a6fSJake Oshins /* 10397f163a6fSJake Oshins * Ignore ranges that are below 1MB, as they're not 10407f163a6fSJake Oshins * necessary or useful here. 10417f163a6fSJake Oshins */ 10427f163a6fSJake Oshins if (end < 0x100000) 10437f163a6fSJake Oshins return AE_OK; 10447f163a6fSJake Oshins 10457f163a6fSJake Oshins new_res = kzalloc(sizeof(*new_res), GFP_ATOMIC); 10467f163a6fSJake Oshins if (!new_res) 10477f163a6fSJake Oshins return AE_NO_MEMORY; 10487f163a6fSJake Oshins 10497f163a6fSJake Oshins /* If this range overlaps the virtual TPM, truncate it. */ 10507f163a6fSJake Oshins if (end > VTPM_BASE_ADDRESS && start < VTPM_BASE_ADDRESS) 10517f163a6fSJake Oshins end = VTPM_BASE_ADDRESS; 10527f163a6fSJake Oshins 10537f163a6fSJake Oshins new_res->name = "hyperv mmio"; 10547f163a6fSJake Oshins new_res->flags = IORESOURCE_MEM; 10557f163a6fSJake Oshins new_res->start = start; 10567f163a6fSJake Oshins new_res->end = end; 10577f163a6fSJake Oshins 105840f26f31SJake Oshins /* 105940f26f31SJake Oshins * If two ranges are adjacent, merge them. 106040f26f31SJake Oshins */ 10617f163a6fSJake Oshins do { 10627f163a6fSJake Oshins if (!*old_res) { 10637f163a6fSJake Oshins *old_res = new_res; 10644eb923f8SGerd Hoffmann break; 106546a97191SGreg Kroah-Hartman } 106646a97191SGreg Kroah-Hartman 106740f26f31SJake Oshins if (((*old_res)->end + 1) == new_res->start) { 106840f26f31SJake Oshins (*old_res)->end = new_res->end; 106940f26f31SJake Oshins kfree(new_res); 107040f26f31SJake Oshins break; 107140f26f31SJake Oshins } 107240f26f31SJake Oshins 107340f26f31SJake Oshins if ((*old_res)->start == new_res->end + 1) { 107440f26f31SJake Oshins (*old_res)->start = new_res->start; 107540f26f31SJake Oshins kfree(new_res); 107640f26f31SJake Oshins break; 107740f26f31SJake Oshins } 107840f26f31SJake Oshins 107923a06831SJake Oshins if ((*old_res)->start > new_res->end) { 10807f163a6fSJake Oshins new_res->sibling = *old_res; 10817f163a6fSJake Oshins if (prev_res) 10827f163a6fSJake Oshins (*prev_res)->sibling = new_res; 10837f163a6fSJake Oshins *old_res = new_res; 10847f163a6fSJake Oshins break; 10857f163a6fSJake Oshins } 10867f163a6fSJake Oshins 10877f163a6fSJake Oshins prev_res = old_res; 10887f163a6fSJake Oshins old_res = &(*old_res)->sibling; 10897f163a6fSJake Oshins 10907f163a6fSJake Oshins } while (1); 10917f163a6fSJake Oshins 109246a97191SGreg Kroah-Hartman return AE_OK; 109346a97191SGreg Kroah-Hartman } 109446a97191SGreg Kroah-Hartman 10957f163a6fSJake Oshins static int vmbus_acpi_remove(struct acpi_device *device) 10967f163a6fSJake Oshins { 10977f163a6fSJake Oshins struct resource *cur_res; 10987f163a6fSJake Oshins struct resource *next_res; 10997f163a6fSJake Oshins 11007f163a6fSJake Oshins if (hyperv_mmio) { 11016d146aefSJake Oshins if (fb_mmio) { 11026d146aefSJake Oshins __release_region(hyperv_mmio, fb_mmio->start, 11036d146aefSJake Oshins resource_size(fb_mmio)); 11046d146aefSJake Oshins fb_mmio = NULL; 11056d146aefSJake Oshins } 11066d146aefSJake Oshins 11077f163a6fSJake Oshins for (cur_res = hyperv_mmio; cur_res; cur_res = next_res) { 11087f163a6fSJake Oshins next_res = cur_res->sibling; 11097f163a6fSJake Oshins kfree(cur_res); 11107f163a6fSJake Oshins } 11117f163a6fSJake Oshins } 11127f163a6fSJake Oshins 11137f163a6fSJake Oshins return 0; 11147f163a6fSJake Oshins } 11157f163a6fSJake Oshins 11166d146aefSJake Oshins static void vmbus_reserve_fb(void) 11176d146aefSJake Oshins { 11186d146aefSJake Oshins int size; 11196d146aefSJake Oshins /* 11206d146aefSJake Oshins * Make a claim for the frame buffer in the resource tree under the 11216d146aefSJake Oshins * first node, which will be the one below 4GB. The length seems to 11226d146aefSJake Oshins * be underreported, particularly in a Generation 1 VM. So start out 11236d146aefSJake Oshins * reserving a larger area and make it smaller until it succeeds. 11246d146aefSJake Oshins */ 11256d146aefSJake Oshins 11266d146aefSJake Oshins if (screen_info.lfb_base) { 11276d146aefSJake Oshins if (efi_enabled(EFI_BOOT)) 11286d146aefSJake Oshins size = max_t(__u32, screen_info.lfb_size, 0x800000); 11296d146aefSJake Oshins else 11306d146aefSJake Oshins size = max_t(__u32, screen_info.lfb_size, 0x4000000); 11316d146aefSJake Oshins 11326d146aefSJake Oshins for (; !fb_mmio && (size >= 0x100000); size >>= 1) { 11336d146aefSJake Oshins fb_mmio = __request_region(hyperv_mmio, 11346d146aefSJake Oshins screen_info.lfb_base, size, 11356d146aefSJake Oshins fb_mmio_name, 0); 11366d146aefSJake Oshins } 11376d146aefSJake Oshins } 11386d146aefSJake Oshins } 11396d146aefSJake Oshins 114035464483SJake Oshins /** 114135464483SJake Oshins * vmbus_allocate_mmio() - Pick a memory-mapped I/O range. 114235464483SJake Oshins * @new: If successful, supplied a pointer to the 114335464483SJake Oshins * allocated MMIO space. 114435464483SJake Oshins * @device_obj: Identifies the caller 114535464483SJake Oshins * @min: Minimum guest physical address of the 114635464483SJake Oshins * allocation 114735464483SJake Oshins * @max: Maximum guest physical address 114835464483SJake Oshins * @size: Size of the range to be allocated 114935464483SJake Oshins * @align: Alignment of the range to be allocated 115035464483SJake Oshins * @fb_overlap_ok: Whether this allocation can be allowed 115135464483SJake Oshins * to overlap the video frame buffer. 115235464483SJake Oshins * 115335464483SJake Oshins * This function walks the resources granted to VMBus by the 115435464483SJake Oshins * _CRS object in the ACPI namespace underneath the parent 115535464483SJake Oshins * "bridge" whether that's a root PCI bus in the Generation 1 115635464483SJake Oshins * case or a Module Device in the Generation 2 case. It then 115735464483SJake Oshins * attempts to allocate from the global MMIO pool in a way that 115835464483SJake Oshins * matches the constraints supplied in these parameters and by 115935464483SJake Oshins * that _CRS. 116035464483SJake Oshins * 116135464483SJake Oshins * Return: 0 on success, -errno on failure 116235464483SJake Oshins */ 116335464483SJake Oshins int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj, 116435464483SJake Oshins resource_size_t min, resource_size_t max, 116535464483SJake Oshins resource_size_t size, resource_size_t align, 116635464483SJake Oshins bool fb_overlap_ok) 116735464483SJake Oshins { 1168be000f93SJake Oshins struct resource *iter, *shadow; 1169ea37a6b8SJake Oshins resource_size_t range_min, range_max, start; 117035464483SJake Oshins const char *dev_n = dev_name(&device_obj->device); 1171ea37a6b8SJake Oshins int retval; 1172e16dad6bSJake Oshins 1173e16dad6bSJake Oshins retval = -ENXIO; 1174e16dad6bSJake Oshins down(&hyperv_mmio_lock); 117535464483SJake Oshins 1176ea37a6b8SJake Oshins /* 1177ea37a6b8SJake Oshins * If overlaps with frame buffers are allowed, then first attempt to 1178ea37a6b8SJake Oshins * make the allocation from within the reserved region. Because it 1179ea37a6b8SJake Oshins * is already reserved, no shadow allocation is necessary. 1180ea37a6b8SJake Oshins */ 1181ea37a6b8SJake Oshins if (fb_overlap_ok && fb_mmio && !(min > fb_mmio->end) && 1182ea37a6b8SJake Oshins !(max < fb_mmio->start)) { 1183ea37a6b8SJake Oshins 1184ea37a6b8SJake Oshins range_min = fb_mmio->start; 1185ea37a6b8SJake Oshins range_max = fb_mmio->end; 1186ea37a6b8SJake Oshins start = (range_min + align - 1) & ~(align - 1); 1187ea37a6b8SJake Oshins for (; start + size - 1 <= range_max; start += align) { 1188ea37a6b8SJake Oshins *new = request_mem_region_exclusive(start, size, dev_n); 1189ea37a6b8SJake Oshins if (*new) { 1190ea37a6b8SJake Oshins retval = 0; 1191ea37a6b8SJake Oshins goto exit; 1192ea37a6b8SJake Oshins } 1193ea37a6b8SJake Oshins } 1194ea37a6b8SJake Oshins } 1195ea37a6b8SJake Oshins 119635464483SJake Oshins for (iter = hyperv_mmio; iter; iter = iter->sibling) { 119735464483SJake Oshins if ((iter->start >= max) || (iter->end <= min)) 119835464483SJake Oshins continue; 119935464483SJake Oshins 120035464483SJake Oshins range_min = iter->start; 120135464483SJake Oshins range_max = iter->end; 1202ea37a6b8SJake Oshins start = (range_min + align - 1) & ~(align - 1); 1203ea37a6b8SJake Oshins for (; start + size - 1 <= range_max; start += align) { 1204ea37a6b8SJake Oshins shadow = __request_region(iter, start, size, NULL, 1205be000f93SJake Oshins IORESOURCE_BUSY); 1206be000f93SJake Oshins if (!shadow) 1207be000f93SJake Oshins continue; 1208be000f93SJake Oshins 1209ea37a6b8SJake Oshins *new = request_mem_region_exclusive(start, size, dev_n); 1210e16dad6bSJake Oshins if (*new) { 1211be000f93SJake Oshins shadow->name = (char *)*new; 1212e16dad6bSJake Oshins retval = 0; 1213e16dad6bSJake Oshins goto exit; 1214e16dad6bSJake Oshins } 1215be000f93SJake Oshins 1216be000f93SJake Oshins __release_region(iter, start, size); 121735464483SJake Oshins } 121835464483SJake Oshins } 121935464483SJake Oshins 1220e16dad6bSJake Oshins exit: 1221e16dad6bSJake Oshins up(&hyperv_mmio_lock); 1222e16dad6bSJake Oshins return retval; 122335464483SJake Oshins } 122435464483SJake Oshins EXPORT_SYMBOL_GPL(vmbus_allocate_mmio); 122535464483SJake Oshins 1226619848bdSJake Oshins /** 122797fb77dcSJake Oshins * vmbus_free_mmio() - Free a memory-mapped I/O range. 122897fb77dcSJake Oshins * @start: Base address of region to release. 122997fb77dcSJake Oshins * @size: Size of the range to be allocated 123097fb77dcSJake Oshins * 123197fb77dcSJake Oshins * This function releases anything requested by 123297fb77dcSJake Oshins * vmbus_mmio_allocate(). 123397fb77dcSJake Oshins */ 123497fb77dcSJake Oshins void vmbus_free_mmio(resource_size_t start, resource_size_t size) 123597fb77dcSJake Oshins { 1236be000f93SJake Oshins struct resource *iter; 1237be000f93SJake Oshins 1238be000f93SJake Oshins down(&hyperv_mmio_lock); 1239be000f93SJake Oshins for (iter = hyperv_mmio; iter; iter = iter->sibling) { 1240be000f93SJake Oshins if ((iter->start >= start + size) || (iter->end <= start)) 1241be000f93SJake Oshins continue; 1242be000f93SJake Oshins 1243be000f93SJake Oshins __release_region(iter, start, size); 1244be000f93SJake Oshins } 124597fb77dcSJake Oshins release_mem_region(start, size); 1246be000f93SJake Oshins up(&hyperv_mmio_lock); 124797fb77dcSJake Oshins 124897fb77dcSJake Oshins } 124997fb77dcSJake Oshins EXPORT_SYMBOL_GPL(vmbus_free_mmio); 125097fb77dcSJake Oshins 125197fb77dcSJake Oshins /** 1252619848bdSJake Oshins * vmbus_cpu_number_to_vp_number() - Map CPU to VP. 1253619848bdSJake Oshins * @cpu_number: CPU number in Linux terms 1254619848bdSJake Oshins * 1255619848bdSJake Oshins * This function returns the mapping between the Linux processor 1256619848bdSJake Oshins * number and the hypervisor's virtual processor number, useful 1257619848bdSJake Oshins * in making hypercalls and such that talk about specific 1258619848bdSJake Oshins * processors. 1259619848bdSJake Oshins * 1260619848bdSJake Oshins * Return: Virtual processor number in Hyper-V terms 1261619848bdSJake Oshins */ 1262619848bdSJake Oshins int vmbus_cpu_number_to_vp_number(int cpu_number) 1263619848bdSJake Oshins { 1264619848bdSJake Oshins return hv_context.vp_index[cpu_number]; 1265619848bdSJake Oshins } 1266619848bdSJake Oshins EXPORT_SYMBOL_GPL(vmbus_cpu_number_to_vp_number); 1267619848bdSJake Oshins 126846a97191SGreg Kroah-Hartman static int vmbus_acpi_add(struct acpi_device *device) 126946a97191SGreg Kroah-Hartman { 127046a97191SGreg Kroah-Hartman acpi_status result; 127190f34535SK. Y. Srinivasan int ret_val = -ENODEV; 12727f163a6fSJake Oshins struct acpi_device *ancestor; 127346a97191SGreg Kroah-Hartman 127446a97191SGreg Kroah-Hartman hv_acpi_dev = device; 127546a97191SGreg Kroah-Hartman 127646a97191SGreg Kroah-Hartman result = acpi_walk_resources(device->handle, METHOD_NAME__CRS, 127790f34535SK. Y. Srinivasan vmbus_walk_resources, NULL); 127846a97191SGreg Kroah-Hartman 127990f34535SK. Y. Srinivasan if (ACPI_FAILURE(result)) 128090f34535SK. Y. Srinivasan goto acpi_walk_err; 128190f34535SK. Y. Srinivasan /* 12827f163a6fSJake Oshins * Some ancestor of the vmbus acpi device (Gen1 or Gen2 12837f163a6fSJake Oshins * firmware) is the VMOD that has the mmio ranges. Get that. 128490f34535SK. Y. Srinivasan */ 12857f163a6fSJake Oshins for (ancestor = device->parent; ancestor; ancestor = ancestor->parent) { 12867f163a6fSJake Oshins result = acpi_walk_resources(ancestor->handle, METHOD_NAME__CRS, 128790f34535SK. Y. Srinivasan vmbus_walk_resources, NULL); 128890f34535SK. Y. Srinivasan 128990f34535SK. Y. Srinivasan if (ACPI_FAILURE(result)) 12907f163a6fSJake Oshins continue; 12916d146aefSJake Oshins if (hyperv_mmio) { 12926d146aefSJake Oshins vmbus_reserve_fb(); 12937f163a6fSJake Oshins break; 129446a97191SGreg Kroah-Hartman } 12956d146aefSJake Oshins } 129690f34535SK. Y. Srinivasan ret_val = 0; 129790f34535SK. Y. Srinivasan 129890f34535SK. Y. Srinivasan acpi_walk_err: 129946a97191SGreg Kroah-Hartman complete(&probe_event); 13007f163a6fSJake Oshins if (ret_val) 13017f163a6fSJake Oshins vmbus_acpi_remove(device); 130290f34535SK. Y. Srinivasan return ret_val; 130346a97191SGreg Kroah-Hartman } 130446a97191SGreg Kroah-Hartman 130546a97191SGreg Kroah-Hartman static const struct acpi_device_id vmbus_acpi_device_ids[] = { 130646a97191SGreg Kroah-Hartman {"VMBUS", 0}, 130746a97191SGreg Kroah-Hartman {"VMBus", 0}, 130846a97191SGreg Kroah-Hartman {"", 0}, 130946a97191SGreg Kroah-Hartman }; 131046a97191SGreg Kroah-Hartman MODULE_DEVICE_TABLE(acpi, vmbus_acpi_device_ids); 131146a97191SGreg Kroah-Hartman 131246a97191SGreg Kroah-Hartman static struct acpi_driver vmbus_acpi_driver = { 131346a97191SGreg Kroah-Hartman .name = "vmbus", 131446a97191SGreg Kroah-Hartman .ids = vmbus_acpi_device_ids, 131546a97191SGreg Kroah-Hartman .ops = { 131646a97191SGreg Kroah-Hartman .add = vmbus_acpi_add, 1317e4ecb41cSVitaly Kuznetsov .remove = vmbus_acpi_remove, 131846a97191SGreg Kroah-Hartman }, 131946a97191SGreg Kroah-Hartman }; 132046a97191SGreg Kroah-Hartman 13212517281dSVitaly Kuznetsov static void hv_kexec_handler(void) 13222517281dSVitaly Kuznetsov { 13232517281dSVitaly Kuznetsov int cpu; 13242517281dSVitaly Kuznetsov 13252517281dSVitaly Kuznetsov hv_synic_clockevents_cleanup(); 132675ff3a8aSVitaly Kuznetsov vmbus_initiate_unload(false); 13272517281dSVitaly Kuznetsov for_each_online_cpu(cpu) 13282517281dSVitaly Kuznetsov smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1); 1329a9f61ca7SVitaly Kuznetsov hv_cleanup(false); 13302517281dSVitaly Kuznetsov }; 13312517281dSVitaly Kuznetsov 1332b4370df2SVitaly Kuznetsov static void hv_crash_handler(struct pt_regs *regs) 1333b4370df2SVitaly Kuznetsov { 133475ff3a8aSVitaly Kuznetsov vmbus_initiate_unload(true); 1335b4370df2SVitaly Kuznetsov /* 1336b4370df2SVitaly Kuznetsov * In crash handler we can't schedule synic cleanup for all CPUs, 1337b4370df2SVitaly Kuznetsov * doing the cleanup for current CPU only. This should be sufficient 1338b4370df2SVitaly Kuznetsov * for kdump. 1339b4370df2SVitaly Kuznetsov */ 1340b4370df2SVitaly Kuznetsov hv_synic_cleanup(NULL); 1341a9f61ca7SVitaly Kuznetsov hv_cleanup(true); 1342b4370df2SVitaly Kuznetsov }; 1343b4370df2SVitaly Kuznetsov 134446a97191SGreg Kroah-Hartman static int __init hv_acpi_init(void) 134546a97191SGreg Kroah-Hartman { 134646a97191SGreg Kroah-Hartman int ret, t; 134746a97191SGreg Kroah-Hartman 13481f94ea81SJason Wang if (x86_hyper != &x86_hyper_ms_hyperv) 13490592969eSJason Wang return -ENODEV; 13500592969eSJason Wang 135146a97191SGreg Kroah-Hartman init_completion(&probe_event); 135246a97191SGreg Kroah-Hartman 135346a97191SGreg Kroah-Hartman /* 1354efc26722SK. Y. Srinivasan * Get ACPI resources first. 135546a97191SGreg Kroah-Hartman */ 135646a97191SGreg Kroah-Hartman ret = acpi_bus_register_driver(&vmbus_acpi_driver); 135746a97191SGreg Kroah-Hartman 135846a97191SGreg Kroah-Hartman if (ret) 135946a97191SGreg Kroah-Hartman return ret; 136046a97191SGreg Kroah-Hartman 136146a97191SGreg Kroah-Hartman t = wait_for_completion_timeout(&probe_event, 5*HZ); 136246a97191SGreg Kroah-Hartman if (t == 0) { 136346a97191SGreg Kroah-Hartman ret = -ETIMEDOUT; 136446a97191SGreg Kroah-Hartman goto cleanup; 136546a97191SGreg Kroah-Hartman } 136646a97191SGreg Kroah-Hartman 1367efc26722SK. Y. Srinivasan ret = vmbus_bus_init(); 136846a97191SGreg Kroah-Hartman if (ret) 136946a97191SGreg Kroah-Hartman goto cleanup; 137046a97191SGreg Kroah-Hartman 13712517281dSVitaly Kuznetsov hv_setup_kexec_handler(hv_kexec_handler); 1372b4370df2SVitaly Kuznetsov hv_setup_crash_handler(hv_crash_handler); 13732517281dSVitaly Kuznetsov 137446a97191SGreg Kroah-Hartman return 0; 137546a97191SGreg Kroah-Hartman 137646a97191SGreg Kroah-Hartman cleanup: 137746a97191SGreg Kroah-Hartman acpi_bus_unregister_driver(&vmbus_acpi_driver); 1378cf6a2eacSK. Y. Srinivasan hv_acpi_dev = NULL; 137946a97191SGreg Kroah-Hartman return ret; 138046a97191SGreg Kroah-Hartman } 138146a97191SGreg Kroah-Hartman 138293e5bd06SK. Y. Srinivasan static void __exit vmbus_exit(void) 138393e5bd06SK. Y. Srinivasan { 1384e72e7ac5SVitaly Kuznetsov int cpu; 1385e72e7ac5SVitaly Kuznetsov 13862517281dSVitaly Kuznetsov hv_remove_kexec_handler(); 1387b4370df2SVitaly Kuznetsov hv_remove_crash_handler(); 138809a19628SVitaly Kuznetsov vmbus_connection.conn_state = DISCONNECTED; 1389e086748cSVitaly Kuznetsov hv_synic_clockevents_cleanup(); 13902db84effSK. Y. Srinivasan vmbus_disconnect(); 139176d388cdSThomas Gleixner hv_remove_vmbus_irq(); 1392d81274aaSK. Y. Srinivasan for_each_online_cpu(cpu) 1393d81274aaSK. Y. Srinivasan tasklet_kill(hv_context.msg_dpc[cpu]); 139493e5bd06SK. Y. Srinivasan vmbus_free_channels(); 1395cc2dd402SDenis V. Lunev if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) { 1396510f7aefSVitaly Kuznetsov unregister_die_notifier(&hyperv_die_block); 1397096c605fSVitaly Kuznetsov atomic_notifier_chain_unregister(&panic_notifier_list, 1398096c605fSVitaly Kuznetsov &hyperv_panic_block); 1399096c605fSVitaly Kuznetsov } 140093e5bd06SK. Y. Srinivasan bus_unregister(&hv_bus); 1401a9f61ca7SVitaly Kuznetsov hv_cleanup(false); 14021959a28eSVitaly Kuznetsov for_each_online_cpu(cpu) { 14031959a28eSVitaly Kuznetsov tasklet_kill(hv_context.event_dpc[cpu]); 1404e72e7ac5SVitaly Kuznetsov smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1); 14051959a28eSVitaly Kuznetsov } 140606210b42SVitaly Kuznetsov hv_synic_free(); 140793e5bd06SK. Y. Srinivasan acpi_bus_unregister_driver(&vmbus_acpi_driver); 1408f39c4280SVitaly Kuznetsov if (vmbus_proto_version > VERSION_WIN7) 1409f39c4280SVitaly Kuznetsov cpu_hotplug_enable(); 141093e5bd06SK. Y. Srinivasan } 141193e5bd06SK. Y. Srinivasan 141246a97191SGreg Kroah-Hartman 141346a97191SGreg Kroah-Hartman MODULE_LICENSE("GPL"); 141446a97191SGreg Kroah-Hartman 141543d4e119SK. Y. Srinivasan subsys_initcall(hv_acpi_init); 141693e5bd06SK. Y. Srinivasan module_exit(vmbus_exit); 1417