smp.c 10.6 KB
Newer Older
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
/*
 * Xen SMP support
 *
 * This file implements the Xen versions of smp_ops.  SMP under Xen is
 * very straightforward.  Bringing a CPU up is simply a matter of
 * loading its initial context and setting it running.
 *
 * IPIs are handled through the Xen event mechanism.
 *
 * Because virtual CPUs can be scheduled onto any real CPU, there's no
 * useful topology information for the kernel to make use of.  As a
 * result, all CPUs are treated as if they're single-core and
 * single-threaded.
 */
#include <linux/sched.h>
#include <linux/err.h>
#include <linux/smp.h>

#include <asm/paravirt.h>
#include <asm/desc.h>
#include <asm/pgtable.h>
#include <asm/cpu.h>

#include <xen/interface/xen.h>
#include <xen/interface/vcpu.h>

#include <asm/xen/interface.h>
#include <asm/xen/hypercall.h>

#include <xen/page.h>
#include <xen/events.h>

#include "xen-ops.h"
#include "mmu.h"

36
cpumask_t xen_cpu_initialized_map;
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
37

38 39 40 41
static DEFINE_PER_CPU(int, resched_irq);
static DEFINE_PER_CPU(int, callfunc_irq);
static DEFINE_PER_CPU(int, callfuncsingle_irq);
static DEFINE_PER_CPU(int, debug_irq) = -1;
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
42 43

static irqreturn_t xen_call_function_interrupt(int irq, void *dev_id);
44
static irqreturn_t xen_call_function_single_interrupt(int irq, void *dev_id);
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
45 46 47 48 49 50 51 52

/*
 * Reschedule call back. Nothing to do,
 * all the work is done automatically when
 * we return from the interrupt.
 */
static irqreturn_t xen_reschedule_interrupt(int irq, void *dev_id)
{
53 54 55 56 57 58
#ifdef CONFIG_X86_32
	__get_cpu_var(irq_stat).irq_resched_count++;
#else
	add_pda(irq_resched_count, 1);
#endif

Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
59 60 61
	return IRQ_HANDLED;
}

Alex Nixon's avatar
Alex Nixon committed
62
static __cpuinit void cpu_bringup(void)
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
63 64 65 66
{
	int cpu = smp_processor_id();

	cpu_init();
Alex Nixon's avatar
Alex Nixon committed
67
	touch_softlockup_watchdog();
68 69
	preempt_disable();

70
	xen_enable_sysenter();
71
	xen_enable_syscall();
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
72

73 74 75 76
	cpu = smp_processor_id();
	smp_store_cpu_info(cpu);
	cpu_data(cpu).x86_max_cores = 1;
	set_cpu_sibling_map(cpu);
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
77 78 79

	xen_setup_cpu_clockevents();

80 81 82 83
	cpu_set(cpu, cpu_online_map);
	x86_write_percpu(cpu_state, CPU_ONLINE);
	wmb();

Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
84 85 86 87
	/* We can take interrupts now: we're officially "up". */
	local_irq_enable();

	wmb();			/* make sure everything is out */
Alex Nixon's avatar
Alex Nixon committed
88 89 90 91 92
}

static __cpuinit void cpu_bringup_and_idle(void)
{
	cpu_bringup();
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
93 94 95 96 97 98
	cpu_idle();
}

