]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blame - drivers/hv/hv.c
Drivers: hv: vmbus: Define APIs to manipulate the message page
[mirror_ubuntu-zesty-kernel.git] / drivers / hv / hv.c
CommitLineData
3e7ee490 1/*
3e7ee490
HJ
2 * Copyright (c) 2009, Microsoft Corporation.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License along with
14 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
15 * Place - Suite 330, Boston, MA 02111-1307 USA.
16 *
17 * Authors:
18 * Haiyang Zhang <haiyangz@microsoft.com>
19 * Hank Janssen <hjanssen@microsoft.com>
20 *
21 */
0a46618d
HJ
22#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
23
a0086dc5
GKH
24#include <linux/kernel.h>
25#include <linux/mm.h>
5a0e3ad6 26#include <linux/slab.h>
b7c947f0 27#include <linux/vmalloc.h>
46a97191 28#include <linux/hyperv.h>
83ba0c4f 29#include <linux/version.h>
db11f12a 30#include <linux/interrupt.h>
4061ed9e 31#include <linux/clockchips.h>
407dd164 32#include <asm/hyperv.h>
4061ed9e 33#include <asm/mshyperv.h>
0f2a6619 34#include "hyperv_vmbus.h"
3e7ee490 35
454f18a9 36/* The one and only */
6a0aaa18
HZ
37struct hv_context hv_context = {
38 .synic_initialized = false,
3e7ee490
HJ
39};
40
4061ed9e
S
41#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
3e189519 45/*
d44890c8 46 * hv_init - Main initialization routine.
0831ad04
GKH
47 *
48 * This routine must be called before any other routines in here are called
49 */
d44890c8 50int hv_init(void)
3e7ee490 51{
3e7ee490 52
14c1bf8a 53 memset(hv_context.synic_event_page, 0, sizeof(void *) * NR_CPUS);
6a0aaa18 54 memset(hv_context.synic_message_page, 0,
14c1bf8a 55 sizeof(void *) * NR_CPUS);
b29ef354
S
56 memset(hv_context.post_msg_page, 0,
57 sizeof(void *) * NR_CPUS);
917ea427
S
58 memset(hv_context.vp_index, 0,
59 sizeof(int) * NR_CPUS);
db11f12a
S
60 memset(hv_context.event_dpc, 0,
61 sizeof(void *) * NR_CPUS);
d81274aa
S
62 memset(hv_context.msg_dpc, 0,
63 sizeof(void *) * NR_CPUS);
4061ed9e
S
64 memset(hv_context.clk_evt, 0,
65 sizeof(void *) * NR_CPUS);
3e7ee490 66
c4398016 67 if (!hv_is_hypercall_page_setup())
b1d6b256 68 return -ENOTSUPP;
3e7ee490 69
5433e003 70 return 0;
3e7ee490
HJ
71}
72
3e189519 73/*
d44890c8 74 * hv_cleanup - Cleanup routine.
0831ad04
GKH
75 *
76 * This routine is called normally during driver unloading or exiting.
77 */
a9f61ca7 78void hv_cleanup(bool crash)
3e7ee490 79{
ca9357bd 80
3e7ee490
HJ
81}
82
3e189519 83/*
d44890c8 84 * hv_post_message - Post a message using the hypervisor message IPC.
0831ad04
GKH
85 *
86 * This involves a hypercall.
87 */
415f0a02 88int hv_post_message(union hv_connection_id connection_id,
b8dfb264
HZ
89 enum hv_message_type message_type,
90 void *payload, size_t payload_size)
3e7ee490 91{
3e7ee490 92
b8dfb264 93 struct hv_input_post_message *aligned_msg;
a108393d 94 u64 status;
3e7ee490 95
b8dfb264 96 if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
39594abc 97 return -EMSGSIZE;
3e7ee490 98
b8dfb264 99 aligned_msg = (struct hv_input_post_message *)
b29ef354 100 hv_context.post_msg_page[get_cpu()];
3e7ee490 101
b8dfb264 102 aligned_msg->connectionid = connection_id;
b29ef354 103 aligned_msg->reserved = 0;
b8dfb264
HZ
104 aligned_msg->message_type = message_type;
105 aligned_msg->payload_size = payload_size;
106 memcpy((void *)aligned_msg->payload, payload, payload_size);
3e7ee490 107
a108393d 108 status = hv_do_hypercall(HVCALL_POST_MESSAGE, aligned_msg, NULL);
3e7ee490 109
b29ef354 110 put_cpu();
a108393d 111 return status & 0xFFFF;
3e7ee490
HJ
112}
113
4061ed9e
S
114static int hv_ce_set_next_event(unsigned long delta,
115 struct clock_event_device *evt)
116{
a5a1d1c2 117 u64 current_tick;
4061ed9e 118
bc609cb4 119 WARN_ON(!clockevent_state_oneshot(evt));
4061ed9e 120
72329152 121 hv_get_current_tick(current_tick);
4061ed9e 122 current_tick += delta;
72329152 123 hv_init_timer(HV_X64_MSR_STIMER0_COUNT, current_tick);
4061ed9e
S
124 return 0;
125}
126
bc609cb4
VK
127static int hv_ce_shutdown(struct clock_event_device *evt)
128{
72329152
S
129 hv_init_timer(HV_X64_MSR_STIMER0_COUNT, 0);
130 hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, 0);
bc609cb4
VK
131
132 return 0;
133}
134
135static int hv_ce_set_oneshot(struct clock_event_device *evt)
4061ed9e
S
136{
137 union hv_timer_config timer_cfg;
138
bc609cb4
VK
139 timer_cfg.enable = 1;
140 timer_cfg.auto_enable = 1;
141 timer_cfg.sintx = VMBUS_MESSAGE_SINT;
72329152 142 hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, timer_cfg.as_uint64);
bc609cb4
VK
143
144 return 0;
4061ed9e
S
145}
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;
e086748c
VK
153 /*
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 */
4061ed9e 158
bc609cb4
VK
159 dev->set_state_shutdown = hv_ce_shutdown;
160 dev->set_state_oneshot = hv_ce_set_oneshot;
4061ed9e
S
161 dev->set_next_event = hv_ce_set_next_event;
162}
163
2608fb65
JW
164
165int hv_synic_alloc(void)
166{
167 size_t size = sizeof(struct tasklet_struct);
4061ed9e 168 size_t ced_size = sizeof(struct clock_event_device);
2608fb65
JW
169 int cpu;
170
9f01ec53
S
171 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
d74e2e80 178 for_each_present_cpu(cpu) {
2608fb65
JW
179 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
d81274aa
S
186 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
4061ed9e
S
193 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 }
9f01ec53 198
4061ed9e
S
199 hv_init_clockevent_device(hv_context.clk_evt[cpu], cpu);
200
2608fb65
JW
201 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 }
b29ef354
S
216
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 }
9b1bf703
VK
224
225 INIT_LIST_HEAD(&hv_context.percpu_list[cpu]);
2608fb65
JW
226 }
227
228 return 0;
229err:
230 return -ENOMEM;
231}
232
8712954d 233static void hv_synic_free_cpu(int cpu)
2608fb65
JW
234{
235 kfree(hv_context.event_dpc[cpu]);
d81274aa 236 kfree(hv_context.msg_dpc[cpu]);
4061ed9e 237 kfree(hv_context.clk_evt[cpu]);
fdf91dae 238 if (hv_context.synic_event_page[cpu])
2608fb65
JW
239 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]);
b29ef354
S
242 if (hv_context.post_msg_page[cpu])
243 free_page((unsigned long)hv_context.post_msg_page[cpu]);
2608fb65
JW
244}
245
246void hv_synic_free(void)
247{
248 int cpu;
249
9f01ec53 250 kfree(hv_context.hv_numa_map);
d74e2e80 251 for_each_present_cpu(cpu)
2608fb65
JW
252 hv_synic_free_cpu(cpu);
253}
254
3e189519 255/*
d44890c8 256 * hv_synic_init - Initialize the Synthethic Interrupt Controller.
0831ad04
GKH
257 *
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 */
fdf5149c 262int hv_synic_init(unsigned int cpu)
3e7ee490 263{
eacb1b4d
GKH
264 union hv_synic_simp simp;
265 union hv_synic_siefp siefp;
b8dfb264 266 union hv_synic_sint shared_sint;
eacb1b4d 267 union hv_synic_scontrol sctrl;
917ea427 268 u64 vp_index;
a73e6b7c 269
a73e6b7c 270 /* Setup the Synic's message page */
a62c7499 271 hv_get_simp(simp.as_uint64);
f6feebe0 272 simp.simp_enabled = 1;
6a0aaa18 273 simp.base_simp_gpa = virt_to_phys(hv_context.synic_message_page[cpu])
a73e6b7c 274 >> PAGE_SHIFT;
3e7ee490 275
a62c7499 276 hv_set_simp(simp.as_uint64);
3e7ee490 277
a73e6b7c 278 /* Setup the Synic's event page */
f6feebe0
HZ
279 rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64);
280 siefp.siefp_enabled = 1;
6a0aaa18 281 siefp.base_siefp_gpa = virt_to_phys(hv_context.synic_event_page[cpu])
a73e6b7c
HJ
282 >> PAGE_SHIFT;
283
f6feebe0 284 wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64);
0831ad04 285
0831ad04 286 /* Setup the shared SINT. */
b8dfb264 287 rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
3e7ee490 288
b8dfb264 289 shared_sint.as_uint64 = 0;
302a3c0f 290 shared_sint.vector = HYPERVISOR_CALLBACK_VECTOR;
b8dfb264 291 shared_sint.masked = false;
b0209501 292 shared_sint.auto_eoi = true;
3e7ee490 293
b8dfb264 294 wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
3e7ee490 295
454f18a9 296 /* Enable the global synic bit */
f6feebe0
HZ
297 rdmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64);
298 sctrl.enable = 1;
3e7ee490 299
f6feebe0 300 wrmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64);
3e7ee490 301
6a0aaa18 302 hv_context.synic_initialized = true;
917ea427
S
303
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;
3a28fa35 311
4061ed9e
S
312 /*
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);
fdf5149c 320 return 0;
3e7ee490
HJ
321}
322
e086748c
VK
323/*
324 * 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
6ffc4b85 333 for_each_present_cpu(cpu)
e086748c
VK
334 clockevents_unbind_device(hv_context.clk_evt[cpu], cpu);
335}
336
3e189519 337/*
d44890c8 338 * hv_synic_cleanup - Cleanup routine for hv_synic_init().
0831ad04 339 */
fdf5149c 340int hv_synic_cleanup(unsigned int cpu)
3e7ee490 341{
b8dfb264 342 union hv_synic_sint shared_sint;
eacb1b4d
GKH
343 union hv_synic_simp simp;
344 union hv_synic_siefp siefp;
e72e7ac5 345 union hv_synic_scontrol sctrl;
ccbf4254
VK
346 struct vmbus_channel *channel, *sc;
347 bool channel_found = false;
348 unsigned long flags;
3e7ee490 349
6a0aaa18 350 if (!hv_context.synic_initialized)
fdf5149c 351 return -EFAULT;
3e7ee490 352
ccbf4254
VK
353 /*
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
e086748c 381 /* Turn off clockevent device */
6ffc4b85
VK
382 if (ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE) {
383 clockevents_unbind_device(hv_context.clk_evt[cpu], cpu);
bc609cb4 384 hv_ce_shutdown(hv_context.clk_evt[cpu]);
6ffc4b85 385 }
e086748c 386
b8dfb264 387 rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
3e7ee490 388
b8dfb264 389 shared_sint.masked = 1;
3e7ee490 390
7692fd4d 391 /* Need to correctly cleanup in the case of SMP!!! */
454f18a9 392 /* Disable the interrupt */
b8dfb264 393 wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
3e7ee490 394
a62c7499 395 hv_get_simp(simp.as_uint64);
f6feebe0
HZ
396 simp.simp_enabled = 0;
397 simp.base_simp_gpa = 0;
3e7ee490 398
a62c7499 399 hv_set_simp(simp.as_uint64);
3e7ee490 400
f6feebe0
HZ
401 rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64);
402 siefp.siefp_enabled = 0;
403 siefp.base_siefp_gpa = 0;
3e7ee490 404
f6feebe0 405 wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64);
3e7ee490 406
e72e7ac5
VK
407 /* 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);
fdf5149c
VK
411
412 return 0;
3e7ee490 413}