162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2009, Microsoft Corporation. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: 662306a36Sopenharmony_ci * Haiyang Zhang <haiyangz@microsoft.com> 762306a36Sopenharmony_ci * Hank Janssen <hjanssen@microsoft.com> 862306a36Sopenharmony_ci * K. Y. Srinivasan <kys@microsoft.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/device.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/sysctl.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/acpi.h> 2062306a36Sopenharmony_ci#include <linux/completion.h> 2162306a36Sopenharmony_ci#include <linux/hyperv.h> 2262306a36Sopenharmony_ci#include <linux/kernel_stat.h> 2362306a36Sopenharmony_ci#include <linux/of_address.h> 2462306a36Sopenharmony_ci#include <linux/clockchips.h> 2562306a36Sopenharmony_ci#include <linux/cpu.h> 2662306a36Sopenharmony_ci#include <linux/sched/isolation.h> 2762306a36Sopenharmony_ci#include <linux/sched/task_stack.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <linux/delay.h> 3062306a36Sopenharmony_ci#include <linux/panic_notifier.h> 3162306a36Sopenharmony_ci#include <linux/ptrace.h> 3262306a36Sopenharmony_ci#include <linux/screen_info.h> 3362306a36Sopenharmony_ci#include <linux/efi.h> 3462306a36Sopenharmony_ci#include <linux/random.h> 3562306a36Sopenharmony_ci#include <linux/kernel.h> 3662306a36Sopenharmony_ci#include <linux/syscore_ops.h> 3762306a36Sopenharmony_ci#include <linux/dma-map-ops.h> 3862306a36Sopenharmony_ci#include <linux/pci.h> 3962306a36Sopenharmony_ci#include <clocksource/hyperv_timer.h> 4062306a36Sopenharmony_ci#include <asm/mshyperv.h> 4162306a36Sopenharmony_ci#include "hyperv_vmbus.h" 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct vmbus_dynid { 4462306a36Sopenharmony_ci struct list_head node; 4562306a36Sopenharmony_ci struct hv_vmbus_device_id id; 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic struct device *hv_dev; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int hyperv_cpuhp_online; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic long __percpu *vmbus_evt; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* Values parsed from ACPI DSDT */ 5562306a36Sopenharmony_ciint vmbus_irq; 5662306a36Sopenharmony_ciint vmbus_interrupt; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * The panic notifier below is responsible solely for unloading the 6062306a36Sopenharmony_ci * vmbus connection, which is necessary in a panic event. 6162306a36Sopenharmony_ci * 6262306a36Sopenharmony_ci * Notice an intrincate relation of this notifier with Hyper-V 6362306a36Sopenharmony_ci * framebuffer panic notifier exists - we need vmbus connection alive 6462306a36Sopenharmony_ci * there in order to succeed, so we need to order both with each other 6562306a36Sopenharmony_ci * [see hvfb_on_panic()] - this is done using notifiers' priorities. 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_cistatic int hv_panic_vmbus_unload(struct notifier_block *nb, unsigned long val, 6862306a36Sopenharmony_ci void *args) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci vmbus_initiate_unload(true); 7162306a36Sopenharmony_ci return NOTIFY_DONE; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_cistatic struct notifier_block hyperv_panic_vmbus_unload_block = { 7462306a36Sopenharmony_ci .notifier_call = hv_panic_vmbus_unload, 7562306a36Sopenharmony_ci .priority = INT_MIN + 1, /* almost the latest one to execute */ 7662306a36Sopenharmony_ci}; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic const char *fb_mmio_name = "fb_range"; 7962306a36Sopenharmony_cistatic struct resource *fb_mmio; 8062306a36Sopenharmony_cistatic struct resource *hyperv_mmio; 8162306a36Sopenharmony_cistatic DEFINE_MUTEX(hyperv_mmio_lock); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic int vmbus_exists(void) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci if (hv_dev == NULL) 8662306a36Sopenharmony_ci return -ENODEV; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic u8 channel_monitor_group(const struct vmbus_channel *channel) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci return (u8)channel->offermsg.monitorid / 32; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic u8 channel_monitor_offset(const struct vmbus_channel *channel) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci return (u8)channel->offermsg.monitorid % 32; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic u32 channel_pending(const struct vmbus_channel *channel, 10262306a36Sopenharmony_ci const struct hv_monitor_page *monitor_page) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci u8 monitor_group = channel_monitor_group(channel); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return monitor_page->trigger_group[monitor_group].pending; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic u32 channel_latency(const struct vmbus_channel *channel, 11062306a36Sopenharmony_ci const struct hv_monitor_page *monitor_page) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci u8 monitor_group = channel_monitor_group(channel); 11362306a36Sopenharmony_ci u8 monitor_offset = channel_monitor_offset(channel); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return monitor_page->latency[monitor_group][monitor_offset]; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic u32 channel_conn_id(struct vmbus_channel *channel, 11962306a36Sopenharmony_ci struct hv_monitor_page *monitor_page) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci u8 monitor_group = channel_monitor_group(channel); 12262306a36Sopenharmony_ci u8 monitor_offset = channel_monitor_offset(channel); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return monitor_page->parameter[monitor_group][monitor_offset].connectionid.u.id; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic ssize_t id_show(struct device *dev, struct device_attribute *dev_attr, 12862306a36Sopenharmony_ci char *buf) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (!hv_dev->channel) 13362306a36Sopenharmony_ci return -ENODEV; 13462306a36Sopenharmony_ci return sprintf(buf, "%d\n", hv_dev->channel->offermsg.child_relid); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(id); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic ssize_t state_show(struct device *dev, struct device_attribute *dev_attr, 13962306a36Sopenharmony_ci char *buf) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (!hv_dev->channel) 14462306a36Sopenharmony_ci return -ENODEV; 14562306a36Sopenharmony_ci return sprintf(buf, "%d\n", hv_dev->channel->state); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(state); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic ssize_t monitor_id_show(struct device *dev, 15062306a36Sopenharmony_ci struct device_attribute *dev_attr, char *buf) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (!hv_dev->channel) 15562306a36Sopenharmony_ci return -ENODEV; 15662306a36Sopenharmony_ci return sprintf(buf, "%d\n", hv_dev->channel->offermsg.monitorid); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(monitor_id); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic ssize_t class_id_show(struct device *dev, 16162306a36Sopenharmony_ci struct device_attribute *dev_attr, char *buf) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (!hv_dev->channel) 16662306a36Sopenharmony_ci return -ENODEV; 16762306a36Sopenharmony_ci return sprintf(buf, "{%pUl}\n", 16862306a36Sopenharmony_ci &hv_dev->channel->offermsg.offer.if_type); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(class_id); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic ssize_t device_id_show(struct device *dev, 17362306a36Sopenharmony_ci struct device_attribute *dev_attr, char *buf) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (!hv_dev->channel) 17862306a36Sopenharmony_ci return -ENODEV; 17962306a36Sopenharmony_ci return sprintf(buf, "{%pUl}\n", 18062306a36Sopenharmony_ci &hv_dev->channel->offermsg.offer.if_instance); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(device_id); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic ssize_t modalias_show(struct device *dev, 18562306a36Sopenharmony_ci struct device_attribute *dev_attr, char *buf) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return sprintf(buf, "vmbus:%*phN\n", UUID_SIZE, &hv_dev->dev_type); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(modalias); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci#ifdef CONFIG_NUMA 19462306a36Sopenharmony_cistatic ssize_t numa_node_show(struct device *dev, 19562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (!hv_dev->channel) 20062306a36Sopenharmony_ci return -ENODEV; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci return sprintf(buf, "%d\n", cpu_to_node(hv_dev->channel->target_cpu)); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(numa_node); 20562306a36Sopenharmony_ci#endif 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic ssize_t server_monitor_pending_show(struct device *dev, 20862306a36Sopenharmony_ci struct device_attribute *dev_attr, 20962306a36Sopenharmony_ci char *buf) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (!hv_dev->channel) 21462306a36Sopenharmony_ci return -ENODEV; 21562306a36Sopenharmony_ci return sprintf(buf, "%d\n", 21662306a36Sopenharmony_ci channel_pending(hv_dev->channel, 21762306a36Sopenharmony_ci vmbus_connection.monitor_pages[0])); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(server_monitor_pending); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic ssize_t client_monitor_pending_show(struct device *dev, 22262306a36Sopenharmony_ci struct device_attribute *dev_attr, 22362306a36Sopenharmony_ci char *buf) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (!hv_dev->channel) 22862306a36Sopenharmony_ci return -ENODEV; 22962306a36Sopenharmony_ci return sprintf(buf, "%d\n", 23062306a36Sopenharmony_ci channel_pending(hv_dev->channel, 23162306a36Sopenharmony_ci vmbus_connection.monitor_pages[1])); 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(client_monitor_pending); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic ssize_t server_monitor_latency_show(struct device *dev, 23662306a36Sopenharmony_ci struct device_attribute *dev_attr, 23762306a36Sopenharmony_ci char *buf) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (!hv_dev->channel) 24262306a36Sopenharmony_ci return -ENODEV; 24362306a36Sopenharmony_ci return sprintf(buf, "%d\n", 24462306a36Sopenharmony_ci channel_latency(hv_dev->channel, 24562306a36Sopenharmony_ci vmbus_connection.monitor_pages[0])); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(server_monitor_latency); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic ssize_t client_monitor_latency_show(struct device *dev, 25062306a36Sopenharmony_ci struct device_attribute *dev_attr, 25162306a36Sopenharmony_ci char *buf) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (!hv_dev->channel) 25662306a36Sopenharmony_ci return -ENODEV; 25762306a36Sopenharmony_ci return sprintf(buf, "%d\n", 25862306a36Sopenharmony_ci channel_latency(hv_dev->channel, 25962306a36Sopenharmony_ci vmbus_connection.monitor_pages[1])); 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(client_monitor_latency); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic ssize_t server_monitor_conn_id_show(struct device *dev, 26462306a36Sopenharmony_ci struct device_attribute *dev_attr, 26562306a36Sopenharmony_ci char *buf) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (!hv_dev->channel) 27062306a36Sopenharmony_ci return -ENODEV; 27162306a36Sopenharmony_ci return sprintf(buf, "%d\n", 27262306a36Sopenharmony_ci channel_conn_id(hv_dev->channel, 27362306a36Sopenharmony_ci vmbus_connection.monitor_pages[0])); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(server_monitor_conn_id); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic ssize_t client_monitor_conn_id_show(struct device *dev, 27862306a36Sopenharmony_ci struct device_attribute *dev_attr, 27962306a36Sopenharmony_ci char *buf) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (!hv_dev->channel) 28462306a36Sopenharmony_ci return -ENODEV; 28562306a36Sopenharmony_ci return sprintf(buf, "%d\n", 28662306a36Sopenharmony_ci channel_conn_id(hv_dev->channel, 28762306a36Sopenharmony_ci vmbus_connection.monitor_pages[1])); 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(client_monitor_conn_id); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic ssize_t out_intr_mask_show(struct device *dev, 29262306a36Sopenharmony_ci struct device_attribute *dev_attr, char *buf) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 29562306a36Sopenharmony_ci struct hv_ring_buffer_debug_info outbound; 29662306a36Sopenharmony_ci int ret; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (!hv_dev->channel) 29962306a36Sopenharmony_ci return -ENODEV; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci ret = hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, 30262306a36Sopenharmony_ci &outbound); 30362306a36Sopenharmony_ci if (ret < 0) 30462306a36Sopenharmony_ci return ret; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return sprintf(buf, "%d\n", outbound.current_interrupt_mask); 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(out_intr_mask); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic ssize_t out_read_index_show(struct device *dev, 31162306a36Sopenharmony_ci struct device_attribute *dev_attr, char *buf) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 31462306a36Sopenharmony_ci struct hv_ring_buffer_debug_info outbound; 31562306a36Sopenharmony_ci int ret; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (!hv_dev->channel) 31862306a36Sopenharmony_ci return -ENODEV; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci ret = hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, 32162306a36Sopenharmony_ci &outbound); 32262306a36Sopenharmony_ci if (ret < 0) 32362306a36Sopenharmony_ci return ret; 32462306a36Sopenharmony_ci return sprintf(buf, "%d\n", outbound.current_read_index); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(out_read_index); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic ssize_t out_write_index_show(struct device *dev, 32962306a36Sopenharmony_ci struct device_attribute *dev_attr, 33062306a36Sopenharmony_ci char *buf) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 33362306a36Sopenharmony_ci struct hv_ring_buffer_debug_info outbound; 33462306a36Sopenharmony_ci int ret; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (!hv_dev->channel) 33762306a36Sopenharmony_ci return -ENODEV; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci ret = hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, 34062306a36Sopenharmony_ci &outbound); 34162306a36Sopenharmony_ci if (ret < 0) 34262306a36Sopenharmony_ci return ret; 34362306a36Sopenharmony_ci return sprintf(buf, "%d\n", outbound.current_write_index); 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(out_write_index); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic ssize_t out_read_bytes_avail_show(struct device *dev, 34862306a36Sopenharmony_ci struct device_attribute *dev_attr, 34962306a36Sopenharmony_ci char *buf) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 35262306a36Sopenharmony_ci struct hv_ring_buffer_debug_info outbound; 35362306a36Sopenharmony_ci int ret; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (!hv_dev->channel) 35662306a36Sopenharmony_ci return -ENODEV; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci ret = hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, 35962306a36Sopenharmony_ci &outbound); 36062306a36Sopenharmony_ci if (ret < 0) 36162306a36Sopenharmony_ci return ret; 36262306a36Sopenharmony_ci return sprintf(buf, "%d\n", outbound.bytes_avail_toread); 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(out_read_bytes_avail); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic ssize_t out_write_bytes_avail_show(struct device *dev, 36762306a36Sopenharmony_ci struct device_attribute *dev_attr, 36862306a36Sopenharmony_ci char *buf) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 37162306a36Sopenharmony_ci struct hv_ring_buffer_debug_info outbound; 37262306a36Sopenharmony_ci int ret; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (!hv_dev->channel) 37562306a36Sopenharmony_ci return -ENODEV; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci ret = hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, 37862306a36Sopenharmony_ci &outbound); 37962306a36Sopenharmony_ci if (ret < 0) 38062306a36Sopenharmony_ci return ret; 38162306a36Sopenharmony_ci return sprintf(buf, "%d\n", outbound.bytes_avail_towrite); 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(out_write_bytes_avail); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic ssize_t in_intr_mask_show(struct device *dev, 38662306a36Sopenharmony_ci struct device_attribute *dev_attr, char *buf) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 38962306a36Sopenharmony_ci struct hv_ring_buffer_debug_info inbound; 39062306a36Sopenharmony_ci int ret; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (!hv_dev->channel) 39362306a36Sopenharmony_ci return -ENODEV; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci ret = hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound); 39662306a36Sopenharmony_ci if (ret < 0) 39762306a36Sopenharmony_ci return ret; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci return sprintf(buf, "%d\n", inbound.current_interrupt_mask); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(in_intr_mask); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic ssize_t in_read_index_show(struct device *dev, 40462306a36Sopenharmony_ci struct device_attribute *dev_attr, char *buf) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 40762306a36Sopenharmony_ci struct hv_ring_buffer_debug_info inbound; 40862306a36Sopenharmony_ci int ret; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (!hv_dev->channel) 41162306a36Sopenharmony_ci return -ENODEV; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci ret = hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound); 41462306a36Sopenharmony_ci if (ret < 0) 41562306a36Sopenharmony_ci return ret; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci return sprintf(buf, "%d\n", inbound.current_read_index); 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(in_read_index); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic ssize_t in_write_index_show(struct device *dev, 42262306a36Sopenharmony_ci struct device_attribute *dev_attr, char *buf) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 42562306a36Sopenharmony_ci struct hv_ring_buffer_debug_info inbound; 42662306a36Sopenharmony_ci int ret; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (!hv_dev->channel) 42962306a36Sopenharmony_ci return -ENODEV; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci ret = hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound); 43262306a36Sopenharmony_ci if (ret < 0) 43362306a36Sopenharmony_ci return ret; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci return sprintf(buf, "%d\n", inbound.current_write_index); 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(in_write_index); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic ssize_t in_read_bytes_avail_show(struct device *dev, 44062306a36Sopenharmony_ci struct device_attribute *dev_attr, 44162306a36Sopenharmony_ci char *buf) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 44462306a36Sopenharmony_ci struct hv_ring_buffer_debug_info inbound; 44562306a36Sopenharmony_ci int ret; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (!hv_dev->channel) 44862306a36Sopenharmony_ci return -ENODEV; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci ret = hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound); 45162306a36Sopenharmony_ci if (ret < 0) 45262306a36Sopenharmony_ci return ret; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci return sprintf(buf, "%d\n", inbound.bytes_avail_toread); 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(in_read_bytes_avail); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic ssize_t in_write_bytes_avail_show(struct device *dev, 45962306a36Sopenharmony_ci struct device_attribute *dev_attr, 46062306a36Sopenharmony_ci char *buf) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 46362306a36Sopenharmony_ci struct hv_ring_buffer_debug_info inbound; 46462306a36Sopenharmony_ci int ret; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (!hv_dev->channel) 46762306a36Sopenharmony_ci return -ENODEV; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci ret = hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound); 47062306a36Sopenharmony_ci if (ret < 0) 47162306a36Sopenharmony_ci return ret; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci return sprintf(buf, "%d\n", inbound.bytes_avail_towrite); 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(in_write_bytes_avail); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic ssize_t channel_vp_mapping_show(struct device *dev, 47862306a36Sopenharmony_ci struct device_attribute *dev_attr, 47962306a36Sopenharmony_ci char *buf) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 48262306a36Sopenharmony_ci struct vmbus_channel *channel = hv_dev->channel, *cur_sc; 48362306a36Sopenharmony_ci int buf_size = PAGE_SIZE, n_written, tot_written; 48462306a36Sopenharmony_ci struct list_head *cur; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (!channel) 48762306a36Sopenharmony_ci return -ENODEV; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci mutex_lock(&vmbus_connection.channel_mutex); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci tot_written = snprintf(buf, buf_size, "%u:%u\n", 49262306a36Sopenharmony_ci channel->offermsg.child_relid, channel->target_cpu); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci list_for_each(cur, &channel->sc_list) { 49562306a36Sopenharmony_ci if (tot_written >= buf_size - 1) 49662306a36Sopenharmony_ci break; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci cur_sc = list_entry(cur, struct vmbus_channel, sc_list); 49962306a36Sopenharmony_ci n_written = scnprintf(buf + tot_written, 50062306a36Sopenharmony_ci buf_size - tot_written, 50162306a36Sopenharmony_ci "%u:%u\n", 50262306a36Sopenharmony_ci cur_sc->offermsg.child_relid, 50362306a36Sopenharmony_ci cur_sc->target_cpu); 50462306a36Sopenharmony_ci tot_written += n_written; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci mutex_unlock(&vmbus_connection.channel_mutex); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci return tot_written; 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(channel_vp_mapping); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic ssize_t vendor_show(struct device *dev, 51462306a36Sopenharmony_ci struct device_attribute *dev_attr, 51562306a36Sopenharmony_ci char *buf) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci return sprintf(buf, "0x%x\n", hv_dev->vendor_id); 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(vendor); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic ssize_t device_show(struct device *dev, 52462306a36Sopenharmony_ci struct device_attribute *dev_attr, 52562306a36Sopenharmony_ci char *buf) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci return sprintf(buf, "0x%x\n", hv_dev->device_id); 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(device); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic ssize_t driver_override_store(struct device *dev, 53462306a36Sopenharmony_ci struct device_attribute *attr, 53562306a36Sopenharmony_ci const char *buf, size_t count) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 53862306a36Sopenharmony_ci int ret; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci ret = driver_set_override(dev, &hv_dev->driver_override, buf, count); 54162306a36Sopenharmony_ci if (ret) 54262306a36Sopenharmony_ci return ret; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci return count; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic ssize_t driver_override_show(struct device *dev, 54862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(dev); 55162306a36Sopenharmony_ci ssize_t len; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci device_lock(dev); 55462306a36Sopenharmony_ci len = snprintf(buf, PAGE_SIZE, "%s\n", hv_dev->driver_override); 55562306a36Sopenharmony_ci device_unlock(dev); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci return len; 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_cistatic DEVICE_ATTR_RW(driver_override); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci/* Set up per device attributes in /sys/bus/vmbus/devices/<bus device> */ 56262306a36Sopenharmony_cistatic struct attribute *vmbus_dev_attrs[] = { 56362306a36Sopenharmony_ci &dev_attr_id.attr, 56462306a36Sopenharmony_ci &dev_attr_state.attr, 56562306a36Sopenharmony_ci &dev_attr_monitor_id.attr, 56662306a36Sopenharmony_ci &dev_attr_class_id.attr, 56762306a36Sopenharmony_ci &dev_attr_device_id.attr, 56862306a36Sopenharmony_ci &dev_attr_modalias.attr, 56962306a36Sopenharmony_ci#ifdef CONFIG_NUMA 57062306a36Sopenharmony_ci &dev_attr_numa_node.attr, 57162306a36Sopenharmony_ci#endif 57262306a36Sopenharmony_ci &dev_attr_server_monitor_pending.attr, 57362306a36Sopenharmony_ci &dev_attr_client_monitor_pending.attr, 57462306a36Sopenharmony_ci &dev_attr_server_monitor_latency.attr, 57562306a36Sopenharmony_ci &dev_attr_client_monitor_latency.attr, 57662306a36Sopenharmony_ci &dev_attr_server_monitor_conn_id.attr, 57762306a36Sopenharmony_ci &dev_attr_client_monitor_conn_id.attr, 57862306a36Sopenharmony_ci &dev_attr_out_intr_mask.attr, 57962306a36Sopenharmony_ci &dev_attr_out_read_index.attr, 58062306a36Sopenharmony_ci &dev_attr_out_write_index.attr, 58162306a36Sopenharmony_ci &dev_attr_out_read_bytes_avail.attr, 58262306a36Sopenharmony_ci &dev_attr_out_write_bytes_avail.attr, 58362306a36Sopenharmony_ci &dev_attr_in_intr_mask.attr, 58462306a36Sopenharmony_ci &dev_attr_in_read_index.attr, 58562306a36Sopenharmony_ci &dev_attr_in_write_index.attr, 58662306a36Sopenharmony_ci &dev_attr_in_read_bytes_avail.attr, 58762306a36Sopenharmony_ci &dev_attr_in_write_bytes_avail.attr, 58862306a36Sopenharmony_ci &dev_attr_channel_vp_mapping.attr, 58962306a36Sopenharmony_ci &dev_attr_vendor.attr, 59062306a36Sopenharmony_ci &dev_attr_device.attr, 59162306a36Sopenharmony_ci &dev_attr_driver_override.attr, 59262306a36Sopenharmony_ci NULL, 59362306a36Sopenharmony_ci}; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci/* 59662306a36Sopenharmony_ci * Device-level attribute_group callback function. Returns the permission for 59762306a36Sopenharmony_ci * each attribute, and returns 0 if an attribute is not visible. 59862306a36Sopenharmony_ci */ 59962306a36Sopenharmony_cistatic umode_t vmbus_dev_attr_is_visible(struct kobject *kobj, 60062306a36Sopenharmony_ci struct attribute *attr, int idx) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 60362306a36Sopenharmony_ci const struct hv_device *hv_dev = device_to_hv_device(dev); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci /* Hide the monitor attributes if the monitor mechanism is not used. */ 60662306a36Sopenharmony_ci if (!hv_dev->channel->offermsg.monitor_allocated && 60762306a36Sopenharmony_ci (attr == &dev_attr_monitor_id.attr || 60862306a36Sopenharmony_ci attr == &dev_attr_server_monitor_pending.attr || 60962306a36Sopenharmony_ci attr == &dev_attr_client_monitor_pending.attr || 61062306a36Sopenharmony_ci attr == &dev_attr_server_monitor_latency.attr || 61162306a36Sopenharmony_ci attr == &dev_attr_client_monitor_latency.attr || 61262306a36Sopenharmony_ci attr == &dev_attr_server_monitor_conn_id.attr || 61362306a36Sopenharmony_ci attr == &dev_attr_client_monitor_conn_id.attr)) 61462306a36Sopenharmony_ci return 0; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci return attr->mode; 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic const struct attribute_group vmbus_dev_group = { 62062306a36Sopenharmony_ci .attrs = vmbus_dev_attrs, 62162306a36Sopenharmony_ci .is_visible = vmbus_dev_attr_is_visible 62262306a36Sopenharmony_ci}; 62362306a36Sopenharmony_ci__ATTRIBUTE_GROUPS(vmbus_dev); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci/* Set up the attribute for /sys/bus/vmbus/hibernation */ 62662306a36Sopenharmony_cistatic ssize_t hibernation_show(const struct bus_type *bus, char *buf) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci return sprintf(buf, "%d\n", !!hv_is_hibernation_supported()); 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic BUS_ATTR_RO(hibernation); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic struct attribute *vmbus_bus_attrs[] = { 63462306a36Sopenharmony_ci &bus_attr_hibernation.attr, 63562306a36Sopenharmony_ci NULL, 63662306a36Sopenharmony_ci}; 63762306a36Sopenharmony_cistatic const struct attribute_group vmbus_bus_group = { 63862306a36Sopenharmony_ci .attrs = vmbus_bus_attrs, 63962306a36Sopenharmony_ci}; 64062306a36Sopenharmony_ci__ATTRIBUTE_GROUPS(vmbus_bus); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci/* 64362306a36Sopenharmony_ci * vmbus_uevent - add uevent for our device 64462306a36Sopenharmony_ci * 64562306a36Sopenharmony_ci * This routine is invoked when a device is added or removed on the vmbus to 64662306a36Sopenharmony_ci * generate a uevent to udev in the userspace. The udev will then look at its 64762306a36Sopenharmony_ci * rule and the uevent generated here to load the appropriate driver 64862306a36Sopenharmony_ci * 64962306a36Sopenharmony_ci * The alias string will be of the form vmbus:guid where guid is the string 65062306a36Sopenharmony_ci * representation of the device guid (each byte of the guid will be 65162306a36Sopenharmony_ci * represented with two hex characters. 65262306a36Sopenharmony_ci */ 65362306a36Sopenharmony_cistatic int vmbus_uevent(const struct device *device, struct kobj_uevent_env *env) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci const struct hv_device *dev = device_to_hv_device(device); 65662306a36Sopenharmony_ci const char *format = "MODALIAS=vmbus:%*phN"; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci return add_uevent_var(env, format, UUID_SIZE, &dev->dev_type); 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic const struct hv_vmbus_device_id * 66262306a36Sopenharmony_cihv_vmbus_dev_match(const struct hv_vmbus_device_id *id, const guid_t *guid) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci if (id == NULL) 66562306a36Sopenharmony_ci return NULL; /* empty device table */ 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci for (; !guid_is_null(&id->guid); id++) 66862306a36Sopenharmony_ci if (guid_equal(&id->guid, guid)) 66962306a36Sopenharmony_ci return id; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci return NULL; 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic const struct hv_vmbus_device_id * 67562306a36Sopenharmony_cihv_vmbus_dynid_match(struct hv_driver *drv, const guid_t *guid) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci const struct hv_vmbus_device_id *id = NULL; 67862306a36Sopenharmony_ci struct vmbus_dynid *dynid; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci spin_lock(&drv->dynids.lock); 68162306a36Sopenharmony_ci list_for_each_entry(dynid, &drv->dynids.list, node) { 68262306a36Sopenharmony_ci if (guid_equal(&dynid->id.guid, guid)) { 68362306a36Sopenharmony_ci id = &dynid->id; 68462306a36Sopenharmony_ci break; 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci spin_unlock(&drv->dynids.lock); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci return id; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic const struct hv_vmbus_device_id vmbus_device_null; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci/* 69562306a36Sopenharmony_ci * Return a matching hv_vmbus_device_id pointer. 69662306a36Sopenharmony_ci * If there is no match, return NULL. 69762306a36Sopenharmony_ci */ 69862306a36Sopenharmony_cistatic const struct hv_vmbus_device_id *hv_vmbus_get_id(struct hv_driver *drv, 69962306a36Sopenharmony_ci struct hv_device *dev) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci const guid_t *guid = &dev->dev_type; 70262306a36Sopenharmony_ci const struct hv_vmbus_device_id *id; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci /* When driver_override is set, only bind to the matching driver */ 70562306a36Sopenharmony_ci if (dev->driver_override && strcmp(dev->driver_override, drv->name)) 70662306a36Sopenharmony_ci return NULL; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci /* Look at the dynamic ids first, before the static ones */ 70962306a36Sopenharmony_ci id = hv_vmbus_dynid_match(drv, guid); 71062306a36Sopenharmony_ci if (!id) 71162306a36Sopenharmony_ci id = hv_vmbus_dev_match(drv->id_table, guid); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci /* driver_override will always match, send a dummy id */ 71462306a36Sopenharmony_ci if (!id && dev->driver_override) 71562306a36Sopenharmony_ci id = &vmbus_device_null; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci return id; 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci/* vmbus_add_dynid - add a new device ID to this driver and re-probe devices */ 72162306a36Sopenharmony_cistatic int vmbus_add_dynid(struct hv_driver *drv, guid_t *guid) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci struct vmbus_dynid *dynid; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); 72662306a36Sopenharmony_ci if (!dynid) 72762306a36Sopenharmony_ci return -ENOMEM; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci dynid->id.guid = *guid; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci spin_lock(&drv->dynids.lock); 73262306a36Sopenharmony_ci list_add_tail(&dynid->node, &drv->dynids.list); 73362306a36Sopenharmony_ci spin_unlock(&drv->dynids.lock); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci return driver_attach(&drv->driver); 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic void vmbus_free_dynids(struct hv_driver *drv) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci struct vmbus_dynid *dynid, *n; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci spin_lock(&drv->dynids.lock); 74362306a36Sopenharmony_ci list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) { 74462306a36Sopenharmony_ci list_del(&dynid->node); 74562306a36Sopenharmony_ci kfree(dynid); 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci spin_unlock(&drv->dynids.lock); 74862306a36Sopenharmony_ci} 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci/* 75162306a36Sopenharmony_ci * store_new_id - sysfs frontend to vmbus_add_dynid() 75262306a36Sopenharmony_ci * 75362306a36Sopenharmony_ci * Allow GUIDs to be added to an existing driver via sysfs. 75462306a36Sopenharmony_ci */ 75562306a36Sopenharmony_cistatic ssize_t new_id_store(struct device_driver *driver, const char *buf, 75662306a36Sopenharmony_ci size_t count) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci struct hv_driver *drv = drv_to_hv_drv(driver); 75962306a36Sopenharmony_ci guid_t guid; 76062306a36Sopenharmony_ci ssize_t retval; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci retval = guid_parse(buf, &guid); 76362306a36Sopenharmony_ci if (retval) 76462306a36Sopenharmony_ci return retval; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci if (hv_vmbus_dynid_match(drv, &guid)) 76762306a36Sopenharmony_ci return -EEXIST; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci retval = vmbus_add_dynid(drv, &guid); 77062306a36Sopenharmony_ci if (retval) 77162306a36Sopenharmony_ci return retval; 77262306a36Sopenharmony_ci return count; 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_cistatic DRIVER_ATTR_WO(new_id); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci/* 77762306a36Sopenharmony_ci * store_remove_id - remove a PCI device ID from this driver 77862306a36Sopenharmony_ci * 77962306a36Sopenharmony_ci * Removes a dynamic pci device ID to this driver. 78062306a36Sopenharmony_ci */ 78162306a36Sopenharmony_cistatic ssize_t remove_id_store(struct device_driver *driver, const char *buf, 78262306a36Sopenharmony_ci size_t count) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci struct hv_driver *drv = drv_to_hv_drv(driver); 78562306a36Sopenharmony_ci struct vmbus_dynid *dynid, *n; 78662306a36Sopenharmony_ci guid_t guid; 78762306a36Sopenharmony_ci ssize_t retval; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci retval = guid_parse(buf, &guid); 79062306a36Sopenharmony_ci if (retval) 79162306a36Sopenharmony_ci return retval; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci retval = -ENODEV; 79462306a36Sopenharmony_ci spin_lock(&drv->dynids.lock); 79562306a36Sopenharmony_ci list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) { 79662306a36Sopenharmony_ci struct hv_vmbus_device_id *id = &dynid->id; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci if (guid_equal(&id->guid, &guid)) { 79962306a36Sopenharmony_ci list_del(&dynid->node); 80062306a36Sopenharmony_ci kfree(dynid); 80162306a36Sopenharmony_ci retval = count; 80262306a36Sopenharmony_ci break; 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci spin_unlock(&drv->dynids.lock); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci return retval; 80862306a36Sopenharmony_ci} 80962306a36Sopenharmony_cistatic DRIVER_ATTR_WO(remove_id); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cistatic struct attribute *vmbus_drv_attrs[] = { 81262306a36Sopenharmony_ci &driver_attr_new_id.attr, 81362306a36Sopenharmony_ci &driver_attr_remove_id.attr, 81462306a36Sopenharmony_ci NULL, 81562306a36Sopenharmony_ci}; 81662306a36Sopenharmony_ciATTRIBUTE_GROUPS(vmbus_drv); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci/* 82062306a36Sopenharmony_ci * vmbus_match - Attempt to match the specified device to the specified driver 82162306a36Sopenharmony_ci */ 82262306a36Sopenharmony_cistatic int vmbus_match(struct device *device, struct device_driver *driver) 82362306a36Sopenharmony_ci{ 82462306a36Sopenharmony_ci struct hv_driver *drv = drv_to_hv_drv(driver); 82562306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(device); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci /* The hv_sock driver handles all hv_sock offers. */ 82862306a36Sopenharmony_ci if (is_hvsock_channel(hv_dev->channel)) 82962306a36Sopenharmony_ci return drv->hvsock; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci if (hv_vmbus_get_id(drv, hv_dev)) 83262306a36Sopenharmony_ci return 1; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci return 0; 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci/* 83862306a36Sopenharmony_ci * vmbus_probe - Add the new vmbus's child device 83962306a36Sopenharmony_ci */ 84062306a36Sopenharmony_cistatic int vmbus_probe(struct device *child_device) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci int ret = 0; 84362306a36Sopenharmony_ci struct hv_driver *drv = 84462306a36Sopenharmony_ci drv_to_hv_drv(child_device->driver); 84562306a36Sopenharmony_ci struct hv_device *dev = device_to_hv_device(child_device); 84662306a36Sopenharmony_ci const struct hv_vmbus_device_id *dev_id; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci dev_id = hv_vmbus_get_id(drv, dev); 84962306a36Sopenharmony_ci if (drv->probe) { 85062306a36Sopenharmony_ci ret = drv->probe(dev, dev_id); 85162306a36Sopenharmony_ci if (ret != 0) 85262306a36Sopenharmony_ci pr_err("probe failed for device %s (%d)\n", 85362306a36Sopenharmony_ci dev_name(child_device), ret); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci } else { 85662306a36Sopenharmony_ci pr_err("probe not set for driver %s\n", 85762306a36Sopenharmony_ci dev_name(child_device)); 85862306a36Sopenharmony_ci ret = -ENODEV; 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci return ret; 86162306a36Sopenharmony_ci} 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci/* 86462306a36Sopenharmony_ci * vmbus_dma_configure -- Configure DMA coherence for VMbus device 86562306a36Sopenharmony_ci */ 86662306a36Sopenharmony_cistatic int vmbus_dma_configure(struct device *child_device) 86762306a36Sopenharmony_ci{ 86862306a36Sopenharmony_ci /* 86962306a36Sopenharmony_ci * On ARM64, propagate the DMA coherence setting from the top level 87062306a36Sopenharmony_ci * VMbus ACPI device to the child VMbus device being added here. 87162306a36Sopenharmony_ci * On x86/x64 coherence is assumed and these calls have no effect. 87262306a36Sopenharmony_ci */ 87362306a36Sopenharmony_ci hv_setup_dma_ops(child_device, 87462306a36Sopenharmony_ci device_get_dma_attr(hv_dev) == DEV_DMA_COHERENT); 87562306a36Sopenharmony_ci return 0; 87662306a36Sopenharmony_ci} 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci/* 87962306a36Sopenharmony_ci * vmbus_remove - Remove a vmbus device 88062306a36Sopenharmony_ci */ 88162306a36Sopenharmony_cistatic void vmbus_remove(struct device *child_device) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci struct hv_driver *drv; 88462306a36Sopenharmony_ci struct hv_device *dev = device_to_hv_device(child_device); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci if (child_device->driver) { 88762306a36Sopenharmony_ci drv = drv_to_hv_drv(child_device->driver); 88862306a36Sopenharmony_ci if (drv->remove) 88962306a36Sopenharmony_ci drv->remove(dev); 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci/* 89462306a36Sopenharmony_ci * vmbus_shutdown - Shutdown a vmbus device 89562306a36Sopenharmony_ci */ 89662306a36Sopenharmony_cistatic void vmbus_shutdown(struct device *child_device) 89762306a36Sopenharmony_ci{ 89862306a36Sopenharmony_ci struct hv_driver *drv; 89962306a36Sopenharmony_ci struct hv_device *dev = device_to_hv_device(child_device); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci /* The device may not be attached yet */ 90362306a36Sopenharmony_ci if (!child_device->driver) 90462306a36Sopenharmony_ci return; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci drv = drv_to_hv_drv(child_device->driver); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci if (drv->shutdown) 90962306a36Sopenharmony_ci drv->shutdown(dev); 91062306a36Sopenharmony_ci} 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 91362306a36Sopenharmony_ci/* 91462306a36Sopenharmony_ci * vmbus_suspend - Suspend a vmbus device 91562306a36Sopenharmony_ci */ 91662306a36Sopenharmony_cistatic int vmbus_suspend(struct device *child_device) 91762306a36Sopenharmony_ci{ 91862306a36Sopenharmony_ci struct hv_driver *drv; 91962306a36Sopenharmony_ci struct hv_device *dev = device_to_hv_device(child_device); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci /* The device may not be attached yet */ 92262306a36Sopenharmony_ci if (!child_device->driver) 92362306a36Sopenharmony_ci return 0; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci drv = drv_to_hv_drv(child_device->driver); 92662306a36Sopenharmony_ci if (!drv->suspend) 92762306a36Sopenharmony_ci return -EOPNOTSUPP; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci return drv->suspend(dev); 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci/* 93362306a36Sopenharmony_ci * vmbus_resume - Resume a vmbus device 93462306a36Sopenharmony_ci */ 93562306a36Sopenharmony_cistatic int vmbus_resume(struct device *child_device) 93662306a36Sopenharmony_ci{ 93762306a36Sopenharmony_ci struct hv_driver *drv; 93862306a36Sopenharmony_ci struct hv_device *dev = device_to_hv_device(child_device); 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci /* The device may not be attached yet */ 94162306a36Sopenharmony_ci if (!child_device->driver) 94262306a36Sopenharmony_ci return 0; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci drv = drv_to_hv_drv(child_device->driver); 94562306a36Sopenharmony_ci if (!drv->resume) 94662306a36Sopenharmony_ci return -EOPNOTSUPP; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci return drv->resume(dev); 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci#else 95162306a36Sopenharmony_ci#define vmbus_suspend NULL 95262306a36Sopenharmony_ci#define vmbus_resume NULL 95362306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci/* 95662306a36Sopenharmony_ci * vmbus_device_release - Final callback release of the vmbus child device 95762306a36Sopenharmony_ci */ 95862306a36Sopenharmony_cistatic void vmbus_device_release(struct device *device) 95962306a36Sopenharmony_ci{ 96062306a36Sopenharmony_ci struct hv_device *hv_dev = device_to_hv_device(device); 96162306a36Sopenharmony_ci struct vmbus_channel *channel = hv_dev->channel; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci hv_debug_rm_dev_dir(hv_dev); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci mutex_lock(&vmbus_connection.channel_mutex); 96662306a36Sopenharmony_ci hv_process_channel_removal(channel); 96762306a36Sopenharmony_ci mutex_unlock(&vmbus_connection.channel_mutex); 96862306a36Sopenharmony_ci kfree(hv_dev); 96962306a36Sopenharmony_ci} 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci/* 97262306a36Sopenharmony_ci * Note: we must use the "noirq" ops: see the comment before vmbus_bus_pm. 97362306a36Sopenharmony_ci * 97462306a36Sopenharmony_ci * suspend_noirq/resume_noirq are set to NULL to support Suspend-to-Idle: we 97562306a36Sopenharmony_ci * shouldn't suspend the vmbus devices upon Suspend-to-Idle, otherwise there 97662306a36Sopenharmony_ci * is no way to wake up a Generation-2 VM. 97762306a36Sopenharmony_ci * 97862306a36Sopenharmony_ci * The other 4 ops are for hibernation. 97962306a36Sopenharmony_ci */ 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_cistatic const struct dev_pm_ops vmbus_pm = { 98262306a36Sopenharmony_ci .suspend_noirq = NULL, 98362306a36Sopenharmony_ci .resume_noirq = NULL, 98462306a36Sopenharmony_ci .freeze_noirq = vmbus_suspend, 98562306a36Sopenharmony_ci .thaw_noirq = vmbus_resume, 98662306a36Sopenharmony_ci .poweroff_noirq = vmbus_suspend, 98762306a36Sopenharmony_ci .restore_noirq = vmbus_resume, 98862306a36Sopenharmony_ci}; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci/* The one and only one */ 99162306a36Sopenharmony_cistatic struct bus_type hv_bus = { 99262306a36Sopenharmony_ci .name = "vmbus", 99362306a36Sopenharmony_ci .match = vmbus_match, 99462306a36Sopenharmony_ci .shutdown = vmbus_shutdown, 99562306a36Sopenharmony_ci .remove = vmbus_remove, 99662306a36Sopenharmony_ci .probe = vmbus_probe, 99762306a36Sopenharmony_ci .uevent = vmbus_uevent, 99862306a36Sopenharmony_ci .dma_configure = vmbus_dma_configure, 99962306a36Sopenharmony_ci .dev_groups = vmbus_dev_groups, 100062306a36Sopenharmony_ci .drv_groups = vmbus_drv_groups, 100162306a36Sopenharmony_ci .bus_groups = vmbus_bus_groups, 100262306a36Sopenharmony_ci .pm = &vmbus_pm, 100362306a36Sopenharmony_ci}; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_cistruct onmessage_work_context { 100662306a36Sopenharmony_ci struct work_struct work; 100762306a36Sopenharmony_ci struct { 100862306a36Sopenharmony_ci struct hv_message_header header; 100962306a36Sopenharmony_ci u8 payload[]; 101062306a36Sopenharmony_ci } msg; 101162306a36Sopenharmony_ci}; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_cistatic void vmbus_onmessage_work(struct work_struct *work) 101462306a36Sopenharmony_ci{ 101562306a36Sopenharmony_ci struct onmessage_work_context *ctx; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci /* Do not process messages if we're in DISCONNECTED state */ 101862306a36Sopenharmony_ci if (vmbus_connection.conn_state == DISCONNECTED) 101962306a36Sopenharmony_ci return; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci ctx = container_of(work, struct onmessage_work_context, 102262306a36Sopenharmony_ci work); 102362306a36Sopenharmony_ci vmbus_onmessage((struct vmbus_channel_message_header *) 102462306a36Sopenharmony_ci &ctx->msg.payload); 102562306a36Sopenharmony_ci kfree(ctx); 102662306a36Sopenharmony_ci} 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_civoid vmbus_on_msg_dpc(unsigned long data) 102962306a36Sopenharmony_ci{ 103062306a36Sopenharmony_ci struct hv_per_cpu_context *hv_cpu = (void *)data; 103162306a36Sopenharmony_ci void *page_addr = hv_cpu->synic_message_page; 103262306a36Sopenharmony_ci struct hv_message msg_copy, *msg = (struct hv_message *)page_addr + 103362306a36Sopenharmony_ci VMBUS_MESSAGE_SINT; 103462306a36Sopenharmony_ci struct vmbus_channel_message_header *hdr; 103562306a36Sopenharmony_ci enum vmbus_channel_message_type msgtype; 103662306a36Sopenharmony_ci const struct vmbus_channel_message_table_entry *entry; 103762306a36Sopenharmony_ci struct onmessage_work_context *ctx; 103862306a36Sopenharmony_ci __u8 payload_size; 103962306a36Sopenharmony_ci u32 message_type; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci /* 104262306a36Sopenharmony_ci * 'enum vmbus_channel_message_type' is supposed to always be 'u32' as 104362306a36Sopenharmony_ci * it is being used in 'struct vmbus_channel_message_header' definition 104462306a36Sopenharmony_ci * which is supposed to match hypervisor ABI. 104562306a36Sopenharmony_ci */ 104662306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(enum vmbus_channel_message_type) != sizeof(u32)); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci /* 104962306a36Sopenharmony_ci * Since the message is in memory shared with the host, an erroneous or 105062306a36Sopenharmony_ci * malicious Hyper-V could modify the message while vmbus_on_msg_dpc() 105162306a36Sopenharmony_ci * or individual message handlers are executing; to prevent this, copy 105262306a36Sopenharmony_ci * the message into private memory. 105362306a36Sopenharmony_ci */ 105462306a36Sopenharmony_ci memcpy(&msg_copy, msg, sizeof(struct hv_message)); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci message_type = msg_copy.header.message_type; 105762306a36Sopenharmony_ci if (message_type == HVMSG_NONE) 105862306a36Sopenharmony_ci /* no msg */ 105962306a36Sopenharmony_ci return; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci hdr = (struct vmbus_channel_message_header *)msg_copy.u.payload; 106262306a36Sopenharmony_ci msgtype = hdr->msgtype; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci trace_vmbus_on_msg_dpc(hdr); 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci if (msgtype >= CHANNELMSG_COUNT) { 106762306a36Sopenharmony_ci WARN_ONCE(1, "unknown msgtype=%d\n", msgtype); 106862306a36Sopenharmony_ci goto msg_handled; 106962306a36Sopenharmony_ci } 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci payload_size = msg_copy.header.payload_size; 107262306a36Sopenharmony_ci if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT) { 107362306a36Sopenharmony_ci WARN_ONCE(1, "payload size is too large (%d)\n", payload_size); 107462306a36Sopenharmony_ci goto msg_handled; 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci entry = &channel_message_table[msgtype]; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci if (!entry->message_handler) 108062306a36Sopenharmony_ci goto msg_handled; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci if (payload_size < entry->min_payload_len) { 108362306a36Sopenharmony_ci WARN_ONCE(1, "message too short: msgtype=%d len=%d\n", msgtype, payload_size); 108462306a36Sopenharmony_ci goto msg_handled; 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci if (entry->handler_type == VMHT_BLOCKING) { 108862306a36Sopenharmony_ci ctx = kmalloc(struct_size(ctx, msg.payload, payload_size), GFP_ATOMIC); 108962306a36Sopenharmony_ci if (ctx == NULL) 109062306a36Sopenharmony_ci return; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci INIT_WORK(&ctx->work, vmbus_onmessage_work); 109362306a36Sopenharmony_ci ctx->msg.header = msg_copy.header; 109462306a36Sopenharmony_ci memcpy(&ctx->msg.payload, msg_copy.u.payload, payload_size); 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci /* 109762306a36Sopenharmony_ci * The host can generate a rescind message while we 109862306a36Sopenharmony_ci * may still be handling the original offer. We deal with 109962306a36Sopenharmony_ci * this condition by relying on the synchronization provided 110062306a36Sopenharmony_ci * by offer_in_progress and by channel_mutex. See also the 110162306a36Sopenharmony_ci * inline comments in vmbus_onoffer_rescind(). 110262306a36Sopenharmony_ci */ 110362306a36Sopenharmony_ci switch (msgtype) { 110462306a36Sopenharmony_ci case CHANNELMSG_RESCIND_CHANNELOFFER: 110562306a36Sopenharmony_ci /* 110662306a36Sopenharmony_ci * If we are handling the rescind message; 110762306a36Sopenharmony_ci * schedule the work on the global work queue. 110862306a36Sopenharmony_ci * 110962306a36Sopenharmony_ci * The OFFER message and the RESCIND message should 111062306a36Sopenharmony_ci * not be handled by the same serialized work queue, 111162306a36Sopenharmony_ci * because the OFFER handler may call vmbus_open(), 111262306a36Sopenharmony_ci * which tries to open the channel by sending an 111362306a36Sopenharmony_ci * OPEN_CHANNEL message to the host and waits for 111462306a36Sopenharmony_ci * the host's response; however, if the host has 111562306a36Sopenharmony_ci * rescinded the channel before it receives the 111662306a36Sopenharmony_ci * OPEN_CHANNEL message, the host just silently 111762306a36Sopenharmony_ci * ignores the OPEN_CHANNEL message; as a result, 111862306a36Sopenharmony_ci * the guest's OFFER handler hangs for ever, if we 111962306a36Sopenharmony_ci * handle the RESCIND message in the same serialized 112062306a36Sopenharmony_ci * work queue: the RESCIND handler can not start to 112162306a36Sopenharmony_ci * run before the OFFER handler finishes. 112262306a36Sopenharmony_ci */ 112362306a36Sopenharmony_ci if (vmbus_connection.ignore_any_offer_msg) 112462306a36Sopenharmony_ci break; 112562306a36Sopenharmony_ci queue_work(vmbus_connection.rescind_work_queue, &ctx->work); 112662306a36Sopenharmony_ci break; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci case CHANNELMSG_OFFERCHANNEL: 112962306a36Sopenharmony_ci /* 113062306a36Sopenharmony_ci * The host sends the offer message of a given channel 113162306a36Sopenharmony_ci * before sending the rescind message of the same 113262306a36Sopenharmony_ci * channel. These messages are sent to the guest's 113362306a36Sopenharmony_ci * connect CPU; the guest then starts processing them 113462306a36Sopenharmony_ci * in the tasklet handler on this CPU: 113562306a36Sopenharmony_ci * 113662306a36Sopenharmony_ci * VMBUS_CONNECT_CPU 113762306a36Sopenharmony_ci * 113862306a36Sopenharmony_ci * [vmbus_on_msg_dpc()] 113962306a36Sopenharmony_ci * atomic_inc() // CHANNELMSG_OFFERCHANNEL 114062306a36Sopenharmony_ci * queue_work() 114162306a36Sopenharmony_ci * ... 114262306a36Sopenharmony_ci * [vmbus_on_msg_dpc()] 114362306a36Sopenharmony_ci * schedule_work() // CHANNELMSG_RESCIND_CHANNELOFFER 114462306a36Sopenharmony_ci * 114562306a36Sopenharmony_ci * We rely on the memory-ordering properties of the 114662306a36Sopenharmony_ci * queue_work() and schedule_work() primitives, which 114762306a36Sopenharmony_ci * guarantee that the atomic increment will be visible 114862306a36Sopenharmony_ci * to the CPUs which will execute the offer & rescind 114962306a36Sopenharmony_ci * works by the time these works will start execution. 115062306a36Sopenharmony_ci */ 115162306a36Sopenharmony_ci if (vmbus_connection.ignore_any_offer_msg) 115262306a36Sopenharmony_ci break; 115362306a36Sopenharmony_ci atomic_inc(&vmbus_connection.offer_in_progress); 115462306a36Sopenharmony_ci fallthrough; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci default: 115762306a36Sopenharmony_ci queue_work(vmbus_connection.work_queue, &ctx->work); 115862306a36Sopenharmony_ci } 115962306a36Sopenharmony_ci } else 116062306a36Sopenharmony_ci entry->message_handler(hdr); 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_cimsg_handled: 116362306a36Sopenharmony_ci vmbus_signal_eom(msg, message_type); 116462306a36Sopenharmony_ci} 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 116762306a36Sopenharmony_ci/* 116862306a36Sopenharmony_ci * Fake RESCIND_CHANNEL messages to clean up hv_sock channels by force for 116962306a36Sopenharmony_ci * hibernation, because hv_sock connections can not persist across hibernation. 117062306a36Sopenharmony_ci */ 117162306a36Sopenharmony_cistatic void vmbus_force_channel_rescinded(struct vmbus_channel *channel) 117262306a36Sopenharmony_ci{ 117362306a36Sopenharmony_ci struct onmessage_work_context *ctx; 117462306a36Sopenharmony_ci struct vmbus_channel_rescind_offer *rescind; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci WARN_ON(!is_hvsock_channel(channel)); 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci /* 117962306a36Sopenharmony_ci * Allocation size is small and the allocation should really not fail, 118062306a36Sopenharmony_ci * otherwise the state of the hv_sock connections ends up in limbo. 118162306a36Sopenharmony_ci */ 118262306a36Sopenharmony_ci ctx = kzalloc(sizeof(*ctx) + sizeof(*rescind), 118362306a36Sopenharmony_ci GFP_KERNEL | __GFP_NOFAIL); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci /* 118662306a36Sopenharmony_ci * So far, these are not really used by Linux. Just set them to the 118762306a36Sopenharmony_ci * reasonable values conforming to the definitions of the fields. 118862306a36Sopenharmony_ci */ 118962306a36Sopenharmony_ci ctx->msg.header.message_type = 1; 119062306a36Sopenharmony_ci ctx->msg.header.payload_size = sizeof(*rescind); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci /* These values are actually used by Linux. */ 119362306a36Sopenharmony_ci rescind = (struct vmbus_channel_rescind_offer *)ctx->msg.payload; 119462306a36Sopenharmony_ci rescind->header.msgtype = CHANNELMSG_RESCIND_CHANNELOFFER; 119562306a36Sopenharmony_ci rescind->child_relid = channel->offermsg.child_relid; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci INIT_WORK(&ctx->work, vmbus_onmessage_work); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci queue_work(vmbus_connection.work_queue, &ctx->work); 120062306a36Sopenharmony_ci} 120162306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci/* 120462306a36Sopenharmony_ci * Schedule all channels with events pending 120562306a36Sopenharmony_ci */ 120662306a36Sopenharmony_cistatic void vmbus_chan_sched(struct hv_per_cpu_context *hv_cpu) 120762306a36Sopenharmony_ci{ 120862306a36Sopenharmony_ci unsigned long *recv_int_page; 120962306a36Sopenharmony_ci u32 maxbits, relid; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci /* 121262306a36Sopenharmony_ci * The event page can be directly checked to get the id of 121362306a36Sopenharmony_ci * the channel that has the interrupt pending. 121462306a36Sopenharmony_ci */ 121562306a36Sopenharmony_ci void *page_addr = hv_cpu->synic_event_page; 121662306a36Sopenharmony_ci union hv_synic_event_flags *event 121762306a36Sopenharmony_ci = (union hv_synic_event_flags *)page_addr + 121862306a36Sopenharmony_ci VMBUS_MESSAGE_SINT; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci maxbits = HV_EVENT_FLAGS_COUNT; 122162306a36Sopenharmony_ci recv_int_page = event->flags; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci if (unlikely(!recv_int_page)) 122462306a36Sopenharmony_ci return; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci for_each_set_bit(relid, recv_int_page, maxbits) { 122762306a36Sopenharmony_ci void (*callback_fn)(void *context); 122862306a36Sopenharmony_ci struct vmbus_channel *channel; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci if (!sync_test_and_clear_bit(relid, recv_int_page)) 123162306a36Sopenharmony_ci continue; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci /* Special case - vmbus channel protocol msg */ 123462306a36Sopenharmony_ci if (relid == 0) 123562306a36Sopenharmony_ci continue; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci /* 123862306a36Sopenharmony_ci * Pairs with the kfree_rcu() in vmbus_chan_release(). 123962306a36Sopenharmony_ci * Guarantees that the channel data structure doesn't 124062306a36Sopenharmony_ci * get freed while the channel pointer below is being 124162306a36Sopenharmony_ci * dereferenced. 124262306a36Sopenharmony_ci */ 124362306a36Sopenharmony_ci rcu_read_lock(); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci /* Find channel based on relid */ 124662306a36Sopenharmony_ci channel = relid2channel(relid); 124762306a36Sopenharmony_ci if (channel == NULL) 124862306a36Sopenharmony_ci goto sched_unlock_rcu; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci if (channel->rescind) 125162306a36Sopenharmony_ci goto sched_unlock_rcu; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci /* 125462306a36Sopenharmony_ci * Make sure that the ring buffer data structure doesn't get 125562306a36Sopenharmony_ci * freed while we dereference the ring buffer pointer. Test 125662306a36Sopenharmony_ci * for the channel's onchannel_callback being NULL within a 125762306a36Sopenharmony_ci * sched_lock critical section. See also the inline comments 125862306a36Sopenharmony_ci * in vmbus_reset_channel_cb(). 125962306a36Sopenharmony_ci */ 126062306a36Sopenharmony_ci spin_lock(&channel->sched_lock); 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci callback_fn = channel->onchannel_callback; 126362306a36Sopenharmony_ci if (unlikely(callback_fn == NULL)) 126462306a36Sopenharmony_ci goto sched_unlock; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci trace_vmbus_chan_sched(channel); 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci ++channel->interrupts; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci switch (channel->callback_mode) { 127162306a36Sopenharmony_ci case HV_CALL_ISR: 127262306a36Sopenharmony_ci (*callback_fn)(channel->channel_callback_context); 127362306a36Sopenharmony_ci break; 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci case HV_CALL_BATCHED: 127662306a36Sopenharmony_ci hv_begin_read(&channel->inbound); 127762306a36Sopenharmony_ci fallthrough; 127862306a36Sopenharmony_ci case HV_CALL_DIRECT: 127962306a36Sopenharmony_ci tasklet_schedule(&channel->callback_event); 128062306a36Sopenharmony_ci } 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_cisched_unlock: 128362306a36Sopenharmony_ci spin_unlock(&channel->sched_lock); 128462306a36Sopenharmony_cisched_unlock_rcu: 128562306a36Sopenharmony_ci rcu_read_unlock(); 128662306a36Sopenharmony_ci } 128762306a36Sopenharmony_ci} 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_cistatic void vmbus_isr(void) 129062306a36Sopenharmony_ci{ 129162306a36Sopenharmony_ci struct hv_per_cpu_context *hv_cpu 129262306a36Sopenharmony_ci = this_cpu_ptr(hv_context.cpu_context); 129362306a36Sopenharmony_ci void *page_addr; 129462306a36Sopenharmony_ci struct hv_message *msg; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci vmbus_chan_sched(hv_cpu); 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci page_addr = hv_cpu->synic_message_page; 129962306a36Sopenharmony_ci msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci /* Check if there are actual msgs to be processed */ 130262306a36Sopenharmony_ci if (msg->header.message_type != HVMSG_NONE) { 130362306a36Sopenharmony_ci if (msg->header.message_type == HVMSG_TIMER_EXPIRED) { 130462306a36Sopenharmony_ci hv_stimer0_isr(); 130562306a36Sopenharmony_ci vmbus_signal_eom(msg, HVMSG_TIMER_EXPIRED); 130662306a36Sopenharmony_ci } else 130762306a36Sopenharmony_ci tasklet_schedule(&hv_cpu->msg_dpc); 130862306a36Sopenharmony_ci } 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci add_interrupt_randomness(vmbus_interrupt); 131162306a36Sopenharmony_ci} 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_cistatic irqreturn_t vmbus_percpu_isr(int irq, void *dev_id) 131462306a36Sopenharmony_ci{ 131562306a36Sopenharmony_ci vmbus_isr(); 131662306a36Sopenharmony_ci return IRQ_HANDLED; 131762306a36Sopenharmony_ci} 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci/* 132062306a36Sopenharmony_ci * vmbus_bus_init -Main vmbus driver initialization routine. 132162306a36Sopenharmony_ci * 132262306a36Sopenharmony_ci * Here, we 132362306a36Sopenharmony_ci * - initialize the vmbus driver context 132462306a36Sopenharmony_ci * - invoke the vmbus hv main init routine 132562306a36Sopenharmony_ci * - retrieve the channel offers 132662306a36Sopenharmony_ci */ 132762306a36Sopenharmony_cistatic int vmbus_bus_init(void) 132862306a36Sopenharmony_ci{ 132962306a36Sopenharmony_ci int ret; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci ret = hv_init(); 133262306a36Sopenharmony_ci if (ret != 0) { 133362306a36Sopenharmony_ci pr_err("Unable to initialize the hypervisor - 0x%x\n", ret); 133462306a36Sopenharmony_ci return ret; 133562306a36Sopenharmony_ci } 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci ret = bus_register(&hv_bus); 133862306a36Sopenharmony_ci if (ret) 133962306a36Sopenharmony_ci return ret; 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci /* 134262306a36Sopenharmony_ci * VMbus interrupts are best modeled as per-cpu interrupts. If 134362306a36Sopenharmony_ci * on an architecture with support for per-cpu IRQs (e.g. ARM64), 134462306a36Sopenharmony_ci * allocate a per-cpu IRQ using standard Linux kernel functionality. 134562306a36Sopenharmony_ci * If not on such an architecture (e.g., x86/x64), then rely on 134662306a36Sopenharmony_ci * code in the arch-specific portion of the code tree to connect 134762306a36Sopenharmony_ci * the VMbus interrupt handler. 134862306a36Sopenharmony_ci */ 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci if (vmbus_irq == -1) { 135162306a36Sopenharmony_ci hv_setup_vmbus_handler(vmbus_isr); 135262306a36Sopenharmony_ci } else { 135362306a36Sopenharmony_ci vmbus_evt = alloc_percpu(long); 135462306a36Sopenharmony_ci ret = request_percpu_irq(vmbus_irq, vmbus_percpu_isr, 135562306a36Sopenharmony_ci "Hyper-V VMbus", vmbus_evt); 135662306a36Sopenharmony_ci if (ret) { 135762306a36Sopenharmony_ci pr_err("Can't request Hyper-V VMbus IRQ %d, Err %d", 135862306a36Sopenharmony_ci vmbus_irq, ret); 135962306a36Sopenharmony_ci free_percpu(vmbus_evt); 136062306a36Sopenharmony_ci goto err_setup; 136162306a36Sopenharmony_ci } 136262306a36Sopenharmony_ci } 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci ret = hv_synic_alloc(); 136562306a36Sopenharmony_ci if (ret) 136662306a36Sopenharmony_ci goto err_alloc; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci /* 136962306a36Sopenharmony_ci * Initialize the per-cpu interrupt state and stimer state. 137062306a36Sopenharmony_ci * Then connect to the host. 137162306a36Sopenharmony_ci */ 137262306a36Sopenharmony_ci ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hyperv/vmbus:online", 137362306a36Sopenharmony_ci hv_synic_init, hv_synic_cleanup); 137462306a36Sopenharmony_ci if (ret < 0) 137562306a36Sopenharmony_ci goto err_alloc; 137662306a36Sopenharmony_ci hyperv_cpuhp_online = ret; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci ret = vmbus_connect(); 137962306a36Sopenharmony_ci if (ret) 138062306a36Sopenharmony_ci goto err_connect; 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci /* 138362306a36Sopenharmony_ci * Always register the vmbus unload panic notifier because we 138462306a36Sopenharmony_ci * need to shut the VMbus channel connection on panic. 138562306a36Sopenharmony_ci */ 138662306a36Sopenharmony_ci atomic_notifier_chain_register(&panic_notifier_list, 138762306a36Sopenharmony_ci &hyperv_panic_vmbus_unload_block); 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci vmbus_request_offers(); 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci return 0; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_cierr_connect: 139462306a36Sopenharmony_ci cpuhp_remove_state(hyperv_cpuhp_online); 139562306a36Sopenharmony_cierr_alloc: 139662306a36Sopenharmony_ci hv_synic_free(); 139762306a36Sopenharmony_ci if (vmbus_irq == -1) { 139862306a36Sopenharmony_ci hv_remove_vmbus_handler(); 139962306a36Sopenharmony_ci } else { 140062306a36Sopenharmony_ci free_percpu_irq(vmbus_irq, vmbus_evt); 140162306a36Sopenharmony_ci free_percpu(vmbus_evt); 140262306a36Sopenharmony_ci } 140362306a36Sopenharmony_cierr_setup: 140462306a36Sopenharmony_ci bus_unregister(&hv_bus); 140562306a36Sopenharmony_ci return ret; 140662306a36Sopenharmony_ci} 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci/** 140962306a36Sopenharmony_ci * __vmbus_driver_register() - Register a vmbus's driver 141062306a36Sopenharmony_ci * @hv_driver: Pointer to driver structure you want to register 141162306a36Sopenharmony_ci * @owner: owner module of the drv 141262306a36Sopenharmony_ci * @mod_name: module name string 141362306a36Sopenharmony_ci * 141462306a36Sopenharmony_ci * Registers the given driver with Linux through the 'driver_register()' call 141562306a36Sopenharmony_ci * and sets up the hyper-v vmbus handling for this driver. 141662306a36Sopenharmony_ci * It will return the state of the 'driver_register()' call. 141762306a36Sopenharmony_ci * 141862306a36Sopenharmony_ci */ 141962306a36Sopenharmony_ciint __vmbus_driver_register(struct hv_driver *hv_driver, struct module *owner, const char *mod_name) 142062306a36Sopenharmony_ci{ 142162306a36Sopenharmony_ci int ret; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci pr_info("registering driver %s\n", hv_driver->name); 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci ret = vmbus_exists(); 142662306a36Sopenharmony_ci if (ret < 0) 142762306a36Sopenharmony_ci return ret; 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci hv_driver->driver.name = hv_driver->name; 143062306a36Sopenharmony_ci hv_driver->driver.owner = owner; 143162306a36Sopenharmony_ci hv_driver->driver.mod_name = mod_name; 143262306a36Sopenharmony_ci hv_driver->driver.bus = &hv_bus; 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci spin_lock_init(&hv_driver->dynids.lock); 143562306a36Sopenharmony_ci INIT_LIST_HEAD(&hv_driver->dynids.list); 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci ret = driver_register(&hv_driver->driver); 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci return ret; 144062306a36Sopenharmony_ci} 144162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__vmbus_driver_register); 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci/** 144462306a36Sopenharmony_ci * vmbus_driver_unregister() - Unregister a vmbus's driver 144562306a36Sopenharmony_ci * @hv_driver: Pointer to driver structure you want to 144662306a36Sopenharmony_ci * un-register 144762306a36Sopenharmony_ci * 144862306a36Sopenharmony_ci * Un-register the given driver that was previous registered with a call to 144962306a36Sopenharmony_ci * vmbus_driver_register() 145062306a36Sopenharmony_ci */ 145162306a36Sopenharmony_civoid vmbus_driver_unregister(struct hv_driver *hv_driver) 145262306a36Sopenharmony_ci{ 145362306a36Sopenharmony_ci pr_info("unregistering driver %s\n", hv_driver->name); 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci if (!vmbus_exists()) { 145662306a36Sopenharmony_ci driver_unregister(&hv_driver->driver); 145762306a36Sopenharmony_ci vmbus_free_dynids(hv_driver); 145862306a36Sopenharmony_ci } 145962306a36Sopenharmony_ci} 146062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_driver_unregister); 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci/* 146462306a36Sopenharmony_ci * Called when last reference to channel is gone. 146562306a36Sopenharmony_ci */ 146662306a36Sopenharmony_cistatic void vmbus_chan_release(struct kobject *kobj) 146762306a36Sopenharmony_ci{ 146862306a36Sopenharmony_ci struct vmbus_channel *channel 146962306a36Sopenharmony_ci = container_of(kobj, struct vmbus_channel, kobj); 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci kfree_rcu(channel, rcu); 147262306a36Sopenharmony_ci} 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_cistruct vmbus_chan_attribute { 147562306a36Sopenharmony_ci struct attribute attr; 147662306a36Sopenharmony_ci ssize_t (*show)(struct vmbus_channel *chan, char *buf); 147762306a36Sopenharmony_ci ssize_t (*store)(struct vmbus_channel *chan, 147862306a36Sopenharmony_ci const char *buf, size_t count); 147962306a36Sopenharmony_ci}; 148062306a36Sopenharmony_ci#define VMBUS_CHAN_ATTR(_name, _mode, _show, _store) \ 148162306a36Sopenharmony_ci struct vmbus_chan_attribute chan_attr_##_name \ 148262306a36Sopenharmony_ci = __ATTR(_name, _mode, _show, _store) 148362306a36Sopenharmony_ci#define VMBUS_CHAN_ATTR_RW(_name) \ 148462306a36Sopenharmony_ci struct vmbus_chan_attribute chan_attr_##_name = __ATTR_RW(_name) 148562306a36Sopenharmony_ci#define VMBUS_CHAN_ATTR_RO(_name) \ 148662306a36Sopenharmony_ci struct vmbus_chan_attribute chan_attr_##_name = __ATTR_RO(_name) 148762306a36Sopenharmony_ci#define VMBUS_CHAN_ATTR_WO(_name) \ 148862306a36Sopenharmony_ci struct vmbus_chan_attribute chan_attr_##_name = __ATTR_WO(_name) 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_cistatic ssize_t vmbus_chan_attr_show(struct kobject *kobj, 149162306a36Sopenharmony_ci struct attribute *attr, char *buf) 149262306a36Sopenharmony_ci{ 149362306a36Sopenharmony_ci const struct vmbus_chan_attribute *attribute 149462306a36Sopenharmony_ci = container_of(attr, struct vmbus_chan_attribute, attr); 149562306a36Sopenharmony_ci struct vmbus_channel *chan 149662306a36Sopenharmony_ci = container_of(kobj, struct vmbus_channel, kobj); 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci if (!attribute->show) 149962306a36Sopenharmony_ci return -EIO; 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci return attribute->show(chan, buf); 150262306a36Sopenharmony_ci} 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_cistatic ssize_t vmbus_chan_attr_store(struct kobject *kobj, 150562306a36Sopenharmony_ci struct attribute *attr, const char *buf, 150662306a36Sopenharmony_ci size_t count) 150762306a36Sopenharmony_ci{ 150862306a36Sopenharmony_ci const struct vmbus_chan_attribute *attribute 150962306a36Sopenharmony_ci = container_of(attr, struct vmbus_chan_attribute, attr); 151062306a36Sopenharmony_ci struct vmbus_channel *chan 151162306a36Sopenharmony_ci = container_of(kobj, struct vmbus_channel, kobj); 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci if (!attribute->store) 151462306a36Sopenharmony_ci return -EIO; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci return attribute->store(chan, buf, count); 151762306a36Sopenharmony_ci} 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_cistatic const struct sysfs_ops vmbus_chan_sysfs_ops = { 152062306a36Sopenharmony_ci .show = vmbus_chan_attr_show, 152162306a36Sopenharmony_ci .store = vmbus_chan_attr_store, 152262306a36Sopenharmony_ci}; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_cistatic ssize_t out_mask_show(struct vmbus_channel *channel, char *buf) 152562306a36Sopenharmony_ci{ 152662306a36Sopenharmony_ci struct hv_ring_buffer_info *rbi = &channel->outbound; 152762306a36Sopenharmony_ci ssize_t ret; 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci mutex_lock(&rbi->ring_buffer_mutex); 153062306a36Sopenharmony_ci if (!rbi->ring_buffer) { 153162306a36Sopenharmony_ci mutex_unlock(&rbi->ring_buffer_mutex); 153262306a36Sopenharmony_ci return -EINVAL; 153362306a36Sopenharmony_ci } 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci ret = sprintf(buf, "%u\n", rbi->ring_buffer->interrupt_mask); 153662306a36Sopenharmony_ci mutex_unlock(&rbi->ring_buffer_mutex); 153762306a36Sopenharmony_ci return ret; 153862306a36Sopenharmony_ci} 153962306a36Sopenharmony_cistatic VMBUS_CHAN_ATTR_RO(out_mask); 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_cistatic ssize_t in_mask_show(struct vmbus_channel *channel, char *buf) 154262306a36Sopenharmony_ci{ 154362306a36Sopenharmony_ci struct hv_ring_buffer_info *rbi = &channel->inbound; 154462306a36Sopenharmony_ci ssize_t ret; 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci mutex_lock(&rbi->ring_buffer_mutex); 154762306a36Sopenharmony_ci if (!rbi->ring_buffer) { 154862306a36Sopenharmony_ci mutex_unlock(&rbi->ring_buffer_mutex); 154962306a36Sopenharmony_ci return -EINVAL; 155062306a36Sopenharmony_ci } 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci ret = sprintf(buf, "%u\n", rbi->ring_buffer->interrupt_mask); 155362306a36Sopenharmony_ci mutex_unlock(&rbi->ring_buffer_mutex); 155462306a36Sopenharmony_ci return ret; 155562306a36Sopenharmony_ci} 155662306a36Sopenharmony_cistatic VMBUS_CHAN_ATTR_RO(in_mask); 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_cistatic ssize_t read_avail_show(struct vmbus_channel *channel, char *buf) 155962306a36Sopenharmony_ci{ 156062306a36Sopenharmony_ci struct hv_ring_buffer_info *rbi = &channel->inbound; 156162306a36Sopenharmony_ci ssize_t ret; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci mutex_lock(&rbi->ring_buffer_mutex); 156462306a36Sopenharmony_ci if (!rbi->ring_buffer) { 156562306a36Sopenharmony_ci mutex_unlock(&rbi->ring_buffer_mutex); 156662306a36Sopenharmony_ci return -EINVAL; 156762306a36Sopenharmony_ci } 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci ret = sprintf(buf, "%u\n", hv_get_bytes_to_read(rbi)); 157062306a36Sopenharmony_ci mutex_unlock(&rbi->ring_buffer_mutex); 157162306a36Sopenharmony_ci return ret; 157262306a36Sopenharmony_ci} 157362306a36Sopenharmony_cistatic VMBUS_CHAN_ATTR_RO(read_avail); 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_cistatic ssize_t write_avail_show(struct vmbus_channel *channel, char *buf) 157662306a36Sopenharmony_ci{ 157762306a36Sopenharmony_ci struct hv_ring_buffer_info *rbi = &channel->outbound; 157862306a36Sopenharmony_ci ssize_t ret; 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci mutex_lock(&rbi->ring_buffer_mutex); 158162306a36Sopenharmony_ci if (!rbi->ring_buffer) { 158262306a36Sopenharmony_ci mutex_unlock(&rbi->ring_buffer_mutex); 158362306a36Sopenharmony_ci return -EINVAL; 158462306a36Sopenharmony_ci } 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci ret = sprintf(buf, "%u\n", hv_get_bytes_to_write(rbi)); 158762306a36Sopenharmony_ci mutex_unlock(&rbi->ring_buffer_mutex); 158862306a36Sopenharmony_ci return ret; 158962306a36Sopenharmony_ci} 159062306a36Sopenharmony_cistatic VMBUS_CHAN_ATTR_RO(write_avail); 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_cistatic ssize_t target_cpu_show(struct vmbus_channel *channel, char *buf) 159362306a36Sopenharmony_ci{ 159462306a36Sopenharmony_ci return sprintf(buf, "%u\n", channel->target_cpu); 159562306a36Sopenharmony_ci} 159662306a36Sopenharmony_cistatic ssize_t target_cpu_store(struct vmbus_channel *channel, 159762306a36Sopenharmony_ci const char *buf, size_t count) 159862306a36Sopenharmony_ci{ 159962306a36Sopenharmony_ci u32 target_cpu, origin_cpu; 160062306a36Sopenharmony_ci ssize_t ret = count; 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci if (vmbus_proto_version < VERSION_WIN10_V4_1) 160362306a36Sopenharmony_ci return -EIO; 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci if (sscanf(buf, "%uu", &target_cpu) != 1) 160662306a36Sopenharmony_ci return -EIO; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci /* Validate target_cpu for the cpumask_test_cpu() operation below. */ 160962306a36Sopenharmony_ci if (target_cpu >= nr_cpumask_bits) 161062306a36Sopenharmony_ci return -EINVAL; 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci if (!cpumask_test_cpu(target_cpu, housekeeping_cpumask(HK_TYPE_MANAGED_IRQ))) 161362306a36Sopenharmony_ci return -EINVAL; 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci /* No CPUs should come up or down during this. */ 161662306a36Sopenharmony_ci cpus_read_lock(); 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci if (!cpu_online(target_cpu)) { 161962306a36Sopenharmony_ci cpus_read_unlock(); 162062306a36Sopenharmony_ci return -EINVAL; 162162306a36Sopenharmony_ci } 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci /* 162462306a36Sopenharmony_ci * Synchronizes target_cpu_store() and channel closure: 162562306a36Sopenharmony_ci * 162662306a36Sopenharmony_ci * { Initially: state = CHANNEL_OPENED } 162762306a36Sopenharmony_ci * 162862306a36Sopenharmony_ci * CPU1 CPU2 162962306a36Sopenharmony_ci * 163062306a36Sopenharmony_ci * [target_cpu_store()] [vmbus_disconnect_ring()] 163162306a36Sopenharmony_ci * 163262306a36Sopenharmony_ci * LOCK channel_mutex LOCK channel_mutex 163362306a36Sopenharmony_ci * LOAD r1 = state LOAD r2 = state 163462306a36Sopenharmony_ci * IF (r1 == CHANNEL_OPENED) IF (r2 == CHANNEL_OPENED) 163562306a36Sopenharmony_ci * SEND MODIFYCHANNEL STORE state = CHANNEL_OPEN 163662306a36Sopenharmony_ci * [...] SEND CLOSECHANNEL 163762306a36Sopenharmony_ci * UNLOCK channel_mutex UNLOCK channel_mutex 163862306a36Sopenharmony_ci * 163962306a36Sopenharmony_ci * Forbids: r1 == r2 == CHANNEL_OPENED (i.e., CPU1's LOCK precedes 164062306a36Sopenharmony_ci * CPU2's LOCK) && CPU2's SEND precedes CPU1's SEND 164162306a36Sopenharmony_ci * 164262306a36Sopenharmony_ci * Note. The host processes the channel messages "sequentially", in 164362306a36Sopenharmony_ci * the order in which they are received on a per-partition basis. 164462306a36Sopenharmony_ci */ 164562306a36Sopenharmony_ci mutex_lock(&vmbus_connection.channel_mutex); 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci /* 164862306a36Sopenharmony_ci * Hyper-V will ignore MODIFYCHANNEL messages for "non-open" channels; 164962306a36Sopenharmony_ci * avoid sending the message and fail here for such channels. 165062306a36Sopenharmony_ci */ 165162306a36Sopenharmony_ci if (channel->state != CHANNEL_OPENED_STATE) { 165262306a36Sopenharmony_ci ret = -EIO; 165362306a36Sopenharmony_ci goto cpu_store_unlock; 165462306a36Sopenharmony_ci } 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci origin_cpu = channel->target_cpu; 165762306a36Sopenharmony_ci if (target_cpu == origin_cpu) 165862306a36Sopenharmony_ci goto cpu_store_unlock; 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci if (vmbus_send_modifychannel(channel, 166162306a36Sopenharmony_ci hv_cpu_number_to_vp_number(target_cpu))) { 166262306a36Sopenharmony_ci ret = -EIO; 166362306a36Sopenharmony_ci goto cpu_store_unlock; 166462306a36Sopenharmony_ci } 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci /* 166762306a36Sopenharmony_ci * For version before VERSION_WIN10_V5_3, the following warning holds: 166862306a36Sopenharmony_ci * 166962306a36Sopenharmony_ci * Warning. At this point, there is *no* guarantee that the host will 167062306a36Sopenharmony_ci * have successfully processed the vmbus_send_modifychannel() request. 167162306a36Sopenharmony_ci * See the header comment of vmbus_send_modifychannel() for more info. 167262306a36Sopenharmony_ci * 167362306a36Sopenharmony_ci * Lags in the processing of the above vmbus_send_modifychannel() can 167462306a36Sopenharmony_ci * result in missed interrupts if the "old" target CPU is taken offline 167562306a36Sopenharmony_ci * before Hyper-V starts sending interrupts to the "new" target CPU. 167662306a36Sopenharmony_ci * But apart from this offlining scenario, the code tolerates such 167762306a36Sopenharmony_ci * lags. It will function correctly even if a channel interrupt comes 167862306a36Sopenharmony_ci * in on a CPU that is different from the channel target_cpu value. 167962306a36Sopenharmony_ci */ 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci channel->target_cpu = target_cpu; 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci /* See init_vp_index(). */ 168462306a36Sopenharmony_ci if (hv_is_perf_channel(channel)) 168562306a36Sopenharmony_ci hv_update_allocated_cpus(origin_cpu, target_cpu); 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci /* Currently set only for storvsc channels. */ 168862306a36Sopenharmony_ci if (channel->change_target_cpu_callback) { 168962306a36Sopenharmony_ci (*channel->change_target_cpu_callback)(channel, 169062306a36Sopenharmony_ci origin_cpu, target_cpu); 169162306a36Sopenharmony_ci } 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_cicpu_store_unlock: 169462306a36Sopenharmony_ci mutex_unlock(&vmbus_connection.channel_mutex); 169562306a36Sopenharmony_ci cpus_read_unlock(); 169662306a36Sopenharmony_ci return ret; 169762306a36Sopenharmony_ci} 169862306a36Sopenharmony_cistatic VMBUS_CHAN_ATTR(cpu, 0644, target_cpu_show, target_cpu_store); 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_cistatic ssize_t channel_pending_show(struct vmbus_channel *channel, 170162306a36Sopenharmony_ci char *buf) 170262306a36Sopenharmony_ci{ 170362306a36Sopenharmony_ci return sprintf(buf, "%d\n", 170462306a36Sopenharmony_ci channel_pending(channel, 170562306a36Sopenharmony_ci vmbus_connection.monitor_pages[1])); 170662306a36Sopenharmony_ci} 170762306a36Sopenharmony_cistatic VMBUS_CHAN_ATTR(pending, 0444, channel_pending_show, NULL); 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_cistatic ssize_t channel_latency_show(struct vmbus_channel *channel, 171062306a36Sopenharmony_ci char *buf) 171162306a36Sopenharmony_ci{ 171262306a36Sopenharmony_ci return sprintf(buf, "%d\n", 171362306a36Sopenharmony_ci channel_latency(channel, 171462306a36Sopenharmony_ci vmbus_connection.monitor_pages[1])); 171562306a36Sopenharmony_ci} 171662306a36Sopenharmony_cistatic VMBUS_CHAN_ATTR(latency, 0444, channel_latency_show, NULL); 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_cistatic ssize_t channel_interrupts_show(struct vmbus_channel *channel, char *buf) 171962306a36Sopenharmony_ci{ 172062306a36Sopenharmony_ci return sprintf(buf, "%llu\n", channel->interrupts); 172162306a36Sopenharmony_ci} 172262306a36Sopenharmony_cistatic VMBUS_CHAN_ATTR(interrupts, 0444, channel_interrupts_show, NULL); 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_cistatic ssize_t channel_events_show(struct vmbus_channel *channel, char *buf) 172562306a36Sopenharmony_ci{ 172662306a36Sopenharmony_ci return sprintf(buf, "%llu\n", channel->sig_events); 172762306a36Sopenharmony_ci} 172862306a36Sopenharmony_cistatic VMBUS_CHAN_ATTR(events, 0444, channel_events_show, NULL); 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_cistatic ssize_t channel_intr_in_full_show(struct vmbus_channel *channel, 173162306a36Sopenharmony_ci char *buf) 173262306a36Sopenharmony_ci{ 173362306a36Sopenharmony_ci return sprintf(buf, "%llu\n", 173462306a36Sopenharmony_ci (unsigned long long)channel->intr_in_full); 173562306a36Sopenharmony_ci} 173662306a36Sopenharmony_cistatic VMBUS_CHAN_ATTR(intr_in_full, 0444, channel_intr_in_full_show, NULL); 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_cistatic ssize_t channel_intr_out_empty_show(struct vmbus_channel *channel, 173962306a36Sopenharmony_ci char *buf) 174062306a36Sopenharmony_ci{ 174162306a36Sopenharmony_ci return sprintf(buf, "%llu\n", 174262306a36Sopenharmony_ci (unsigned long long)channel->intr_out_empty); 174362306a36Sopenharmony_ci} 174462306a36Sopenharmony_cistatic VMBUS_CHAN_ATTR(intr_out_empty, 0444, channel_intr_out_empty_show, NULL); 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_cistatic ssize_t channel_out_full_first_show(struct vmbus_channel *channel, 174762306a36Sopenharmony_ci char *buf) 174862306a36Sopenharmony_ci{ 174962306a36Sopenharmony_ci return sprintf(buf, "%llu\n", 175062306a36Sopenharmony_ci (unsigned long long)channel->out_full_first); 175162306a36Sopenharmony_ci} 175262306a36Sopenharmony_cistatic VMBUS_CHAN_ATTR(out_full_first, 0444, channel_out_full_first_show, NULL); 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_cistatic ssize_t channel_out_full_total_show(struct vmbus_channel *channel, 175562306a36Sopenharmony_ci char *buf) 175662306a36Sopenharmony_ci{ 175762306a36Sopenharmony_ci return sprintf(buf, "%llu\n", 175862306a36Sopenharmony_ci (unsigned long long)channel->out_full_total); 175962306a36Sopenharmony_ci} 176062306a36Sopenharmony_cistatic VMBUS_CHAN_ATTR(out_full_total, 0444, channel_out_full_total_show, NULL); 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_cistatic ssize_t subchannel_monitor_id_show(struct vmbus_channel *channel, 176362306a36Sopenharmony_ci char *buf) 176462306a36Sopenharmony_ci{ 176562306a36Sopenharmony_ci return sprintf(buf, "%u\n", channel->offermsg.monitorid); 176662306a36Sopenharmony_ci} 176762306a36Sopenharmony_cistatic VMBUS_CHAN_ATTR(monitor_id, 0444, subchannel_monitor_id_show, NULL); 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_cistatic ssize_t subchannel_id_show(struct vmbus_channel *channel, 177062306a36Sopenharmony_ci char *buf) 177162306a36Sopenharmony_ci{ 177262306a36Sopenharmony_ci return sprintf(buf, "%u\n", 177362306a36Sopenharmony_ci channel->offermsg.offer.sub_channel_index); 177462306a36Sopenharmony_ci} 177562306a36Sopenharmony_cistatic VMBUS_CHAN_ATTR_RO(subchannel_id); 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_cistatic struct attribute *vmbus_chan_attrs[] = { 177862306a36Sopenharmony_ci &chan_attr_out_mask.attr, 177962306a36Sopenharmony_ci &chan_attr_in_mask.attr, 178062306a36Sopenharmony_ci &chan_attr_read_avail.attr, 178162306a36Sopenharmony_ci &chan_attr_write_avail.attr, 178262306a36Sopenharmony_ci &chan_attr_cpu.attr, 178362306a36Sopenharmony_ci &chan_attr_pending.attr, 178462306a36Sopenharmony_ci &chan_attr_latency.attr, 178562306a36Sopenharmony_ci &chan_attr_interrupts.attr, 178662306a36Sopenharmony_ci &chan_attr_events.attr, 178762306a36Sopenharmony_ci &chan_attr_intr_in_full.attr, 178862306a36Sopenharmony_ci &chan_attr_intr_out_empty.attr, 178962306a36Sopenharmony_ci &chan_attr_out_full_first.attr, 179062306a36Sopenharmony_ci &chan_attr_out_full_total.attr, 179162306a36Sopenharmony_ci &chan_attr_monitor_id.attr, 179262306a36Sopenharmony_ci &chan_attr_subchannel_id.attr, 179362306a36Sopenharmony_ci NULL 179462306a36Sopenharmony_ci}; 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci/* 179762306a36Sopenharmony_ci * Channel-level attribute_group callback function. Returns the permission for 179862306a36Sopenharmony_ci * each attribute, and returns 0 if an attribute is not visible. 179962306a36Sopenharmony_ci */ 180062306a36Sopenharmony_cistatic umode_t vmbus_chan_attr_is_visible(struct kobject *kobj, 180162306a36Sopenharmony_ci struct attribute *attr, int idx) 180262306a36Sopenharmony_ci{ 180362306a36Sopenharmony_ci const struct vmbus_channel *channel = 180462306a36Sopenharmony_ci container_of(kobj, struct vmbus_channel, kobj); 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci /* Hide the monitor attributes if the monitor mechanism is not used. */ 180762306a36Sopenharmony_ci if (!channel->offermsg.monitor_allocated && 180862306a36Sopenharmony_ci (attr == &chan_attr_pending.attr || 180962306a36Sopenharmony_ci attr == &chan_attr_latency.attr || 181062306a36Sopenharmony_ci attr == &chan_attr_monitor_id.attr)) 181162306a36Sopenharmony_ci return 0; 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci return attr->mode; 181462306a36Sopenharmony_ci} 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_cistatic struct attribute_group vmbus_chan_group = { 181762306a36Sopenharmony_ci .attrs = vmbus_chan_attrs, 181862306a36Sopenharmony_ci .is_visible = vmbus_chan_attr_is_visible 181962306a36Sopenharmony_ci}; 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_cistatic struct kobj_type vmbus_chan_ktype = { 182262306a36Sopenharmony_ci .sysfs_ops = &vmbus_chan_sysfs_ops, 182362306a36Sopenharmony_ci .release = vmbus_chan_release, 182462306a36Sopenharmony_ci}; 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci/* 182762306a36Sopenharmony_ci * vmbus_add_channel_kobj - setup a sub-directory under device/channels 182862306a36Sopenharmony_ci */ 182962306a36Sopenharmony_ciint vmbus_add_channel_kobj(struct hv_device *dev, struct vmbus_channel *channel) 183062306a36Sopenharmony_ci{ 183162306a36Sopenharmony_ci const struct device *device = &dev->device; 183262306a36Sopenharmony_ci struct kobject *kobj = &channel->kobj; 183362306a36Sopenharmony_ci u32 relid = channel->offermsg.child_relid; 183462306a36Sopenharmony_ci int ret; 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci kobj->kset = dev->channels_kset; 183762306a36Sopenharmony_ci ret = kobject_init_and_add(kobj, &vmbus_chan_ktype, NULL, 183862306a36Sopenharmony_ci "%u", relid); 183962306a36Sopenharmony_ci if (ret) { 184062306a36Sopenharmony_ci kobject_put(kobj); 184162306a36Sopenharmony_ci return ret; 184262306a36Sopenharmony_ci } 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci ret = sysfs_create_group(kobj, &vmbus_chan_group); 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci if (ret) { 184762306a36Sopenharmony_ci /* 184862306a36Sopenharmony_ci * The calling functions' error handling paths will cleanup the 184962306a36Sopenharmony_ci * empty channel directory. 185062306a36Sopenharmony_ci */ 185162306a36Sopenharmony_ci kobject_put(kobj); 185262306a36Sopenharmony_ci dev_err(device, "Unable to set up channel sysfs files\n"); 185362306a36Sopenharmony_ci return ret; 185462306a36Sopenharmony_ci } 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci kobject_uevent(kobj, KOBJ_ADD); 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci return 0; 185962306a36Sopenharmony_ci} 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci/* 186262306a36Sopenharmony_ci * vmbus_remove_channel_attr_group - remove the channel's attribute group 186362306a36Sopenharmony_ci */ 186462306a36Sopenharmony_civoid vmbus_remove_channel_attr_group(struct vmbus_channel *channel) 186562306a36Sopenharmony_ci{ 186662306a36Sopenharmony_ci sysfs_remove_group(&channel->kobj, &vmbus_chan_group); 186762306a36Sopenharmony_ci} 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci/* 187062306a36Sopenharmony_ci * vmbus_device_create - Creates and registers a new child device 187162306a36Sopenharmony_ci * on the vmbus. 187262306a36Sopenharmony_ci */ 187362306a36Sopenharmony_cistruct hv_device *vmbus_device_create(const guid_t *type, 187462306a36Sopenharmony_ci const guid_t *instance, 187562306a36Sopenharmony_ci struct vmbus_channel *channel) 187662306a36Sopenharmony_ci{ 187762306a36Sopenharmony_ci struct hv_device *child_device_obj; 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci child_device_obj = kzalloc(sizeof(struct hv_device), GFP_KERNEL); 188062306a36Sopenharmony_ci if (!child_device_obj) { 188162306a36Sopenharmony_ci pr_err("Unable to allocate device object for child device\n"); 188262306a36Sopenharmony_ci return NULL; 188362306a36Sopenharmony_ci } 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci child_device_obj->channel = channel; 188662306a36Sopenharmony_ci guid_copy(&child_device_obj->dev_type, type); 188762306a36Sopenharmony_ci guid_copy(&child_device_obj->dev_instance, instance); 188862306a36Sopenharmony_ci child_device_obj->vendor_id = PCI_VENDOR_ID_MICROSOFT; 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci return child_device_obj; 189162306a36Sopenharmony_ci} 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci/* 189462306a36Sopenharmony_ci * vmbus_device_register - Register the child device 189562306a36Sopenharmony_ci */ 189662306a36Sopenharmony_ciint vmbus_device_register(struct hv_device *child_device_obj) 189762306a36Sopenharmony_ci{ 189862306a36Sopenharmony_ci struct kobject *kobj = &child_device_obj->device.kobj; 189962306a36Sopenharmony_ci int ret; 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci dev_set_name(&child_device_obj->device, "%pUl", 190262306a36Sopenharmony_ci &child_device_obj->channel->offermsg.offer.if_instance); 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci child_device_obj->device.bus = &hv_bus; 190562306a36Sopenharmony_ci child_device_obj->device.parent = hv_dev; 190662306a36Sopenharmony_ci child_device_obj->device.release = vmbus_device_release; 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci child_device_obj->device.dma_parms = &child_device_obj->dma_parms; 190962306a36Sopenharmony_ci child_device_obj->device.dma_mask = &child_device_obj->dma_mask; 191062306a36Sopenharmony_ci dma_set_mask(&child_device_obj->device, DMA_BIT_MASK(64)); 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci /* 191362306a36Sopenharmony_ci * Register with the LDM. This will kick off the driver/device 191462306a36Sopenharmony_ci * binding...which will eventually call vmbus_match() and vmbus_probe() 191562306a36Sopenharmony_ci */ 191662306a36Sopenharmony_ci ret = device_register(&child_device_obj->device); 191762306a36Sopenharmony_ci if (ret) { 191862306a36Sopenharmony_ci pr_err("Unable to register child device\n"); 191962306a36Sopenharmony_ci put_device(&child_device_obj->device); 192062306a36Sopenharmony_ci return ret; 192162306a36Sopenharmony_ci } 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci child_device_obj->channels_kset = kset_create_and_add("channels", 192462306a36Sopenharmony_ci NULL, kobj); 192562306a36Sopenharmony_ci if (!child_device_obj->channels_kset) { 192662306a36Sopenharmony_ci ret = -ENOMEM; 192762306a36Sopenharmony_ci goto err_dev_unregister; 192862306a36Sopenharmony_ci } 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci ret = vmbus_add_channel_kobj(child_device_obj, 193162306a36Sopenharmony_ci child_device_obj->channel); 193262306a36Sopenharmony_ci if (ret) { 193362306a36Sopenharmony_ci pr_err("Unable to register primary channeln"); 193462306a36Sopenharmony_ci goto err_kset_unregister; 193562306a36Sopenharmony_ci } 193662306a36Sopenharmony_ci hv_debug_add_dev_dir(child_device_obj); 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci return 0; 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_cierr_kset_unregister: 194162306a36Sopenharmony_ci kset_unregister(child_device_obj->channels_kset); 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_cierr_dev_unregister: 194462306a36Sopenharmony_ci device_unregister(&child_device_obj->device); 194562306a36Sopenharmony_ci return ret; 194662306a36Sopenharmony_ci} 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci/* 194962306a36Sopenharmony_ci * vmbus_device_unregister - Remove the specified child device 195062306a36Sopenharmony_ci * from the vmbus. 195162306a36Sopenharmony_ci */ 195262306a36Sopenharmony_civoid vmbus_device_unregister(struct hv_device *device_obj) 195362306a36Sopenharmony_ci{ 195462306a36Sopenharmony_ci pr_debug("child device %s unregistered\n", 195562306a36Sopenharmony_ci dev_name(&device_obj->device)); 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci kset_unregister(device_obj->channels_kset); 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci /* 196062306a36Sopenharmony_ci * Kick off the process of unregistering the device. 196162306a36Sopenharmony_ci * This will call vmbus_remove() and eventually vmbus_device_release() 196262306a36Sopenharmony_ci */ 196362306a36Sopenharmony_ci device_unregister(&device_obj->device); 196462306a36Sopenharmony_ci} 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci#ifdef CONFIG_ACPI 196762306a36Sopenharmony_ci/* 196862306a36Sopenharmony_ci * VMBUS is an acpi enumerated device. Get the information we 196962306a36Sopenharmony_ci * need from DSDT. 197062306a36Sopenharmony_ci */ 197162306a36Sopenharmony_cistatic acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx) 197262306a36Sopenharmony_ci{ 197362306a36Sopenharmony_ci resource_size_t start = 0; 197462306a36Sopenharmony_ci resource_size_t end = 0; 197562306a36Sopenharmony_ci struct resource *new_res; 197662306a36Sopenharmony_ci struct resource **old_res = &hyperv_mmio; 197762306a36Sopenharmony_ci struct resource **prev_res = NULL; 197862306a36Sopenharmony_ci struct resource r; 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci switch (res->type) { 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci /* 198362306a36Sopenharmony_ci * "Address" descriptors are for bus windows. Ignore 198462306a36Sopenharmony_ci * "memory" descriptors, which are for registers on 198562306a36Sopenharmony_ci * devices. 198662306a36Sopenharmony_ci */ 198762306a36Sopenharmony_ci case ACPI_RESOURCE_TYPE_ADDRESS32: 198862306a36Sopenharmony_ci start = res->data.address32.address.minimum; 198962306a36Sopenharmony_ci end = res->data.address32.address.maximum; 199062306a36Sopenharmony_ci break; 199162306a36Sopenharmony_ci 199262306a36Sopenharmony_ci case ACPI_RESOURCE_TYPE_ADDRESS64: 199362306a36Sopenharmony_ci start = res->data.address64.address.minimum; 199462306a36Sopenharmony_ci end = res->data.address64.address.maximum; 199562306a36Sopenharmony_ci break; 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci /* 199862306a36Sopenharmony_ci * The IRQ information is needed only on ARM64, which Hyper-V 199962306a36Sopenharmony_ci * sets up in the extended format. IRQ information is present 200062306a36Sopenharmony_ci * on x86/x64 in the non-extended format but it is not used by 200162306a36Sopenharmony_ci * Linux. So don't bother checking for the non-extended format. 200262306a36Sopenharmony_ci */ 200362306a36Sopenharmony_ci case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: 200462306a36Sopenharmony_ci if (!acpi_dev_resource_interrupt(res, 0, &r)) { 200562306a36Sopenharmony_ci pr_err("Unable to parse Hyper-V ACPI interrupt\n"); 200662306a36Sopenharmony_ci return AE_ERROR; 200762306a36Sopenharmony_ci } 200862306a36Sopenharmony_ci /* ARM64 INTID for VMbus */ 200962306a36Sopenharmony_ci vmbus_interrupt = res->data.extended_irq.interrupts[0]; 201062306a36Sopenharmony_ci /* Linux IRQ number */ 201162306a36Sopenharmony_ci vmbus_irq = r.start; 201262306a36Sopenharmony_ci return AE_OK; 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci default: 201562306a36Sopenharmony_ci /* Unused resource type */ 201662306a36Sopenharmony_ci return AE_OK; 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci } 201962306a36Sopenharmony_ci /* 202062306a36Sopenharmony_ci * Ignore ranges that are below 1MB, as they're not 202162306a36Sopenharmony_ci * necessary or useful here. 202262306a36Sopenharmony_ci */ 202362306a36Sopenharmony_ci if (end < 0x100000) 202462306a36Sopenharmony_ci return AE_OK; 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci new_res = kzalloc(sizeof(*new_res), GFP_ATOMIC); 202762306a36Sopenharmony_ci if (!new_res) 202862306a36Sopenharmony_ci return AE_NO_MEMORY; 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ci /* If this range overlaps the virtual TPM, truncate it. */ 203162306a36Sopenharmony_ci if (end > VTPM_BASE_ADDRESS && start < VTPM_BASE_ADDRESS) 203262306a36Sopenharmony_ci end = VTPM_BASE_ADDRESS; 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_ci new_res->name = "hyperv mmio"; 203562306a36Sopenharmony_ci new_res->flags = IORESOURCE_MEM; 203662306a36Sopenharmony_ci new_res->start = start; 203762306a36Sopenharmony_ci new_res->end = end; 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_ci /* 204062306a36Sopenharmony_ci * If two ranges are adjacent, merge them. 204162306a36Sopenharmony_ci */ 204262306a36Sopenharmony_ci do { 204362306a36Sopenharmony_ci if (!*old_res) { 204462306a36Sopenharmony_ci *old_res = new_res; 204562306a36Sopenharmony_ci break; 204662306a36Sopenharmony_ci } 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci if (((*old_res)->end + 1) == new_res->start) { 204962306a36Sopenharmony_ci (*old_res)->end = new_res->end; 205062306a36Sopenharmony_ci kfree(new_res); 205162306a36Sopenharmony_ci break; 205262306a36Sopenharmony_ci } 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci if ((*old_res)->start == new_res->end + 1) { 205562306a36Sopenharmony_ci (*old_res)->start = new_res->start; 205662306a36Sopenharmony_ci kfree(new_res); 205762306a36Sopenharmony_ci break; 205862306a36Sopenharmony_ci } 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci if ((*old_res)->start > new_res->end) { 206162306a36Sopenharmony_ci new_res->sibling = *old_res; 206262306a36Sopenharmony_ci if (prev_res) 206362306a36Sopenharmony_ci (*prev_res)->sibling = new_res; 206462306a36Sopenharmony_ci *old_res = new_res; 206562306a36Sopenharmony_ci break; 206662306a36Sopenharmony_ci } 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci prev_res = old_res; 206962306a36Sopenharmony_ci old_res = &(*old_res)->sibling; 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci } while (1); 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_ci return AE_OK; 207462306a36Sopenharmony_ci} 207562306a36Sopenharmony_ci#endif 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_cistatic void vmbus_mmio_remove(void) 207862306a36Sopenharmony_ci{ 207962306a36Sopenharmony_ci struct resource *cur_res; 208062306a36Sopenharmony_ci struct resource *next_res; 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci if (hyperv_mmio) { 208362306a36Sopenharmony_ci if (fb_mmio) { 208462306a36Sopenharmony_ci __release_region(hyperv_mmio, fb_mmio->start, 208562306a36Sopenharmony_ci resource_size(fb_mmio)); 208662306a36Sopenharmony_ci fb_mmio = NULL; 208762306a36Sopenharmony_ci } 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci for (cur_res = hyperv_mmio; cur_res; cur_res = next_res) { 209062306a36Sopenharmony_ci next_res = cur_res->sibling; 209162306a36Sopenharmony_ci kfree(cur_res); 209262306a36Sopenharmony_ci } 209362306a36Sopenharmony_ci } 209462306a36Sopenharmony_ci} 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_cistatic void __maybe_unused vmbus_reserve_fb(void) 209762306a36Sopenharmony_ci{ 209862306a36Sopenharmony_ci resource_size_t start = 0, size; 209962306a36Sopenharmony_ci struct pci_dev *pdev; 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci if (efi_enabled(EFI_BOOT)) { 210262306a36Sopenharmony_ci /* Gen2 VM: get FB base from EFI framebuffer */ 210362306a36Sopenharmony_ci start = screen_info.lfb_base; 210462306a36Sopenharmony_ci size = max_t(__u32, screen_info.lfb_size, 0x800000); 210562306a36Sopenharmony_ci } else { 210662306a36Sopenharmony_ci /* Gen1 VM: get FB base from PCI */ 210762306a36Sopenharmony_ci pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT, 210862306a36Sopenharmony_ci PCI_DEVICE_ID_HYPERV_VIDEO, NULL); 210962306a36Sopenharmony_ci if (!pdev) 211062306a36Sopenharmony_ci return; 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci if (pdev->resource[0].flags & IORESOURCE_MEM) { 211362306a36Sopenharmony_ci start = pci_resource_start(pdev, 0); 211462306a36Sopenharmony_ci size = pci_resource_len(pdev, 0); 211562306a36Sopenharmony_ci } 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ci /* 211862306a36Sopenharmony_ci * Release the PCI device so hyperv_drm or hyperv_fb driver can 211962306a36Sopenharmony_ci * grab it later. 212062306a36Sopenharmony_ci */ 212162306a36Sopenharmony_ci pci_dev_put(pdev); 212262306a36Sopenharmony_ci } 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci if (!start) 212562306a36Sopenharmony_ci return; 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci /* 212862306a36Sopenharmony_ci * Make a claim for the frame buffer in the resource tree under the 212962306a36Sopenharmony_ci * first node, which will be the one below 4GB. The length seems to 213062306a36Sopenharmony_ci * be underreported, particularly in a Generation 1 VM. So start out 213162306a36Sopenharmony_ci * reserving a larger area and make it smaller until it succeeds. 213262306a36Sopenharmony_ci */ 213362306a36Sopenharmony_ci for (; !fb_mmio && (size >= 0x100000); size >>= 1) 213462306a36Sopenharmony_ci fb_mmio = __request_region(hyperv_mmio, start, size, fb_mmio_name, 0); 213562306a36Sopenharmony_ci} 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci/** 213862306a36Sopenharmony_ci * vmbus_allocate_mmio() - Pick a memory-mapped I/O range. 213962306a36Sopenharmony_ci * @new: If successful, supplied a pointer to the 214062306a36Sopenharmony_ci * allocated MMIO space. 214162306a36Sopenharmony_ci * @device_obj: Identifies the caller 214262306a36Sopenharmony_ci * @min: Minimum guest physical address of the 214362306a36Sopenharmony_ci * allocation 214462306a36Sopenharmony_ci * @max: Maximum guest physical address 214562306a36Sopenharmony_ci * @size: Size of the range to be allocated 214662306a36Sopenharmony_ci * @align: Alignment of the range to be allocated 214762306a36Sopenharmony_ci * @fb_overlap_ok: Whether this allocation can be allowed 214862306a36Sopenharmony_ci * to overlap the video frame buffer. 214962306a36Sopenharmony_ci * 215062306a36Sopenharmony_ci * This function walks the resources granted to VMBus by the 215162306a36Sopenharmony_ci * _CRS object in the ACPI namespace underneath the parent 215262306a36Sopenharmony_ci * "bridge" whether that's a root PCI bus in the Generation 1 215362306a36Sopenharmony_ci * case or a Module Device in the Generation 2 case. It then 215462306a36Sopenharmony_ci * attempts to allocate from the global MMIO pool in a way that 215562306a36Sopenharmony_ci * matches the constraints supplied in these parameters and by 215662306a36Sopenharmony_ci * that _CRS. 215762306a36Sopenharmony_ci * 215862306a36Sopenharmony_ci * Return: 0 on success, -errno on failure 215962306a36Sopenharmony_ci */ 216062306a36Sopenharmony_ciint vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj, 216162306a36Sopenharmony_ci resource_size_t min, resource_size_t max, 216262306a36Sopenharmony_ci resource_size_t size, resource_size_t align, 216362306a36Sopenharmony_ci bool fb_overlap_ok) 216462306a36Sopenharmony_ci{ 216562306a36Sopenharmony_ci struct resource *iter, *shadow; 216662306a36Sopenharmony_ci resource_size_t range_min, range_max, start, end; 216762306a36Sopenharmony_ci const char *dev_n = dev_name(&device_obj->device); 216862306a36Sopenharmony_ci int retval; 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci retval = -ENXIO; 217162306a36Sopenharmony_ci mutex_lock(&hyperv_mmio_lock); 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci /* 217462306a36Sopenharmony_ci * If overlaps with frame buffers are allowed, then first attempt to 217562306a36Sopenharmony_ci * make the allocation from within the reserved region. Because it 217662306a36Sopenharmony_ci * is already reserved, no shadow allocation is necessary. 217762306a36Sopenharmony_ci */ 217862306a36Sopenharmony_ci if (fb_overlap_ok && fb_mmio && !(min > fb_mmio->end) && 217962306a36Sopenharmony_ci !(max < fb_mmio->start)) { 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci range_min = fb_mmio->start; 218262306a36Sopenharmony_ci range_max = fb_mmio->end; 218362306a36Sopenharmony_ci start = (range_min + align - 1) & ~(align - 1); 218462306a36Sopenharmony_ci for (; start + size - 1 <= range_max; start += align) { 218562306a36Sopenharmony_ci *new = request_mem_region_exclusive(start, size, dev_n); 218662306a36Sopenharmony_ci if (*new) { 218762306a36Sopenharmony_ci retval = 0; 218862306a36Sopenharmony_ci goto exit; 218962306a36Sopenharmony_ci } 219062306a36Sopenharmony_ci } 219162306a36Sopenharmony_ci } 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci for (iter = hyperv_mmio; iter; iter = iter->sibling) { 219462306a36Sopenharmony_ci if ((iter->start >= max) || (iter->end <= min)) 219562306a36Sopenharmony_ci continue; 219662306a36Sopenharmony_ci 219762306a36Sopenharmony_ci range_min = iter->start; 219862306a36Sopenharmony_ci range_max = iter->end; 219962306a36Sopenharmony_ci start = (range_min + align - 1) & ~(align - 1); 220062306a36Sopenharmony_ci for (; start + size - 1 <= range_max; start += align) { 220162306a36Sopenharmony_ci end = start + size - 1; 220262306a36Sopenharmony_ci 220362306a36Sopenharmony_ci /* Skip the whole fb_mmio region if not fb_overlap_ok */ 220462306a36Sopenharmony_ci if (!fb_overlap_ok && fb_mmio && 220562306a36Sopenharmony_ci (((start >= fb_mmio->start) && (start <= fb_mmio->end)) || 220662306a36Sopenharmony_ci ((end >= fb_mmio->start) && (end <= fb_mmio->end)))) 220762306a36Sopenharmony_ci continue; 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci shadow = __request_region(iter, start, size, NULL, 221062306a36Sopenharmony_ci IORESOURCE_BUSY); 221162306a36Sopenharmony_ci if (!shadow) 221262306a36Sopenharmony_ci continue; 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_ci *new = request_mem_region_exclusive(start, size, dev_n); 221562306a36Sopenharmony_ci if (*new) { 221662306a36Sopenharmony_ci shadow->name = (char *)*new; 221762306a36Sopenharmony_ci retval = 0; 221862306a36Sopenharmony_ci goto exit; 221962306a36Sopenharmony_ci } 222062306a36Sopenharmony_ci 222162306a36Sopenharmony_ci __release_region(iter, start, size); 222262306a36Sopenharmony_ci } 222362306a36Sopenharmony_ci } 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ciexit: 222662306a36Sopenharmony_ci mutex_unlock(&hyperv_mmio_lock); 222762306a36Sopenharmony_ci return retval; 222862306a36Sopenharmony_ci} 222962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_allocate_mmio); 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci/** 223262306a36Sopenharmony_ci * vmbus_free_mmio() - Free a memory-mapped I/O range. 223362306a36Sopenharmony_ci * @start: Base address of region to release. 223462306a36Sopenharmony_ci * @size: Size of the range to be allocated 223562306a36Sopenharmony_ci * 223662306a36Sopenharmony_ci * This function releases anything requested by 223762306a36Sopenharmony_ci * vmbus_mmio_allocate(). 223862306a36Sopenharmony_ci */ 223962306a36Sopenharmony_civoid vmbus_free_mmio(resource_size_t start, resource_size_t size) 224062306a36Sopenharmony_ci{ 224162306a36Sopenharmony_ci struct resource *iter; 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci mutex_lock(&hyperv_mmio_lock); 224462306a36Sopenharmony_ci for (iter = hyperv_mmio; iter; iter = iter->sibling) { 224562306a36Sopenharmony_ci if ((iter->start >= start + size) || (iter->end <= start)) 224662306a36Sopenharmony_ci continue; 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ci __release_region(iter, start, size); 224962306a36Sopenharmony_ci } 225062306a36Sopenharmony_ci release_mem_region(start, size); 225162306a36Sopenharmony_ci mutex_unlock(&hyperv_mmio_lock); 225262306a36Sopenharmony_ci 225362306a36Sopenharmony_ci} 225462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_free_mmio); 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_ci#ifdef CONFIG_ACPI 225762306a36Sopenharmony_cistatic int vmbus_acpi_add(struct platform_device *pdev) 225862306a36Sopenharmony_ci{ 225962306a36Sopenharmony_ci acpi_status result; 226062306a36Sopenharmony_ci int ret_val = -ENODEV; 226162306a36Sopenharmony_ci struct acpi_device *ancestor; 226262306a36Sopenharmony_ci struct acpi_device *device = ACPI_COMPANION(&pdev->dev); 226362306a36Sopenharmony_ci 226462306a36Sopenharmony_ci hv_dev = &device->dev; 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_ci /* 226762306a36Sopenharmony_ci * Older versions of Hyper-V for ARM64 fail to include the _CCA 226862306a36Sopenharmony_ci * method on the top level VMbus device in the DSDT. But devices 226962306a36Sopenharmony_ci * are hardware coherent in all current Hyper-V use cases, so fix 227062306a36Sopenharmony_ci * up the ACPI device to behave as if _CCA is present and indicates 227162306a36Sopenharmony_ci * hardware coherence. 227262306a36Sopenharmony_ci */ 227362306a36Sopenharmony_ci ACPI_COMPANION_SET(&device->dev, device); 227462306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_ACPI_CCA_REQUIRED) && 227562306a36Sopenharmony_ci device_get_dma_attr(&device->dev) == DEV_DMA_NOT_SUPPORTED) { 227662306a36Sopenharmony_ci pr_info("No ACPI _CCA found; assuming coherent device I/O\n"); 227762306a36Sopenharmony_ci device->flags.cca_seen = true; 227862306a36Sopenharmony_ci device->flags.coherent_dma = true; 227962306a36Sopenharmony_ci } 228062306a36Sopenharmony_ci 228162306a36Sopenharmony_ci result = acpi_walk_resources(device->handle, METHOD_NAME__CRS, 228262306a36Sopenharmony_ci vmbus_walk_resources, NULL); 228362306a36Sopenharmony_ci 228462306a36Sopenharmony_ci if (ACPI_FAILURE(result)) 228562306a36Sopenharmony_ci goto acpi_walk_err; 228662306a36Sopenharmony_ci /* 228762306a36Sopenharmony_ci * Some ancestor of the vmbus acpi device (Gen1 or Gen2 228862306a36Sopenharmony_ci * firmware) is the VMOD that has the mmio ranges. Get that. 228962306a36Sopenharmony_ci */ 229062306a36Sopenharmony_ci for (ancestor = acpi_dev_parent(device); 229162306a36Sopenharmony_ci ancestor && ancestor->handle != ACPI_ROOT_OBJECT; 229262306a36Sopenharmony_ci ancestor = acpi_dev_parent(ancestor)) { 229362306a36Sopenharmony_ci result = acpi_walk_resources(ancestor->handle, METHOD_NAME__CRS, 229462306a36Sopenharmony_ci vmbus_walk_resources, NULL); 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_ci if (ACPI_FAILURE(result)) 229762306a36Sopenharmony_ci continue; 229862306a36Sopenharmony_ci if (hyperv_mmio) { 229962306a36Sopenharmony_ci vmbus_reserve_fb(); 230062306a36Sopenharmony_ci break; 230162306a36Sopenharmony_ci } 230262306a36Sopenharmony_ci } 230362306a36Sopenharmony_ci ret_val = 0; 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ciacpi_walk_err: 230662306a36Sopenharmony_ci if (ret_val) 230762306a36Sopenharmony_ci vmbus_mmio_remove(); 230862306a36Sopenharmony_ci return ret_val; 230962306a36Sopenharmony_ci} 231062306a36Sopenharmony_ci#else 231162306a36Sopenharmony_cistatic int vmbus_acpi_add(struct platform_device *pdev) 231262306a36Sopenharmony_ci{ 231362306a36Sopenharmony_ci return 0; 231462306a36Sopenharmony_ci} 231562306a36Sopenharmony_ci#endif 231662306a36Sopenharmony_ci 231762306a36Sopenharmony_cistatic int vmbus_device_add(struct platform_device *pdev) 231862306a36Sopenharmony_ci{ 231962306a36Sopenharmony_ci struct resource **cur_res = &hyperv_mmio; 232062306a36Sopenharmony_ci struct of_range range; 232162306a36Sopenharmony_ci struct of_range_parser parser; 232262306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 232362306a36Sopenharmony_ci int ret; 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_ci hv_dev = &pdev->dev; 232662306a36Sopenharmony_ci 232762306a36Sopenharmony_ci ret = of_range_parser_init(&parser, np); 232862306a36Sopenharmony_ci if (ret) 232962306a36Sopenharmony_ci return ret; 233062306a36Sopenharmony_ci 233162306a36Sopenharmony_ci for_each_of_range(&parser, &range) { 233262306a36Sopenharmony_ci struct resource *res; 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_ci res = kzalloc(sizeof(*res), GFP_KERNEL); 233562306a36Sopenharmony_ci if (!res) { 233662306a36Sopenharmony_ci vmbus_mmio_remove(); 233762306a36Sopenharmony_ci return -ENOMEM; 233862306a36Sopenharmony_ci } 233962306a36Sopenharmony_ci 234062306a36Sopenharmony_ci res->name = "hyperv mmio"; 234162306a36Sopenharmony_ci res->flags = range.flags; 234262306a36Sopenharmony_ci res->start = range.cpu_addr; 234362306a36Sopenharmony_ci res->end = range.cpu_addr + range.size; 234462306a36Sopenharmony_ci 234562306a36Sopenharmony_ci *cur_res = res; 234662306a36Sopenharmony_ci cur_res = &res->sibling; 234762306a36Sopenharmony_ci } 234862306a36Sopenharmony_ci 234962306a36Sopenharmony_ci return ret; 235062306a36Sopenharmony_ci} 235162306a36Sopenharmony_ci 235262306a36Sopenharmony_cistatic int vmbus_platform_driver_probe(struct platform_device *pdev) 235362306a36Sopenharmony_ci{ 235462306a36Sopenharmony_ci if (acpi_disabled) 235562306a36Sopenharmony_ci return vmbus_device_add(pdev); 235662306a36Sopenharmony_ci else 235762306a36Sopenharmony_ci return vmbus_acpi_add(pdev); 235862306a36Sopenharmony_ci} 235962306a36Sopenharmony_ci 236062306a36Sopenharmony_cistatic int vmbus_platform_driver_remove(struct platform_device *pdev) 236162306a36Sopenharmony_ci{ 236262306a36Sopenharmony_ci vmbus_mmio_remove(); 236362306a36Sopenharmony_ci return 0; 236462306a36Sopenharmony_ci} 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 236762306a36Sopenharmony_cistatic int vmbus_bus_suspend(struct device *dev) 236862306a36Sopenharmony_ci{ 236962306a36Sopenharmony_ci struct hv_per_cpu_context *hv_cpu = per_cpu_ptr( 237062306a36Sopenharmony_ci hv_context.cpu_context, VMBUS_CONNECT_CPU); 237162306a36Sopenharmony_ci struct vmbus_channel *channel, *sc; 237262306a36Sopenharmony_ci 237362306a36Sopenharmony_ci tasklet_disable(&hv_cpu->msg_dpc); 237462306a36Sopenharmony_ci vmbus_connection.ignore_any_offer_msg = true; 237562306a36Sopenharmony_ci /* The tasklet_enable() takes care of providing a memory barrier */ 237662306a36Sopenharmony_ci tasklet_enable(&hv_cpu->msg_dpc); 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci /* Drain all the workqueues as we are in suspend */ 237962306a36Sopenharmony_ci drain_workqueue(vmbus_connection.rescind_work_queue); 238062306a36Sopenharmony_ci drain_workqueue(vmbus_connection.work_queue); 238162306a36Sopenharmony_ci drain_workqueue(vmbus_connection.handle_primary_chan_wq); 238262306a36Sopenharmony_ci drain_workqueue(vmbus_connection.handle_sub_chan_wq); 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci mutex_lock(&vmbus_connection.channel_mutex); 238562306a36Sopenharmony_ci list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { 238662306a36Sopenharmony_ci if (!is_hvsock_channel(channel)) 238762306a36Sopenharmony_ci continue; 238862306a36Sopenharmony_ci 238962306a36Sopenharmony_ci vmbus_force_channel_rescinded(channel); 239062306a36Sopenharmony_ci } 239162306a36Sopenharmony_ci mutex_unlock(&vmbus_connection.channel_mutex); 239262306a36Sopenharmony_ci 239362306a36Sopenharmony_ci /* 239462306a36Sopenharmony_ci * Wait until all the sub-channels and hv_sock channels have been 239562306a36Sopenharmony_ci * cleaned up. Sub-channels should be destroyed upon suspend, otherwise 239662306a36Sopenharmony_ci * they would conflict with the new sub-channels that will be created 239762306a36Sopenharmony_ci * in the resume path. hv_sock channels should also be destroyed, but 239862306a36Sopenharmony_ci * a hv_sock channel of an established hv_sock connection can not be 239962306a36Sopenharmony_ci * really destroyed since it may still be referenced by the userspace 240062306a36Sopenharmony_ci * application, so we just force the hv_sock channel to be rescinded 240162306a36Sopenharmony_ci * by vmbus_force_channel_rescinded(), and the userspace application 240262306a36Sopenharmony_ci * will thoroughly destroy the channel after hibernation. 240362306a36Sopenharmony_ci * 240462306a36Sopenharmony_ci * Note: the counter nr_chan_close_on_suspend may never go above 0 if 240562306a36Sopenharmony_ci * the VM has no sub-channel and hv_sock channel, e.g. a 1-vCPU VM. 240662306a36Sopenharmony_ci */ 240762306a36Sopenharmony_ci if (atomic_read(&vmbus_connection.nr_chan_close_on_suspend) > 0) 240862306a36Sopenharmony_ci wait_for_completion(&vmbus_connection.ready_for_suspend_event); 240962306a36Sopenharmony_ci 241062306a36Sopenharmony_ci if (atomic_read(&vmbus_connection.nr_chan_fixup_on_resume) != 0) { 241162306a36Sopenharmony_ci pr_err("Can not suspend due to a previous failed resuming\n"); 241262306a36Sopenharmony_ci return -EBUSY; 241362306a36Sopenharmony_ci } 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci mutex_lock(&vmbus_connection.channel_mutex); 241662306a36Sopenharmony_ci 241762306a36Sopenharmony_ci list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { 241862306a36Sopenharmony_ci /* 241962306a36Sopenharmony_ci * Remove the channel from the array of channels and invalidate 242062306a36Sopenharmony_ci * the channel's relid. Upon resume, vmbus_onoffer() will fix 242162306a36Sopenharmony_ci * up the relid (and other fields, if necessary) and add the 242262306a36Sopenharmony_ci * channel back to the array. 242362306a36Sopenharmony_ci */ 242462306a36Sopenharmony_ci vmbus_channel_unmap_relid(channel); 242562306a36Sopenharmony_ci channel->offermsg.child_relid = INVALID_RELID; 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci if (is_hvsock_channel(channel)) { 242862306a36Sopenharmony_ci if (!channel->rescind) { 242962306a36Sopenharmony_ci pr_err("hv_sock channel not rescinded!\n"); 243062306a36Sopenharmony_ci WARN_ON_ONCE(1); 243162306a36Sopenharmony_ci } 243262306a36Sopenharmony_ci continue; 243362306a36Sopenharmony_ci } 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci list_for_each_entry(sc, &channel->sc_list, sc_list) { 243662306a36Sopenharmony_ci pr_err("Sub-channel not deleted!\n"); 243762306a36Sopenharmony_ci WARN_ON_ONCE(1); 243862306a36Sopenharmony_ci } 243962306a36Sopenharmony_ci 244062306a36Sopenharmony_ci atomic_inc(&vmbus_connection.nr_chan_fixup_on_resume); 244162306a36Sopenharmony_ci } 244262306a36Sopenharmony_ci 244362306a36Sopenharmony_ci mutex_unlock(&vmbus_connection.channel_mutex); 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_ci vmbus_initiate_unload(false); 244662306a36Sopenharmony_ci 244762306a36Sopenharmony_ci /* Reset the event for the next resume. */ 244862306a36Sopenharmony_ci reinit_completion(&vmbus_connection.ready_for_resume_event); 244962306a36Sopenharmony_ci 245062306a36Sopenharmony_ci return 0; 245162306a36Sopenharmony_ci} 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_cistatic int vmbus_bus_resume(struct device *dev) 245462306a36Sopenharmony_ci{ 245562306a36Sopenharmony_ci struct vmbus_channel_msginfo *msginfo; 245662306a36Sopenharmony_ci size_t msgsize; 245762306a36Sopenharmony_ci int ret; 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_ci vmbus_connection.ignore_any_offer_msg = false; 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_ci /* 246262306a36Sopenharmony_ci * We only use the 'vmbus_proto_version', which was in use before 246362306a36Sopenharmony_ci * hibernation, to re-negotiate with the host. 246462306a36Sopenharmony_ci */ 246562306a36Sopenharmony_ci if (!vmbus_proto_version) { 246662306a36Sopenharmony_ci pr_err("Invalid proto version = 0x%x\n", vmbus_proto_version); 246762306a36Sopenharmony_ci return -EINVAL; 246862306a36Sopenharmony_ci } 246962306a36Sopenharmony_ci 247062306a36Sopenharmony_ci msgsize = sizeof(*msginfo) + 247162306a36Sopenharmony_ci sizeof(struct vmbus_channel_initiate_contact); 247262306a36Sopenharmony_ci 247362306a36Sopenharmony_ci msginfo = kzalloc(msgsize, GFP_KERNEL); 247462306a36Sopenharmony_ci 247562306a36Sopenharmony_ci if (msginfo == NULL) 247662306a36Sopenharmony_ci return -ENOMEM; 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_ci ret = vmbus_negotiate_version(msginfo, vmbus_proto_version); 247962306a36Sopenharmony_ci 248062306a36Sopenharmony_ci kfree(msginfo); 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_ci if (ret != 0) 248362306a36Sopenharmony_ci return ret; 248462306a36Sopenharmony_ci 248562306a36Sopenharmony_ci WARN_ON(atomic_read(&vmbus_connection.nr_chan_fixup_on_resume) == 0); 248662306a36Sopenharmony_ci 248762306a36Sopenharmony_ci vmbus_request_offers(); 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_ci if (wait_for_completion_timeout( 249062306a36Sopenharmony_ci &vmbus_connection.ready_for_resume_event, 10 * HZ) == 0) 249162306a36Sopenharmony_ci pr_err("Some vmbus device is missing after suspending?\n"); 249262306a36Sopenharmony_ci 249362306a36Sopenharmony_ci /* Reset the event for the next suspend. */ 249462306a36Sopenharmony_ci reinit_completion(&vmbus_connection.ready_for_suspend_event); 249562306a36Sopenharmony_ci 249662306a36Sopenharmony_ci return 0; 249762306a36Sopenharmony_ci} 249862306a36Sopenharmony_ci#else 249962306a36Sopenharmony_ci#define vmbus_bus_suspend NULL 250062306a36Sopenharmony_ci#define vmbus_bus_resume NULL 250162306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 250262306a36Sopenharmony_ci 250362306a36Sopenharmony_cistatic const __maybe_unused struct of_device_id vmbus_of_match[] = { 250462306a36Sopenharmony_ci { 250562306a36Sopenharmony_ci .compatible = "microsoft,vmbus", 250662306a36Sopenharmony_ci }, 250762306a36Sopenharmony_ci { 250862306a36Sopenharmony_ci /* sentinel */ 250962306a36Sopenharmony_ci }, 251062306a36Sopenharmony_ci}; 251162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, vmbus_of_match); 251262306a36Sopenharmony_ci 251362306a36Sopenharmony_cistatic const __maybe_unused struct acpi_device_id vmbus_acpi_device_ids[] = { 251462306a36Sopenharmony_ci {"VMBUS", 0}, 251562306a36Sopenharmony_ci {"VMBus", 0}, 251662306a36Sopenharmony_ci {"", 0}, 251762306a36Sopenharmony_ci}; 251862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, vmbus_acpi_device_ids); 251962306a36Sopenharmony_ci 252062306a36Sopenharmony_ci/* 252162306a36Sopenharmony_ci * Note: we must use the "no_irq" ops, otherwise hibernation can not work with 252262306a36Sopenharmony_ci * PCI device assignment, because "pci_dev_pm_ops" uses the "noirq" ops: in 252362306a36Sopenharmony_ci * the resume path, the pci "noirq" restore op runs before "non-noirq" op (see 252462306a36Sopenharmony_ci * resume_target_kernel() -> dpm_resume_start(), and hibernation_restore() -> 252562306a36Sopenharmony_ci * dpm_resume_end()). This means vmbus_bus_resume() and the pci-hyperv's 252662306a36Sopenharmony_ci * resume callback must also run via the "noirq" ops. 252762306a36Sopenharmony_ci * 252862306a36Sopenharmony_ci * Set suspend_noirq/resume_noirq to NULL for Suspend-to-Idle: see the comment 252962306a36Sopenharmony_ci * earlier in this file before vmbus_pm. 253062306a36Sopenharmony_ci */ 253162306a36Sopenharmony_ci 253262306a36Sopenharmony_cistatic const struct dev_pm_ops vmbus_bus_pm = { 253362306a36Sopenharmony_ci .suspend_noirq = NULL, 253462306a36Sopenharmony_ci .resume_noirq = NULL, 253562306a36Sopenharmony_ci .freeze_noirq = vmbus_bus_suspend, 253662306a36Sopenharmony_ci .thaw_noirq = vmbus_bus_resume, 253762306a36Sopenharmony_ci .poweroff_noirq = vmbus_bus_suspend, 253862306a36Sopenharmony_ci .restore_noirq = vmbus_bus_resume 253962306a36Sopenharmony_ci}; 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_cistatic struct platform_driver vmbus_platform_driver = { 254262306a36Sopenharmony_ci .probe = vmbus_platform_driver_probe, 254362306a36Sopenharmony_ci .remove = vmbus_platform_driver_remove, 254462306a36Sopenharmony_ci .driver = { 254562306a36Sopenharmony_ci .name = "vmbus", 254662306a36Sopenharmony_ci .acpi_match_table = ACPI_PTR(vmbus_acpi_device_ids), 254762306a36Sopenharmony_ci .of_match_table = of_match_ptr(vmbus_of_match), 254862306a36Sopenharmony_ci .pm = &vmbus_bus_pm, 254962306a36Sopenharmony_ci .probe_type = PROBE_FORCE_SYNCHRONOUS, 255062306a36Sopenharmony_ci } 255162306a36Sopenharmony_ci}; 255262306a36Sopenharmony_ci 255362306a36Sopenharmony_cistatic void hv_kexec_handler(void) 255462306a36Sopenharmony_ci{ 255562306a36Sopenharmony_ci hv_stimer_global_cleanup(); 255662306a36Sopenharmony_ci vmbus_initiate_unload(false); 255762306a36Sopenharmony_ci /* Make sure conn_state is set as hv_synic_cleanup checks for it */ 255862306a36Sopenharmony_ci mb(); 255962306a36Sopenharmony_ci cpuhp_remove_state(hyperv_cpuhp_online); 256062306a36Sopenharmony_ci}; 256162306a36Sopenharmony_ci 256262306a36Sopenharmony_cistatic void hv_crash_handler(struct pt_regs *regs) 256362306a36Sopenharmony_ci{ 256462306a36Sopenharmony_ci int cpu; 256562306a36Sopenharmony_ci 256662306a36Sopenharmony_ci vmbus_initiate_unload(true); 256762306a36Sopenharmony_ci /* 256862306a36Sopenharmony_ci * In crash handler we can't schedule synic cleanup for all CPUs, 256962306a36Sopenharmony_ci * doing the cleanup for current CPU only. This should be sufficient 257062306a36Sopenharmony_ci * for kdump. 257162306a36Sopenharmony_ci */ 257262306a36Sopenharmony_ci cpu = smp_processor_id(); 257362306a36Sopenharmony_ci hv_stimer_cleanup(cpu); 257462306a36Sopenharmony_ci hv_synic_disable_regs(cpu); 257562306a36Sopenharmony_ci}; 257662306a36Sopenharmony_ci 257762306a36Sopenharmony_cistatic int hv_synic_suspend(void) 257862306a36Sopenharmony_ci{ 257962306a36Sopenharmony_ci /* 258062306a36Sopenharmony_ci * When we reach here, all the non-boot CPUs have been offlined. 258162306a36Sopenharmony_ci * If we're in a legacy configuration where stimer Direct Mode is 258262306a36Sopenharmony_ci * not enabled, the stimers on the non-boot CPUs have been unbound 258362306a36Sopenharmony_ci * in hv_synic_cleanup() -> hv_stimer_legacy_cleanup() -> 258462306a36Sopenharmony_ci * hv_stimer_cleanup() -> clockevents_unbind_device(). 258562306a36Sopenharmony_ci * 258662306a36Sopenharmony_ci * hv_synic_suspend() only runs on CPU0 with interrupts disabled. 258762306a36Sopenharmony_ci * Here we do not call hv_stimer_legacy_cleanup() on CPU0 because: 258862306a36Sopenharmony_ci * 1) it's unnecessary as interrupts remain disabled between 258962306a36Sopenharmony_ci * syscore_suspend() and syscore_resume(): see create_image() and 259062306a36Sopenharmony_ci * resume_target_kernel() 259162306a36Sopenharmony_ci * 2) the stimer on CPU0 is automatically disabled later by 259262306a36Sopenharmony_ci * syscore_suspend() -> timekeeping_suspend() -> tick_suspend() -> ... 259362306a36Sopenharmony_ci * -> clockevents_shutdown() -> ... -> hv_ce_shutdown() 259462306a36Sopenharmony_ci * 3) a warning would be triggered if we call 259562306a36Sopenharmony_ci * clockevents_unbind_device(), which may sleep, in an 259662306a36Sopenharmony_ci * interrupts-disabled context. 259762306a36Sopenharmony_ci */ 259862306a36Sopenharmony_ci 259962306a36Sopenharmony_ci hv_synic_disable_regs(0); 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_ci return 0; 260262306a36Sopenharmony_ci} 260362306a36Sopenharmony_ci 260462306a36Sopenharmony_cistatic void hv_synic_resume(void) 260562306a36Sopenharmony_ci{ 260662306a36Sopenharmony_ci hv_synic_enable_regs(0); 260762306a36Sopenharmony_ci 260862306a36Sopenharmony_ci /* 260962306a36Sopenharmony_ci * Note: we don't need to call hv_stimer_init(0), because the timer 261062306a36Sopenharmony_ci * on CPU0 is not unbound in hv_synic_suspend(), and the timer is 261162306a36Sopenharmony_ci * automatically re-enabled in timekeeping_resume(). 261262306a36Sopenharmony_ci */ 261362306a36Sopenharmony_ci} 261462306a36Sopenharmony_ci 261562306a36Sopenharmony_ci/* The callbacks run only on CPU0, with irqs_disabled. */ 261662306a36Sopenharmony_cistatic struct syscore_ops hv_synic_syscore_ops = { 261762306a36Sopenharmony_ci .suspend = hv_synic_suspend, 261862306a36Sopenharmony_ci .resume = hv_synic_resume, 261962306a36Sopenharmony_ci}; 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_cistatic int __init hv_acpi_init(void) 262262306a36Sopenharmony_ci{ 262362306a36Sopenharmony_ci int ret; 262462306a36Sopenharmony_ci 262562306a36Sopenharmony_ci if (!hv_is_hyperv_initialized()) 262662306a36Sopenharmony_ci return -ENODEV; 262762306a36Sopenharmony_ci 262862306a36Sopenharmony_ci if (hv_root_partition && !hv_nested) 262962306a36Sopenharmony_ci return 0; 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci /* 263262306a36Sopenharmony_ci * Get ACPI resources first. 263362306a36Sopenharmony_ci */ 263462306a36Sopenharmony_ci ret = platform_driver_register(&vmbus_platform_driver); 263562306a36Sopenharmony_ci if (ret) 263662306a36Sopenharmony_ci return ret; 263762306a36Sopenharmony_ci 263862306a36Sopenharmony_ci if (!hv_dev) { 263962306a36Sopenharmony_ci ret = -ENODEV; 264062306a36Sopenharmony_ci goto cleanup; 264162306a36Sopenharmony_ci } 264262306a36Sopenharmony_ci 264362306a36Sopenharmony_ci /* 264462306a36Sopenharmony_ci * If we're on an architecture with a hardcoded hypervisor 264562306a36Sopenharmony_ci * vector (i.e. x86/x64), override the VMbus interrupt found 264662306a36Sopenharmony_ci * in the ACPI tables. Ensure vmbus_irq is not set since the 264762306a36Sopenharmony_ci * normal Linux IRQ mechanism is not used in this case. 264862306a36Sopenharmony_ci */ 264962306a36Sopenharmony_ci#ifdef HYPERVISOR_CALLBACK_VECTOR 265062306a36Sopenharmony_ci vmbus_interrupt = HYPERVISOR_CALLBACK_VECTOR; 265162306a36Sopenharmony_ci vmbus_irq = -1; 265262306a36Sopenharmony_ci#endif 265362306a36Sopenharmony_ci 265462306a36Sopenharmony_ci hv_debug_init(); 265562306a36Sopenharmony_ci 265662306a36Sopenharmony_ci ret = vmbus_bus_init(); 265762306a36Sopenharmony_ci if (ret) 265862306a36Sopenharmony_ci goto cleanup; 265962306a36Sopenharmony_ci 266062306a36Sopenharmony_ci hv_setup_kexec_handler(hv_kexec_handler); 266162306a36Sopenharmony_ci hv_setup_crash_handler(hv_crash_handler); 266262306a36Sopenharmony_ci 266362306a36Sopenharmony_ci register_syscore_ops(&hv_synic_syscore_ops); 266462306a36Sopenharmony_ci 266562306a36Sopenharmony_ci return 0; 266662306a36Sopenharmony_ci 266762306a36Sopenharmony_cicleanup: 266862306a36Sopenharmony_ci platform_driver_unregister(&vmbus_platform_driver); 266962306a36Sopenharmony_ci hv_dev = NULL; 267062306a36Sopenharmony_ci return ret; 267162306a36Sopenharmony_ci} 267262306a36Sopenharmony_ci 267362306a36Sopenharmony_cistatic void __exit vmbus_exit(void) 267462306a36Sopenharmony_ci{ 267562306a36Sopenharmony_ci int cpu; 267662306a36Sopenharmony_ci 267762306a36Sopenharmony_ci unregister_syscore_ops(&hv_synic_syscore_ops); 267862306a36Sopenharmony_ci 267962306a36Sopenharmony_ci hv_remove_kexec_handler(); 268062306a36Sopenharmony_ci hv_remove_crash_handler(); 268162306a36Sopenharmony_ci vmbus_connection.conn_state = DISCONNECTED; 268262306a36Sopenharmony_ci hv_stimer_global_cleanup(); 268362306a36Sopenharmony_ci vmbus_disconnect(); 268462306a36Sopenharmony_ci if (vmbus_irq == -1) { 268562306a36Sopenharmony_ci hv_remove_vmbus_handler(); 268662306a36Sopenharmony_ci } else { 268762306a36Sopenharmony_ci free_percpu_irq(vmbus_irq, vmbus_evt); 268862306a36Sopenharmony_ci free_percpu(vmbus_evt); 268962306a36Sopenharmony_ci } 269062306a36Sopenharmony_ci for_each_online_cpu(cpu) { 269162306a36Sopenharmony_ci struct hv_per_cpu_context *hv_cpu 269262306a36Sopenharmony_ci = per_cpu_ptr(hv_context.cpu_context, cpu); 269362306a36Sopenharmony_ci 269462306a36Sopenharmony_ci tasklet_kill(&hv_cpu->msg_dpc); 269562306a36Sopenharmony_ci } 269662306a36Sopenharmony_ci hv_debug_rm_all_dir(); 269762306a36Sopenharmony_ci 269862306a36Sopenharmony_ci vmbus_free_channels(); 269962306a36Sopenharmony_ci kfree(vmbus_connection.channels); 270062306a36Sopenharmony_ci 270162306a36Sopenharmony_ci /* 270262306a36Sopenharmony_ci * The vmbus panic notifier is always registered, hence we should 270362306a36Sopenharmony_ci * also unconditionally unregister it here as well. 270462306a36Sopenharmony_ci */ 270562306a36Sopenharmony_ci atomic_notifier_chain_unregister(&panic_notifier_list, 270662306a36Sopenharmony_ci &hyperv_panic_vmbus_unload_block); 270762306a36Sopenharmony_ci 270862306a36Sopenharmony_ci bus_unregister(&hv_bus); 270962306a36Sopenharmony_ci 271062306a36Sopenharmony_ci cpuhp_remove_state(hyperv_cpuhp_online); 271162306a36Sopenharmony_ci hv_synic_free(); 271262306a36Sopenharmony_ci platform_driver_unregister(&vmbus_platform_driver); 271362306a36Sopenharmony_ci} 271462306a36Sopenharmony_ci 271562306a36Sopenharmony_ci 271662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 271762306a36Sopenharmony_ciMODULE_DESCRIPTION("Microsoft Hyper-V VMBus Driver"); 271862306a36Sopenharmony_ci 271962306a36Sopenharmony_cisubsys_initcall(hv_acpi_init); 272062306a36Sopenharmony_cimodule_exit(vmbus_exit); 2721