static int xen_smp_intr_init(unsigned int cpu)
{
	int rc;
99
	const char *resched_name, *callfunc_name, *debug_name;
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122

	resched_name = kasprintf(GFP_KERNEL, "resched%d", cpu);
	rc = bind_ipi_to_irqhandler(XEN_RESCHEDULE_VECTOR,
				    cpu,
				    xen_reschedule_interrupt,
				    IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING,
				    resched_name,
				    NULL);
	if (rc < 0)
		goto fail;
	per_cpu(resched_irq, cpu) = rc;

	callfunc_name = kasprintf(GFP_KERNEL, "callfunc%d", cpu);
	rc = bind_ipi_to_irqhandler(XEN_CALL_FUNCTION_VECTOR,
				    cpu,
				    xen_call_function_interrupt,
				    IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING,
				    callfunc_name,
				    NULL);
	if (rc < 0)
		goto fail;
	per_cpu(callfunc_irq, cpu) = rc;

123 124 125 126 127 128 129 130
	debug_name = kasprintf(GFP_KERNEL, "debug%d", cpu);
	rc = bind_virq_to_irqhandler(VIRQ_DEBUG, cpu, xen_debug_interrupt,
				     IRQF_DISABLED | IRQF_PERCPU | IRQF_NOBALANCING,
				     debug_name, NULL);
	if (rc < 0)
		goto fail;
	per_cpu(debug_irq, cpu) = rc;

131 132 133 134 135 136 137 138 139 140 141
	callfunc_name = kasprintf(GFP_KERNEL, "callfuncsingle%d", cpu);
	rc = bind_ipi_to_irqhandler(XEN_CALL_FUNCTION_SINGLE_VECTOR,
				    cpu,
				    xen_call_function_single_interrupt,
				    IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING,
				    callfunc_name,
				    NULL);
	if (rc < 0)
		goto fail;
	per_cpu(callfuncsingle_irq, cpu) = rc;

Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
142 143 144 145 146 147 148
	return 0;

 fail:
	if (per_cpu(resched_irq, cpu) >= 0)
		unbind_from_irqhandler(per_cpu(resched_irq, cpu), NULL);
	if (per_cpu(callfunc_irq, cpu) >= 0)
		unbind_from_irqhandler(per_cpu(callfunc_irq, cpu), NULL);
149 150
	if (per_cpu(debug_irq, cpu) >= 0)
		unbind_from_irqhandler(per_cpu(debug_irq, cpu), NULL);
151 152 153
	if (per_cpu(callfuncsingle_irq, cpu) >= 0)
		unbind_from_irqhandler(per_cpu(callfuncsingle_irq, cpu), NULL);

Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
154 155 156
	return rc;
}

157
static void __init xen_fill_possible_map(void)
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
158 159 160 161 162
{
	int i, rc;

	for (i = 0; i < NR_CPUS; i++) {
		rc = HYPERVISOR_vcpu_op(VCPUOP_is_up, i, NULL);
163 164
		if (rc >= 0) {
			num_processors++;
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
165
			cpu_set(i, cpu_possible_map);
166
		}
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
167 168 169
	}
}

170
static void __init xen_smp_prepare_boot_cpu(void)
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
171 172 173 174 175 176
{
	BUG_ON(smp_processor_id() != 0);
	native_smp_prepare_boot_cpu();

	/* We've switched to the "real" per-cpu gdt, so make sure the
	   old memory can be recycled */
177
	make_lowmem_page_readwrite(&per_cpu_var(gdt_page));
178 179

	xen_setup_vcpu_info_placement();
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
180 181
}

182
static void __init xen_smp_prepare_cpus(unsigned int max_cpus)
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
183 184 185
{
	unsigned cpu;

186 187
	xen_init_lock_cpu(0);

Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
188
	smp_store_cpu_info(0);
189
	cpu_data(0).x86_max_cores = 1;
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
190 191 192 193 194
	set_cpu_sibling_map(0);

	if (xen_smp_intr_init(0))
		BUG();

195
	xen_cpu_initialized_map = cpumask_of_cpu(0);
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
196 197 198

	/* Restrict the possible_map according to max_cpus. */
	while ((num_possible_cpus() > 1) && (num_possible_cpus() > max_cpus)) {
199
		for (cpu = NR_CPUS - 1; !cpu_possible(cpu); cpu--)
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
			continue;
		cpu_clear(cpu, cpu_possible_map);
	}

	for_each_possible_cpu (cpu) {
		struct task_struct *idle;

		if (cpu == 0)
			continue;

		idle = fork_idle(cpu);
		if (IS_ERR(idle))
			panic("failed fork for CPU %d", cpu);

		cpu_set(cpu, cpu_present_map);
	}
}

static __cpuinit int
cpu_initialize_context(unsigned int cpu, struct task_struct *idle)
{
	struct vcpu_guest_context *ctxt;
222
	struct desc_struct *gdt;
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
223

224
	if (cpu_test_and_set(cpu, xen_cpu_initialized_map))
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
225 226 227 228 229 230
		return 0;

	ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL);
	if (ctxt == NULL)
		return -ENOMEM;

231 232
	gdt = get_cpu_gdt_table(cpu);

Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
233 234 235 236
	ctxt->flags = VGCF_IN_KERNEL;
	ctxt->user_regs.ds = __USER_DS;
	ctxt->user_regs.es = __USER_DS;
	ctxt->user_regs.ss = __KERNEL_DS;
237 238 239
#ifdef CONFIG_X86_32
	ctxt->user_regs.fs = __KERNEL_PERCPU;
