18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2009, Microsoft Corporation. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: 68c2ecf20Sopenharmony_ci * Haiyang Zhang <haiyangz@microsoft.com> 78c2ecf20Sopenharmony_ci * Hank Janssen <hjanssen@microsoft.com> 88c2ecf20Sopenharmony_ci * K. Y. Srinivasan <kys@microsoft.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/device.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/sysctl.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/acpi.h> 198c2ecf20Sopenharmony_ci#include <linux/completion.h> 208c2ecf20Sopenharmony_ci#include <linux/hyperv.h> 218c2ecf20Sopenharmony_ci#include <linux/kernel_stat.h> 228c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 238c2ecf20Sopenharmony_ci#include <linux/cpu.h> 248c2ecf20Sopenharmony_ci#include <linux/sched/task_stack.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <linux/delay.h> 278c2ecf20Sopenharmony_ci#include <linux/notifier.h> 288c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 298c2ecf20Sopenharmony_ci#include <linux/screen_info.h> 308c2ecf20Sopenharmony_ci#include <linux/kdebug.h> 318c2ecf20Sopenharmony_ci#include <linux/efi.h> 328c2ecf20Sopenharmony_ci#include <linux/random.h> 338c2ecf20Sopenharmony_ci#include <linux/kernel.h> 348c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h> 358c2ecf20Sopenharmony_ci#include <clocksource/hyperv_timer.h> 368c2ecf20Sopenharmony_ci#include "hyperv_vmbus.h" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistruct vmbus_dynid { 398c2ecf20Sopenharmony_ci struct list_head node; 408c2ecf20Sopenharmony_ci struct hv_vmbus_device_id id; 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic struct acpi_device *hv_acpi_dev; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic struct completion probe_event; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int hyperv_cpuhp_online; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic void *hv_panic_page; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* Values parsed from ACPI DSDT */ 528c2ecf20Sopenharmony_cistatic int vmbus_irq; 538c2ecf20Sopenharmony_ciint vmbus_interrupt; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* 568c2ecf20Sopenharmony_ci * Boolean to control whether to report panic messages over Hyper-V. 578c2ecf20Sopenharmony_ci * 588c2ecf20Sopenharmony_ci * It can be set via /proc/sys/kernel/hyperv/record_panic_msg 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_cistatic int sysctl_record_panic_msg = 1; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int hyperv_report_reg(void) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci return !sysctl_record_panic_msg || !hv_panic_page; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int hyperv_panic_event(struct notifier_block *nb, unsigned long val, 688c2ecf20Sopenharmony_ci void *args) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct pt_regs *regs; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci vmbus_initiate_unload(true); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* 758c2ecf20Sopenharmony_ci * Hyper-V should be notified only once about a panic. If we will be 768c2ecf20Sopenharmony_ci * doing hyperv_report_panic_msg() later with kmsg data, don't do 778c2ecf20Sopenharmony_ci * the notification here. 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_ci if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE 808c2ecf20Sopenharmony_ci && hyperv_report_reg()) { 818c2ecf20Sopenharmony_ci regs = current_pt_regs(); 828c2ecf20Sopenharmony_ci hyperv_report_panic(regs, val, false); 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci return NOTIFY_DONE; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic int hyperv_die_event(struct notifier_block *nb, unsigned long val, 888c2ecf20Sopenharmony_ci void *args) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct die_args *die = args; 918c2ecf20Sopenharmony_ci struct pt_regs *regs = die->regs; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* Don't notify Hyper-V if the die event is other than oops */ 948c2ecf20Sopenharmony_ci if (val != DIE_OOPS) 958c2ecf20Sopenharmony_ci return NOTIFY_DONE; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci /* 988c2ecf20Sopenharmony_ci * Hyper-V should be notified only once about a panic. If we will be 998c2ecf20Sopenharmony_ci * doing hyperv_report_panic_msg() later with kmsg data, don't do 1008c2ecf20Sopenharmony_ci * the notification here. 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci if (hyperv_report_reg()) 1038c2ecf20Sopenharmony_ci hyperv_report_panic(regs, val, true); 1048c2ecf20Sopenharmony_ci return NOTIFY_DONE; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic struct notifier_block hyperv_die_block = { 1088c2ecf20Sopenharmony_ci .notifier_call = hyperv_die_event, 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_cistatic struct notifier_block hyperv_panic_block = { 1118c2ecf20Sopenharmony_ci .notifier_call = hyperv_panic_event, 1128c2ecf20Sopenharmony_ci}; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic const char *fb_mmio_name = "fb_range"; 1158c2ecf20Sopenharmony_cistatic struct resource *fb_mmio; 1168c2ecf20Sopenharmony_cistatic struct resource *hyperv_mmio; 1178c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(hyperv_mmio_lock); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int vmbus_exists(void) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci if (hv_acpi_dev == NULL) 1228c2ecf20Sopenharmony_ci return -ENODEV; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return 0; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic u8 channel_monitor_group(const struct vmbus_channel *channel) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci return (u8)channel->offermsg.monitorid / 32; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic u8 channel_monitor_offset(const struct vmbus_channel *channel) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci return (u8)channel->offermsg.monitorid % 32; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic u32 channel_pending(const struct vmbus_channel *channel, 1388c2ecf20Sopenharmony_ci const struct hv_monitor_page *monitor_page) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci u8 monitor_group = channel_monitor_group(channel); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return monitor_page->trigger_group[monitor_group].pending; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic u32 channel_latency(const struct vmbus_channel *channel, 1468c2ecf20Sopenharmony_ci const struct hv_monitor_page *monitor_page) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci u8 monitor_group = channel_monitor_group(channel); 1498c2ecf20Sopenharmony_ci u8 monitor_offset = channel_monitor_offset(channel); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return monitor_page->latency[monitor_group][monitor_offset]; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic u32 channel_conn_id(struct vmbus_channel *channel, 1558c2ecf20Sopenharmony_ci struct hv_monitor_page *monitor_page) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci u8 monitor_group = channel_monitor_group(channel); 1588c2ecf20Sopenharmony_ci u8 monitor_offset = channel_monitor_offset(channel); 1598c2ecf20Sopenharmony_ci return monitor_page->parameter[monitor_group][monitor_offset].connectionid.u.id; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic ssize_t id_show(struct device *dev, struct device_attribute *dev_attr, 1638c2ecf20Sopenharmony_ci char *buf) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (!hv_dev->channel) 1688c2ecf20Sopenharmony_ci return -ENODEV; 1698c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", hv_dev->channel->offermsg.child_relid); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(id); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic ssize_t state_show(struct device *dev, struct device_attribute *dev_attr, 1748c2ecf20Sopenharmony_ci char *buf) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (!hv_dev->channel) 1798c2ecf20Sopenharmony_ci return -ENODEV; 1808c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", hv_dev->channel->state); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(state); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic ssize_t monitor_id_show(struct device *dev, 1858c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, char *buf) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (!hv_dev->channel) 1908c2ecf20Sopenharmony_ci return -ENODEV; 1918c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", hv_dev->channel->offermsg.monitorid); 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(monitor_id); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic ssize_t class_id_show(struct device *dev, 1968c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, char *buf) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (!hv_dev->channel) 2018c2ecf20Sopenharmony_ci return -ENODEV; 2028c2ecf20Sopenharmony_ci return sprintf(buf, "{%pUl}\n", 2038c2ecf20Sopenharmony_ci &hv_dev->channel->offermsg.offer.if_type); 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(class_id); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic ssize_t device_id_show(struct device *dev, 2088c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, char *buf) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (!hv_dev->channel) 2138c2ecf20Sopenharmony_ci return -ENODEV; 2148c2ecf20Sopenharmony_ci return sprintf(buf, "{%pUl}\n", 2158c2ecf20Sopenharmony_ci &hv_dev->channel->offermsg.offer.if_instance); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(device_id); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic ssize_t modalias_show(struct device *dev, 2208c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, char *buf) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return sprintf(buf, "vmbus:%*phN\n", UUID_SIZE, &hv_dev->dev_type); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(modalias); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci#ifdef CONFIG_NUMA 2298c2ecf20Sopenharmony_cistatic ssize_t numa_node_show(struct device *dev, 2308c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (!hv_dev->channel) 2358c2ecf20Sopenharmony_ci return -ENODEV; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", cpu_to_node(hv_dev->channel->target_cpu)); 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(numa_node); 2408c2ecf20Sopenharmony_ci#endif 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic ssize_t server_monitor_pending_show(struct device *dev, 2438c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, 2448c2ecf20Sopenharmony_ci char *buf) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (!hv_dev->channel) 2498c2ecf20Sopenharmony_ci return -ENODEV; 2508c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", 2518c2ecf20Sopenharmony_ci channel_pending(hv_dev->channel, 2528c2ecf20Sopenharmony_ci vmbus_connection.monitor_pages[0])); 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(server_monitor_pending); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic ssize_t client_monitor_pending_show(struct device *dev, 2578c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, 2588c2ecf20Sopenharmony_ci char *buf) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (!hv_dev->channel) 2638c2ecf20Sopenharmony_ci return -ENODEV; 2648c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", 2658c2ecf20Sopenharmony_ci channel_pending(hv_dev->channel, 2668c2ecf20Sopenharmony_ci vmbus_connection.monitor_pages[1])); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(client_monitor_pending); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic ssize_t server_monitor_latency_show(struct device *dev, 2718c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, 2728c2ecf20Sopenharmony_ci char *buf) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (!hv_dev->channel) 2778c2ecf20Sopenharmony_ci return -ENODEV; 2788c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", 2798c2ecf20Sopenharmony_ci channel_latency(hv_dev->channel, 2808c2ecf20Sopenharmony_ci vmbus_connection.monitor_pages[0])); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(server_monitor_latency); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic ssize_t client_monitor_latency_show(struct device *dev, 2858c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, 2868c2ecf20Sopenharmony_ci char *buf) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (!hv_dev->channel) 2918c2ecf20Sopenharmony_ci return -ENODEV; 2928c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", 2938c2ecf20Sopenharmony_ci channel_latency(hv_dev->channel, 2948c2ecf20Sopenharmony_ci vmbus_connection.monitor_pages[1])); 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(client_monitor_latency); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic ssize_t server_monitor_conn_id_show(struct device *dev, 2998c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, 3008c2ecf20Sopenharmony_ci char *buf) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (!hv_dev->channel) 3058c2ecf20Sopenharmony_ci return -ENODEV; 3068c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", 3078c2ecf20Sopenharmony_ci channel_conn_id(hv_dev->channel, 3088c2ecf20Sopenharmony_ci vmbus_connection.monitor_pages[0])); 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(server_monitor_conn_id); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic ssize_t client_monitor_conn_id_show(struct device *dev, 3138c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, 3148c2ecf20Sopenharmony_ci char *buf) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (!hv_dev->channel) 3198c2ecf20Sopenharmony_ci return -ENODEV; 3208c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", 3218c2ecf20Sopenharmony_ci channel_conn_id(hv_dev->channel, 3228c2ecf20Sopenharmony_ci vmbus_connection.monitor_pages[1])); 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(client_monitor_conn_id); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic ssize_t out_intr_mask_show(struct device *dev, 3278c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, char *buf) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 3308c2ecf20Sopenharmony_ci struct hv_ring_buffer_debug_info outbound; 3318c2ecf20Sopenharmony_ci int ret; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (!hv_dev->channel) 3348c2ecf20Sopenharmony_ci return -ENODEV; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci ret = hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, 3378c2ecf20Sopenharmony_ci &outbound); 3388c2ecf20Sopenharmony_ci if (ret < 0) 3398c2ecf20Sopenharmony_ci return ret; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", outbound.current_interrupt_mask); 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(out_intr_mask); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic ssize_t out_read_index_show(struct device *dev, 3468c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, char *buf) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 3498c2ecf20Sopenharmony_ci struct hv_ring_buffer_debug_info outbound; 3508c2ecf20Sopenharmony_ci int ret; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (!hv_dev->channel) 3538c2ecf20Sopenharmony_ci return -ENODEV; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci ret = hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, 3568c2ecf20Sopenharmony_ci &outbound); 3578c2ecf20Sopenharmony_ci if (ret < 0) 3588c2ecf20Sopenharmony_ci return ret; 3598c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", outbound.current_read_index); 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(out_read_index); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic ssize_t out_write_index_show(struct device *dev, 3648c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, 3658c2ecf20Sopenharmony_ci char *buf) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 3688c2ecf20Sopenharmony_ci struct hv_ring_buffer_debug_info outbound; 3698c2ecf20Sopenharmony_ci int ret; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (!hv_dev->channel) 3728c2ecf20Sopenharmony_ci return -ENODEV; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci ret = hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, 3758c2ecf20Sopenharmony_ci &outbound); 3768c2ecf20Sopenharmony_ci if (ret < 0) 3778c2ecf20Sopenharmony_ci return ret; 3788c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", outbound.current_write_index); 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(out_write_index); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic ssize_t out_read_bytes_avail_show(struct device *dev, 3838c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, 3848c2ecf20Sopenharmony_ci char *buf) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 3878c2ecf20Sopenharmony_ci struct hv_ring_buffer_debug_info outbound; 3888c2ecf20Sopenharmony_ci int ret; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (!hv_dev->channel) 3918c2ecf20Sopenharmony_ci return -ENODEV; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci ret = hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, 3948c2ecf20Sopenharmony_ci &outbound); 3958c2ecf20Sopenharmony_ci if (ret < 0) 3968c2ecf20Sopenharmony_ci return ret; 3978c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", outbound.bytes_avail_toread); 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(out_read_bytes_avail); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic ssize_t out_write_bytes_avail_show(struct device *dev, 4028c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, 4038c2ecf20Sopenharmony_ci char *buf) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 4068c2ecf20Sopenharmony_ci struct hv_ring_buffer_debug_info outbound; 4078c2ecf20Sopenharmony_ci int ret; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (!hv_dev->channel) 4108c2ecf20Sopenharmony_ci return -ENODEV; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci ret = hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, 4138c2ecf20Sopenharmony_ci &outbound); 4148c2ecf20Sopenharmony_ci if (ret < 0) 4158c2ecf20Sopenharmony_ci return ret; 4168c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", outbound.bytes_avail_towrite); 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(out_write_bytes_avail); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic ssize_t in_intr_mask_show(struct device *dev, 4218c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, char *buf) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 4248c2ecf20Sopenharmony_ci struct hv_ring_buffer_debug_info inbound; 4258c2ecf20Sopenharmony_ci int ret; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (!hv_dev->channel) 4288c2ecf20Sopenharmony_ci return -ENODEV; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci ret = hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound); 4318c2ecf20Sopenharmony_ci if (ret < 0) 4328c2ecf20Sopenharmony_ci return ret; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", inbound.current_interrupt_mask); 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(in_intr_mask); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic ssize_t in_read_index_show(struct device *dev, 4398c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, char *buf) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 4428c2ecf20Sopenharmony_ci struct hv_ring_buffer_debug_info inbound; 4438c2ecf20Sopenharmony_ci int ret; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (!hv_dev->channel) 4468c2ecf20Sopenharmony_ci return -ENODEV; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci ret = hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound); 4498c2ecf20Sopenharmony_ci if (ret < 0) 4508c2ecf20Sopenharmony_ci return ret; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", inbound.current_read_index); 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(in_read_index); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic ssize_t in_write_index_show(struct device *dev, 4578c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, char *buf) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 4608c2ecf20Sopenharmony_ci struct hv_ring_buffer_debug_info inbound; 4618c2ecf20Sopenharmony_ci int ret; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (!hv_dev->channel) 4648c2ecf20Sopenharmony_ci return -ENODEV; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci ret = hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound); 4678c2ecf20Sopenharmony_ci if (ret < 0) 4688c2ecf20Sopenharmony_ci return ret; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", inbound.current_write_index); 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(in_write_index); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic ssize_t in_read_bytes_avail_show(struct device *dev, 4758c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, 4768c2ecf20Sopenharmony_ci char *buf) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 4798c2ecf20Sopenharmony_ci struct hv_ring_buffer_debug_info inbound; 4808c2ecf20Sopenharmony_ci int ret; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (!hv_dev->channel) 4838c2ecf20Sopenharmony_ci return -ENODEV; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci ret = hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound); 4868c2ecf20Sopenharmony_ci if (ret < 0) 4878c2ecf20Sopenharmony_ci return ret; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", inbound.bytes_avail_toread); 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(in_read_bytes_avail); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic ssize_t in_write_bytes_avail_show(struct device *dev, 4948c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, 4958c2ecf20Sopenharmony_ci char *buf) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 4988c2ecf20Sopenharmony_ci struct hv_ring_buffer_debug_info inbound; 4998c2ecf20Sopenharmony_ci int ret; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (!hv_dev->channel) 5028c2ecf20Sopenharmony_ci return -ENODEV; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci ret = hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound); 5058c2ecf20Sopenharmony_ci if (ret < 0) 5068c2ecf20Sopenharmony_ci return ret; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", inbound.bytes_avail_towrite); 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(in_write_bytes_avail); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_cistatic ssize_t channel_vp_mapping_show(struct device *dev, 5138c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, 5148c2ecf20Sopenharmony_ci char *buf) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 5178c2ecf20Sopenharmony_ci struct vmbus_channel *channel = hv_dev->channel, *cur_sc; 5188c2ecf20Sopenharmony_ci int buf_size = PAGE_SIZE, n_written, tot_written; 5198c2ecf20Sopenharmony_ci struct list_head *cur; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (!channel) 5228c2ecf20Sopenharmony_ci return -ENODEV; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci mutex_lock(&vmbus_connection.channel_mutex); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci tot_written = snprintf(buf, buf_size, "%u:%u\n", 5278c2ecf20Sopenharmony_ci channel->offermsg.child_relid, channel->target_cpu); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci list_for_each(cur, &channel->sc_list) { 5308c2ecf20Sopenharmony_ci if (tot_written >= buf_size - 1) 5318c2ecf20Sopenharmony_ci break; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci cur_sc = list_entry(cur, struct vmbus_channel, sc_list); 5348c2ecf20Sopenharmony_ci n_written = scnprintf(buf + tot_written, 5358c2ecf20Sopenharmony_ci buf_size - tot_written, 5368c2ecf20Sopenharmony_ci "%u:%u\n", 5378c2ecf20Sopenharmony_ci cur_sc->offermsg.child_relid, 5388c2ecf20Sopenharmony_ci cur_sc->target_cpu); 5398c2ecf20Sopenharmony_ci tot_written += n_written; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci mutex_unlock(&vmbus_connection.channel_mutex); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci return tot_written; 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(channel_vp_mapping); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cistatic ssize_t vendor_show(struct device *dev, 5498c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, 5508c2ecf20Sopenharmony_ci char *buf) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 5538c2ecf20Sopenharmony_ci return sprintf(buf, "0x%x\n", hv_dev->vendor_id); 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(vendor); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic ssize_t device_show(struct device *dev, 5588c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, 5598c2ecf20Sopenharmony_ci char *buf) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 5628c2ecf20Sopenharmony_ci return sprintf(buf, "0x%x\n", hv_dev->device_id); 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(device); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic ssize_t driver_override_store(struct device *dev, 5678c2ecf20Sopenharmony_ci struct device_attribute *attr, 5688c2ecf20Sopenharmony_ci const char *buf, size_t count) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 5718c2ecf20Sopenharmony_ci char *driver_override, *old, *cp; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci /* We need to keep extra room for a newline */ 5748c2ecf20Sopenharmony_ci if (count >= (PAGE_SIZE - 1)) 5758c2ecf20Sopenharmony_ci return -EINVAL; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci driver_override = kstrndup(buf, count, GFP_KERNEL); 5788c2ecf20Sopenharmony_ci if (!driver_override) 5798c2ecf20Sopenharmony_ci return -ENOMEM; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci cp = strchr(driver_override, '\n'); 5828c2ecf20Sopenharmony_ci if (cp) 5838c2ecf20Sopenharmony_ci *cp = '\0'; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci device_lock(dev); 5868c2ecf20Sopenharmony_ci old = hv_dev->driver_override; 5878c2ecf20Sopenharmony_ci if (strlen(driver_override)) { 5888c2ecf20Sopenharmony_ci hv_dev->driver_override = driver_override; 5898c2ecf20Sopenharmony_ci } else { 5908c2ecf20Sopenharmony_ci kfree(driver_override); 5918c2ecf20Sopenharmony_ci hv_dev->driver_override = NULL; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci device_unlock(dev); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci kfree(old); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci return count; 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_cistatic ssize_t driver_override_show(struct device *dev, 6018c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 6048c2ecf20Sopenharmony_ci ssize_t len; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci device_lock(dev); 6078c2ecf20Sopenharmony_ci len = snprintf(buf, PAGE_SIZE, "%s\n", hv_dev->driver_override); 6088c2ecf20Sopenharmony_ci device_unlock(dev); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci return len; 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(driver_override); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci/* Set up per device attributes in /sys/bus/vmbus/devices/<bus device> */ 6158c2ecf20Sopenharmony_cistatic struct attribute *vmbus_dev_attrs[] = { 6168c2ecf20Sopenharmony_ci &dev_attr_id.attr, 6178c2ecf20Sopenharmony_ci &dev_attr_state.attr, 6188c2ecf20Sopenharmony_ci &dev_attr_monitor_id.attr, 6198c2ecf20Sopenharmony_ci &dev_attr_class_id.attr, 6208c2ecf20Sopenharmony_ci &dev_attr_device_id.attr, 6218c2ecf20Sopenharmony_ci &dev_attr_modalias.attr, 6228c2ecf20Sopenharmony_ci#ifdef CONFIG_NUMA 6238c2ecf20Sopenharmony_ci &dev_attr_numa_node.attr, 6248c2ecf20Sopenharmony_ci#endif 6258c2ecf20Sopenharmony_ci &dev_attr_server_monitor_pending.attr, 6268c2ecf20Sopenharmony_ci &dev_attr_client_monitor_pending.attr, 6278c2ecf20Sopenharmony_ci &dev_attr_server_monitor_latency.attr, 6288c2ecf20Sopenharmony_ci &dev_attr_client_monitor_latency.attr, 6298c2ecf20Sopenharmony_ci &dev_attr_server_monitor_conn_id.attr, 6308c2ecf20Sopenharmony_ci &dev_attr_client_monitor_conn_id.attr, 6318c2ecf20Sopenharmony_ci &dev_attr_out_intr_mask.attr, 6328c2ecf20Sopenharmony_ci &dev_attr_out_read_index.attr, 6338c2ecf20Sopenharmony_ci &dev_attr_out_write_index.attr, 6348c2ecf20Sopenharmony_ci &dev_attr_out_read_bytes_avail.attr, 6358c2ecf20Sopenharmony_ci &dev_attr_out_write_bytes_avail.attr, 6368c2ecf20Sopenharmony_ci &dev_attr_in_intr_mask.attr, 6378c2ecf20Sopenharmony_ci &dev_attr_in_read_index.attr, 6388c2ecf20Sopenharmony_ci &dev_attr_in_write_index.attr, 6398c2ecf20Sopenharmony_ci &dev_attr_in_read_bytes_avail.attr, 6408c2ecf20Sopenharmony_ci &dev_attr_in_write_bytes_avail.attr, 6418c2ecf20Sopenharmony_ci &dev_attr_channel_vp_mapping.attr, 6428c2ecf20Sopenharmony_ci &dev_attr_vendor.attr, 6438c2ecf20Sopenharmony_ci &dev_attr_device.attr, 6448c2ecf20Sopenharmony_ci &dev_attr_driver_override.attr, 6458c2ecf20Sopenharmony_ci NULL, 6468c2ecf20Sopenharmony_ci}; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci/* 6498c2ecf20Sopenharmony_ci * Device-level attribute_group callback function. Returns the permission for 6508c2ecf20Sopenharmony_ci * each attribute, and returns 0 if an attribute is not visible. 6518c2ecf20Sopenharmony_ci */ 6528c2ecf20Sopenharmony_cistatic umode_t vmbus_dev_attr_is_visible(struct kobject *kobj, 6538c2ecf20Sopenharmony_ci struct attribute *attr, int idx) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 6568c2ecf20Sopenharmony_ci const struct hv_device *hv_dev = device_to_hv_device(dev); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci /* Hide the monitor attributes if the monitor mechanism is not used. */ 6598c2ecf20Sopenharmony_ci if (!hv_dev->channel->offermsg.monitor_allocated && 6608c2ecf20Sopenharmony_ci (attr == &dev_attr_monitor_id.attr || 6618c2ecf20Sopenharmony_ci attr == &dev_attr_server_monitor_pending.attr || 6628c2ecf20Sopenharmony_ci attr == &dev_attr_client_monitor_pending.attr || 6638c2ecf20Sopenharmony_ci attr == &dev_attr_server_monitor_latency.attr || 6648c2ecf20Sopenharmony_ci attr == &dev_attr_client_monitor_latency.attr || 6658c2ecf20Sopenharmony_ci attr == &dev_attr_server_monitor_conn_id.attr || 6668c2ecf20Sopenharmony_ci attr == &dev_attr_client_monitor_conn_id.attr)) 6678c2ecf20Sopenharmony_ci return 0; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci return attr->mode; 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic const struct attribute_group vmbus_dev_group = { 6738c2ecf20Sopenharmony_ci .attrs = vmbus_dev_attrs, 6748c2ecf20Sopenharmony_ci .is_visible = vmbus_dev_attr_is_visible 6758c2ecf20Sopenharmony_ci}; 6768c2ecf20Sopenharmony_ci__ATTRIBUTE_GROUPS(vmbus_dev); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci/* 6798c2ecf20Sopenharmony_ci * vmbus_uevent - add uevent for our device 6808c2ecf20Sopenharmony_ci * 6818c2ecf20Sopenharmony_ci * This routine is invoked when a device is added or removed on the vmbus to 6828c2ecf20Sopenharmony_ci * generate a uevent to udev in the userspace. The udev will then look at its 6838c2ecf20Sopenharmony_ci * rule and the uevent generated here to load the appropriate driver 6848c2ecf20Sopenharmony_ci * 6858c2ecf20Sopenharmony_ci * The alias string will be of the form vmbus:guid where guid is the string 6868c2ecf20Sopenharmony_ci * representation of the device guid (each byte of the guid will be 6878c2ecf20Sopenharmony_ci * represented with two hex characters. 6888c2ecf20Sopenharmony_ci */ 6898c2ecf20Sopenharmony_cistatic int vmbus_uevent(struct device *device, struct kobj_uevent_env *env) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci struct hv_device *dev = device_to_hv_device(device); 6928c2ecf20Sopenharmony_ci const char *format = "MODALIAS=vmbus:%*phN"; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci return add_uevent_var(env, format, UUID_SIZE, &dev->dev_type); 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_cistatic const struct hv_vmbus_device_id * 6988c2ecf20Sopenharmony_cihv_vmbus_dev_match(const struct hv_vmbus_device_id *id, const guid_t *guid) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci if (id == NULL) 7018c2ecf20Sopenharmony_ci return NULL; /* empty device table */ 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci for (; !guid_is_null(&id->guid); id++) 7048c2ecf20Sopenharmony_ci if (guid_equal(&id->guid, guid)) 7058c2ecf20Sopenharmony_ci return id; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci return NULL; 7088c2ecf20Sopenharmony_ci} 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_cistatic const struct hv_vmbus_device_id * 7118c2ecf20Sopenharmony_cihv_vmbus_dynid_match(struct hv_driver *drv, const guid_t *guid) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci const struct hv_vmbus_device_id *id = NULL; 7148c2ecf20Sopenharmony_ci struct vmbus_dynid *dynid; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci spin_lock(&drv->dynids.lock); 7178c2ecf20Sopenharmony_ci list_for_each_entry(dynid, &drv->dynids.list, node) { 7188c2ecf20Sopenharmony_ci if (guid_equal(&dynid->id.guid, guid)) { 7198c2ecf20Sopenharmony_ci id = &dynid->id; 7208c2ecf20Sopenharmony_ci break; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci spin_unlock(&drv->dynids.lock); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci return id; 7268c2ecf20Sopenharmony_ci} 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_cistatic const struct hv_vmbus_device_id vmbus_device_null; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci/* 7318c2ecf20Sopenharmony_ci * Return a matching hv_vmbus_device_id pointer. 7328c2ecf20Sopenharmony_ci * If there is no match, return NULL. 7338c2ecf20Sopenharmony_ci */ 7348c2ecf20Sopenharmony_cistatic const struct hv_vmbus_device_id *hv_vmbus_get_id(struct hv_driver *drv, 7358c2ecf20Sopenharmony_ci struct hv_device *dev) 7368c2ecf20Sopenharmony_ci{ 7378c2ecf20Sopenharmony_ci const guid_t *guid = &dev->dev_type; 7388c2ecf20Sopenharmony_ci const struct hv_vmbus_device_id *id; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci /* When driver_override is set, only bind to the matching driver */ 7418c2ecf20Sopenharmony_ci if (dev->driver_override && strcmp(dev->driver_override, drv->name)) 7428c2ecf20Sopenharmony_ci return NULL; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci /* Look at the dynamic ids first, before the static ones */ 7458c2ecf20Sopenharmony_ci id = hv_vmbus_dynid_match(drv, guid); 7468c2ecf20Sopenharmony_ci if (!id) 7478c2ecf20Sopenharmony_ci id = hv_vmbus_dev_match(drv->id_table, guid); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci /* driver_override will always match, send a dummy id */ 7508c2ecf20Sopenharmony_ci if (!id && dev->driver_override) 7518c2ecf20Sopenharmony_ci id = &vmbus_device_null; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci return id; 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci/* vmbus_add_dynid - add a new device ID to this driver and re-probe devices */ 7578c2ecf20Sopenharmony_cistatic int vmbus_add_dynid(struct hv_driver *drv, guid_t *guid) 7588c2ecf20Sopenharmony_ci{ 7598c2ecf20Sopenharmony_ci struct vmbus_dynid *dynid; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); 7628c2ecf20Sopenharmony_ci if (!dynid) 7638c2ecf20Sopenharmony_ci return -ENOMEM; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci dynid->id.guid = *guid; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci spin_lock(&drv->dynids.lock); 7688c2ecf20Sopenharmony_ci list_add_tail(&dynid->node, &drv->dynids.list); 7698c2ecf20Sopenharmony_ci spin_unlock(&drv->dynids.lock); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci return driver_attach(&drv->driver); 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_cistatic void vmbus_free_dynids(struct hv_driver *drv) 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci struct vmbus_dynid *dynid, *n; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci spin_lock(&drv->dynids.lock); 7798c2ecf20Sopenharmony_ci list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) { 7808c2ecf20Sopenharmony_ci list_del(&dynid->node); 7818c2ecf20Sopenharmony_ci kfree(dynid); 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci spin_unlock(&drv->dynids.lock); 7848c2ecf20Sopenharmony_ci} 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci/* 7878c2ecf20Sopenharmony_ci * store_new_id - sysfs frontend to vmbus_add_dynid() 7888c2ecf20Sopenharmony_ci * 7898c2ecf20Sopenharmony_ci * Allow GUIDs to be added to an existing driver via sysfs. 7908c2ecf20Sopenharmony_ci */ 7918c2ecf20Sopenharmony_cistatic ssize_t new_id_store(struct device_driver *driver, const char *buf, 7928c2ecf20Sopenharmony_ci size_t count) 7938c2ecf20Sopenharmony_ci{ 7948c2ecf20Sopenharmony_ci struct hv_driver *drv = drv_to_hv_drv(driver); 7958c2ecf20Sopenharmony_ci guid_t guid; 7968c2ecf20Sopenharmony_ci ssize_t retval; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci retval = guid_parse(buf, &guid); 7998c2ecf20Sopenharmony_ci if (retval) 8008c2ecf20Sopenharmony_ci return retval; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci if (hv_vmbus_dynid_match(drv, &guid)) 8038c2ecf20Sopenharmony_ci return -EEXIST; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci retval = vmbus_add_dynid(drv, &guid); 8068c2ecf20Sopenharmony_ci if (retval) 8078c2ecf20Sopenharmony_ci return retval; 8088c2ecf20Sopenharmony_ci return count; 8098c2ecf20Sopenharmony_ci} 8108c2ecf20Sopenharmony_cistatic DRIVER_ATTR_WO(new_id); 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci/* 8138c2ecf20Sopenharmony_ci * store_remove_id - remove a PCI device ID from this driver 8148c2ecf20Sopenharmony_ci * 8158c2ecf20Sopenharmony_ci * Removes a dynamic pci device ID to this driver. 8168c2ecf20Sopenharmony_ci */ 8178c2ecf20Sopenharmony_cistatic ssize_t remove_id_store(struct device_driver *driver, const char *buf, 8188c2ecf20Sopenharmony_ci size_t count) 8198c2ecf20Sopenharmony_ci{ 8208c2ecf20Sopenharmony_ci struct hv_driver *drv = drv_to_hv_drv(driver); 8218c2ecf20Sopenharmony_ci struct vmbus_dynid *dynid, *n; 8228c2ecf20Sopenharmony_ci guid_t guid; 8238c2ecf20Sopenharmony_ci ssize_t retval; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci retval = guid_parse(buf, &guid); 8268c2ecf20Sopenharmony_ci if (retval) 8278c2ecf20Sopenharmony_ci return retval; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci retval = -ENODEV; 8308c2ecf20Sopenharmony_ci spin_lock(&drv->dynids.lock); 8318c2ecf20Sopenharmony_ci list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) { 8328c2ecf20Sopenharmony_ci struct hv_vmbus_device_id *id = &dynid->id; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci if (guid_equal(&id->guid, &guid)) { 8358c2ecf20Sopenharmony_ci list_del(&dynid->node); 8368c2ecf20Sopenharmony_ci kfree(dynid); 8378c2ecf20Sopenharmony_ci retval = count; 8388c2ecf20Sopenharmony_ci break; 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci spin_unlock(&drv->dynids.lock); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci return retval; 8448c2ecf20Sopenharmony_ci} 8458c2ecf20Sopenharmony_cistatic DRIVER_ATTR_WO(remove_id); 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_cistatic struct attribute *vmbus_drv_attrs[] = { 8488c2ecf20Sopenharmony_ci &driver_attr_new_id.attr, 8498c2ecf20Sopenharmony_ci &driver_attr_remove_id.attr, 8508c2ecf20Sopenharmony_ci NULL, 8518c2ecf20Sopenharmony_ci}; 8528c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(vmbus_drv); 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci/* 8568c2ecf20Sopenharmony_ci * vmbus_match - Attempt to match the specified device to the specified driver 8578c2ecf20Sopenharmony_ci */ 8588c2ecf20Sopenharmony_cistatic int vmbus_match(struct device *device, struct device_driver *driver) 8598c2ecf20Sopenharmony_ci{ 8608c2ecf20Sopenharmony_ci struct hv_driver *drv = drv_to_hv_drv(driver); 8618c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(device); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci /* The hv_sock driver handles all hv_sock offers. */ 8648c2ecf20Sopenharmony_ci if (is_hvsock_channel(hv_dev->channel)) 8658c2ecf20Sopenharmony_ci return drv->hvsock; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci if (hv_vmbus_get_id(drv, hv_dev)) 8688c2ecf20Sopenharmony_ci return 1; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci return 0; 8718c2ecf20Sopenharmony_ci} 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci/* 8748c2ecf20Sopenharmony_ci * vmbus_probe - Add the new vmbus's child device 8758c2ecf20Sopenharmony_ci */ 8768c2ecf20Sopenharmony_cistatic int vmbus_probe(struct device *child_device) 8778c2ecf20Sopenharmony_ci{ 8788c2ecf20Sopenharmony_ci int ret = 0; 8798c2ecf20Sopenharmony_ci struct hv_driver *drv = 8808c2ecf20Sopenharmony_ci drv_to_hv_drv(child_device->driver); 8818c2ecf20Sopenharmony_ci struct hv_device *dev = device_to_hv_device(child_device); 8828c2ecf20Sopenharmony_ci const struct hv_vmbus_device_id *dev_id; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci dev_id = hv_vmbus_get_id(drv, dev); 8858c2ecf20Sopenharmony_ci if (drv->probe) { 8868c2ecf20Sopenharmony_ci ret = drv->probe(dev, dev_id); 8878c2ecf20Sopenharmony_ci if (ret != 0) 8888c2ecf20Sopenharmony_ci pr_err("probe failed for device %s (%d)\n", 8898c2ecf20Sopenharmony_ci dev_name(child_device), ret); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci } else { 8928c2ecf20Sopenharmony_ci pr_err("probe not set for driver %s\n", 8938c2ecf20Sopenharmony_ci dev_name(child_device)); 8948c2ecf20Sopenharmony_ci ret = -ENODEV; 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci return ret; 8978c2ecf20Sopenharmony_ci} 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci/* 9008c2ecf20Sopenharmony_ci * vmbus_remove - Remove a vmbus device 9018c2ecf20Sopenharmony_ci */ 9028c2ecf20Sopenharmony_cistatic int vmbus_remove(struct device *child_device) 9038c2ecf20Sopenharmony_ci{ 9048c2ecf20Sopenharmony_ci struct hv_driver *drv; 9058c2ecf20Sopenharmony_ci struct hv_device *dev = device_to_hv_device(child_device); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci if (child_device->driver) { 9088c2ecf20Sopenharmony_ci drv = drv_to_hv_drv(child_device->driver); 9098c2ecf20Sopenharmony_ci if (drv->remove) 9108c2ecf20Sopenharmony_ci drv->remove(dev); 9118c2ecf20Sopenharmony_ci } 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci return 0; 9148c2ecf20Sopenharmony_ci} 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci/* 9188c2ecf20Sopenharmony_ci * vmbus_shutdown - Shutdown a vmbus device 9198c2ecf20Sopenharmony_ci */ 9208c2ecf20Sopenharmony_cistatic void vmbus_shutdown(struct device *child_device) 9218c2ecf20Sopenharmony_ci{ 9228c2ecf20Sopenharmony_ci struct hv_driver *drv; 9238c2ecf20Sopenharmony_ci struct hv_device *dev = device_to_hv_device(child_device); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci /* The device may not be attached yet */ 9278c2ecf20Sopenharmony_ci if (!child_device->driver) 9288c2ecf20Sopenharmony_ci return; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci drv = drv_to_hv_drv(child_device->driver); 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci if (drv->shutdown) 9338c2ecf20Sopenharmony_ci drv->shutdown(dev); 9348c2ecf20Sopenharmony_ci} 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 9378c2ecf20Sopenharmony_ci/* 9388c2ecf20Sopenharmony_ci * vmbus_suspend - Suspend a vmbus device 9398c2ecf20Sopenharmony_ci */ 9408c2ecf20Sopenharmony_cistatic int vmbus_suspend(struct device *child_device) 9418c2ecf20Sopenharmony_ci{ 9428c2ecf20Sopenharmony_ci struct hv_driver *drv; 9438c2ecf20Sopenharmony_ci struct hv_device *dev = device_to_hv_device(child_device); 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci /* The device may not be attached yet */ 9468c2ecf20Sopenharmony_ci if (!child_device->driver) 9478c2ecf20Sopenharmony_ci return 0; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci drv = drv_to_hv_drv(child_device->driver); 9508c2ecf20Sopenharmony_ci if (!drv->suspend) 9518c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci return drv->suspend(dev); 9548c2ecf20Sopenharmony_ci} 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci/* 9578c2ecf20Sopenharmony_ci * vmbus_resume - Resume a vmbus device 9588c2ecf20Sopenharmony_ci */ 9598c2ecf20Sopenharmony_cistatic int vmbus_resume(struct device *child_device) 9608c2ecf20Sopenharmony_ci{ 9618c2ecf20Sopenharmony_ci struct hv_driver *drv; 9628c2ecf20Sopenharmony_ci struct hv_device *dev = device_to_hv_device(child_device); 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci /* The device may not be attached yet */ 9658c2ecf20Sopenharmony_ci if (!child_device->driver) 9668c2ecf20Sopenharmony_ci return 0; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci drv = drv_to_hv_drv(child_device->driver); 9698c2ecf20Sopenharmony_ci if (!drv->resume) 9708c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci return drv->resume(dev); 9738c2ecf20Sopenharmony_ci} 9748c2ecf20Sopenharmony_ci#else 9758c2ecf20Sopenharmony_ci#define vmbus_suspend NULL 9768c2ecf20Sopenharmony_ci#define vmbus_resume NULL 9778c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci/* 9808c2ecf20Sopenharmony_ci * vmbus_device_release - Final callback release of the vmbus child device 9818c2ecf20Sopenharmony_ci */ 9828c2ecf20Sopenharmony_cistatic void vmbus_device_release(struct device *device) 9838c2ecf20Sopenharmony_ci{ 9848c2ecf20Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(device); 9858c2ecf20Sopenharmony_ci struct vmbus_channel *channel = hv_dev->channel; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci hv_debug_rm_dev_dir(hv_dev); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci mutex_lock(&vmbus_connection.channel_mutex); 9908c2ecf20Sopenharmony_ci hv_process_channel_removal(channel); 9918c2ecf20Sopenharmony_ci mutex_unlock(&vmbus_connection.channel_mutex); 9928c2ecf20Sopenharmony_ci kfree(hv_dev); 9938c2ecf20Sopenharmony_ci} 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci/* 9968c2ecf20Sopenharmony_ci * Note: we must use the "noirq" ops: see the comment before vmbus_bus_pm. 9978c2ecf20Sopenharmony_ci * 9988c2ecf20Sopenharmony_ci * suspend_noirq/resume_noirq are set to NULL to support Suspend-to-Idle: we 9998c2ecf20Sopenharmony_ci * shouldn't suspend the vmbus devices upon Suspend-to-Idle, otherwise there 10008c2ecf20Sopenharmony_ci * is no way to wake up a Generation-2 VM. 10018c2ecf20Sopenharmony_ci * 10028c2ecf20Sopenharmony_ci * The other 4 ops are for hibernation. 10038c2ecf20Sopenharmony_ci */ 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_cistatic const struct dev_pm_ops vmbus_pm = { 10068c2ecf20Sopenharmony_ci .suspend_noirq = NULL, 10078c2ecf20Sopenharmony_ci .resume_noirq = NULL, 10088c2ecf20Sopenharmony_ci .freeze_noirq = vmbus_suspend, 10098c2ecf20Sopenharmony_ci .thaw_noirq = vmbus_resume, 10108c2ecf20Sopenharmony_ci .poweroff_noirq = vmbus_suspend, 10118c2ecf20Sopenharmony_ci .restore_noirq = vmbus_resume, 10128c2ecf20Sopenharmony_ci}; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci/* The one and only one */ 10158c2ecf20Sopenharmony_cistatic struct bus_type hv_bus = { 10168c2ecf20Sopenharmony_ci .name = "vmbus", 10178c2ecf20Sopenharmony_ci .match = vmbus_match, 10188c2ecf20Sopenharmony_ci .shutdown = vmbus_shutdown, 10198c2ecf20Sopenharmony_ci .remove = vmbus_remove, 10208c2ecf20Sopenharmony_ci .probe = vmbus_probe, 10218c2ecf20Sopenharmony_ci .uevent = vmbus_uevent, 10228c2ecf20Sopenharmony_ci .dev_groups = vmbus_dev_groups, 10238c2ecf20Sopenharmony_ci .drv_groups = vmbus_drv_groups, 10248c2ecf20Sopenharmony_ci .pm = &vmbus_pm, 10258c2ecf20Sopenharmony_ci}; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_cistruct onmessage_work_context { 10288c2ecf20Sopenharmony_ci struct work_struct work; 10298c2ecf20Sopenharmony_ci struct { 10308c2ecf20Sopenharmony_ci struct hv_message_header header; 10318c2ecf20Sopenharmony_ci u8 payload[]; 10328c2ecf20Sopenharmony_ci } msg; 10338c2ecf20Sopenharmony_ci}; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_cistatic void vmbus_onmessage_work(struct work_struct *work) 10368c2ecf20Sopenharmony_ci{ 10378c2ecf20Sopenharmony_ci struct onmessage_work_context *ctx; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci /* Do not process messages if we're in DISCONNECTED state */ 10408c2ecf20Sopenharmony_ci if (vmbus_connection.conn_state == DISCONNECTED) 10418c2ecf20Sopenharmony_ci return; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci ctx = container_of(work, struct onmessage_work_context, 10448c2ecf20Sopenharmony_ci work); 10458c2ecf20Sopenharmony_ci vmbus_onmessage((struct vmbus_channel_message_header *) 10468c2ecf20Sopenharmony_ci &ctx->msg.payload); 10478c2ecf20Sopenharmony_ci kfree(ctx); 10488c2ecf20Sopenharmony_ci} 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_civoid vmbus_on_msg_dpc(unsigned long data) 10518c2ecf20Sopenharmony_ci{ 10528c2ecf20Sopenharmony_ci struct hv_per_cpu_context *hv_cpu = (void *)data; 10538c2ecf20Sopenharmony_ci void *page_addr = hv_cpu->synic_message_page; 10548c2ecf20Sopenharmony_ci struct hv_message *msg = (struct hv_message *)page_addr + 10558c2ecf20Sopenharmony_ci VMBUS_MESSAGE_SINT; 10568c2ecf20Sopenharmony_ci struct vmbus_channel_message_header *hdr; 10578c2ecf20Sopenharmony_ci const struct vmbus_channel_message_table_entry *entry; 10588c2ecf20Sopenharmony_ci struct onmessage_work_context *ctx; 10598c2ecf20Sopenharmony_ci u32 message_type = msg->header.message_type; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci /* 10628c2ecf20Sopenharmony_ci * 'enum vmbus_channel_message_type' is supposed to always be 'u32' as 10638c2ecf20Sopenharmony_ci * it is being used in 'struct vmbus_channel_message_header' definition 10648c2ecf20Sopenharmony_ci * which is supposed to match hypervisor ABI. 10658c2ecf20Sopenharmony_ci */ 10668c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(enum vmbus_channel_message_type) != sizeof(u32)); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci if (message_type == HVMSG_NONE) 10698c2ecf20Sopenharmony_ci /* no msg */ 10708c2ecf20Sopenharmony_ci return; 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci hdr = (struct vmbus_channel_message_header *)msg->u.payload; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci trace_vmbus_on_msg_dpc(hdr); 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci if (hdr->msgtype >= CHANNELMSG_COUNT) { 10778c2ecf20Sopenharmony_ci WARN_ONCE(1, "unknown msgtype=%d\n", hdr->msgtype); 10788c2ecf20Sopenharmony_ci goto msg_handled; 10798c2ecf20Sopenharmony_ci } 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci if (msg->header.payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT) { 10828c2ecf20Sopenharmony_ci WARN_ONCE(1, "payload size is too large (%d)\n", 10838c2ecf20Sopenharmony_ci msg->header.payload_size); 10848c2ecf20Sopenharmony_ci goto msg_handled; 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci entry = &channel_message_table[hdr->msgtype]; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci if (!entry->message_handler) 10908c2ecf20Sopenharmony_ci goto msg_handled; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci if (msg->header.payload_size < entry->min_payload_len) { 10938c2ecf20Sopenharmony_ci WARN_ONCE(1, "message too short: msgtype=%d len=%d\n", 10948c2ecf20Sopenharmony_ci hdr->msgtype, msg->header.payload_size); 10958c2ecf20Sopenharmony_ci goto msg_handled; 10968c2ecf20Sopenharmony_ci } 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci if (entry->handler_type == VMHT_BLOCKING) { 10998c2ecf20Sopenharmony_ci ctx = kmalloc(sizeof(*ctx) + msg->header.payload_size, 11008c2ecf20Sopenharmony_ci GFP_ATOMIC); 11018c2ecf20Sopenharmony_ci if (ctx == NULL) 11028c2ecf20Sopenharmony_ci return; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci INIT_WORK(&ctx->work, vmbus_onmessage_work); 11058c2ecf20Sopenharmony_ci memcpy(&ctx->msg, msg, sizeof(msg->header) + 11068c2ecf20Sopenharmony_ci msg->header.payload_size); 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci /* 11098c2ecf20Sopenharmony_ci * The host can generate a rescind message while we 11108c2ecf20Sopenharmony_ci * may still be handling the original offer. We deal with 11118c2ecf20Sopenharmony_ci * this condition by relying on the synchronization provided 11128c2ecf20Sopenharmony_ci * by offer_in_progress and by channel_mutex. See also the 11138c2ecf20Sopenharmony_ci * inline comments in vmbus_onoffer_rescind(). 11148c2ecf20Sopenharmony_ci */ 11158c2ecf20Sopenharmony_ci switch (hdr->msgtype) { 11168c2ecf20Sopenharmony_ci case CHANNELMSG_RESCIND_CHANNELOFFER: 11178c2ecf20Sopenharmony_ci /* 11188c2ecf20Sopenharmony_ci * If we are handling the rescind message; 11198c2ecf20Sopenharmony_ci * schedule the work on the global work queue. 11208c2ecf20Sopenharmony_ci * 11218c2ecf20Sopenharmony_ci * The OFFER message and the RESCIND message should 11228c2ecf20Sopenharmony_ci * not be handled by the same serialized work queue, 11238c2ecf20Sopenharmony_ci * because the OFFER handler may call vmbus_open(), 11248c2ecf20Sopenharmony_ci * which tries to open the channel by sending an 11258c2ecf20Sopenharmony_ci * OPEN_CHANNEL message to the host and waits for 11268c2ecf20Sopenharmony_ci * the host's response; however, if the host has 11278c2ecf20Sopenharmony_ci * rescinded the channel before it receives the 11288c2ecf20Sopenharmony_ci * OPEN_CHANNEL message, the host just silently 11298c2ecf20Sopenharmony_ci * ignores the OPEN_CHANNEL message; as a result, 11308c2ecf20Sopenharmony_ci * the guest's OFFER handler hangs for ever, if we 11318c2ecf20Sopenharmony_ci * handle the RESCIND message in the same serialized 11328c2ecf20Sopenharmony_ci * work queue: the RESCIND handler can not start to 11338c2ecf20Sopenharmony_ci * run before the OFFER handler finishes. 11348c2ecf20Sopenharmony_ci */ 11358c2ecf20Sopenharmony_ci schedule_work(&ctx->work); 11368c2ecf20Sopenharmony_ci break; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci case CHANNELMSG_OFFERCHANNEL: 11398c2ecf20Sopenharmony_ci /* 11408c2ecf20Sopenharmony_ci * The host sends the offer message of a given channel 11418c2ecf20Sopenharmony_ci * before sending the rescind message of the same 11428c2ecf20Sopenharmony_ci * channel. These messages are sent to the guest's 11438c2ecf20Sopenharmony_ci * connect CPU; the guest then starts processing them 11448c2ecf20Sopenharmony_ci * in the tasklet handler on this CPU: 11458c2ecf20Sopenharmony_ci * 11468c2ecf20Sopenharmony_ci * VMBUS_CONNECT_CPU 11478c2ecf20Sopenharmony_ci * 11488c2ecf20Sopenharmony_ci * [vmbus_on_msg_dpc()] 11498c2ecf20Sopenharmony_ci * atomic_inc() // CHANNELMSG_OFFERCHANNEL 11508c2ecf20Sopenharmony_ci * queue_work() 11518c2ecf20Sopenharmony_ci * ... 11528c2ecf20Sopenharmony_ci * [vmbus_on_msg_dpc()] 11538c2ecf20Sopenharmony_ci * schedule_work() // CHANNELMSG_RESCIND_CHANNELOFFER 11548c2ecf20Sopenharmony_ci * 11558c2ecf20Sopenharmony_ci * We rely on the memory-ordering properties of the 11568c2ecf20Sopenharmony_ci * queue_work() and schedule_work() primitives, which 11578c2ecf20Sopenharmony_ci * guarantee that the atomic increment will be visible 11588c2ecf20Sopenharmony_ci * to the CPUs which will execute the offer & rescind 11598c2ecf20Sopenharmony_ci * works by the time these works will start execution. 11608c2ecf20Sopenharmony_ci */ 11618c2ecf20Sopenharmony_ci atomic_inc(&vmbus_connection.offer_in_progress); 11628c2ecf20Sopenharmony_ci fallthrough; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci default: 11658c2ecf20Sopenharmony_ci queue_work(vmbus_connection.work_queue, &ctx->work); 11668c2ecf20Sopenharmony_ci } 11678c2ecf20Sopenharmony_ci } else 11688c2ecf20Sopenharmony_ci entry->message_handler(hdr); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_cimsg_handled: 11718c2ecf20Sopenharmony_ci vmbus_signal_eom(msg, message_type); 11728c2ecf20Sopenharmony_ci} 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 11758c2ecf20Sopenharmony_ci/* 11768c2ecf20Sopenharmony_ci * Fake RESCIND_CHANNEL messages to clean up hv_sock channels by force for 11778c2ecf20Sopenharmony_ci * hibernation, because hv_sock connections can not persist across hibernation. 11788c2ecf20Sopenharmony_ci */ 11798c2ecf20Sopenharmony_cistatic void vmbus_force_channel_rescinded(struct vmbus_channel *channel) 11808c2ecf20Sopenharmony_ci{ 11818c2ecf20Sopenharmony_ci struct onmessage_work_context *ctx; 11828c2ecf20Sopenharmony_ci struct vmbus_channel_rescind_offer *rescind; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci WARN_ON(!is_hvsock_channel(channel)); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci /* 11878c2ecf20Sopenharmony_ci * Allocation size is small and the allocation should really not fail, 11888c2ecf20Sopenharmony_ci * otherwise the state of the hv_sock connections ends up in limbo. 11898c2ecf20Sopenharmony_ci */ 11908c2ecf20Sopenharmony_ci ctx = kzalloc(sizeof(*ctx) + sizeof(*rescind), 11918c2ecf20Sopenharmony_ci GFP_KERNEL | __GFP_NOFAIL); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci /* 11948c2ecf20Sopenharmony_ci * So far, these are not really used by Linux. Just set them to the 11958c2ecf20Sopenharmony_ci * reasonable values conforming to the definitions of the fields. 11968c2ecf20Sopenharmony_ci */ 11978c2ecf20Sopenharmony_ci ctx->msg.header.message_type = 1; 11988c2ecf20Sopenharmony_ci ctx->msg.header.payload_size = sizeof(*rescind); 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci /* These values are actually used by Linux. */ 12018c2ecf20Sopenharmony_ci rescind = (struct vmbus_channel_rescind_offer *)ctx->msg.payload; 12028c2ecf20Sopenharmony_ci rescind->header.msgtype = CHANNELMSG_RESCIND_CHANNELOFFER; 12038c2ecf20Sopenharmony_ci rescind->child_relid = channel->offermsg.child_relid; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci INIT_WORK(&ctx->work, vmbus_onmessage_work); 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci queue_work(vmbus_connection.work_queue, &ctx->work); 12088c2ecf20Sopenharmony_ci} 12098c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci/* 12128c2ecf20Sopenharmony_ci * Schedule all channels with events pending 12138c2ecf20Sopenharmony_ci */ 12148c2ecf20Sopenharmony_cistatic void vmbus_chan_sched(struct hv_per_cpu_context *hv_cpu) 12158c2ecf20Sopenharmony_ci{ 12168c2ecf20Sopenharmony_ci unsigned long *recv_int_page; 12178c2ecf20Sopenharmony_ci u32 maxbits, relid; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci if (vmbus_proto_version < VERSION_WIN8) { 12208c2ecf20Sopenharmony_ci maxbits = MAX_NUM_CHANNELS_SUPPORTED; 12218c2ecf20Sopenharmony_ci recv_int_page = vmbus_connection.recv_int_page; 12228c2ecf20Sopenharmony_ci } else { 12238c2ecf20Sopenharmony_ci /* 12248c2ecf20Sopenharmony_ci * When the host is win8 and beyond, the event page 12258c2ecf20Sopenharmony_ci * can be directly checked to get the id of the channel 12268c2ecf20Sopenharmony_ci * that has the interrupt pending. 12278c2ecf20Sopenharmony_ci */ 12288c2ecf20Sopenharmony_ci void *page_addr = hv_cpu->synic_event_page; 12298c2ecf20Sopenharmony_ci union hv_synic_event_flags *event 12308c2ecf20Sopenharmony_ci = (union hv_synic_event_flags *)page_addr + 12318c2ecf20Sopenharmony_ci VMBUS_MESSAGE_SINT; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci maxbits = HV_EVENT_FLAGS_COUNT; 12348c2ecf20Sopenharmony_ci recv_int_page = event->flags; 12358c2ecf20Sopenharmony_ci } 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci if (unlikely(!recv_int_page)) 12388c2ecf20Sopenharmony_ci return; 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci for_each_set_bit(relid, recv_int_page, maxbits) { 12418c2ecf20Sopenharmony_ci void (*callback_fn)(void *context); 12428c2ecf20Sopenharmony_ci struct vmbus_channel *channel; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci if (!sync_test_and_clear_bit(relid, recv_int_page)) 12458c2ecf20Sopenharmony_ci continue; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci /* Special case - vmbus channel protocol msg */ 12488c2ecf20Sopenharmony_ci if (relid == 0) 12498c2ecf20Sopenharmony_ci continue; 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci /* 12528c2ecf20Sopenharmony_ci * Pairs with the kfree_rcu() in vmbus_chan_release(). 12538c2ecf20Sopenharmony_ci * Guarantees that the channel data structure doesn't 12548c2ecf20Sopenharmony_ci * get freed while the channel pointer below is being 12558c2ecf20Sopenharmony_ci * dereferenced. 12568c2ecf20Sopenharmony_ci */ 12578c2ecf20Sopenharmony_ci rcu_read_lock(); 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci /* Find channel based on relid */ 12608c2ecf20Sopenharmony_ci channel = relid2channel(relid); 12618c2ecf20Sopenharmony_ci if (channel == NULL) 12628c2ecf20Sopenharmony_ci goto sched_unlock_rcu; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci if (channel->rescind) 12658c2ecf20Sopenharmony_ci goto sched_unlock_rcu; 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci /* 12688c2ecf20Sopenharmony_ci * Make sure that the ring buffer data structure doesn't get 12698c2ecf20Sopenharmony_ci * freed while we dereference the ring buffer pointer. Test 12708c2ecf20Sopenharmony_ci * for the channel's onchannel_callback being NULL within a 12718c2ecf20Sopenharmony_ci * sched_lock critical section. See also the inline comments 12728c2ecf20Sopenharmony_ci * in vmbus_reset_channel_cb(). 12738c2ecf20Sopenharmony_ci */ 12748c2ecf20Sopenharmony_ci spin_lock(&channel->sched_lock); 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci callback_fn = channel->onchannel_callback; 12778c2ecf20Sopenharmony_ci if (unlikely(callback_fn == NULL)) 12788c2ecf20Sopenharmony_ci goto sched_unlock; 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci trace_vmbus_chan_sched(channel); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci ++channel->interrupts; 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci switch (channel->callback_mode) { 12858c2ecf20Sopenharmony_ci case HV_CALL_ISR: 12868c2ecf20Sopenharmony_ci (*callback_fn)(channel->channel_callback_context); 12878c2ecf20Sopenharmony_ci break; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci case HV_CALL_BATCHED: 12908c2ecf20Sopenharmony_ci hv_begin_read(&channel->inbound); 12918c2ecf20Sopenharmony_ci fallthrough; 12928c2ecf20Sopenharmony_ci case HV_CALL_DIRECT: 12938c2ecf20Sopenharmony_ci tasklet_schedule(&channel->callback_event); 12948c2ecf20Sopenharmony_ci } 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_cisched_unlock: 12978c2ecf20Sopenharmony_ci spin_unlock(&channel->sched_lock); 12988c2ecf20Sopenharmony_cisched_unlock_rcu: 12998c2ecf20Sopenharmony_ci rcu_read_unlock(); 13008c2ecf20Sopenharmony_ci } 13018c2ecf20Sopenharmony_ci} 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_cistatic void vmbus_isr(void) 13048c2ecf20Sopenharmony_ci{ 13058c2ecf20Sopenharmony_ci struct hv_per_cpu_context *hv_cpu 13068c2ecf20Sopenharmony_ci = this_cpu_ptr(hv_context.cpu_context); 13078c2ecf20Sopenharmony_ci void *page_addr = hv_cpu->synic_event_page; 13088c2ecf20Sopenharmony_ci struct hv_message *msg; 13098c2ecf20Sopenharmony_ci union hv_synic_event_flags *event; 13108c2ecf20Sopenharmony_ci bool handled = false; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci if (unlikely(page_addr == NULL)) 13138c2ecf20Sopenharmony_ci return; 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci event = (union hv_synic_event_flags *)page_addr + 13168c2ecf20Sopenharmony_ci VMBUS_MESSAGE_SINT; 13178c2ecf20Sopenharmony_ci /* 13188c2ecf20Sopenharmony_ci * Check for events before checking for messages. This is the order 13198c2ecf20Sopenharmony_ci * in which events and messages are checked in Windows guests on 13208c2ecf20Sopenharmony_ci * Hyper-V, and the Windows team suggested we do the same. 13218c2ecf20Sopenharmony_ci */ 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci if ((vmbus_proto_version == VERSION_WS2008) || 13248c2ecf20Sopenharmony_ci (vmbus_proto_version == VERSION_WIN7)) { 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci /* Since we are a child, we only need to check bit 0 */ 13278c2ecf20Sopenharmony_ci if (sync_test_and_clear_bit(0, event->flags)) 13288c2ecf20Sopenharmony_ci handled = true; 13298c2ecf20Sopenharmony_ci } else { 13308c2ecf20Sopenharmony_ci /* 13318c2ecf20Sopenharmony_ci * Our host is win8 or above. The signaling mechanism 13328c2ecf20Sopenharmony_ci * has changed and we can directly look at the event page. 13338c2ecf20Sopenharmony_ci * If bit n is set then we have an interrup on the channel 13348c2ecf20Sopenharmony_ci * whose id is n. 13358c2ecf20Sopenharmony_ci */ 13368c2ecf20Sopenharmony_ci handled = true; 13378c2ecf20Sopenharmony_ci } 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci if (handled) 13408c2ecf20Sopenharmony_ci vmbus_chan_sched(hv_cpu); 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci page_addr = hv_cpu->synic_message_page; 13438c2ecf20Sopenharmony_ci msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci /* Check if there are actual msgs to be processed */ 13468c2ecf20Sopenharmony_ci if (msg->header.message_type != HVMSG_NONE) { 13478c2ecf20Sopenharmony_ci if (msg->header.message_type == HVMSG_TIMER_EXPIRED) { 13488c2ecf20Sopenharmony_ci hv_stimer0_isr(); 13498c2ecf20Sopenharmony_ci vmbus_signal_eom(msg, HVMSG_TIMER_EXPIRED); 13508c2ecf20Sopenharmony_ci } else 13518c2ecf20Sopenharmony_ci tasklet_schedule(&hv_cpu->msg_dpc); 13528c2ecf20Sopenharmony_ci } 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci add_interrupt_randomness(hv_get_vector()); 13558c2ecf20Sopenharmony_ci} 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci/* 13588c2ecf20Sopenharmony_ci * Callback from kmsg_dump. Grab as much as possible from the end of the kmsg 13598c2ecf20Sopenharmony_ci * buffer and call into Hyper-V to transfer the data. 13608c2ecf20Sopenharmony_ci */ 13618c2ecf20Sopenharmony_cistatic void hv_kmsg_dump(struct kmsg_dumper *dumper, 13628c2ecf20Sopenharmony_ci enum kmsg_dump_reason reason) 13638c2ecf20Sopenharmony_ci{ 13648c2ecf20Sopenharmony_ci size_t bytes_written; 13658c2ecf20Sopenharmony_ci phys_addr_t panic_pa; 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci /* We are only interested in panics. */ 13688c2ecf20Sopenharmony_ci if ((reason != KMSG_DUMP_PANIC) || (!sysctl_record_panic_msg)) 13698c2ecf20Sopenharmony_ci return; 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci panic_pa = virt_to_phys(hv_panic_page); 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci /* 13748c2ecf20Sopenharmony_ci * Write dump contents to the page. No need to synchronize; panic should 13758c2ecf20Sopenharmony_ci * be single-threaded. 13768c2ecf20Sopenharmony_ci */ 13778c2ecf20Sopenharmony_ci kmsg_dump_get_buffer(dumper, false, hv_panic_page, HV_HYP_PAGE_SIZE, 13788c2ecf20Sopenharmony_ci &bytes_written); 13798c2ecf20Sopenharmony_ci if (bytes_written) 13808c2ecf20Sopenharmony_ci hyperv_report_panic_msg(panic_pa, bytes_written); 13818c2ecf20Sopenharmony_ci} 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_cistatic struct kmsg_dumper hv_kmsg_dumper = { 13848c2ecf20Sopenharmony_ci .dump = hv_kmsg_dump, 13858c2ecf20Sopenharmony_ci}; 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_cistatic struct ctl_table_header *hv_ctl_table_hdr; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci/* 13908c2ecf20Sopenharmony_ci * sysctl option to allow the user to control whether kmsg data should be 13918c2ecf20Sopenharmony_ci * reported to Hyper-V on panic. 13928c2ecf20Sopenharmony_ci */ 13938c2ecf20Sopenharmony_cistatic struct ctl_table hv_ctl_table[] = { 13948c2ecf20Sopenharmony_ci { 13958c2ecf20Sopenharmony_ci .procname = "hyperv_record_panic_msg", 13968c2ecf20Sopenharmony_ci .data = &sysctl_record_panic_msg, 13978c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 13988c2ecf20Sopenharmony_ci .mode = 0644, 13998c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_minmax, 14008c2ecf20Sopenharmony_ci .extra1 = SYSCTL_ZERO, 14018c2ecf20Sopenharmony_ci .extra2 = SYSCTL_ONE 14028c2ecf20Sopenharmony_ci }, 14038c2ecf20Sopenharmony_ci {} 14048c2ecf20Sopenharmony_ci}; 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_cistatic struct ctl_table hv_root_table[] = { 14078c2ecf20Sopenharmony_ci { 14088c2ecf20Sopenharmony_ci .procname = "kernel", 14098c2ecf20Sopenharmony_ci .mode = 0555, 14108c2ecf20Sopenharmony_ci .child = hv_ctl_table 14118c2ecf20Sopenharmony_ci }, 14128c2ecf20Sopenharmony_ci {} 14138c2ecf20Sopenharmony_ci}; 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci/* 14168c2ecf20Sopenharmony_ci * vmbus_bus_init -Main vmbus driver initialization routine. 14178c2ecf20Sopenharmony_ci * 14188c2ecf20Sopenharmony_ci * Here, we 14198c2ecf20Sopenharmony_ci * - initialize the vmbus driver context 14208c2ecf20Sopenharmony_ci * - invoke the vmbus hv main init routine 14218c2ecf20Sopenharmony_ci * - retrieve the channel offers 14228c2ecf20Sopenharmony_ci */ 14238c2ecf20Sopenharmony_cistatic int vmbus_bus_init(void) 14248c2ecf20Sopenharmony_ci{ 14258c2ecf20Sopenharmony_ci int ret; 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci ret = hv_init(); 14288c2ecf20Sopenharmony_ci if (ret != 0) { 14298c2ecf20Sopenharmony_ci pr_err("Unable to initialize the hypervisor - 0x%x\n", ret); 14308c2ecf20Sopenharmony_ci return ret; 14318c2ecf20Sopenharmony_ci } 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci ret = bus_register(&hv_bus); 14348c2ecf20Sopenharmony_ci if (ret) 14358c2ecf20Sopenharmony_ci return ret; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci ret = hv_setup_vmbus_irq(vmbus_irq, vmbus_isr); 14388c2ecf20Sopenharmony_ci if (ret) 14398c2ecf20Sopenharmony_ci goto err_setup; 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci ret = hv_synic_alloc(); 14428c2ecf20Sopenharmony_ci if (ret) 14438c2ecf20Sopenharmony_ci goto err_alloc; 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci /* 14468c2ecf20Sopenharmony_ci * Initialize the per-cpu interrupt state and stimer state. 14478c2ecf20Sopenharmony_ci * Then connect to the host. 14488c2ecf20Sopenharmony_ci */ 14498c2ecf20Sopenharmony_ci ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hyperv/vmbus:online", 14508c2ecf20Sopenharmony_ci hv_synic_init, hv_synic_cleanup); 14518c2ecf20Sopenharmony_ci if (ret < 0) 14528c2ecf20Sopenharmony_ci goto err_cpuhp; 14538c2ecf20Sopenharmony_ci hyperv_cpuhp_online = ret; 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci ret = vmbus_connect(); 14568c2ecf20Sopenharmony_ci if (ret) 14578c2ecf20Sopenharmony_ci goto err_connect; 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci /* 14608c2ecf20Sopenharmony_ci * Only register if the crash MSRs are available 14618c2ecf20Sopenharmony_ci */ 14628c2ecf20Sopenharmony_ci if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) { 14638c2ecf20Sopenharmony_ci u64 hyperv_crash_ctl; 14648c2ecf20Sopenharmony_ci /* 14658c2ecf20Sopenharmony_ci * Sysctl registration is not fatal, since by default 14668c2ecf20Sopenharmony_ci * reporting is enabled. 14678c2ecf20Sopenharmony_ci */ 14688c2ecf20Sopenharmony_ci hv_ctl_table_hdr = register_sysctl_table(hv_root_table); 14698c2ecf20Sopenharmony_ci if (!hv_ctl_table_hdr) 14708c2ecf20Sopenharmony_ci pr_err("Hyper-V: sysctl table register error"); 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci /* 14738c2ecf20Sopenharmony_ci * Register for panic kmsg callback only if the right 14748c2ecf20Sopenharmony_ci * capability is supported by the hypervisor. 14758c2ecf20Sopenharmony_ci */ 14768c2ecf20Sopenharmony_ci hv_get_crash_ctl(hyperv_crash_ctl); 14778c2ecf20Sopenharmony_ci if (hyperv_crash_ctl & HV_CRASH_CTL_CRASH_NOTIFY_MSG) { 14788c2ecf20Sopenharmony_ci hv_panic_page = (void *)hv_alloc_hyperv_zeroed_page(); 14798c2ecf20Sopenharmony_ci if (hv_panic_page) { 14808c2ecf20Sopenharmony_ci ret = kmsg_dump_register(&hv_kmsg_dumper); 14818c2ecf20Sopenharmony_ci if (ret) { 14828c2ecf20Sopenharmony_ci pr_err("Hyper-V: kmsg dump register " 14838c2ecf20Sopenharmony_ci "error 0x%x\n", ret); 14848c2ecf20Sopenharmony_ci hv_free_hyperv_page( 14858c2ecf20Sopenharmony_ci (unsigned long)hv_panic_page); 14868c2ecf20Sopenharmony_ci hv_panic_page = NULL; 14878c2ecf20Sopenharmony_ci } 14888c2ecf20Sopenharmony_ci } else 14898c2ecf20Sopenharmony_ci pr_err("Hyper-V: panic message page memory " 14908c2ecf20Sopenharmony_ci "allocation failed"); 14918c2ecf20Sopenharmony_ci } 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci register_die_notifier(&hyperv_die_block); 14948c2ecf20Sopenharmony_ci } 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci /* 14978c2ecf20Sopenharmony_ci * Always register the panic notifier because we need to unload 14988c2ecf20Sopenharmony_ci * the VMbus channel connection to prevent any VMbus 14998c2ecf20Sopenharmony_ci * activity after the VM panics. 15008c2ecf20Sopenharmony_ci */ 15018c2ecf20Sopenharmony_ci atomic_notifier_chain_register(&panic_notifier_list, 15028c2ecf20Sopenharmony_ci &hyperv_panic_block); 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci vmbus_request_offers(); 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci return 0; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_cierr_connect: 15098c2ecf20Sopenharmony_ci cpuhp_remove_state(hyperv_cpuhp_online); 15108c2ecf20Sopenharmony_cierr_cpuhp: 15118c2ecf20Sopenharmony_ci hv_synic_free(); 15128c2ecf20Sopenharmony_cierr_alloc: 15138c2ecf20Sopenharmony_ci hv_remove_vmbus_irq(); 15148c2ecf20Sopenharmony_cierr_setup: 15158c2ecf20Sopenharmony_ci bus_unregister(&hv_bus); 15168c2ecf20Sopenharmony_ci unregister_sysctl_table(hv_ctl_table_hdr); 15178c2ecf20Sopenharmony_ci hv_ctl_table_hdr = NULL; 15188c2ecf20Sopenharmony_ci return ret; 15198c2ecf20Sopenharmony_ci} 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci/** 15228c2ecf20Sopenharmony_ci * __vmbus_child_driver_register() - Register a vmbus's driver 15238c2ecf20Sopenharmony_ci * @hv_driver: Pointer to driver structure you want to register 15248c2ecf20Sopenharmony_ci * @owner: owner module of the drv 15258c2ecf20Sopenharmony_ci * @mod_name: module name string 15268c2ecf20Sopenharmony_ci * 15278c2ecf20Sopenharmony_ci * Registers the given driver with Linux through the 'driver_register()' call 15288c2ecf20Sopenharmony_ci * and sets up the hyper-v vmbus handling for this driver. 15298c2ecf20Sopenharmony_ci * It will return the state of the 'driver_register()' call. 15308c2ecf20Sopenharmony_ci * 15318c2ecf20Sopenharmony_ci */ 15328c2ecf20Sopenharmony_ciint __vmbus_driver_register(struct hv_driver *hv_driver, struct module *owner, const char *mod_name) 15338c2ecf20Sopenharmony_ci{ 15348c2ecf20Sopenharmony_ci int ret; 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci pr_info("registering driver %s\n", hv_driver->name); 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci ret = vmbus_exists(); 15398c2ecf20Sopenharmony_ci if (ret < 0) 15408c2ecf20Sopenharmony_ci return ret; 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci hv_driver->driver.name = hv_driver->name; 15438c2ecf20Sopenharmony_ci hv_driver->driver.owner = owner; 15448c2ecf20Sopenharmony_ci hv_driver->driver.mod_name = mod_name; 15458c2ecf20Sopenharmony_ci hv_driver->driver.bus = &hv_bus; 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci spin_lock_init(&hv_driver->dynids.lock); 15488c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&hv_driver->dynids.list); 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci ret = driver_register(&hv_driver->driver); 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci return ret; 15538c2ecf20Sopenharmony_ci} 15548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__vmbus_driver_register); 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci/** 15578c2ecf20Sopenharmony_ci * vmbus_driver_unregister() - Unregister a vmbus's driver 15588c2ecf20Sopenharmony_ci * @hv_driver: Pointer to driver structure you want to 15598c2ecf20Sopenharmony_ci * un-register 15608c2ecf20Sopenharmony_ci * 15618c2ecf20Sopenharmony_ci * Un-register the given driver that was previous registered with a call to 15628c2ecf20Sopenharmony_ci * vmbus_driver_register() 15638c2ecf20Sopenharmony_ci */ 15648c2ecf20Sopenharmony_civoid vmbus_driver_unregister(struct hv_driver *hv_driver) 15658c2ecf20Sopenharmony_ci{ 15668c2ecf20Sopenharmony_ci pr_info("unregistering driver %s\n", hv_driver->name); 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci if (!vmbus_exists()) { 15698c2ecf20Sopenharmony_ci driver_unregister(&hv_driver->driver); 15708c2ecf20Sopenharmony_ci vmbus_free_dynids(hv_driver); 15718c2ecf20Sopenharmony_ci } 15728c2ecf20Sopenharmony_ci} 15738c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_driver_unregister); 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci/* 15778c2ecf20Sopenharmony_ci * Called when last reference to channel is gone. 15788c2ecf20Sopenharmony_ci */ 15798c2ecf20Sopenharmony_cistatic void vmbus_chan_release(struct kobject *kobj) 15808c2ecf20Sopenharmony_ci{ 15818c2ecf20Sopenharmony_ci struct vmbus_channel *channel 15828c2ecf20Sopenharmony_ci = container_of(kobj, struct vmbus_channel, kobj); 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci kfree_rcu(channel, rcu); 15858c2ecf20Sopenharmony_ci} 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_cistruct vmbus_chan_attribute { 15888c2ecf20Sopenharmony_ci struct attribute attr; 15898c2ecf20Sopenharmony_ci ssize_t (*show)(struct vmbus_channel *chan, char *buf); 15908c2ecf20Sopenharmony_ci ssize_t (*store)(struct vmbus_channel *chan, 15918c2ecf20Sopenharmony_ci const char *buf, size_t count); 15928c2ecf20Sopenharmony_ci}; 15938c2ecf20Sopenharmony_ci#define VMBUS_CHAN_ATTR(_name, _mode, _show, _store) \ 15948c2ecf20Sopenharmony_ci struct vmbus_chan_attribute chan_attr_##_name \ 15958c2ecf20Sopenharmony_ci = __ATTR(_name, _mode, _show, _store) 15968c2ecf20Sopenharmony_ci#define VMBUS_CHAN_ATTR_RW(_name) \ 15978c2ecf20Sopenharmony_ci struct vmbus_chan_attribute chan_attr_##_name = __ATTR_RW(_name) 15988c2ecf20Sopenharmony_ci#define VMBUS_CHAN_ATTR_RO(_name) \ 15998c2ecf20Sopenharmony_ci struct vmbus_chan_attribute chan_attr_##_name = __ATTR_RO(_name) 16008c2ecf20Sopenharmony_ci#define VMBUS_CHAN_ATTR_WO(_name) \ 16018c2ecf20Sopenharmony_ci struct vmbus_chan_attribute chan_attr_##_name = __ATTR_WO(_name) 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_cistatic ssize_t vmbus_chan_attr_show(struct kobject *kobj, 16048c2ecf20Sopenharmony_ci struct attribute *attr, char *buf) 16058c2ecf20Sopenharmony_ci{ 16068c2ecf20Sopenharmony_ci const struct vmbus_chan_attribute *attribute 16078c2ecf20Sopenharmony_ci = container_of(attr, struct vmbus_chan_attribute, attr); 16088c2ecf20Sopenharmony_ci struct vmbus_channel *chan 16098c2ecf20Sopenharmony_ci = container_of(kobj, struct vmbus_channel, kobj); 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci if (!attribute->show) 16128c2ecf20Sopenharmony_ci return -EIO; 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci return attribute->show(chan, buf); 16158c2ecf20Sopenharmony_ci} 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_cistatic ssize_t vmbus_chan_attr_store(struct kobject *kobj, 16188c2ecf20Sopenharmony_ci struct attribute *attr, const char *buf, 16198c2ecf20Sopenharmony_ci size_t count) 16208c2ecf20Sopenharmony_ci{ 16218c2ecf20Sopenharmony_ci const struct vmbus_chan_attribute *attribute 16228c2ecf20Sopenharmony_ci = container_of(attr, struct vmbus_chan_attribute, attr); 16238c2ecf20Sopenharmony_ci struct vmbus_channel *chan 16248c2ecf20Sopenharmony_ci = container_of(kobj, struct vmbus_channel, kobj); 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci if (!attribute->store) 16278c2ecf20Sopenharmony_ci return -EIO; 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci return attribute->store(chan, buf, count); 16308c2ecf20Sopenharmony_ci} 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_cistatic const struct sysfs_ops vmbus_chan_sysfs_ops = { 16338c2ecf20Sopenharmony_ci .show = vmbus_chan_attr_show, 16348c2ecf20Sopenharmony_ci .store = vmbus_chan_attr_store, 16358c2ecf20Sopenharmony_ci}; 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_cistatic ssize_t out_mask_show(struct vmbus_channel *channel, char *buf) 16388c2ecf20Sopenharmony_ci{ 16398c2ecf20Sopenharmony_ci struct hv_ring_buffer_info *rbi = &channel->outbound; 16408c2ecf20Sopenharmony_ci ssize_t ret; 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci mutex_lock(&rbi->ring_buffer_mutex); 16438c2ecf20Sopenharmony_ci if (!rbi->ring_buffer) { 16448c2ecf20Sopenharmony_ci mutex_unlock(&rbi->ring_buffer_mutex); 16458c2ecf20Sopenharmony_ci return -EINVAL; 16468c2ecf20Sopenharmony_ci } 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci ret = sprintf(buf, "%u\n", rbi->ring_buffer->interrupt_mask); 16498c2ecf20Sopenharmony_ci mutex_unlock(&rbi->ring_buffer_mutex); 16508c2ecf20Sopenharmony_ci return ret; 16518c2ecf20Sopenharmony_ci} 16528c2ecf20Sopenharmony_cistatic VMBUS_CHAN_ATTR_RO(out_mask); 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_cistatic ssize_t in_mask_show(struct vmbus_channel *channel, char *buf) 16558c2ecf20Sopenharmony_ci{ 16568c2ecf20Sopenharmony_ci struct hv_ring_buffer_info *rbi = &channel->inbound; 16578c2ecf20Sopenharmony_ci ssize_t ret; 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci mutex_lock(&rbi->ring_buffer_mutex); 16608c2ecf20Sopenharmony_ci if (!rbi->ring_buffer) { 16618c2ecf20Sopenharmony_ci mutex_unlock(&rbi->ring_buffer_mutex); 16628c2ecf20Sopenharmony_ci return -EINVAL; 16638c2ecf20Sopenharmony_ci } 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci ret = sprintf(buf, "%u\n", rbi->ring_buffer->interrupt_mask); 16668c2ecf20Sopenharmony_ci mutex_unlock(&rbi->ring_buffer_mutex); 16678c2ecf20Sopenharmony_ci return ret; 16688c2ecf20Sopenharmony_ci} 16698c2ecf20Sopenharmony_cistatic VMBUS_CHAN_ATTR_RO(in_mask); 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_cistatic ssize_t read_avail_show(struct vmbus_channel *channel, char *buf) 16728c2ecf20Sopenharmony_ci{ 16738c2ecf20Sopenharmony_ci struct hv_ring_buffer_info *rbi = &channel->inbound; 16748c2ecf20Sopenharmony_ci ssize_t ret; 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci mutex_lock(&rbi->ring_buffer_mutex); 16778c2ecf20Sopenharmony_ci if (!rbi->ring_buffer) { 16788c2ecf20Sopenharmony_ci mutex_unlock(&rbi->ring_buffer_mutex); 16798c2ecf20Sopenharmony_ci return -EINVAL; 16808c2ecf20Sopenharmony_ci } 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci ret = sprintf(buf, "%u\n", hv_get_bytes_to_read(rbi)); 16838c2ecf20Sopenharmony_ci mutex_unlock(&rbi->ring_buffer_mutex); 16848c2ecf20Sopenharmony_ci return ret; 16858c2ecf20Sopenharmony_ci} 16868c2ecf20Sopenharmony_cistatic VMBUS_CHAN_ATTR_RO(read_avail); 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_cistatic ssize_t write_avail_show(struct vmbus_channel *channel, char *buf) 16898c2ecf20Sopenharmony_ci{ 16908c2ecf20Sopenharmony_ci struct hv_ring_buffer_info *rbi = &channel->outbound; 16918c2ecf20Sopenharmony_ci ssize_t ret; 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci mutex_lock(&rbi->ring_buffer_mutex); 16948c2ecf20Sopenharmony_ci if (!rbi->ring_buffer) { 16958c2ecf20Sopenharmony_ci mutex_unlock(&rbi->ring_buffer_mutex); 16968c2ecf20Sopenharmony_ci return -EINVAL; 16978c2ecf20Sopenharmony_ci } 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci ret = sprintf(buf, "%u\n", hv_get_bytes_to_write(rbi)); 17008c2ecf20Sopenharmony_ci mutex_unlock(&rbi->ring_buffer_mutex); 17018c2ecf20Sopenharmony_ci return ret; 17028c2ecf20Sopenharmony_ci} 17038c2ecf20Sopenharmony_cistatic VMBUS_CHAN_ATTR_RO(write_avail); 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_cistatic ssize_t target_cpu_show(struct vmbus_channel *channel, char *buf) 17068c2ecf20Sopenharmony_ci{ 17078c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", channel->target_cpu); 17088c2ecf20Sopenharmony_ci} 17098c2ecf20Sopenharmony_cistatic ssize_t target_cpu_store(struct vmbus_channel *channel, 17108c2ecf20Sopenharmony_ci const char *buf, size_t count) 17118c2ecf20Sopenharmony_ci{ 17128c2ecf20Sopenharmony_ci u32 target_cpu, origin_cpu; 17138c2ecf20Sopenharmony_ci ssize_t ret = count; 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci if (vmbus_proto_version < VERSION_WIN10_V4_1) 17168c2ecf20Sopenharmony_ci return -EIO; 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci if (sscanf(buf, "%uu", &target_cpu) != 1) 17198c2ecf20Sopenharmony_ci return -EIO; 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci /* Validate target_cpu for the cpumask_test_cpu() operation below. */ 17228c2ecf20Sopenharmony_ci if (target_cpu >= nr_cpumask_bits) 17238c2ecf20Sopenharmony_ci return -EINVAL; 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci /* No CPUs should come up or down during this. */ 17268c2ecf20Sopenharmony_ci cpus_read_lock(); 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci if (!cpu_online(target_cpu)) { 17298c2ecf20Sopenharmony_ci cpus_read_unlock(); 17308c2ecf20Sopenharmony_ci return -EINVAL; 17318c2ecf20Sopenharmony_ci } 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci /* 17348c2ecf20Sopenharmony_ci * Synchronizes target_cpu_store() and channel closure: 17358c2ecf20Sopenharmony_ci * 17368c2ecf20Sopenharmony_ci * { Initially: state = CHANNEL_OPENED } 17378c2ecf20Sopenharmony_ci * 17388c2ecf20Sopenharmony_ci * CPU1 CPU2 17398c2ecf20Sopenharmony_ci * 17408c2ecf20Sopenharmony_ci * [target_cpu_store()] [vmbus_disconnect_ring()] 17418c2ecf20Sopenharmony_ci * 17428c2ecf20Sopenharmony_ci * LOCK channel_mutex LOCK channel_mutex 17438c2ecf20Sopenharmony_ci * LOAD r1 = state LOAD r2 = state 17448c2ecf20Sopenharmony_ci * IF (r1 == CHANNEL_OPENED) IF (r2 == CHANNEL_OPENED) 17458c2ecf20Sopenharmony_ci * SEND MODIFYCHANNEL STORE state = CHANNEL_OPEN 17468c2ecf20Sopenharmony_ci * [...] SEND CLOSECHANNEL 17478c2ecf20Sopenharmony_ci * UNLOCK channel_mutex UNLOCK channel_mutex 17488c2ecf20Sopenharmony_ci * 17498c2ecf20Sopenharmony_ci * Forbids: r1 == r2 == CHANNEL_OPENED (i.e., CPU1's LOCK precedes 17508c2ecf20Sopenharmony_ci * CPU2's LOCK) && CPU2's SEND precedes CPU1's SEND 17518c2ecf20Sopenharmony_ci * 17528c2ecf20Sopenharmony_ci * Note. The host processes the channel messages "sequentially", in 17538c2ecf20Sopenharmony_ci * the order in which they are received on a per-partition basis. 17548c2ecf20Sopenharmony_ci */ 17558c2ecf20Sopenharmony_ci mutex_lock(&vmbus_connection.channel_mutex); 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci /* 17588c2ecf20Sopenharmony_ci * Hyper-V will ignore MODIFYCHANNEL messages for "non-open" channels; 17598c2ecf20Sopenharmony_ci * avoid sending the message and fail here for such channels. 17608c2ecf20Sopenharmony_ci */ 17618c2ecf20Sopenharmony_ci if (channel->state != CHANNEL_OPENED_STATE) { 17628c2ecf20Sopenharmony_ci ret = -EIO; 17638c2ecf20Sopenharmony_ci goto cpu_store_unlock; 17648c2ecf20Sopenharmony_ci } 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci origin_cpu = channel->target_cpu; 17678c2ecf20Sopenharmony_ci if (target_cpu == origin_cpu) 17688c2ecf20Sopenharmony_ci goto cpu_store_unlock; 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci if (vmbus_send_modifychannel(channel->offermsg.child_relid, 17718c2ecf20Sopenharmony_ci hv_cpu_number_to_vp_number(target_cpu))) { 17728c2ecf20Sopenharmony_ci ret = -EIO; 17738c2ecf20Sopenharmony_ci goto cpu_store_unlock; 17748c2ecf20Sopenharmony_ci } 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci /* 17778c2ecf20Sopenharmony_ci * Warning. At this point, there is *no* guarantee that the host will 17788c2ecf20Sopenharmony_ci * have successfully processed the vmbus_send_modifychannel() request. 17798c2ecf20Sopenharmony_ci * See the header comment of vmbus_send_modifychannel() for more info. 17808c2ecf20Sopenharmony_ci * 17818c2ecf20Sopenharmony_ci * Lags in the processing of the above vmbus_send_modifychannel() can 17828c2ecf20Sopenharmony_ci * result in missed interrupts if the "old" target CPU is taken offline 17838c2ecf20Sopenharmony_ci * before Hyper-V starts sending interrupts to the "new" target CPU. 17848c2ecf20Sopenharmony_ci * But apart from this offlining scenario, the code tolerates such 17858c2ecf20Sopenharmony_ci * lags. It will function correctly even if a channel interrupt comes 17868c2ecf20Sopenharmony_ci * in on a CPU that is different from the channel target_cpu value. 17878c2ecf20Sopenharmony_ci */ 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci channel->target_cpu = target_cpu; 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci /* See init_vp_index(). */ 17928c2ecf20Sopenharmony_ci if (hv_is_perf_channel(channel)) 17938c2ecf20Sopenharmony_ci hv_update_alloced_cpus(origin_cpu, target_cpu); 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci /* Currently set only for storvsc channels. */ 17968c2ecf20Sopenharmony_ci if (channel->change_target_cpu_callback) { 17978c2ecf20Sopenharmony_ci (*channel->change_target_cpu_callback)(channel, 17988c2ecf20Sopenharmony_ci origin_cpu, target_cpu); 17998c2ecf20Sopenharmony_ci } 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_cicpu_store_unlock: 18028c2ecf20Sopenharmony_ci mutex_unlock(&vmbus_connection.channel_mutex); 18038c2ecf20Sopenharmony_ci cpus_read_unlock(); 18048c2ecf20Sopenharmony_ci return ret; 18058c2ecf20Sopenharmony_ci} 18068c2ecf20Sopenharmony_cistatic VMBUS_CHAN_ATTR(cpu, 0644, target_cpu_show, target_cpu_store); 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_cistatic ssize_t channel_pending_show(struct vmbus_channel *channel, 18098c2ecf20Sopenharmony_ci char *buf) 18108c2ecf20Sopenharmony_ci{ 18118c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", 18128c2ecf20Sopenharmony_ci channel_pending(channel, 18138c2ecf20Sopenharmony_ci vmbus_connection.monitor_pages[1])); 18148c2ecf20Sopenharmony_ci} 18158c2ecf20Sopenharmony_cistatic VMBUS_CHAN_ATTR(pending, S_IRUGO, channel_pending_show, NULL); 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_cistatic ssize_t channel_latency_show(struct vmbus_channel *channel, 18188c2ecf20Sopenharmony_ci char *buf) 18198c2ecf20Sopenharmony_ci{ 18208c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", 18218c2ecf20Sopenharmony_ci channel_latency(channel, 18228c2ecf20Sopenharmony_ci vmbus_connection.monitor_pages[1])); 18238c2ecf20Sopenharmony_ci} 18248c2ecf20Sopenharmony_cistatic VMBUS_CHAN_ATTR(latency, S_IRUGO, channel_latency_show, NULL); 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_cistatic ssize_t channel_interrupts_show(struct vmbus_channel *channel, char *buf) 18278c2ecf20Sopenharmony_ci{ 18288c2ecf20Sopenharmony_ci return sprintf(buf, "%llu\n", channel->interrupts); 18298c2ecf20Sopenharmony_ci} 18308c2ecf20Sopenharmony_cistatic VMBUS_CHAN_ATTR(interrupts, S_IRUGO, channel_interrupts_show, NULL); 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_cistatic ssize_t channel_events_show(struct vmbus_channel *channel, char *buf) 18338c2ecf20Sopenharmony_ci{ 18348c2ecf20Sopenharmony_ci return sprintf(buf, "%llu\n", channel->sig_events); 18358c2ecf20Sopenharmony_ci} 18368c2ecf20Sopenharmony_cistatic VMBUS_CHAN_ATTR(events, S_IRUGO, channel_events_show, NULL); 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_cistatic ssize_t channel_intr_in_full_show(struct vmbus_channel *channel, 18398c2ecf20Sopenharmony_ci char *buf) 18408c2ecf20Sopenharmony_ci{ 18418c2ecf20Sopenharmony_ci return sprintf(buf, "%llu\n", 18428c2ecf20Sopenharmony_ci (unsigned long long)channel->intr_in_full); 18438c2ecf20Sopenharmony_ci} 18448c2ecf20Sopenharmony_cistatic VMBUS_CHAN_ATTR(intr_in_full, 0444, channel_intr_in_full_show, NULL); 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_cistatic ssize_t channel_intr_out_empty_show(struct vmbus_channel *channel, 18478c2ecf20Sopenharmony_ci char *buf) 18488c2ecf20Sopenharmony_ci{ 18498c2ecf20Sopenharmony_ci return sprintf(buf, "%llu\n", 18508c2ecf20Sopenharmony_ci (unsigned long long)channel->intr_out_empty); 18518c2ecf20Sopenharmony_ci} 18528c2ecf20Sopenharmony_cistatic VMBUS_CHAN_ATTR(intr_out_empty, 0444, channel_intr_out_empty_show, NULL); 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_cistatic ssize_t channel_out_full_first_show(struct vmbus_channel *channel, 18558c2ecf20Sopenharmony_ci char *buf) 18568c2ecf20Sopenharmony_ci{ 18578c2ecf20Sopenharmony_ci return sprintf(buf, "%llu\n", 18588c2ecf20Sopenharmony_ci (unsigned long long)channel->out_full_first); 18598c2ecf20Sopenharmony_ci} 18608c2ecf20Sopenharmony_cistatic VMBUS_CHAN_ATTR(out_full_first, 0444, channel_out_full_first_show, NULL); 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_cistatic ssize_t channel_out_full_total_show(struct vmbus_channel *channel, 18638c2ecf20Sopenharmony_ci char *buf) 18648c2ecf20Sopenharmony_ci{ 18658c2ecf20Sopenharmony_ci return sprintf(buf, "%llu\n", 18668c2ecf20Sopenharmony_ci (unsigned long long)channel->out_full_total); 18678c2ecf20Sopenharmony_ci} 18688c2ecf20Sopenharmony_cistatic VMBUS_CHAN_ATTR(out_full_total, 0444, channel_out_full_total_show, NULL); 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_cistatic ssize_t subchannel_monitor_id_show(struct vmbus_channel *channel, 18718c2ecf20Sopenharmony_ci char *buf) 18728c2ecf20Sopenharmony_ci{ 18738c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", channel->offermsg.monitorid); 18748c2ecf20Sopenharmony_ci} 18758c2ecf20Sopenharmony_cistatic VMBUS_CHAN_ATTR(monitor_id, S_IRUGO, subchannel_monitor_id_show, NULL); 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_cistatic ssize_t subchannel_id_show(struct vmbus_channel *channel, 18788c2ecf20Sopenharmony_ci char *buf) 18798c2ecf20Sopenharmony_ci{ 18808c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", 18818c2ecf20Sopenharmony_ci channel->offermsg.offer.sub_channel_index); 18828c2ecf20Sopenharmony_ci} 18838c2ecf20Sopenharmony_cistatic VMBUS_CHAN_ATTR_RO(subchannel_id); 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_cistatic struct attribute *vmbus_chan_attrs[] = { 18868c2ecf20Sopenharmony_ci &chan_attr_out_mask.attr, 18878c2ecf20Sopenharmony_ci &chan_attr_in_mask.attr, 18888c2ecf20Sopenharmony_ci &chan_attr_read_avail.attr, 18898c2ecf20Sopenharmony_ci &chan_attr_write_avail.attr, 18908c2ecf20Sopenharmony_ci &chan_attr_cpu.attr, 18918c2ecf20Sopenharmony_ci &chan_attr_pending.attr, 18928c2ecf20Sopenharmony_ci &chan_attr_latency.attr, 18938c2ecf20Sopenharmony_ci &chan_attr_interrupts.attr, 18948c2ecf20Sopenharmony_ci &chan_attr_events.attr, 18958c2ecf20Sopenharmony_ci &chan_attr_intr_in_full.attr, 18968c2ecf20Sopenharmony_ci &chan_attr_intr_out_empty.attr, 18978c2ecf20Sopenharmony_ci &chan_attr_out_full_first.attr, 18988c2ecf20Sopenharmony_ci &chan_attr_out_full_total.attr, 18998c2ecf20Sopenharmony_ci &chan_attr_monitor_id.attr, 19008c2ecf20Sopenharmony_ci &chan_attr_subchannel_id.attr, 19018c2ecf20Sopenharmony_ci NULL 19028c2ecf20Sopenharmony_ci}; 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci/* 19058c2ecf20Sopenharmony_ci * Channel-level attribute_group callback function. Returns the permission for 19068c2ecf20Sopenharmony_ci * each attribute, and returns 0 if an attribute is not visible. 19078c2ecf20Sopenharmony_ci */ 19088c2ecf20Sopenharmony_cistatic umode_t vmbus_chan_attr_is_visible(struct kobject *kobj, 19098c2ecf20Sopenharmony_ci struct attribute *attr, int idx) 19108c2ecf20Sopenharmony_ci{ 19118c2ecf20Sopenharmony_ci const struct vmbus_channel *channel = 19128c2ecf20Sopenharmony_ci container_of(kobj, struct vmbus_channel, kobj); 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci /* Hide the monitor attributes if the monitor mechanism is not used. */ 19158c2ecf20Sopenharmony_ci if (!channel->offermsg.monitor_allocated && 19168c2ecf20Sopenharmony_ci (attr == &chan_attr_pending.attr || 19178c2ecf20Sopenharmony_ci attr == &chan_attr_latency.attr || 19188c2ecf20Sopenharmony_ci attr == &chan_attr_monitor_id.attr)) 19198c2ecf20Sopenharmony_ci return 0; 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci return attr->mode; 19228c2ecf20Sopenharmony_ci} 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_cistatic struct attribute_group vmbus_chan_group = { 19258c2ecf20Sopenharmony_ci .attrs = vmbus_chan_attrs, 19268c2ecf20Sopenharmony_ci .is_visible = vmbus_chan_attr_is_visible 19278c2ecf20Sopenharmony_ci}; 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_cistatic struct kobj_type vmbus_chan_ktype = { 19308c2ecf20Sopenharmony_ci .sysfs_ops = &vmbus_chan_sysfs_ops, 19318c2ecf20Sopenharmony_ci .release = vmbus_chan_release, 19328c2ecf20Sopenharmony_ci}; 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci/* 19358c2ecf20Sopenharmony_ci * vmbus_add_channel_kobj - setup a sub-directory under device/channels 19368c2ecf20Sopenharmony_ci */ 19378c2ecf20Sopenharmony_ciint vmbus_add_channel_kobj(struct hv_device *dev, struct vmbus_channel *channel) 19388c2ecf20Sopenharmony_ci{ 19398c2ecf20Sopenharmony_ci const struct device *device = &dev->device; 19408c2ecf20Sopenharmony_ci struct kobject *kobj = &channel->kobj; 19418c2ecf20Sopenharmony_ci u32 relid = channel->offermsg.child_relid; 19428c2ecf20Sopenharmony_ci int ret; 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_ci kobj->kset = dev->channels_kset; 19458c2ecf20Sopenharmony_ci ret = kobject_init_and_add(kobj, &vmbus_chan_ktype, NULL, 19468c2ecf20Sopenharmony_ci "%u", relid); 19478c2ecf20Sopenharmony_ci if (ret) { 19488c2ecf20Sopenharmony_ci kobject_put(kobj); 19498c2ecf20Sopenharmony_ci return ret; 19508c2ecf20Sopenharmony_ci } 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci ret = sysfs_create_group(kobj, &vmbus_chan_group); 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci if (ret) { 19558c2ecf20Sopenharmony_ci /* 19568c2ecf20Sopenharmony_ci * The calling functions' error handling paths will cleanup the 19578c2ecf20Sopenharmony_ci * empty channel directory. 19588c2ecf20Sopenharmony_ci */ 19598c2ecf20Sopenharmony_ci kobject_put(kobj); 19608c2ecf20Sopenharmony_ci dev_err(device, "Unable to set up channel sysfs files\n"); 19618c2ecf20Sopenharmony_ci return ret; 19628c2ecf20Sopenharmony_ci } 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci kobject_uevent(kobj, KOBJ_ADD); 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci return 0; 19678c2ecf20Sopenharmony_ci} 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci/* 19708c2ecf20Sopenharmony_ci * vmbus_remove_channel_attr_group - remove the channel's attribute group 19718c2ecf20Sopenharmony_ci */ 19728c2ecf20Sopenharmony_civoid vmbus_remove_channel_attr_group(struct vmbus_channel *channel) 19738c2ecf20Sopenharmony_ci{ 19748c2ecf20Sopenharmony_ci sysfs_remove_group(&channel->kobj, &vmbus_chan_group); 19758c2ecf20Sopenharmony_ci} 19768c2ecf20Sopenharmony_ci 19778c2ecf20Sopenharmony_ci/* 19788c2ecf20Sopenharmony_ci * vmbus_device_create - Creates and registers a new child device 19798c2ecf20Sopenharmony_ci * on the vmbus. 19808c2ecf20Sopenharmony_ci */ 19818c2ecf20Sopenharmony_cistruct hv_device *vmbus_device_create(const guid_t *type, 19828c2ecf20Sopenharmony_ci const guid_t *instance, 19838c2ecf20Sopenharmony_ci struct vmbus_channel *channel) 19848c2ecf20Sopenharmony_ci{ 19858c2ecf20Sopenharmony_ci struct hv_device *child_device_obj; 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci child_device_obj = kzalloc(sizeof(struct hv_device), GFP_KERNEL); 19888c2ecf20Sopenharmony_ci if (!child_device_obj) { 19898c2ecf20Sopenharmony_ci pr_err("Unable to allocate device object for child device\n"); 19908c2ecf20Sopenharmony_ci return NULL; 19918c2ecf20Sopenharmony_ci } 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci child_device_obj->channel = channel; 19948c2ecf20Sopenharmony_ci guid_copy(&child_device_obj->dev_type, type); 19958c2ecf20Sopenharmony_ci guid_copy(&child_device_obj->dev_instance, instance); 19968c2ecf20Sopenharmony_ci child_device_obj->vendor_id = 0x1414; /* MSFT vendor ID */ 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci return child_device_obj; 19998c2ecf20Sopenharmony_ci} 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci/* 20028c2ecf20Sopenharmony_ci * vmbus_device_register - Register the child device 20038c2ecf20Sopenharmony_ci */ 20048c2ecf20Sopenharmony_ciint vmbus_device_register(struct hv_device *child_device_obj) 20058c2ecf20Sopenharmony_ci{ 20068c2ecf20Sopenharmony_ci struct kobject *kobj = &child_device_obj->device.kobj; 20078c2ecf20Sopenharmony_ci int ret; 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci dev_set_name(&child_device_obj->device, "%pUl", 20108c2ecf20Sopenharmony_ci &child_device_obj->channel->offermsg.offer.if_instance); 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci child_device_obj->device.bus = &hv_bus; 20138c2ecf20Sopenharmony_ci child_device_obj->device.parent = &hv_acpi_dev->dev; 20148c2ecf20Sopenharmony_ci child_device_obj->device.release = vmbus_device_release; 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci /* 20178c2ecf20Sopenharmony_ci * Register with the LDM. This will kick off the driver/device 20188c2ecf20Sopenharmony_ci * binding...which will eventually call vmbus_match() and vmbus_probe() 20198c2ecf20Sopenharmony_ci */ 20208c2ecf20Sopenharmony_ci ret = device_register(&child_device_obj->device); 20218c2ecf20Sopenharmony_ci if (ret) { 20228c2ecf20Sopenharmony_ci pr_err("Unable to register child device\n"); 20238c2ecf20Sopenharmony_ci put_device(&child_device_obj->device); 20248c2ecf20Sopenharmony_ci return ret; 20258c2ecf20Sopenharmony_ci } 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci child_device_obj->channels_kset = kset_create_and_add("channels", 20288c2ecf20Sopenharmony_ci NULL, kobj); 20298c2ecf20Sopenharmony_ci if (!child_device_obj->channels_kset) { 20308c2ecf20Sopenharmony_ci ret = -ENOMEM; 20318c2ecf20Sopenharmony_ci goto err_dev_unregister; 20328c2ecf20Sopenharmony_ci } 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_ci ret = vmbus_add_channel_kobj(child_device_obj, 20358c2ecf20Sopenharmony_ci child_device_obj->channel); 20368c2ecf20Sopenharmony_ci if (ret) { 20378c2ecf20Sopenharmony_ci pr_err("Unable to register primary channeln"); 20388c2ecf20Sopenharmony_ci goto err_kset_unregister; 20398c2ecf20Sopenharmony_ci } 20408c2ecf20Sopenharmony_ci hv_debug_add_dev_dir(child_device_obj); 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci return 0; 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_cierr_kset_unregister: 20458c2ecf20Sopenharmony_ci kset_unregister(child_device_obj->channels_kset); 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_cierr_dev_unregister: 20488c2ecf20Sopenharmony_ci device_unregister(&child_device_obj->device); 20498c2ecf20Sopenharmony_ci return ret; 20508c2ecf20Sopenharmony_ci} 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ci/* 20538c2ecf20Sopenharmony_ci * vmbus_device_unregister - Remove the specified child device 20548c2ecf20Sopenharmony_ci * from the vmbus. 20558c2ecf20Sopenharmony_ci */ 20568c2ecf20Sopenharmony_civoid vmbus_device_unregister(struct hv_device *device_obj) 20578c2ecf20Sopenharmony_ci{ 20588c2ecf20Sopenharmony_ci pr_debug("child device %s unregistered\n", 20598c2ecf20Sopenharmony_ci dev_name(&device_obj->device)); 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ci kset_unregister(device_obj->channels_kset); 20628c2ecf20Sopenharmony_ci 20638c2ecf20Sopenharmony_ci /* 20648c2ecf20Sopenharmony_ci * Kick off the process of unregistering the device. 20658c2ecf20Sopenharmony_ci * This will call vmbus_remove() and eventually vmbus_device_release() 20668c2ecf20Sopenharmony_ci */ 20678c2ecf20Sopenharmony_ci device_unregister(&device_obj->device); 20688c2ecf20Sopenharmony_ci} 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_ci/* 20728c2ecf20Sopenharmony_ci * VMBUS is an acpi enumerated device. Get the information we 20738c2ecf20Sopenharmony_ci * need from DSDT. 20748c2ecf20Sopenharmony_ci */ 20758c2ecf20Sopenharmony_ci#define VTPM_BASE_ADDRESS 0xfed40000 20768c2ecf20Sopenharmony_cistatic acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx) 20778c2ecf20Sopenharmony_ci{ 20788c2ecf20Sopenharmony_ci resource_size_t start = 0; 20798c2ecf20Sopenharmony_ci resource_size_t end = 0; 20808c2ecf20Sopenharmony_ci struct resource *new_res; 20818c2ecf20Sopenharmony_ci struct resource **old_res = &hyperv_mmio; 20828c2ecf20Sopenharmony_ci struct resource **prev_res = NULL; 20838c2ecf20Sopenharmony_ci struct resource r; 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_ci switch (res->type) { 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci /* 20888c2ecf20Sopenharmony_ci * "Address" descriptors are for bus windows. Ignore 20898c2ecf20Sopenharmony_ci * "memory" descriptors, which are for registers on 20908c2ecf20Sopenharmony_ci * devices. 20918c2ecf20Sopenharmony_ci */ 20928c2ecf20Sopenharmony_ci case ACPI_RESOURCE_TYPE_ADDRESS32: 20938c2ecf20Sopenharmony_ci start = res->data.address32.address.minimum; 20948c2ecf20Sopenharmony_ci end = res->data.address32.address.maximum; 20958c2ecf20Sopenharmony_ci break; 20968c2ecf20Sopenharmony_ci 20978c2ecf20Sopenharmony_ci case ACPI_RESOURCE_TYPE_ADDRESS64: 20988c2ecf20Sopenharmony_ci start = res->data.address64.address.minimum; 20998c2ecf20Sopenharmony_ci end = res->data.address64.address.maximum; 21008c2ecf20Sopenharmony_ci break; 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_ci /* 21038c2ecf20Sopenharmony_ci * The IRQ information is needed only on ARM64, which Hyper-V 21048c2ecf20Sopenharmony_ci * sets up in the extended format. IRQ information is present 21058c2ecf20Sopenharmony_ci * on x86/x64 in the non-extended format but it is not used by 21068c2ecf20Sopenharmony_ci * Linux. So don't bother checking for the non-extended format. 21078c2ecf20Sopenharmony_ci */ 21088c2ecf20Sopenharmony_ci case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: 21098c2ecf20Sopenharmony_ci if (!acpi_dev_resource_interrupt(res, 0, &r)) { 21108c2ecf20Sopenharmony_ci pr_err("Unable to parse Hyper-V ACPI interrupt\n"); 21118c2ecf20Sopenharmony_ci return AE_ERROR; 21128c2ecf20Sopenharmony_ci } 21138c2ecf20Sopenharmony_ci /* ARM64 INTID for VMbus */ 21148c2ecf20Sopenharmony_ci vmbus_interrupt = res->data.extended_irq.interrupts[0]; 21158c2ecf20Sopenharmony_ci /* Linux IRQ number */ 21168c2ecf20Sopenharmony_ci vmbus_irq = r.start; 21178c2ecf20Sopenharmony_ci return AE_OK; 21188c2ecf20Sopenharmony_ci 21198c2ecf20Sopenharmony_ci default: 21208c2ecf20Sopenharmony_ci /* Unused resource type */ 21218c2ecf20Sopenharmony_ci return AE_OK; 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_ci } 21248c2ecf20Sopenharmony_ci /* 21258c2ecf20Sopenharmony_ci * Ignore ranges that are below 1MB, as they're not 21268c2ecf20Sopenharmony_ci * necessary or useful here. 21278c2ecf20Sopenharmony_ci */ 21288c2ecf20Sopenharmony_ci if (end < 0x100000) 21298c2ecf20Sopenharmony_ci return AE_OK; 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci new_res = kzalloc(sizeof(*new_res), GFP_ATOMIC); 21328c2ecf20Sopenharmony_ci if (!new_res) 21338c2ecf20Sopenharmony_ci return AE_NO_MEMORY; 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci /* If this range overlaps the virtual TPM, truncate it. */ 21368c2ecf20Sopenharmony_ci if (end > VTPM_BASE_ADDRESS && start < VTPM_BASE_ADDRESS) 21378c2ecf20Sopenharmony_ci end = VTPM_BASE_ADDRESS; 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci new_res->name = "hyperv mmio"; 21408c2ecf20Sopenharmony_ci new_res->flags = IORESOURCE_MEM; 21418c2ecf20Sopenharmony_ci new_res->start = start; 21428c2ecf20Sopenharmony_ci new_res->end = end; 21438c2ecf20Sopenharmony_ci 21448c2ecf20Sopenharmony_ci /* 21458c2ecf20Sopenharmony_ci * If two ranges are adjacent, merge them. 21468c2ecf20Sopenharmony_ci */ 21478c2ecf20Sopenharmony_ci do { 21488c2ecf20Sopenharmony_ci if (!*old_res) { 21498c2ecf20Sopenharmony_ci *old_res = new_res; 21508c2ecf20Sopenharmony_ci break; 21518c2ecf20Sopenharmony_ci } 21528c2ecf20Sopenharmony_ci 21538c2ecf20Sopenharmony_ci if (((*old_res)->end + 1) == new_res->start) { 21548c2ecf20Sopenharmony_ci (*old_res)->end = new_res->end; 21558c2ecf20Sopenharmony_ci kfree(new_res); 21568c2ecf20Sopenharmony_ci break; 21578c2ecf20Sopenharmony_ci } 21588c2ecf20Sopenharmony_ci 21598c2ecf20Sopenharmony_ci if ((*old_res)->start == new_res->end + 1) { 21608c2ecf20Sopenharmony_ci (*old_res)->start = new_res->start; 21618c2ecf20Sopenharmony_ci kfree(new_res); 21628c2ecf20Sopenharmony_ci break; 21638c2ecf20Sopenharmony_ci } 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_ci if ((*old_res)->start > new_res->end) { 21668c2ecf20Sopenharmony_ci new_res->sibling = *old_res; 21678c2ecf20Sopenharmony_ci if (prev_res) 21688c2ecf20Sopenharmony_ci (*prev_res)->sibling = new_res; 21698c2ecf20Sopenharmony_ci *old_res = new_res; 21708c2ecf20Sopenharmony_ci break; 21718c2ecf20Sopenharmony_ci } 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci prev_res = old_res; 21748c2ecf20Sopenharmony_ci old_res = &(*old_res)->sibling; 21758c2ecf20Sopenharmony_ci 21768c2ecf20Sopenharmony_ci } while (1); 21778c2ecf20Sopenharmony_ci 21788c2ecf20Sopenharmony_ci return AE_OK; 21798c2ecf20Sopenharmony_ci} 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_cistatic int vmbus_acpi_remove(struct acpi_device *device) 21828c2ecf20Sopenharmony_ci{ 21838c2ecf20Sopenharmony_ci struct resource *cur_res; 21848c2ecf20Sopenharmony_ci struct resource *next_res; 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci if (hyperv_mmio) { 21878c2ecf20Sopenharmony_ci if (fb_mmio) { 21888c2ecf20Sopenharmony_ci __release_region(hyperv_mmio, fb_mmio->start, 21898c2ecf20Sopenharmony_ci resource_size(fb_mmio)); 21908c2ecf20Sopenharmony_ci fb_mmio = NULL; 21918c2ecf20Sopenharmony_ci } 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_ci for (cur_res = hyperv_mmio; cur_res; cur_res = next_res) { 21948c2ecf20Sopenharmony_ci next_res = cur_res->sibling; 21958c2ecf20Sopenharmony_ci kfree(cur_res); 21968c2ecf20Sopenharmony_ci } 21978c2ecf20Sopenharmony_ci } 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_ci return 0; 22008c2ecf20Sopenharmony_ci} 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_cistatic void vmbus_reserve_fb(void) 22038c2ecf20Sopenharmony_ci{ 22048c2ecf20Sopenharmony_ci int size; 22058c2ecf20Sopenharmony_ci /* 22068c2ecf20Sopenharmony_ci * Make a claim for the frame buffer in the resource tree under the 22078c2ecf20Sopenharmony_ci * first node, which will be the one below 4GB. The length seems to 22088c2ecf20Sopenharmony_ci * be underreported, particularly in a Generation 1 VM. So start out 22098c2ecf20Sopenharmony_ci * reserving a larger area and make it smaller until it succeeds. 22108c2ecf20Sopenharmony_ci */ 22118c2ecf20Sopenharmony_ci 22128c2ecf20Sopenharmony_ci if (screen_info.lfb_base) { 22138c2ecf20Sopenharmony_ci if (efi_enabled(EFI_BOOT)) 22148c2ecf20Sopenharmony_ci size = max_t(__u32, screen_info.lfb_size, 0x800000); 22158c2ecf20Sopenharmony_ci else 22168c2ecf20Sopenharmony_ci size = max_t(__u32, screen_info.lfb_size, 0x4000000); 22178c2ecf20Sopenharmony_ci 22188c2ecf20Sopenharmony_ci for (; !fb_mmio && (size >= 0x100000); size >>= 1) { 22198c2ecf20Sopenharmony_ci fb_mmio = __request_region(hyperv_mmio, 22208c2ecf20Sopenharmony_ci screen_info.lfb_base, size, 22218c2ecf20Sopenharmony_ci fb_mmio_name, 0); 22228c2ecf20Sopenharmony_ci } 22238c2ecf20Sopenharmony_ci } 22248c2ecf20Sopenharmony_ci} 22258c2ecf20Sopenharmony_ci 22268c2ecf20Sopenharmony_ci/** 22278c2ecf20Sopenharmony_ci * vmbus_allocate_mmio() - Pick a memory-mapped I/O range. 22288c2ecf20Sopenharmony_ci * @new: If successful, supplied a pointer to the 22298c2ecf20Sopenharmony_ci * allocated MMIO space. 22308c2ecf20Sopenharmony_ci * @device_obj: Identifies the caller 22318c2ecf20Sopenharmony_ci * @min: Minimum guest physical address of the 22328c2ecf20Sopenharmony_ci * allocation 22338c2ecf20Sopenharmony_ci * @max: Maximum guest physical address 22348c2ecf20Sopenharmony_ci * @size: Size of the range to be allocated 22358c2ecf20Sopenharmony_ci * @align: Alignment of the range to be allocated 22368c2ecf20Sopenharmony_ci * @fb_overlap_ok: Whether this allocation can be allowed 22378c2ecf20Sopenharmony_ci * to overlap the video frame buffer. 22388c2ecf20Sopenharmony_ci * 22398c2ecf20Sopenharmony_ci * This function walks the resources granted to VMBus by the 22408c2ecf20Sopenharmony_ci * _CRS object in the ACPI namespace underneath the parent 22418c2ecf20Sopenharmony_ci * "bridge" whether that's a root PCI bus in the Generation 1 22428c2ecf20Sopenharmony_ci * case or a Module Device in the Generation 2 case. It then 22438c2ecf20Sopenharmony_ci * attempts to allocate from the global MMIO pool in a way that 22448c2ecf20Sopenharmony_ci * matches the constraints supplied in these parameters and by 22458c2ecf20Sopenharmony_ci * that _CRS. 22468c2ecf20Sopenharmony_ci * 22478c2ecf20Sopenharmony_ci * Return: 0 on success, -errno on failure 22488c2ecf20Sopenharmony_ci */ 22498c2ecf20Sopenharmony_ciint vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj, 22508c2ecf20Sopenharmony_ci resource_size_t min, resource_size_t max, 22518c2ecf20Sopenharmony_ci resource_size_t size, resource_size_t align, 22528c2ecf20Sopenharmony_ci bool fb_overlap_ok) 22538c2ecf20Sopenharmony_ci{ 22548c2ecf20Sopenharmony_ci struct resource *iter, *shadow; 22558c2ecf20Sopenharmony_ci resource_size_t range_min, range_max, start, end; 22568c2ecf20Sopenharmony_ci const char *dev_n = dev_name(&device_obj->device); 22578c2ecf20Sopenharmony_ci int retval; 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_ci retval = -ENXIO; 22608c2ecf20Sopenharmony_ci mutex_lock(&hyperv_mmio_lock); 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_ci /* 22638c2ecf20Sopenharmony_ci * If overlaps with frame buffers are allowed, then first attempt to 22648c2ecf20Sopenharmony_ci * make the allocation from within the reserved region. Because it 22658c2ecf20Sopenharmony_ci * is already reserved, no shadow allocation is necessary. 22668c2ecf20Sopenharmony_ci */ 22678c2ecf20Sopenharmony_ci if (fb_overlap_ok && fb_mmio && !(min > fb_mmio->end) && 22688c2ecf20Sopenharmony_ci !(max < fb_mmio->start)) { 22698c2ecf20Sopenharmony_ci 22708c2ecf20Sopenharmony_ci range_min = fb_mmio->start; 22718c2ecf20Sopenharmony_ci range_max = fb_mmio->end; 22728c2ecf20Sopenharmony_ci start = (range_min + align - 1) & ~(align - 1); 22738c2ecf20Sopenharmony_ci for (; start + size - 1 <= range_max; start += align) { 22748c2ecf20Sopenharmony_ci *new = request_mem_region_exclusive(start, size, dev_n); 22758c2ecf20Sopenharmony_ci if (*new) { 22768c2ecf20Sopenharmony_ci retval = 0; 22778c2ecf20Sopenharmony_ci goto exit; 22788c2ecf20Sopenharmony_ci } 22798c2ecf20Sopenharmony_ci } 22808c2ecf20Sopenharmony_ci } 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci for (iter = hyperv_mmio; iter; iter = iter->sibling) { 22838c2ecf20Sopenharmony_ci if ((iter->start >= max) || (iter->end <= min)) 22848c2ecf20Sopenharmony_ci continue; 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_ci range_min = iter->start; 22878c2ecf20Sopenharmony_ci range_max = iter->end; 22888c2ecf20Sopenharmony_ci start = (range_min + align - 1) & ~(align - 1); 22898c2ecf20Sopenharmony_ci for (; start + size - 1 <= range_max; start += align) { 22908c2ecf20Sopenharmony_ci end = start + size - 1; 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci /* Skip the whole fb_mmio region if not fb_overlap_ok */ 22938c2ecf20Sopenharmony_ci if (!fb_overlap_ok && fb_mmio && 22948c2ecf20Sopenharmony_ci (((start >= fb_mmio->start) && (start <= fb_mmio->end)) || 22958c2ecf20Sopenharmony_ci ((end >= fb_mmio->start) && (end <= fb_mmio->end)))) 22968c2ecf20Sopenharmony_ci continue; 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ci shadow = __request_region(iter, start, size, NULL, 22998c2ecf20Sopenharmony_ci IORESOURCE_BUSY); 23008c2ecf20Sopenharmony_ci if (!shadow) 23018c2ecf20Sopenharmony_ci continue; 23028c2ecf20Sopenharmony_ci 23038c2ecf20Sopenharmony_ci *new = request_mem_region_exclusive(start, size, dev_n); 23048c2ecf20Sopenharmony_ci if (*new) { 23058c2ecf20Sopenharmony_ci shadow->name = (char *)*new; 23068c2ecf20Sopenharmony_ci retval = 0; 23078c2ecf20Sopenharmony_ci goto exit; 23088c2ecf20Sopenharmony_ci } 23098c2ecf20Sopenharmony_ci 23108c2ecf20Sopenharmony_ci __release_region(iter, start, size); 23118c2ecf20Sopenharmony_ci } 23128c2ecf20Sopenharmony_ci } 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_ciexit: 23158c2ecf20Sopenharmony_ci mutex_unlock(&hyperv_mmio_lock); 23168c2ecf20Sopenharmony_ci return retval; 23178c2ecf20Sopenharmony_ci} 23188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_allocate_mmio); 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_ci/** 23218c2ecf20Sopenharmony_ci * vmbus_free_mmio() - Free a memory-mapped I/O range. 23228c2ecf20Sopenharmony_ci * @start: Base address of region to release. 23238c2ecf20Sopenharmony_ci * @size: Size of the range to be allocated 23248c2ecf20Sopenharmony_ci * 23258c2ecf20Sopenharmony_ci * This function releases anything requested by 23268c2ecf20Sopenharmony_ci * vmbus_mmio_allocate(). 23278c2ecf20Sopenharmony_ci */ 23288c2ecf20Sopenharmony_civoid vmbus_free_mmio(resource_size_t start, resource_size_t size) 23298c2ecf20Sopenharmony_ci{ 23308c2ecf20Sopenharmony_ci struct resource *iter; 23318c2ecf20Sopenharmony_ci 23328c2ecf20Sopenharmony_ci mutex_lock(&hyperv_mmio_lock); 23338c2ecf20Sopenharmony_ci for (iter = hyperv_mmio; iter; iter = iter->sibling) { 23348c2ecf20Sopenharmony_ci if ((iter->start >= start + size) || (iter->end <= start)) 23358c2ecf20Sopenharmony_ci continue; 23368c2ecf20Sopenharmony_ci 23378c2ecf20Sopenharmony_ci __release_region(iter, start, size); 23388c2ecf20Sopenharmony_ci } 23398c2ecf20Sopenharmony_ci release_mem_region(start, size); 23408c2ecf20Sopenharmony_ci mutex_unlock(&hyperv_mmio_lock); 23418c2ecf20Sopenharmony_ci 23428c2ecf20Sopenharmony_ci} 23438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_free_mmio); 23448c2ecf20Sopenharmony_ci 23458c2ecf20Sopenharmony_cistatic int vmbus_acpi_add(struct acpi_device *device) 23468c2ecf20Sopenharmony_ci{ 23478c2ecf20Sopenharmony_ci acpi_status result; 23488c2ecf20Sopenharmony_ci int ret_val = -ENODEV; 23498c2ecf20Sopenharmony_ci struct acpi_device *ancestor; 23508c2ecf20Sopenharmony_ci 23518c2ecf20Sopenharmony_ci hv_acpi_dev = device; 23528c2ecf20Sopenharmony_ci 23538c2ecf20Sopenharmony_ci result = acpi_walk_resources(device->handle, METHOD_NAME__CRS, 23548c2ecf20Sopenharmony_ci vmbus_walk_resources, NULL); 23558c2ecf20Sopenharmony_ci 23568c2ecf20Sopenharmony_ci if (ACPI_FAILURE(result)) 23578c2ecf20Sopenharmony_ci goto acpi_walk_err; 23588c2ecf20Sopenharmony_ci /* 23598c2ecf20Sopenharmony_ci * Some ancestor of the vmbus acpi device (Gen1 or Gen2 23608c2ecf20Sopenharmony_ci * firmware) is the VMOD that has the mmio ranges. Get that. 23618c2ecf20Sopenharmony_ci */ 23628c2ecf20Sopenharmony_ci for (ancestor = device->parent; ancestor; ancestor = ancestor->parent) { 23638c2ecf20Sopenharmony_ci result = acpi_walk_resources(ancestor->handle, METHOD_NAME__CRS, 23648c2ecf20Sopenharmony_ci vmbus_walk_resources, NULL); 23658c2ecf20Sopenharmony_ci 23668c2ecf20Sopenharmony_ci if (ACPI_FAILURE(result)) 23678c2ecf20Sopenharmony_ci continue; 23688c2ecf20Sopenharmony_ci if (hyperv_mmio) { 23698c2ecf20Sopenharmony_ci vmbus_reserve_fb(); 23708c2ecf20Sopenharmony_ci break; 23718c2ecf20Sopenharmony_ci } 23728c2ecf20Sopenharmony_ci } 23738c2ecf20Sopenharmony_ci ret_val = 0; 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_ciacpi_walk_err: 23768c2ecf20Sopenharmony_ci complete(&probe_event); 23778c2ecf20Sopenharmony_ci if (ret_val) 23788c2ecf20Sopenharmony_ci vmbus_acpi_remove(device); 23798c2ecf20Sopenharmony_ci return ret_val; 23808c2ecf20Sopenharmony_ci} 23818c2ecf20Sopenharmony_ci 23828c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 23838c2ecf20Sopenharmony_cistatic int vmbus_bus_suspend(struct device *dev) 23848c2ecf20Sopenharmony_ci{ 23858c2ecf20Sopenharmony_ci struct vmbus_channel *channel, *sc; 23868c2ecf20Sopenharmony_ci 23878c2ecf20Sopenharmony_ci while (atomic_read(&vmbus_connection.offer_in_progress) != 0) { 23888c2ecf20Sopenharmony_ci /* 23898c2ecf20Sopenharmony_ci * We wait here until the completion of any channel 23908c2ecf20Sopenharmony_ci * offers that are currently in progress. 23918c2ecf20Sopenharmony_ci */ 23928c2ecf20Sopenharmony_ci msleep(1); 23938c2ecf20Sopenharmony_ci } 23948c2ecf20Sopenharmony_ci 23958c2ecf20Sopenharmony_ci mutex_lock(&vmbus_connection.channel_mutex); 23968c2ecf20Sopenharmony_ci list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { 23978c2ecf20Sopenharmony_ci if (!is_hvsock_channel(channel)) 23988c2ecf20Sopenharmony_ci continue; 23998c2ecf20Sopenharmony_ci 24008c2ecf20Sopenharmony_ci vmbus_force_channel_rescinded(channel); 24018c2ecf20Sopenharmony_ci } 24028c2ecf20Sopenharmony_ci mutex_unlock(&vmbus_connection.channel_mutex); 24038c2ecf20Sopenharmony_ci 24048c2ecf20Sopenharmony_ci /* 24058c2ecf20Sopenharmony_ci * Wait until all the sub-channels and hv_sock channels have been 24068c2ecf20Sopenharmony_ci * cleaned up. Sub-channels should be destroyed upon suspend, otherwise 24078c2ecf20Sopenharmony_ci * they would conflict with the new sub-channels that will be created 24088c2ecf20Sopenharmony_ci * in the resume path. hv_sock channels should also be destroyed, but 24098c2ecf20Sopenharmony_ci * a hv_sock channel of an established hv_sock connection can not be 24108c2ecf20Sopenharmony_ci * really destroyed since it may still be referenced by the userspace 24118c2ecf20Sopenharmony_ci * application, so we just force the hv_sock channel to be rescinded 24128c2ecf20Sopenharmony_ci * by vmbus_force_channel_rescinded(), and the userspace application 24138c2ecf20Sopenharmony_ci * will thoroughly destroy the channel after hibernation. 24148c2ecf20Sopenharmony_ci * 24158c2ecf20Sopenharmony_ci * Note: the counter nr_chan_close_on_suspend may never go above 0 if 24168c2ecf20Sopenharmony_ci * the VM has no sub-channel and hv_sock channel, e.g. a 1-vCPU VM. 24178c2ecf20Sopenharmony_ci */ 24188c2ecf20Sopenharmony_ci if (atomic_read(&vmbus_connection.nr_chan_close_on_suspend) > 0) 24198c2ecf20Sopenharmony_ci wait_for_completion(&vmbus_connection.ready_for_suspend_event); 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci if (atomic_read(&vmbus_connection.nr_chan_fixup_on_resume) != 0) { 24228c2ecf20Sopenharmony_ci pr_err("Can not suspend due to a previous failed resuming\n"); 24238c2ecf20Sopenharmony_ci return -EBUSY; 24248c2ecf20Sopenharmony_ci } 24258c2ecf20Sopenharmony_ci 24268c2ecf20Sopenharmony_ci mutex_lock(&vmbus_connection.channel_mutex); 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_ci list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { 24298c2ecf20Sopenharmony_ci /* 24308c2ecf20Sopenharmony_ci * Remove the channel from the array of channels and invalidate 24318c2ecf20Sopenharmony_ci * the channel's relid. Upon resume, vmbus_onoffer() will fix 24328c2ecf20Sopenharmony_ci * up the relid (and other fields, if necessary) and add the 24338c2ecf20Sopenharmony_ci * channel back to the array. 24348c2ecf20Sopenharmony_ci */ 24358c2ecf20Sopenharmony_ci vmbus_channel_unmap_relid(channel); 24368c2ecf20Sopenharmony_ci channel->offermsg.child_relid = INVALID_RELID; 24378c2ecf20Sopenharmony_ci 24388c2ecf20Sopenharmony_ci if (is_hvsock_channel(channel)) { 24398c2ecf20Sopenharmony_ci if (!channel->rescind) { 24408c2ecf20Sopenharmony_ci pr_err("hv_sock channel not rescinded!\n"); 24418c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 24428c2ecf20Sopenharmony_ci } 24438c2ecf20Sopenharmony_ci continue; 24448c2ecf20Sopenharmony_ci } 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_ci list_for_each_entry(sc, &channel->sc_list, sc_list) { 24478c2ecf20Sopenharmony_ci pr_err("Sub-channel not deleted!\n"); 24488c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 24498c2ecf20Sopenharmony_ci } 24508c2ecf20Sopenharmony_ci 24518c2ecf20Sopenharmony_ci atomic_inc(&vmbus_connection.nr_chan_fixup_on_resume); 24528c2ecf20Sopenharmony_ci } 24538c2ecf20Sopenharmony_ci 24548c2ecf20Sopenharmony_ci mutex_unlock(&vmbus_connection.channel_mutex); 24558c2ecf20Sopenharmony_ci 24568c2ecf20Sopenharmony_ci vmbus_initiate_unload(false); 24578c2ecf20Sopenharmony_ci 24588c2ecf20Sopenharmony_ci /* Reset the event for the next resume. */ 24598c2ecf20Sopenharmony_ci reinit_completion(&vmbus_connection.ready_for_resume_event); 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_ci return 0; 24628c2ecf20Sopenharmony_ci} 24638c2ecf20Sopenharmony_ci 24648c2ecf20Sopenharmony_cistatic int vmbus_bus_resume(struct device *dev) 24658c2ecf20Sopenharmony_ci{ 24668c2ecf20Sopenharmony_ci struct vmbus_channel_msginfo *msginfo; 24678c2ecf20Sopenharmony_ci size_t msgsize; 24688c2ecf20Sopenharmony_ci int ret; 24698c2ecf20Sopenharmony_ci 24708c2ecf20Sopenharmony_ci /* 24718c2ecf20Sopenharmony_ci * We only use the 'vmbus_proto_version', which was in use before 24728c2ecf20Sopenharmony_ci * hibernation, to re-negotiate with the host. 24738c2ecf20Sopenharmony_ci */ 24748c2ecf20Sopenharmony_ci if (!vmbus_proto_version) { 24758c2ecf20Sopenharmony_ci pr_err("Invalid proto version = 0x%x\n", vmbus_proto_version); 24768c2ecf20Sopenharmony_ci return -EINVAL; 24778c2ecf20Sopenharmony_ci } 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci msgsize = sizeof(*msginfo) + 24808c2ecf20Sopenharmony_ci sizeof(struct vmbus_channel_initiate_contact); 24818c2ecf20Sopenharmony_ci 24828c2ecf20Sopenharmony_ci msginfo = kzalloc(msgsize, GFP_KERNEL); 24838c2ecf20Sopenharmony_ci 24848c2ecf20Sopenharmony_ci if (msginfo == NULL) 24858c2ecf20Sopenharmony_ci return -ENOMEM; 24868c2ecf20Sopenharmony_ci 24878c2ecf20Sopenharmony_ci ret = vmbus_negotiate_version(msginfo, vmbus_proto_version); 24888c2ecf20Sopenharmony_ci 24898c2ecf20Sopenharmony_ci kfree(msginfo); 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_ci if (ret != 0) 24928c2ecf20Sopenharmony_ci return ret; 24938c2ecf20Sopenharmony_ci 24948c2ecf20Sopenharmony_ci WARN_ON(atomic_read(&vmbus_connection.nr_chan_fixup_on_resume) == 0); 24958c2ecf20Sopenharmony_ci 24968c2ecf20Sopenharmony_ci vmbus_request_offers(); 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_ci if (wait_for_completion_timeout( 24998c2ecf20Sopenharmony_ci &vmbus_connection.ready_for_resume_event, 10 * HZ) == 0) 25008c2ecf20Sopenharmony_ci pr_err("Some vmbus device is missing after suspending?\n"); 25018c2ecf20Sopenharmony_ci 25028c2ecf20Sopenharmony_ci /* Reset the event for the next suspend. */ 25038c2ecf20Sopenharmony_ci reinit_completion(&vmbus_connection.ready_for_suspend_event); 25048c2ecf20Sopenharmony_ci 25058c2ecf20Sopenharmony_ci return 0; 25068c2ecf20Sopenharmony_ci} 25078c2ecf20Sopenharmony_ci#else 25088c2ecf20Sopenharmony_ci#define vmbus_bus_suspend NULL 25098c2ecf20Sopenharmony_ci#define vmbus_bus_resume NULL 25108c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 25118c2ecf20Sopenharmony_ci 25128c2ecf20Sopenharmony_cistatic const struct acpi_device_id vmbus_acpi_device_ids[] = { 25138c2ecf20Sopenharmony_ci {"VMBUS", 0}, 25148c2ecf20Sopenharmony_ci {"VMBus", 0}, 25158c2ecf20Sopenharmony_ci {"", 0}, 25168c2ecf20Sopenharmony_ci}; 25178c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, vmbus_acpi_device_ids); 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_ci/* 25208c2ecf20Sopenharmony_ci * Note: we must use the "no_irq" ops, otherwise hibernation can not work with 25218c2ecf20Sopenharmony_ci * PCI device assignment, because "pci_dev_pm_ops" uses the "noirq" ops: in 25228c2ecf20Sopenharmony_ci * the resume path, the pci "noirq" restore op runs before "non-noirq" op (see 25238c2ecf20Sopenharmony_ci * resume_target_kernel() -> dpm_resume_start(), and hibernation_restore() -> 25248c2ecf20Sopenharmony_ci * dpm_resume_end()). This means vmbus_bus_resume() and the pci-hyperv's 25258c2ecf20Sopenharmony_ci * resume callback must also run via the "noirq" ops. 25268c2ecf20Sopenharmony_ci * 25278c2ecf20Sopenharmony_ci * Set suspend_noirq/resume_noirq to NULL for Suspend-to-Idle: see the comment 25288c2ecf20Sopenharmony_ci * earlier in this file before vmbus_pm. 25298c2ecf20Sopenharmony_ci */ 25308c2ecf20Sopenharmony_ci 25318c2ecf20Sopenharmony_cistatic const struct dev_pm_ops vmbus_bus_pm = { 25328c2ecf20Sopenharmony_ci .suspend_noirq = NULL, 25338c2ecf20Sopenharmony_ci .resume_noirq = NULL, 25348c2ecf20Sopenharmony_ci .freeze_noirq = vmbus_bus_suspend, 25358c2ecf20Sopenharmony_ci .thaw_noirq = vmbus_bus_resume, 25368c2ecf20Sopenharmony_ci .poweroff_noirq = vmbus_bus_suspend, 25378c2ecf20Sopenharmony_ci .restore_noirq = vmbus_bus_resume 25388c2ecf20Sopenharmony_ci}; 25398c2ecf20Sopenharmony_ci 25408c2ecf20Sopenharmony_cistatic struct acpi_driver vmbus_acpi_driver = { 25418c2ecf20Sopenharmony_ci .name = "vmbus", 25428c2ecf20Sopenharmony_ci .ids = vmbus_acpi_device_ids, 25438c2ecf20Sopenharmony_ci .ops = { 25448c2ecf20Sopenharmony_ci .add = vmbus_acpi_add, 25458c2ecf20Sopenharmony_ci .remove = vmbus_acpi_remove, 25468c2ecf20Sopenharmony_ci }, 25478c2ecf20Sopenharmony_ci .drv.pm = &vmbus_bus_pm, 25488c2ecf20Sopenharmony_ci}; 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_cistatic void hv_kexec_handler(void) 25518c2ecf20Sopenharmony_ci{ 25528c2ecf20Sopenharmony_ci hv_stimer_global_cleanup(); 25538c2ecf20Sopenharmony_ci vmbus_initiate_unload(false); 25548c2ecf20Sopenharmony_ci /* Make sure conn_state is set as hv_synic_cleanup checks for it */ 25558c2ecf20Sopenharmony_ci mb(); 25568c2ecf20Sopenharmony_ci cpuhp_remove_state(hyperv_cpuhp_online); 25578c2ecf20Sopenharmony_ci}; 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_cistatic void hv_crash_handler(struct pt_regs *regs) 25608c2ecf20Sopenharmony_ci{ 25618c2ecf20Sopenharmony_ci int cpu; 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_ci vmbus_initiate_unload(true); 25648c2ecf20Sopenharmony_ci /* 25658c2ecf20Sopenharmony_ci * In crash handler we can't schedule synic cleanup for all CPUs, 25668c2ecf20Sopenharmony_ci * doing the cleanup for current CPU only. This should be sufficient 25678c2ecf20Sopenharmony_ci * for kdump. 25688c2ecf20Sopenharmony_ci */ 25698c2ecf20Sopenharmony_ci cpu = smp_processor_id(); 25708c2ecf20Sopenharmony_ci hv_stimer_cleanup(cpu); 25718c2ecf20Sopenharmony_ci hv_synic_disable_regs(cpu); 25728c2ecf20Sopenharmony_ci}; 25738c2ecf20Sopenharmony_ci 25748c2ecf20Sopenharmony_cistatic int hv_synic_suspend(void) 25758c2ecf20Sopenharmony_ci{ 25768c2ecf20Sopenharmony_ci /* 25778c2ecf20Sopenharmony_ci * When we reach here, all the non-boot CPUs have been offlined. 25788c2ecf20Sopenharmony_ci * If we're in a legacy configuration where stimer Direct Mode is 25798c2ecf20Sopenharmony_ci * not enabled, the stimers on the non-boot CPUs have been unbound 25808c2ecf20Sopenharmony_ci * in hv_synic_cleanup() -> hv_stimer_legacy_cleanup() -> 25818c2ecf20Sopenharmony_ci * hv_stimer_cleanup() -> clockevents_unbind_device(). 25828c2ecf20Sopenharmony_ci * 25838c2ecf20Sopenharmony_ci * hv_synic_suspend() only runs on CPU0 with interrupts disabled. 25848c2ecf20Sopenharmony_ci * Here we do not call hv_stimer_legacy_cleanup() on CPU0 because: 25858c2ecf20Sopenharmony_ci * 1) it's unnecessary as interrupts remain disabled between 25868c2ecf20Sopenharmony_ci * syscore_suspend() and syscore_resume(): see create_image() and 25878c2ecf20Sopenharmony_ci * resume_target_kernel() 25888c2ecf20Sopenharmony_ci * 2) the stimer on CPU0 is automatically disabled later by 25898c2ecf20Sopenharmony_ci * syscore_suspend() -> timekeeping_suspend() -> tick_suspend() -> ... 25908c2ecf20Sopenharmony_ci * -> clockevents_shutdown() -> ... -> hv_ce_shutdown() 25918c2ecf20Sopenharmony_ci * 3) a warning would be triggered if we call 25928c2ecf20Sopenharmony_ci * clockevents_unbind_device(), which may sleep, in an 25938c2ecf20Sopenharmony_ci * interrupts-disabled context. 25948c2ecf20Sopenharmony_ci */ 25958c2ecf20Sopenharmony_ci 25968c2ecf20Sopenharmony_ci hv_synic_disable_regs(0); 25978c2ecf20Sopenharmony_ci 25988c2ecf20Sopenharmony_ci return 0; 25998c2ecf20Sopenharmony_ci} 26008c2ecf20Sopenharmony_ci 26018c2ecf20Sopenharmony_cistatic void hv_synic_resume(void) 26028c2ecf20Sopenharmony_ci{ 26038c2ecf20Sopenharmony_ci hv_synic_enable_regs(0); 26048c2ecf20Sopenharmony_ci 26058c2ecf20Sopenharmony_ci /* 26068c2ecf20Sopenharmony_ci * Note: we don't need to call hv_stimer_init(0), because the timer 26078c2ecf20Sopenharmony_ci * on CPU0 is not unbound in hv_synic_suspend(), and the timer is 26088c2ecf20Sopenharmony_ci * automatically re-enabled in timekeeping_resume(). 26098c2ecf20Sopenharmony_ci */ 26108c2ecf20Sopenharmony_ci} 26118c2ecf20Sopenharmony_ci 26128c2ecf20Sopenharmony_ci/* The callbacks run only on CPU0, with irqs_disabled. */ 26138c2ecf20Sopenharmony_cistatic struct syscore_ops hv_synic_syscore_ops = { 26148c2ecf20Sopenharmony_ci .suspend = hv_synic_suspend, 26158c2ecf20Sopenharmony_ci .resume = hv_synic_resume, 26168c2ecf20Sopenharmony_ci}; 26178c2ecf20Sopenharmony_ci 26188c2ecf20Sopenharmony_cistatic int __init hv_acpi_init(void) 26198c2ecf20Sopenharmony_ci{ 26208c2ecf20Sopenharmony_ci int ret, t; 26218c2ecf20Sopenharmony_ci 26228c2ecf20Sopenharmony_ci if (!hv_is_hyperv_initialized()) 26238c2ecf20Sopenharmony_ci return -ENODEV; 26248c2ecf20Sopenharmony_ci 26258c2ecf20Sopenharmony_ci init_completion(&probe_event); 26268c2ecf20Sopenharmony_ci 26278c2ecf20Sopenharmony_ci /* 26288c2ecf20Sopenharmony_ci * Get ACPI resources first. 26298c2ecf20Sopenharmony_ci */ 26308c2ecf20Sopenharmony_ci ret = acpi_bus_register_driver(&vmbus_acpi_driver); 26318c2ecf20Sopenharmony_ci 26328c2ecf20Sopenharmony_ci if (ret) 26338c2ecf20Sopenharmony_ci return ret; 26348c2ecf20Sopenharmony_ci 26358c2ecf20Sopenharmony_ci t = wait_for_completion_timeout(&probe_event, 5*HZ); 26368c2ecf20Sopenharmony_ci if (t == 0) { 26378c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 26388c2ecf20Sopenharmony_ci goto cleanup; 26398c2ecf20Sopenharmony_ci } 26408c2ecf20Sopenharmony_ci hv_debug_init(); 26418c2ecf20Sopenharmony_ci 26428c2ecf20Sopenharmony_ci ret = vmbus_bus_init(); 26438c2ecf20Sopenharmony_ci if (ret) 26448c2ecf20Sopenharmony_ci goto cleanup; 26458c2ecf20Sopenharmony_ci 26468c2ecf20Sopenharmony_ci hv_setup_kexec_handler(hv_kexec_handler); 26478c2ecf20Sopenharmony_ci hv_setup_crash_handler(hv_crash_handler); 26488c2ecf20Sopenharmony_ci 26498c2ecf20Sopenharmony_ci register_syscore_ops(&hv_synic_syscore_ops); 26508c2ecf20Sopenharmony_ci 26518c2ecf20Sopenharmony_ci return 0; 26528c2ecf20Sopenharmony_ci 26538c2ecf20Sopenharmony_cicleanup: 26548c2ecf20Sopenharmony_ci acpi_bus_unregister_driver(&vmbus_acpi_driver); 26558c2ecf20Sopenharmony_ci hv_acpi_dev = NULL; 26568c2ecf20Sopenharmony_ci return ret; 26578c2ecf20Sopenharmony_ci} 26588c2ecf20Sopenharmony_ci 26598c2ecf20Sopenharmony_cistatic void __exit vmbus_exit(void) 26608c2ecf20Sopenharmony_ci{ 26618c2ecf20Sopenharmony_ci int cpu; 26628c2ecf20Sopenharmony_ci 26638c2ecf20Sopenharmony_ci unregister_syscore_ops(&hv_synic_syscore_ops); 26648c2ecf20Sopenharmony_ci 26658c2ecf20Sopenharmony_ci hv_remove_kexec_handler(); 26668c2ecf20Sopenharmony_ci hv_remove_crash_handler(); 26678c2ecf20Sopenharmony_ci vmbus_connection.conn_state = DISCONNECTED; 26688c2ecf20Sopenharmony_ci hv_stimer_global_cleanup(); 26698c2ecf20Sopenharmony_ci vmbus_disconnect(); 26708c2ecf20Sopenharmony_ci hv_remove_vmbus_irq(); 26718c2ecf20Sopenharmony_ci for_each_online_cpu(cpu) { 26728c2ecf20Sopenharmony_ci struct hv_per_cpu_context *hv_cpu 26738c2ecf20Sopenharmony_ci = per_cpu_ptr(hv_context.cpu_context, cpu); 26748c2ecf20Sopenharmony_ci 26758c2ecf20Sopenharmony_ci tasklet_kill(&hv_cpu->msg_dpc); 26768c2ecf20Sopenharmony_ci } 26778c2ecf20Sopenharmony_ci hv_debug_rm_all_dir(); 26788c2ecf20Sopenharmony_ci 26798c2ecf20Sopenharmony_ci vmbus_free_channels(); 26808c2ecf20Sopenharmony_ci kfree(vmbus_connection.channels); 26818c2ecf20Sopenharmony_ci 26828c2ecf20Sopenharmony_ci if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) { 26838c2ecf20Sopenharmony_ci kmsg_dump_unregister(&hv_kmsg_dumper); 26848c2ecf20Sopenharmony_ci unregister_die_notifier(&hyperv_die_block); 26858c2ecf20Sopenharmony_ci } 26868c2ecf20Sopenharmony_ci 26878c2ecf20Sopenharmony_ci /* 26888c2ecf20Sopenharmony_ci * The panic notifier is always registered, hence we should 26898c2ecf20Sopenharmony_ci * also unconditionally unregister it here as well. 26908c2ecf20Sopenharmony_ci */ 26918c2ecf20Sopenharmony_ci atomic_notifier_chain_unregister(&panic_notifier_list, 26928c2ecf20Sopenharmony_ci &hyperv_panic_block); 26938c2ecf20Sopenharmony_ci 26948c2ecf20Sopenharmony_ci free_page((unsigned long)hv_panic_page); 26958c2ecf20Sopenharmony_ci unregister_sysctl_table(hv_ctl_table_hdr); 26968c2ecf20Sopenharmony_ci hv_ctl_table_hdr = NULL; 26978c2ecf20Sopenharmony_ci bus_unregister(&hv_bus); 26988c2ecf20Sopenharmony_ci 26998c2ecf20Sopenharmony_ci cpuhp_remove_state(hyperv_cpuhp_online); 27008c2ecf20Sopenharmony_ci hv_synic_free(); 27018c2ecf20Sopenharmony_ci acpi_bus_unregister_driver(&vmbus_acpi_driver); 27028c2ecf20Sopenharmony_ci} 27038c2ecf20Sopenharmony_ci 27048c2ecf20Sopenharmony_ci 27058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 27068c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Microsoft Hyper-V VMBus Driver"); 27078c2ecf20Sopenharmony_ci 27088c2ecf20Sopenharmony_cisubsys_initcall(hv_acpi_init); 27098c2ecf20Sopenharmony_cimodule_exit(vmbus_exit); 2710