ia64/xen-unstable

view xen/common/domain.c @ 9597:8f7aad20b4a5

Backtrack on the new interface for reserved event-channel
ports, as binding them in user space via the evtchn driver
would be a pain. Instead extend VIRQs so they can be
classified as 'global' or 'per vcpu'. The former can only
be allocated once per guest, but can be re-bound to
an arbitrary VCPU.

Signed-off-by: Keir Fraser <keir@xensource.com>
author kaf24@firebug.cl.cam.ac.uk
date Wed Apr 05 19:30:02 2006 +0100 (2006-04-05)
parents a4acdf920c27
children 4e1b8be54311
line source
1 /******************************************************************************
2 * domain.c
3 *
4 * Generic domain-handling functions.
5 */
7 #include <xen/config.h>
8 #include <xen/init.h>
9 #include <xen/lib.h>
10 #include <xen/errno.h>
11 #include <xen/sched.h>
12 #include <xen/domain.h>
13 #include <xen/mm.h>
14 #include <xen/event.h>
15 #include <xen/time.h>
16 #include <xen/console.h>
17 #include <xen/softirq.h>
18 #include <xen/domain_page.h>
19 #include <xen/rangeset.h>
20 #include <xen/guest_access.h>
21 #include <xen/hypercall.h>
22 #include <xen/delay.h>
23 #include <asm/debugger.h>
24 #include <public/dom0_ops.h>
25 #include <public/sched.h>
26 #include <public/vcpu.h>
28 /* Both these structures are protected by the domlist_lock. */
29 rwlock_t domlist_lock = RW_LOCK_UNLOCKED;
30 struct domain *domain_hash[DOMAIN_HASH_SIZE];
31 struct domain *domain_list;
33 struct domain *dom0;
35 struct domain *domain_create(domid_t dom_id, unsigned int cpu)
36 {
37 struct domain *d, **pd;
38 struct vcpu *v;
40 if ( (d = alloc_domain()) == NULL )
41 return NULL;
43 d->domain_id = dom_id;
45 atomic_set(&d->refcnt, 1);
47 spin_lock_init(&d->big_lock);
48 spin_lock_init(&d->page_alloc_lock);
49 INIT_LIST_HEAD(&d->page_list);
50 INIT_LIST_HEAD(&d->xenpage_list);
52 rangeset_domain_initialise(d);
54 if ( !is_idle_domain(d) )
55 {
56 set_bit(_DOMF_ctrl_pause, &d->domain_flags);
57 if ( evtchn_init(d) != 0 )
58 goto fail1;
59 if ( grant_table_create(d) != 0 )
60 goto fail2;
61 }
63 if ( arch_domain_create(d) != 0 )
64 goto fail3;
66 if ( (v = alloc_vcpu(d, 0, cpu)) == NULL )
67 goto fail4;
69 d->iomem_caps = rangeset_new(d, "I/O Memory", RANGESETF_prettyprint_hex);
70 d->irq_caps = rangeset_new(d, "Interrupts", 0);
71 if ( (d->iomem_caps == NULL) || (d->irq_caps == NULL) )
72 goto fail4; /* NB. alloc_vcpu() is undone in free_domain() */
74 if ( !is_idle_domain(d) )
75 {
76 write_lock(&domlist_lock);
77 pd = &domain_list; /* NB. domain_list maintained in order of dom_id. */
78 for ( pd = &domain_list; *pd != NULL; pd = &(*pd)->next_in_list )
79 if ( (*pd)->domain_id > d->domain_id )
80 break;
81 d->next_in_list = *pd;
82 *pd = d;
83 d->next_in_hashbucket = domain_hash[DOMAIN_HASH(dom_id)];
84 domain_hash[DOMAIN_HASH(dom_id)] = d;
85 write_unlock(&domlist_lock);
86 }
88 return d;
90 fail4:
91 arch_domain_destroy(d);
92 fail3:
93 if ( !is_idle_domain(d) )
94 grant_table_destroy(d);
95 fail2:
96 if ( !is_idle_domain(d) )
97 evtchn_destroy(d);
98 fail1:
99 rangeset_domain_destroy(d);
100 free_domain(d);
101 return NULL;
102 }
105 struct domain *find_domain_by_id(domid_t dom)
106 {
107 struct domain *d;
109 read_lock(&domlist_lock);
110 d = domain_hash[DOMAIN_HASH(dom)];
111 while ( d != NULL )
112 {
113 if ( d->domain_id == dom )
114 {
115 if ( unlikely(!get_domain(d)) )
116 d = NULL;
117 break;
118 }
119 d = d->next_in_hashbucket;
120 }
121 read_unlock(&domlist_lock);
123 return d;
124 }
127 void domain_kill(struct domain *d)
128 {
129 struct vcpu *v;
131 domain_pause(d);
132 if ( !test_and_set_bit(_DOMF_dying, &d->domain_flags) )
133 {
134 for_each_vcpu(d, v)
135 sched_rem_domain(v);
136 gnttab_release_mappings(d);
137 domain_relinquish_resources(d);
138 put_domain(d);
140 send_guest_global_virq(dom0, VIRQ_DOM_EXC);
141 }
142 }
145 void __domain_crash(struct domain *d)
146 {
147 if ( d == current->domain )
148 {
149 printk("Domain %d (vcpu#%d) crashed on cpu#%d:\n",
150 d->domain_id, current->vcpu_id, smp_processor_id());
151 show_registers(guest_cpu_user_regs());
152 }
153 else
154 {
155 printk("Domain %d reported crashed by domain %d on cpu#%d:\n",
156 d->domain_id, current->domain->domain_id, smp_processor_id());
157 }
159 domain_shutdown(d, SHUTDOWN_crash);
160 }
163 void __domain_crash_synchronous(void)
164 {
165 __domain_crash(current->domain);
166 for ( ; ; )
167 do_softirq();
168 }
171 static struct domain *domain_shuttingdown[NR_CPUS];
173 static void domain_shutdown_finalise(void)
174 {
175 struct domain *d;
176 struct vcpu *v;
178 d = domain_shuttingdown[smp_processor_id()];
179 domain_shuttingdown[smp_processor_id()] = NULL;
181 BUG_ON(d == NULL);
182 BUG_ON(d == current->domain);
184 LOCK_BIGLOCK(d);
186 /* Make sure that every vcpu is descheduled before we finalise. */
187 for_each_vcpu ( d, v )
188 vcpu_sleep_sync(v);
189 BUG_ON(!cpus_empty(d->domain_dirty_cpumask));
191 sync_pagetable_state(d);
193 /* Don't set DOMF_shutdown until execution contexts are sync'ed. */
194 if ( !test_and_set_bit(_DOMF_shutdown, &d->domain_flags) )
195 send_guest_global_virq(dom0, VIRQ_DOM_EXC);
197 UNLOCK_BIGLOCK(d);
199 put_domain(d);
200 }
202 static __init int domain_shutdown_finaliser_init(void)
203 {
204 open_softirq(DOMAIN_SHUTDOWN_FINALISE_SOFTIRQ, domain_shutdown_finalise);
205 return 0;
206 }
207 __initcall(domain_shutdown_finaliser_init);
210 void domain_shutdown(struct domain *d, u8 reason)
211 {
212 struct vcpu *v;
214 if ( d->domain_id == 0 )
215 {
216 extern void machine_restart(char *);
217 extern void machine_halt(void);
219 debugger_trap_immediate();
221 if ( reason == SHUTDOWN_poweroff )
222 {
223 printk("Domain 0 halted: halting machine.\n");
224 machine_halt();
225 }
226 else if ( reason == SHUTDOWN_crash )
227 {
228 printk("Domain 0 crashed: rebooting machine in 5 seconds.\n");
229 watchdog_disable();
230 mdelay(5000);
231 machine_restart(0);
232 }
233 else
234 {
235 printk("Domain 0 shutdown: rebooting machine.\n");
236 machine_restart(0);
237 }
238 }
240 /* Mark the domain as shutting down. */
241 d->shutdown_code = reason;
243 /* Put every vcpu to sleep, but don't wait (avoids inter-vcpu deadlock). */
244 for_each_vcpu ( d, v )
245 {
246 atomic_inc(&v->pausecnt);
247 vcpu_sleep_nosync(v);
248 }
250 get_knownalive_domain(d);
251 domain_shuttingdown[smp_processor_id()] = d;
252 raise_softirq(DOMAIN_SHUTDOWN_FINALISE_SOFTIRQ);
253 }
256 void domain_pause_for_debugger(void)
257 {
258 struct domain *d = current->domain;
259 struct vcpu *v;
261 /*
262 * NOTE: This does not synchronously pause the domain. The debugger
263 * must issue a PAUSEDOMAIN command to ensure that all execution
264 * has ceased and guest state is committed to memory.
265 */
266 set_bit(_DOMF_ctrl_pause, &d->domain_flags);
267 for_each_vcpu ( d, v )
268 vcpu_sleep_nosync(v);
270 send_guest_global_virq(dom0, VIRQ_DEBUGGER);
271 }
274 /* Release resources belonging to task @p. */
275 void domain_destroy(struct domain *d)
276 {
277 struct domain **pd;
278 atomic_t old, new;
280 BUG_ON(!test_bit(_DOMF_dying, &d->domain_flags));
282 /* May be already destroyed, or get_domain() can race us. */
283 _atomic_set(old, 0);
284 _atomic_set(new, DOMAIN_DESTROYED);
285 old = atomic_compareandswap(old, new, &d->refcnt);
286 if ( _atomic_read(old) != 0 )
287 return;
289 /* Delete from task list and task hashtable. */
290 write_lock(&domlist_lock);
291 pd = &domain_list;
292 while ( *pd != d )
293 pd = &(*pd)->next_in_list;
294 *pd = d->next_in_list;
295 pd = &domain_hash[DOMAIN_HASH(d->domain_id)];
296 while ( *pd != d )
297 pd = &(*pd)->next_in_hashbucket;
298 *pd = d->next_in_hashbucket;
299 write_unlock(&domlist_lock);
301 rangeset_domain_destroy(d);
303 evtchn_destroy(d);
304 grant_table_destroy(d);
306 arch_domain_destroy(d);
308 free_domain(d);
310 send_guest_global_virq(dom0, VIRQ_DOM_EXC);
311 }
313 void vcpu_pause(struct vcpu *v)
314 {
315 BUG_ON(v == current);
316 atomic_inc(&v->pausecnt);
317 vcpu_sleep_sync(v);
318 }
320 void domain_pause(struct domain *d)
321 {
322 struct vcpu *v;
324 for_each_vcpu( d, v )
325 vcpu_pause(v);
327 sync_pagetable_state(d);
328 }
330 void vcpu_unpause(struct vcpu *v)
331 {
332 BUG_ON(v == current);
333 if ( atomic_dec_and_test(&v->pausecnt) )
334 vcpu_wake(v);
335 }
337 void domain_unpause(struct domain *d)
338 {
339 struct vcpu *v;
341 for_each_vcpu( d, v )
342 vcpu_unpause(v);
343 }
345 void domain_pause_by_systemcontroller(struct domain *d)
346 {
347 struct vcpu *v;
349 BUG_ON(current->domain == d);
351 if ( !test_and_set_bit(_DOMF_ctrl_pause, &d->domain_flags) )
352 {
353 for_each_vcpu ( d, v )
354 vcpu_sleep_sync(v);
355 }
357 sync_pagetable_state(d);
358 }
360 void domain_unpause_by_systemcontroller(struct domain *d)
361 {
362 struct vcpu *v;
364 if ( test_and_clear_bit(_DOMF_ctrl_pause, &d->domain_flags) )
365 {
366 for_each_vcpu ( d, v )
367 vcpu_wake(v);
368 }
369 }
372 /*
373 * set_info_guest is used for final setup, launching, and state modification
374 * of domains other than domain 0. ie. the domains that are being built by
375 * the userspace dom0 domain builder.
376 */
377 int set_info_guest(struct domain *d, dom0_setvcpucontext_t *setvcpucontext)
378 {
379 int rc = 0;
380 struct vcpu_guest_context *c = NULL;
381 unsigned long vcpu = setvcpucontext->vcpu;
382 struct vcpu *v;
384 if ( (vcpu >= MAX_VIRT_CPUS) || ((v = d->vcpu[vcpu]) == NULL) )
385 return -EINVAL;
387 if ( (c = xmalloc(struct vcpu_guest_context)) == NULL )
388 return -ENOMEM;
390 domain_pause(d);
392 rc = -EFAULT;
393 if ( copy_from_guest(c, setvcpucontext->ctxt, 1) == 0 )
394 rc = arch_set_info_guest(v, c);
396 domain_unpause(d);
398 xfree(c);
399 return rc;
400 }
402 int boot_vcpu(struct domain *d, int vcpuid, struct vcpu_guest_context *ctxt)
403 {
404 struct vcpu *v = d->vcpu[vcpuid];
406 BUG_ON(test_bit(_VCPUF_initialised, &v->vcpu_flags));
408 return arch_set_info_guest(v, ctxt);
409 }
411 long do_vcpu_op(int cmd, int vcpuid, GUEST_HANDLE(void) arg)
412 {
413 struct domain *d = current->domain;
414 struct vcpu *v;
415 struct vcpu_guest_context *ctxt;
416 long rc = 0;
418 if ( (vcpuid < 0) || (vcpuid >= MAX_VIRT_CPUS) )
419 return -EINVAL;
421 if ( (v = d->vcpu[vcpuid]) == NULL )
422 return -ENOENT;
424 switch ( cmd )
425 {
426 case VCPUOP_initialise:
427 if ( (ctxt = xmalloc(struct vcpu_guest_context)) == NULL )
428 {
429 rc = -ENOMEM;
430 break;
431 }
433 if ( copy_from_guest(ctxt, arg, 1) )
434 {
435 xfree(ctxt);
436 rc = -EFAULT;
437 break;
438 }
440 LOCK_BIGLOCK(d);
441 rc = -EEXIST;
442 if ( !test_bit(_VCPUF_initialised, &v->vcpu_flags) )
443 rc = boot_vcpu(d, vcpuid, ctxt);
444 UNLOCK_BIGLOCK(d);
446 xfree(ctxt);
447 break;
449 case VCPUOP_up:
450 if ( !test_bit(_VCPUF_initialised, &v->vcpu_flags) )
451 rc = -EINVAL;
452 else if ( test_and_clear_bit(_VCPUF_down, &v->vcpu_flags) )
453 vcpu_wake(v);
454 break;
456 case VCPUOP_down:
457 if ( !test_and_set_bit(_VCPUF_down, &v->vcpu_flags) )
458 vcpu_sleep_nosync(v);
459 break;
461 case VCPUOP_is_up:
462 rc = !test_bit(_VCPUF_down, &v->vcpu_flags);
463 break;
465 case VCPUOP_get_runstate_info:
466 {
467 struct vcpu_runstate_info runstate;
468 vcpu_runstate_get(v, &runstate);
469 if ( copy_to_guest(arg, &runstate, 1) )
470 rc = -EFAULT;
471 break;
472 }
474 default:
475 rc = arch_do_vcpu_op(cmd, v, arg);
476 break;
477 }
479 return rc;
480 }
482 long vm_assist(struct domain *p, unsigned int cmd, unsigned int type)
483 {
484 if ( type > MAX_VMASST_TYPE )
485 return -EINVAL;
487 switch ( cmd )
488 {
489 case VMASST_CMD_enable:
490 set_bit(type, &p->vm_assist);
491 return 0;
492 case VMASST_CMD_disable:
493 clear_bit(type, &p->vm_assist);
494 return 0;
495 }
497 return -ENOSYS;
498 }
500 /*
501 * Local variables:
502 * mode: C
503 * c-set-style: "BSD"
504 * c-basic-offset: 4
505 * tab-width: 4
506 * indent-tabs-mode: nil
507 * End:
508 */