#endif
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
240 241 242 243 244 245 246 247 248
	ctxt->user_regs.eip = (unsigned long)cpu_bringup_and_idle;
	ctxt->user_regs.eflags = 0x1000; /* IOPL_RING1 */

	memset(&ctxt->fpu_ctxt, 0, sizeof(ctxt->fpu_ctxt));

	xen_copy_trap_info(ctxt->trap_ctxt);

	ctxt->ldt_ents = 0;

249 250
	BUG_ON((unsigned long)gdt & ~PAGE_MASK);
	make_lowmem_page_readonly(gdt);
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
251

252 253
	ctxt->gdt_frames[0] = virt_to_mfn(gdt);
	ctxt->gdt_ents      = GDT_ENTRIES;
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
254 255

	ctxt->user_regs.cs = __KERNEL_CS;
256
	ctxt->user_regs.esp = idle->thread.sp0 - sizeof(struct pt_regs);
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
257 258

	ctxt->kernel_ss = __KERNEL_DS;
259
	ctxt->kernel_sp = idle->thread.sp0;
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
260

261
#ifdef CONFIG_X86_32
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
262 263
	ctxt->event_callback_cs     = __KERNEL_CS;
	ctxt->failsafe_callback_cs  = __KERNEL_CS;
264 265
#endif
	ctxt->event_callback_eip    = (unsigned long)xen_hypervisor_callback;
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
266 267 268 269 270 271 272 273 274 275 276 277
	ctxt->failsafe_callback_eip = (unsigned long)xen_failsafe_callback;

	per_cpu(xen_cr3, cpu) = __pa(swapper_pg_dir);
	ctxt->ctrlreg[3] = xen_pfn_to_cr3(virt_to_mfn(swapper_pg_dir));

	if (HYPERVISOR_vcpu_op(VCPUOP_initialise, cpu, ctxt))
		BUG();

	kfree(ctxt);
	return 0;
}

278
static int __cpuinit xen_cpu_up(unsigned int cpu)
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
279 280 281 282
{
	struct task_struct *idle = idle_task(cpu);
	int rc;

283 284 285 286 287 288 289 290 291 292 293
#ifdef CONFIG_X86_64
	/* Allocate node local memory for AP pdas */
	WARN_ON(cpu == 0);
	if (cpu > 0) {
		rc = get_local_pda(cpu);
		if (rc)
			return rc;
	}
#endif

#ifdef CONFIG_X86_32
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
294 295 296
	init_gdt(cpu);
	per_cpu(current_task, cpu) = idle;
	irq_ctx_init(cpu);
297 298 299 300
#else
	cpu_pda(cpu)->pcurrent = idle;
	clear_tsk_thread_flag(idle, TIF_FORK);
#endif
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
301
	xen_setup_timer(cpu);
302
	xen_init_lock_cpu(cpu);
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
303

304 305
	per_cpu(cpu_state, cpu) = CPU_UP_PREPARE;

Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
	/* make sure interrupts start blocked */
	per_cpu(xen_vcpu, cpu)->evtchn_upcall_mask = 1;

	rc = cpu_initialize_context(cpu, idle);
	if (rc)
		return rc;

	if (num_online_cpus() == 1)
		alternatives_smp_switch(1);

	rc = xen_smp_intr_init(cpu);
	if (rc)
		return rc;

	rc = HYPERVISOR_vcpu_op(VCPUOP_up, cpu, NULL);
	BUG_ON(rc);

323 324 325 326 327
	while(per_cpu(cpu_state, cpu) != CPU_ONLINE) {
		HYPERVISOR_sched_op(SCHEDOP_yield, 0);
		barrier();
	}

Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
328 329 330
	return 0;
}

331
static void xen_smp_cpus_done(unsigned int max_cpus)
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
332 333 334
{
}

Alex Nixon's avatar
Alex Nixon committed
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
int xen_cpu_disable(void)
{
	unsigned int cpu = smp_processor_id();
	if (cpu == 0)
		return -EBUSY;

	cpu_disable_common();

	load_cr3(swapper_pg_dir);
	return 0;
}

void xen_cpu_die(unsigned int cpu)
{
	while (HYPERVISOR_vcpu_op(VCPUOP_is_up, cpu, NULL)) {
		current->state = TASK_UNINTERRUPTIBLE;
		schedule_timeout(HZ/10);
	}
	unbind_from_irqhandler(per_cpu(resched_irq, cpu), NULL);
	unbind_from_irqhandler(per_cpu(callfunc_irq, cpu), NULL);
	unbind_from_irqhandler(per_cpu(debug_irq, cpu), NULL);
	unbind_from_irqhandler(per_cpu(callfuncsingle_irq, cpu), NULL);
	xen_uninit_lock_cpu(cpu);
	xen_teardown_timer(cpu);

	if (num_online_cpus() == 1)
		alternatives_smp_switch(0);
}

