blob: 04ad977498843227362fc21bc8b08e1b6bff3e59 [file] [log] [blame]
Hank Janssen3e7ee492009-07-13 16:02:34 -07001/*
Hank Janssen3e7ee492009-07-13 16:02:34 -07002 * Copyright (c) 2009, Microsoft Corporation.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License along with
14 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
15 * Place - Suite 330, Boston, MA 02111-1307 USA.
16 *
17 * Authors:
18 * Haiyang Zhang <haiyangz@microsoft.com>
19 * Hank Janssen <hjanssen@microsoft.com>
20 *
21 */
Hank Janssen0a466182011-03-29 13:58:47 -070022#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
23
Greg Kroah-Hartmana0086dc2009-08-17 17:22:08 -070024#include <linux/kernel.h>
25#include <linux/mm.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090026#include <linux/slab.h>
Bill Pembertonb7c947f2009-07-29 17:00:13 -040027#include <linux/vmalloc.h>
Greg Kroah-Hartman46a97192011-10-04 12:29:52 -070028#include <linux/hyperv.h>
K. Y. Srinivasan83ba0c42012-07-24 16:11:58 -070029#include <linux/version.h>
K. Y. Srinivasandb11f122012-12-01 06:46:53 -080030#include <linux/interrupt.h>
K. Y. Srinivasan4061ed92015-01-09 23:54:32 -080031#include <linux/clockchips.h>
Greg Kroah-Hartman407dd162011-10-11 08:36:44 -060032#include <asm/hyperv.h>
K. Y. Srinivasan4061ed92015-01-09 23:54:32 -080033#include <asm/mshyperv.h>
K. Y. Srinivasan0f2a6612011-05-12 19:34:28 -070034#include "hyperv_vmbus.h"
Hank Janssen3e7ee492009-07-13 16:02:34 -070035
Bill Pemberton454f18a2009-07-27 16:47:24 -040036/* The one and only */
Haiyang Zhang6a0aaa12010-11-08 14:04:40 -080037struct hv_context hv_context = {
38 .synic_initialized = false,
Hank Janssen3e7ee492009-07-13 16:02:34 -070039};
40
K. Y. Srinivasan4061ed92015-01-09 23:54:32 -080041#define HV_TIMER_FREQUENCY (10 * 1000 * 1000) /* 100ns period */
42#define HV_MAX_MAX_DELTA_TICKS 0xffffffff
43#define HV_MIN_DELTA_TICKS 1
44
Hank Janssen3e189512010-03-04 22:11:00 +000045/*
Haiyang Zhangd44890c2010-11-08 14:04:42 -080046 * hv_init - Main initialization routine.
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -070047 *
48 * This routine must be called before any other routines in here are called
49 */
Haiyang Zhangd44890c2010-11-08 14:04:42 -080050int hv_init(void)
Hank Janssen3e7ee492009-07-13 16:02:34 -070051{
Hank Janssen3e7ee492009-07-13 16:02:34 -070052
K. Y. Srinivasan14c1bf82012-02-02 16:56:51 -080053 memset(hv_context.synic_event_page, 0, sizeof(void *) * NR_CPUS);
Haiyang Zhang6a0aaa12010-11-08 14:04:40 -080054 memset(hv_context.synic_message_page, 0,
K. Y. Srinivasan14c1bf82012-02-02 16:56:51 -080055 sizeof(void *) * NR_CPUS);
K. Y. Srinivasanb29ef352014-08-28 18:29:52 -070056 memset(hv_context.post_msg_page, 0,
57 sizeof(void *) * NR_CPUS);
K. Y. Srinivasan917ea422012-12-01 06:46:47 -080058 memset(hv_context.vp_index, 0,
59 sizeof(int) * NR_CPUS);
K. Y. Srinivasandb11f122012-12-01 06:46:53 -080060 memset(hv_context.event_dpc, 0,
61 sizeof(void *) * NR_CPUS);
K. Y. Srinivasand81274a2016-02-26 15:13:21 -080062 memset(hv_context.msg_dpc, 0,
63 sizeof(void *) * NR_CPUS);
K. Y. Srinivasan4061ed92015-01-09 23:54:32 -080064 memset(hv_context.clk_evt, 0,
65 sizeof(void *) * NR_CPUS);
Hank Janssen3e7ee492009-07-13 16:02:34 -070066
K. Y. Srinivasan73638cd2017-01-19 11:51:49 -070067 if (!hv_is_hypercall_page_setup())
K. Y. Srinivasan87300462017-01-18 16:45:02 -070068 return -ENOTSUPP;
Hank Janssena73e6b72010-01-22 19:17:50 +000069
K. Y. Srinivasan5433e002011-08-25 09:48:51 -070070 return 0;
Hank Janssen3e7ee492009-07-13 16:02:34 -070071}
72
Hank Janssen3e189512010-03-04 22:11:00 +000073/*
Haiyang Zhangd44890c2010-11-08 14:04:42 -080074 * hv_cleanup - Cleanup routine.
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -070075 *
76 * This routine is called normally during driver unloading or exiting.
77 */
Vitaly Kuznetsova9f61ca2016-06-03 17:09:22 -070078void hv_cleanup(bool crash)
Hank Janssen3e7ee492009-07-13 16:02:34 -070079{
K. Y. Srinivasanca9357b2015-08-05 00:52:42 -070080
Hank Janssen3e7ee492009-07-13 16:02:34 -070081}
82
Hank Janssen3e189512010-03-04 22:11:00 +000083/*
Haiyang Zhangd44890c2010-11-08 14:04:42 -080084 * hv_post_message - Post a message using the hypervisor message IPC.
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -070085 *
86 * This involves a hypercall.
87 */
Dan Carpenter415f0a02012-03-28 09:58:07 +030088int hv_post_message(union hv_connection_id connection_id,
Haiyang Zhangb8dfb262010-11-08 14:04:41 -080089 enum hv_message_type message_type,
90 void *payload, size_t payload_size)
Hank Janssen3e7ee492009-07-13 16:02:34 -070091{
Hank Janssen3e7ee492009-07-13 16:02:34 -070092
Haiyang Zhangb8dfb262010-11-08 14:04:41 -080093 struct hv_input_post_message *aligned_msg;
Jake Oshinsa1083932015-12-14 16:01:40 -080094 u64 status;
Hank Janssen3e7ee492009-07-13 16:02:34 -070095
Haiyang Zhangb8dfb262010-11-08 14:04:41 -080096 if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
K. Y. Srinivasan39594ab2011-06-06 15:50:09 -070097 return -EMSGSIZE;
Hank Janssen3e7ee492009-07-13 16:02:34 -070098
Haiyang Zhangb8dfb262010-11-08 14:04:41 -080099 aligned_msg = (struct hv_input_post_message *)
K. Y. Srinivasanb29ef352014-08-28 18:29:52 -0700100 hv_context.post_msg_page[get_cpu()];
Hank Janssen3e7ee492009-07-13 16:02:34 -0700101
Haiyang Zhangb8dfb262010-11-08 14:04:41 -0800102 aligned_msg->connectionid = connection_id;
K. Y. Srinivasanb29ef352014-08-28 18:29:52 -0700103 aligned_msg->reserved = 0;
Haiyang Zhangb8dfb262010-11-08 14:04:41 -0800104 aligned_msg->message_type = message_type;
105 aligned_msg->payload_size = payload_size;
106 memcpy((void *)aligned_msg->payload, payload, payload_size);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700107
Jake Oshinsa1083932015-12-14 16:01:40 -0800108 status = hv_do_hypercall(HVCALL_POST_MESSAGE, aligned_msg, NULL);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700109
K. Y. Srinivasanb29ef352014-08-28 18:29:52 -0700110 put_cpu();
Jake Oshinsa1083932015-12-14 16:01:40 -0800111 return status & 0xFFFF;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700112}
113
K. Y. Srinivasan4061ed92015-01-09 23:54:32 -0800114static int hv_ce_set_next_event(unsigned long delta,
115 struct clock_event_device *evt)
116{
Thomas Gleixnera5a1d1c2016-12-21 20:32:01 +0100117 u64 current_tick;
K. Y. Srinivasan4061ed92015-01-09 23:54:32 -0800118
Viresh Kumarbc609cb2015-08-05 00:52:41 -0700119 WARN_ON(!clockevent_state_oneshot(evt));
K. Y. Srinivasan4061ed92015-01-09 23:54:32 -0800120
K. Y. Srinivasand5116b42017-01-19 11:51:51 -0700121 hv_get_current_tick(current_tick);
K. Y. Srinivasan4061ed92015-01-09 23:54:32 -0800122 current_tick += delta;
K. Y. Srinivasand5116b42017-01-19 11:51:51 -0700123 hv_init_timer(HV_X64_MSR_STIMER0_COUNT, current_tick);
K. Y. Srinivasan4061ed92015-01-09 23:54:32 -0800124 return 0;
125}
126
Viresh Kumarbc609cb2015-08-05 00:52:41 -0700127static int hv_ce_shutdown(struct clock_event_device *evt)
128{
K. Y. Srinivasand5116b42017-01-19 11:51:51 -0700129 hv_init_timer(HV_X64_MSR_STIMER0_COUNT, 0);
130 hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, 0);
Viresh Kumarbc609cb2015-08-05 00:52:41 -0700131
132 return 0;
133}
134
135static int hv_ce_set_oneshot(struct clock_event_device *evt)
K. Y. Srinivasan4061ed92015-01-09 23:54:32 -0800136{
137 union hv_timer_config timer_cfg;
138
Viresh Kumarbc609cb2015-08-05 00:52:41 -0700139 timer_cfg.enable = 1;
140 timer_cfg.auto_enable = 1;
141 timer_cfg.sintx = VMBUS_MESSAGE_SINT;
K. Y. Srinivasand5116b42017-01-19 11:51:51 -0700142 hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, timer_cfg.as_uint64);
K. Y. Srinivasan4061ed92015-01-09 23:54:32 -0800143
Viresh Kumarbc609cb2015-08-05 00:52:41 -0700144 return 0;
K. Y. Srinivasan4061ed92015-01-09 23:54:32 -0800145}
146
147static void hv_init_clockevent_device(struct clock_event_device *dev, int cpu)
148{
149 dev->name = "Hyper-V clockevent";
150 dev->features = CLOCK_EVT_FEAT_ONESHOT;
151 dev->cpumask = cpumask_of(cpu);
152 dev->rating = 1000;
Vitaly Kuznetsove0867482015-02-27 11:25:57 -0800153 /*
154 * Avoid settint dev->owner = THIS_MODULE deliberately as doing so will
155 * result in clockevents_config_and_register() taking additional
156 * references to the hv_vmbus module making it impossible to unload.
157 */
K. Y. Srinivasan4061ed92015-01-09 23:54:32 -0800158
Viresh Kumarbc609cb2015-08-05 00:52:41 -0700159 dev->set_state_shutdown = hv_ce_shutdown;
160 dev->set_state_oneshot = hv_ce_set_oneshot;
K. Y. Srinivasan4061ed92015-01-09 23:54:32 -0800161 dev->set_next_event = hv_ce_set_next_event;
162}
163
Jason Wang2608fb62013-06-19 11:28:10 +0800164
165int hv_synic_alloc(void)
166{
167 size_t size = sizeof(struct tasklet_struct);
K. Y. Srinivasan4061ed92015-01-09 23:54:32 -0800168 size_t ced_size = sizeof(struct clock_event_device);
Jason Wang2608fb62013-06-19 11:28:10 +0800169 int cpu;
170
K. Y. Srinivasan9f01ec52015-08-05 00:52:38 -0700171 hv_context.hv_numa_map = kzalloc(sizeof(struct cpumask) * nr_node_ids,
172 GFP_ATOMIC);
173 if (hv_context.hv_numa_map == NULL) {
174 pr_err("Unable to allocate NUMA map\n");
175 goto err;
176 }
177
Vitaly Kuznetsov421b8f22016-12-07 01:16:25 -0800178 for_each_present_cpu(cpu) {
Jason Wang2608fb62013-06-19 11:28:10 +0800179 hv_context.event_dpc[cpu] = kmalloc(size, GFP_ATOMIC);
180 if (hv_context.event_dpc[cpu] == NULL) {
181 pr_err("Unable to allocate event dpc\n");
182 goto err;
183 }
184 tasklet_init(hv_context.event_dpc[cpu], vmbus_on_event, cpu);
185
K. Y. Srinivasand81274a2016-02-26 15:13:21 -0800186 hv_context.msg_dpc[cpu] = kmalloc(size, GFP_ATOMIC);
187 if (hv_context.msg_dpc[cpu] == NULL) {
188 pr_err("Unable to allocate event dpc\n");
189 goto err;
190 }
191 tasklet_init(hv_context.msg_dpc[cpu], vmbus_on_msg_dpc, cpu);
192
K. Y. Srinivasan4061ed92015-01-09 23:54:32 -0800193 hv_context.clk_evt[cpu] = kzalloc(ced_size, GFP_ATOMIC);
194 if (hv_context.clk_evt[cpu] == NULL) {
195 pr_err("Unable to allocate clock event device\n");
196 goto err;
197 }
K. Y. Srinivasan9f01ec52015-08-05 00:52:38 -0700198
K. Y. Srinivasan4061ed92015-01-09 23:54:32 -0800199 hv_init_clockevent_device(hv_context.clk_evt[cpu], cpu);
200
Jason Wang2608fb62013-06-19 11:28:10 +0800201 hv_context.synic_message_page[cpu] =
202 (void *)get_zeroed_page(GFP_ATOMIC);
203
204 if (hv_context.synic_message_page[cpu] == NULL) {
205 pr_err("Unable to allocate SYNIC message page\n");
206 goto err;
207 }
208
209 hv_context.synic_event_page[cpu] =
210 (void *)get_zeroed_page(GFP_ATOMIC);
211
212 if (hv_context.synic_event_page[cpu] == NULL) {
213 pr_err("Unable to allocate SYNIC event page\n");
214 goto err;
215 }
K. Y. Srinivasanb29ef352014-08-28 18:29:52 -0700216
217 hv_context.post_msg_page[cpu] =
218 (void *)get_zeroed_page(GFP_ATOMIC);
219
220 if (hv_context.post_msg_page[cpu] == NULL) {
221 pr_err("Unable to allocate post msg page\n");
222 goto err;
223 }
Vitaly Kuznetsov3c7630d2016-12-07 01:16:26 -0800224
225 INIT_LIST_HEAD(&hv_context.percpu_list[cpu]);
Jason Wang2608fb62013-06-19 11:28:10 +0800226 }
227
228 return 0;
229err:
230 return -ENOMEM;
231}
232
Rashika Kheria87129542013-12-14 19:00:06 +0530233static void hv_synic_free_cpu(int cpu)
Jason Wang2608fb62013-06-19 11:28:10 +0800234{
235 kfree(hv_context.event_dpc[cpu]);
K. Y. Srinivasand81274a2016-02-26 15:13:21 -0800236 kfree(hv_context.msg_dpc[cpu]);
K. Y. Srinivasan4061ed92015-01-09 23:54:32 -0800237 kfree(hv_context.clk_evt[cpu]);
Felipe Penafdf91da2013-10-15 20:22:32 -0300238 if (hv_context.synic_event_page[cpu])
Jason Wang2608fb62013-06-19 11:28:10 +0800239 free_page((unsigned long)hv_context.synic_event_page[cpu]);
240 if (hv_context.synic_message_page[cpu])
241 free_page((unsigned long)hv_context.synic_message_page[cpu]);
K. Y. Srinivasanb29ef352014-08-28 18:29:52 -0700242 if (hv_context.post_msg_page[cpu])
243 free_page((unsigned long)hv_context.post_msg_page[cpu]);
Jason Wang2608fb62013-06-19 11:28:10 +0800244}
245
246void hv_synic_free(void)
247{
248 int cpu;
249
K. Y. Srinivasan9f01ec52015-08-05 00:52:38 -0700250 kfree(hv_context.hv_numa_map);
Vitaly Kuznetsov421b8f22016-12-07 01:16:25 -0800251 for_each_present_cpu(cpu)
Jason Wang2608fb62013-06-19 11:28:10 +0800252 hv_synic_free_cpu(cpu);
253}
254
Hank Janssen3e189512010-03-04 22:11:00 +0000255/*
Haiyang Zhangd44890c2010-11-08 14:04:42 -0800256 * hv_synic_init - Initialize the Synthethic Interrupt Controller.
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700257 *
258 * If it is already initialized by another entity (ie x2v shim), we need to
259 * retrieve the initialized message and event pages. Otherwise, we create and
260 * initialize the message and event pages.
261 */
Vitaly Kuznetsov76d36ab2016-12-07 14:53:11 -0800262int hv_synic_init(unsigned int cpu)
Hank Janssen3e7ee492009-07-13 16:02:34 -0700263{
Greg Kroah-Hartmaneacb1b42009-08-20 12:11:26 -0700264 union hv_synic_simp simp;
265 union hv_synic_siefp siefp;
Haiyang Zhangb8dfb262010-11-08 14:04:41 -0800266 union hv_synic_sint shared_sint;
Greg Kroah-Hartmaneacb1b42009-08-20 12:11:26 -0700267 union hv_synic_scontrol sctrl;
K. Y. Srinivasan917ea422012-12-01 06:46:47 -0800268 u64 vp_index;
Hank Janssena73e6b72010-01-22 19:17:50 +0000269
Hank Janssena73e6b72010-01-22 19:17:50 +0000270 /* Setup the Synic's message page */
K. Y. Srinivasan155e4a22017-01-19 11:51:54 -0700271 hv_get_simp(simp.as_uint64);
Haiyang Zhangf6feebe2010-11-08 14:04:39 -0800272 simp.simp_enabled = 1;
Haiyang Zhang6a0aaa12010-11-08 14:04:40 -0800273 simp.base_simp_gpa = virt_to_phys(hv_context.synic_message_page[cpu])
Hank Janssena73e6b72010-01-22 19:17:50 +0000274 >> PAGE_SHIFT;
275
K. Y. Srinivasan155e4a22017-01-19 11:51:54 -0700276 hv_set_simp(simp.as_uint64);
Hank Janssena73e6b72010-01-22 19:17:50 +0000277
278 /* Setup the Synic's event page */
Haiyang Zhangf6feebe2010-11-08 14:04:39 -0800279 rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64);
280 siefp.siefp_enabled = 1;
Haiyang Zhang6a0aaa12010-11-08 14:04:40 -0800281 siefp.base_siefp_gpa = virt_to_phys(hv_context.synic_event_page[cpu])
Hank Janssena73e6b72010-01-22 19:17:50 +0000282 >> PAGE_SHIFT;
283
Haiyang Zhangf6feebe2010-11-08 14:04:39 -0800284 wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64);
Hank Janssena73e6b72010-01-22 19:17:50 +0000285
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700286 /* Setup the shared SINT. */
Haiyang Zhangb8dfb262010-11-08 14:04:41 -0800287 rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700288
Haiyang Zhangb8dfb262010-11-08 14:04:41 -0800289 shared_sint.as_uint64 = 0;
K. Y. Srinivasan302a3c02013-02-17 11:30:44 -0800290 shared_sint.vector = HYPERVISOR_CALLBACK_VECTOR;
Haiyang Zhangb8dfb262010-11-08 14:04:41 -0800291 shared_sint.masked = false;
K. Y. Srinivasanb0209502012-12-01 06:46:54 -0800292 shared_sint.auto_eoi = true;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700293
Haiyang Zhangb8dfb262010-11-08 14:04:41 -0800294 wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700295
Bill Pemberton454f18a2009-07-27 16:47:24 -0400296 /* Enable the global synic bit */
Haiyang Zhangf6feebe2010-11-08 14:04:39 -0800297 rdmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64);
298 sctrl.enable = 1;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700299
Haiyang Zhangf6feebe2010-11-08 14:04:39 -0800300 wrmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700301
Haiyang Zhang6a0aaa12010-11-08 14:04:40 -0800302 hv_context.synic_initialized = true;
K. Y. Srinivasan917ea422012-12-01 06:46:47 -0800303
304 /*
305 * Setup the mapping between Hyper-V's notion
306 * of cpuid and Linux' notion of cpuid.
307 * This array will be indexed using Linux cpuid.
308 */
309 rdmsrl(HV_X64_MSR_VP_INDEX, vp_index);
310 hv_context.vp_index[cpu] = (u32)vp_index;
K. Y. Srinivasan3a28fa32014-04-08 18:45:54 -0700311
K. Y. Srinivasan4061ed92015-01-09 23:54:32 -0800312 /*
313 * Register the per-cpu clockevent source.
314 */
315 if (ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE)
316 clockevents_config_and_register(hv_context.clk_evt[cpu],
317 HV_TIMER_FREQUENCY,
318 HV_MIN_DELTA_TICKS,
319 HV_MAX_MAX_DELTA_TICKS);
Vitaly Kuznetsov76d36ab2016-12-07 14:53:11 -0800320 return 0;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700321}
322
Hank Janssen3e189512010-03-04 22:11:00 +0000323/*
Vitaly Kuznetsove0867482015-02-27 11:25:57 -0800324 * hv_synic_clockevents_cleanup - Cleanup clockevent devices
325 */
326void hv_synic_clockevents_cleanup(void)
327{
328 int cpu;
329
330 if (!(ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE))
331 return;
332
Vitaly Kuznetsov6ffc4b82016-12-03 12:34:35 -0800333 for_each_present_cpu(cpu)
Vitaly Kuznetsove0867482015-02-27 11:25:57 -0800334 clockevents_unbind_device(hv_context.clk_evt[cpu], cpu);
335}
336
337/*
Haiyang Zhangd44890c2010-11-08 14:04:42 -0800338 * hv_synic_cleanup - Cleanup routine for hv_synic_init().
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700339 */
Vitaly Kuznetsov76d36ab2016-12-07 14:53:11 -0800340int hv_synic_cleanup(unsigned int cpu)
Hank Janssen3e7ee492009-07-13 16:02:34 -0700341{
Haiyang Zhangb8dfb262010-11-08 14:04:41 -0800342 union hv_synic_sint shared_sint;
Greg Kroah-Hartmaneacb1b42009-08-20 12:11:26 -0700343 union hv_synic_simp simp;
344 union hv_synic_siefp siefp;
Vitaly Kuznetsove72e7ac2015-02-27 11:25:55 -0800345 union hv_synic_scontrol sctrl;
Vitaly Kuznetsov523b9402016-12-07 14:53:12 -0800346 struct vmbus_channel *channel, *sc;
347 bool channel_found = false;
348 unsigned long flags;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700349
Haiyang Zhang6a0aaa12010-11-08 14:04:40 -0800350 if (!hv_context.synic_initialized)
Vitaly Kuznetsov76d36ab2016-12-07 14:53:11 -0800351 return -EFAULT;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700352
Vitaly Kuznetsov523b9402016-12-07 14:53:12 -0800353 /*
354 * Search for channels which are bound to the CPU we're about to
355 * cleanup. In case we find one and vmbus is still connected we need to
356 * fail, this will effectively prevent CPU offlining. There is no way
357 * we can re-bind channels to different CPUs for now.
358 */
359 mutex_lock(&vmbus_connection.channel_mutex);
360 list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
361 if (channel->target_cpu == cpu) {
362 channel_found = true;
363 break;
364 }
365 spin_lock_irqsave(&channel->lock, flags);
366 list_for_each_entry(sc, &channel->sc_list, sc_list) {
367 if (sc->target_cpu == cpu) {
368 channel_found = true;
369 break;
370 }
371 }
372 spin_unlock_irqrestore(&channel->lock, flags);
373 if (channel_found)
374 break;
375 }
376 mutex_unlock(&vmbus_connection.channel_mutex);
377
378 if (channel_found && vmbus_connection.conn_state == CONNECTED)
379 return -EBUSY;
380
Vitaly Kuznetsove0867482015-02-27 11:25:57 -0800381 /* Turn off clockevent device */
Vitaly Kuznetsov6ffc4b82016-12-03 12:34:35 -0800382 if (ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE) {
383 clockevents_unbind_device(hv_context.clk_evt[cpu], cpu);
Viresh Kumarbc609cb2015-08-05 00:52:41 -0700384 hv_ce_shutdown(hv_context.clk_evt[cpu]);
Vitaly Kuznetsov6ffc4b82016-12-03 12:34:35 -0800385 }
Vitaly Kuznetsove0867482015-02-27 11:25:57 -0800386
Haiyang Zhangb8dfb262010-11-08 14:04:41 -0800387 rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700388
Haiyang Zhangb8dfb262010-11-08 14:04:41 -0800389 shared_sint.masked = 1;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700390
Greg Kroah-Hartman7692fd42010-01-08 09:06:40 -0800391 /* Need to correctly cleanup in the case of SMP!!! */
Bill Pemberton454f18a2009-07-27 16:47:24 -0400392 /* Disable the interrupt */
Haiyang Zhangb8dfb262010-11-08 14:04:41 -0800393 wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700394
K. Y. Srinivasan155e4a22017-01-19 11:51:54 -0700395 hv_get_simp(simp.as_uint64);
Haiyang Zhangf6feebe2010-11-08 14:04:39 -0800396 simp.simp_enabled = 0;
397 simp.base_simp_gpa = 0;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700398
K. Y. Srinivasan155e4a22017-01-19 11:51:54 -0700399 hv_set_simp(simp.as_uint64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700400
Haiyang Zhangf6feebe2010-11-08 14:04:39 -0800401 rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64);
402 siefp.siefp_enabled = 0;
403 siefp.base_siefp_gpa = 0;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700404
Haiyang Zhangf6feebe2010-11-08 14:04:39 -0800405 wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700406
Vitaly Kuznetsove72e7ac2015-02-27 11:25:55 -0800407 /* Disable the global synic bit */
408 rdmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64);
409 sctrl.enable = 0;
410 wrmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64);
Vitaly Kuznetsov76d36ab2016-12-07 14:53:11 -0800411
412 return 0;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700413}