void xen_play_dead(void)
{
	play_dead_common();
	HYPERVISOR_vcpu_op(VCPUOP_down, smp_processor_id(), NULL);
	cpu_bringup();
}

Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
371 372 373 374 375 376 377 378 379 380 381 382
static void stop_self(void *v)
{
	int cpu = smp_processor_id();

	/* make sure we're not pinning something down */
	load_cr3(swapper_pg_dir);
	/* should set up a minimal gdt */

	HYPERVISOR_vcpu_op(VCPUOP_down, cpu, NULL);
	BUG();
}

383
static void xen_smp_send_stop(void)
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
384
{
385
	smp_call_function(stop_self, NULL, 0);
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
386 387
}

388
static void xen_smp_send_reschedule(int cpu)
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
389 390 391 392 393 394 395 396 397 398
{
	xen_send_IPI_one(cpu, XEN_RESCHEDULE_VECTOR);
}

static void xen_send_IPI_mask(cpumask_t mask, enum ipi_vector vector)
{
	unsigned cpu;

	cpus_and(mask, mask, cpu_online_map);

399
	for_each_cpu_mask_nr(cpu, mask)
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
400 401 402
		xen_send_IPI_one(cpu, vector);
}

403
static void xen_smp_send_call_function_ipi(cpumask_t mask)
404 405 406 407 408 409
{
	int cpu;

	xen_send_IPI_mask(mask, XEN_CALL_FUNCTION_VECTOR);

	/* Make sure other vcpus get a chance to run if they need to. */
410
	for_each_cpu_mask_nr(cpu, mask) {
411 412 413 414 415 416 417
		if (xen_vcpu_stolen(cpu)) {
			HYPERVISOR_sched_op(SCHEDOP_yield, 0);
			break;
		}
	}
}

418
static void xen_smp_send_call_function_single_ipi(int cpu)
419 420 421 422
{
	xen_send_IPI_mask(cpumask_of_cpu(cpu), XEN_CALL_FUNCTION_SINGLE_VECTOR);
}

Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
423 424 425
static irqreturn_t xen_call_function_interrupt(int irq, void *dev_id)
{
	irq_enter();
426
	generic_smp_call_function_interrupt();
427
#ifdef CONFIG_X86_32
428
	__get_cpu_var(irq_stat).irq_call_count++;
429 430 431
#else
	add_pda(irq_call_count, 1);
#endif
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
432 433 434 435 436
	irq_exit();

	return IRQ_HANDLED;
}

437
static irqreturn_t xen_call_function_single_interrupt(int irq, void *dev_id)
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
438
{
439 440
	irq_enter();
	generic_smp_call_function_single_interrupt();
441
#ifdef CONFIG_X86_32
442
	__get_cpu_var(irq_stat).irq_call_count++;
443 444 445
#else
	add_pda(irq_call_count, 1);
#endif
446
	irq_exit();
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
447

448
	return IRQ_HANDLED;
Jeremy Fitzhardinge's avatar
Jeremy Fitzhardinge committed
449
}
450 451 452 453 454 455

static const struct smp_ops xen_smp_ops __initdata = {
	.smp_prepare_boot_cpu = xen_smp_prepare_boot_cpu,
	.smp_prepare_cpus = xen_smp_prepare_cpus,
	.smp_cpus_done = xen_smp_cpus_done,

Alex Nixon's avatar
Alex Nixon committed
456 457 458 459 460
	.cpu_up = xen_cpu_up,
	.cpu_die = xen_cpu_die,
	.cpu_disable = xen_cpu_disable,
	.play_dead = xen_play_dead,

461 462 463 464 465 466 467 468 469 470
	.smp_send_stop = xen_smp_send_stop,
	.smp_send_reschedule = xen_smp_send_reschedule,

	.send_call_func_ipi = xen_smp_send_call_function_ipi,
	.send_call_func_single_ipi = xen_smp_send_call_function_single_ipi,
};

void __init xen_smp_init(void)
{
	smp_ops = xen_smp_ops;
471
	xen_fill_possible_map();
472
	xen_init_spinlocks();
473
}