ia64/xen-unstable

view xen/common/event_channel.c @ 6680:4309a1fd8447

Always bind dom0 virqs to vcpu 0.
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author cl349@firebug.cl.cam.ac.uk
date Wed Sep 07 16:50:55 2005 +0000 (2005-09-07)
parents dd668f7527cb
children 422fee1de8e7
line source
1 /******************************************************************************
2 * event_channel.c
3 *
4 * Event notifications from VIRQs, PIRQs, and other domains.
5 *
6 * Copyright (c) 2003-2004, K A Fraser.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 */
18 #include <xen/config.h>
19 #include <xen/init.h>
20 #include <xen/lib.h>
21 #include <xen/errno.h>
22 #include <xen/sched.h>
23 #include <xen/event.h>
24 #include <xen/irq.h>
25 #include <asm/current.h>
27 #include <public/xen.h>
28 #include <public/event_channel.h>
29 #include <acm/acm_hooks.h>
31 #define bucket_from_port(d,p) \
32 ((d)->evtchn[(p)/EVTCHNS_PER_BUCKET])
33 #define port_is_valid(d,p) \
34 (((p) >= 0) && ((p) < MAX_EVTCHNS) && \
35 (bucket_from_port(d,p) != NULL))
36 #define evtchn_from_port(d,p) \
37 (&(bucket_from_port(d,p))[(p)&(EVTCHNS_PER_BUCKET-1)])
39 #define ERROR_EXIT(_errno) do { rc = (_errno); goto out; } while ( 0 )
41 static int get_free_port(struct domain *d)
42 {
43 struct evtchn *chn;
44 int port;
46 for ( port = 0; port_is_valid(d, port); port++ )
47 if ( evtchn_from_port(d, port)->state == ECS_FREE )
48 return port;
50 if ( port == MAX_EVTCHNS )
51 return -ENOSPC;
53 chn = xmalloc_array(struct evtchn, EVTCHNS_PER_BUCKET);
54 if ( unlikely(chn == NULL) )
55 return -ENOMEM;
56 memset(chn, 0, EVTCHNS_PER_BUCKET * sizeof(*chn));
57 bucket_from_port(d, port) = chn;
59 return port;
60 }
63 static long evtchn_alloc_unbound(evtchn_alloc_unbound_t *alloc)
64 {
65 struct evtchn *chn;
66 struct domain *d = current->domain;
67 int port = alloc->port;
68 long rc = 0;
70 spin_lock(&d->evtchn_lock);
72 /* Obtain, or ensure that we already have, a valid <port>. */
73 if ( port == 0 )
74 {
75 if ( (port = get_free_port(d)) < 0 )
76 ERROR_EXIT(port);
77 }
78 else if ( !port_is_valid(d, port) )
79 ERROR_EXIT(-EINVAL);
80 chn = evtchn_from_port(d, port);
82 /* Validate channel's current state. */
83 switch ( chn->state )
84 {
85 case ECS_FREE:
86 chn->state = ECS_UNBOUND;
87 chn->u.unbound.remote_domid = alloc->dom;
88 break;
90 case ECS_UNBOUND:
91 if ( chn->u.unbound.remote_domid != alloc->dom )
92 ERROR_EXIT(-EINVAL);
93 break;
95 default:
96 ERROR_EXIT(-EINVAL);
97 }
99 out:
100 spin_unlock(&d->evtchn_lock);
102 alloc->port = port;
103 return rc;
104 }
107 static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind)
108 {
109 struct evtchn *chn1, *chn2;
110 struct domain *d1, *d2;
111 int port1 = bind->port1, port2 = bind->port2;
112 domid_t dom1 = bind->dom1, dom2 = bind->dom2;
113 long rc = 0;
115 if ( !IS_PRIV(current->domain) && (dom1 != DOMID_SELF) )
116 return -EPERM;
118 if ( dom1 == DOMID_SELF )
119 dom1 = current->domain->domain_id;
120 if ( dom2 == DOMID_SELF )
121 dom2 = current->domain->domain_id;
123 if ( ((d1 = find_domain_by_id(dom1)) == NULL) ||
124 ((d2 = find_domain_by_id(dom2)) == NULL) )
125 {
126 if ( d1 != NULL )
127 put_domain(d1);
128 return -ESRCH;
129 }
131 /* Avoid deadlock by first acquiring lock of domain with smaller id. */
132 if ( d1 < d2 )
133 {
134 spin_lock(&d1->evtchn_lock);
135 spin_lock(&d2->evtchn_lock);
136 }
137 else
138 {
139 if ( d1 != d2 )
140 spin_lock(&d2->evtchn_lock);
141 spin_lock(&d1->evtchn_lock);
142 }
144 /* Obtain, or ensure that we already have, a valid <port1>. */
145 if ( port1 == 0 )
146 {
147 if ( (port1 = get_free_port(d1)) < 0 )
148 ERROR_EXIT(port1);
149 }
150 else if ( !port_is_valid(d1, port1) )
151 ERROR_EXIT(-EINVAL);
152 chn1 = evtchn_from_port(d1, port1);
154 /* Obtain, or ensure that we already have, a valid <port2>. */
155 if ( port2 == 0 )
156 {
157 /* Make port1 non-free while we allocate port2 (in case dom1==dom2). */
158 u16 state = chn1->state;
159 chn1->state = ECS_INTERDOMAIN;
160 port2 = get_free_port(d2);
161 chn1->state = state;
162 if ( port2 < 0 )
163 ERROR_EXIT(port2);
164 }
165 else if ( !port_is_valid(d2, port2) )
166 ERROR_EXIT(-EINVAL);
167 chn2 = evtchn_from_port(d2, port2);
169 /* Validate <dom1,port1>'s current state. */
170 switch ( chn1->state )
171 {
172 case ECS_FREE:
173 break;
175 case ECS_UNBOUND:
176 if ( chn1->u.unbound.remote_domid != dom2 )
177 ERROR_EXIT(-EINVAL);
178 break;
180 case ECS_INTERDOMAIN:
181 if ( chn1->u.interdomain.remote_dom != d2 )
182 ERROR_EXIT(-EINVAL);
183 if ( (chn1->u.interdomain.remote_port != port2) && (bind->port2 != 0) )
184 ERROR_EXIT(-EINVAL);
185 port2 = chn1->u.interdomain.remote_port;
186 goto out;
188 default:
189 ERROR_EXIT(-EINVAL);
190 }
192 /* Validate <dom2,port2>'s current state. */
193 switch ( chn2->state )
194 {
195 case ECS_FREE:
196 if ( !IS_PRIV(current->domain) && (dom2 != DOMID_SELF) )
197 ERROR_EXIT(-EPERM);
198 break;
200 case ECS_UNBOUND:
201 if ( chn2->u.unbound.remote_domid != dom1 )
202 ERROR_EXIT(-EINVAL);
203 break;
205 case ECS_INTERDOMAIN:
206 if ( chn2->u.interdomain.remote_dom != d1 )
207 ERROR_EXIT(-EINVAL);
208 if ( (chn2->u.interdomain.remote_port != port1) && (bind->port1 != 0) )
209 ERROR_EXIT(-EINVAL);
210 port1 = chn2->u.interdomain.remote_port;
211 goto out;
213 default:
214 ERROR_EXIT(-EINVAL);
215 }
217 /*
218 * Everything checked out okay -- bind <dom1,port1> to <dom2,port2>.
219 */
221 chn1->u.interdomain.remote_dom = d2;
222 chn1->u.interdomain.remote_port = (u16)port2;
223 chn1->state = ECS_INTERDOMAIN;
225 chn2->u.interdomain.remote_dom = d1;
226 chn2->u.interdomain.remote_port = (u16)port1;
227 chn2->state = ECS_INTERDOMAIN;
229 out:
230 spin_unlock(&d1->evtchn_lock);
231 if ( d1 != d2 )
232 spin_unlock(&d2->evtchn_lock);
234 put_domain(d1);
235 put_domain(d2);
237 bind->port1 = port1;
238 bind->port2 = port2;
240 return rc;
241 }
244 static long evtchn_bind_virq(evtchn_bind_virq_t *bind)
245 {
246 struct evtchn *chn;
247 struct vcpu *v = current;
248 struct domain *d = v->domain;
249 int port, virq = bind->virq;
251 if ( virq >= ARRAY_SIZE(v->virq_to_evtchn) )
252 return -EINVAL;
254 if ( d->domain_id == 0 && virq >= VIRQ_CONSOLE )
255 v = d->vcpu[0];
257 spin_lock(&d->evtchn_lock);
259 /*
260 * Port 0 is the fallback port for VIRQs that haven't been explicitly
261 * bound yet.
262 */
263 if ( ((port = v->virq_to_evtchn[virq]) != 0) ||
264 ((port = get_free_port(d)) < 0) )
265 goto out;
267 chn = evtchn_from_port(d, port);
268 chn->state = ECS_VIRQ;
269 chn->notify_vcpu_id = v->vcpu_id;
270 chn->u.virq = virq;
272 v->virq_to_evtchn[virq] = port;
274 out:
275 spin_unlock(&d->evtchn_lock);
277 printk("evtchn_bind_virq %d port %d\n", virq, port);
278 if ( port < 0 )
279 return port;
281 bind->port = port;
282 return 0;
283 }
286 static long evtchn_bind_ipi(evtchn_bind_ipi_t *bind)
287 {
288 struct evtchn *chn;
289 struct domain *d = current->domain;
290 int port;
292 spin_lock(&d->evtchn_lock);
294 if ( (port = get_free_port(d)) >= 0 )
295 {
296 chn = evtchn_from_port(d, port);
297 chn->state = ECS_IPI;
298 chn->notify_vcpu_id = current->vcpu_id;
299 }
301 spin_unlock(&d->evtchn_lock);
303 if ( port < 0 )
304 return port;
306 bind->port = port;
307 return 0;
308 }
311 static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind)
312 {
313 struct evtchn *chn;
314 struct domain *d = current->domain;
315 int port, rc, pirq = bind->pirq;
317 if ( pirq >= ARRAY_SIZE(d->pirq_to_evtchn) )
318 return -EINVAL;
320 spin_lock(&d->evtchn_lock);
322 if ( ((rc = port = d->pirq_to_evtchn[pirq]) != 0) ||
323 ((rc = port = get_free_port(d)) < 0) )
324 goto out;
326 chn = evtchn_from_port(d, port);
328 d->pirq_to_evtchn[pirq] = port;
329 rc = pirq_guest_bind(d->vcpu[0], pirq,
330 !!(bind->flags & BIND_PIRQ__WILL_SHARE));
331 if ( rc != 0 )
332 {
333 d->pirq_to_evtchn[pirq] = 0;
334 goto out;
335 }
337 chn->state = ECS_PIRQ;
338 chn->u.pirq = pirq;
340 out:
341 spin_unlock(&d->evtchn_lock);
343 if ( rc < 0 )
344 return rc;
346 bind->port = port;
347 return 0;
348 }
351 static long __evtchn_close(struct domain *d1, int port1)
352 {
353 struct domain *d2 = NULL;
354 struct vcpu *v;
355 struct evtchn *chn1, *chn2;
356 int port2;
357 long rc = 0;
359 again:
360 spin_lock(&d1->evtchn_lock);
362 if ( !port_is_valid(d1, port1) )
363 {
364 rc = -EINVAL;
365 goto out;
366 }
368 chn1 = evtchn_from_port(d1, port1);
369 switch ( chn1->state )
370 {
371 case ECS_FREE:
372 case ECS_RESERVED:
373 rc = -EINVAL;
374 goto out;
376 case ECS_UNBOUND:
377 break;
379 case ECS_PIRQ:
380 if ( (rc = pirq_guest_unbind(d1, chn1->u.pirq)) == 0 )
381 d1->pirq_to_evtchn[chn1->u.pirq] = 0;
382 break;
384 case ECS_VIRQ:
385 for_each_vcpu ( d1, v )
386 if ( v->virq_to_evtchn[chn1->u.virq] == port1 )
387 v->virq_to_evtchn[chn1->u.virq] = 0;
388 break;
390 case ECS_IPI:
391 break;
393 case ECS_INTERDOMAIN:
394 if ( d2 == NULL )
395 {
396 d2 = chn1->u.interdomain.remote_dom;
398 /* If we unlock d1 then we could lose d2. Must get a reference. */
399 if ( unlikely(!get_domain(d2)) )
400 {
401 /*
402 * Failed to obtain a reference. No matter: d2 must be dying
403 * and so will close this event channel for us.
404 */
405 d2 = NULL;
406 goto out;
407 }
409 if ( d1 < d2 )
410 {
411 spin_lock(&d2->evtchn_lock);
412 }
413 else if ( d1 != d2 )
414 {
415 spin_unlock(&d1->evtchn_lock);
416 spin_lock(&d2->evtchn_lock);
417 goto again;
418 }
419 }
420 else if ( d2 != chn1->u.interdomain.remote_dom )
421 {
422 rc = -EINVAL;
423 goto out;
424 }
426 port2 = chn1->u.interdomain.remote_port;
427 BUG_ON(!port_is_valid(d2, port2));
429 chn2 = evtchn_from_port(d2, port2);
430 BUG_ON(chn2->state != ECS_INTERDOMAIN);
431 BUG_ON(chn2->u.interdomain.remote_dom != d1);
433 chn2->state = ECS_UNBOUND;
434 chn2->u.unbound.remote_domid = d1->domain_id;
435 break;
437 default:
438 BUG();
439 }
441 /* Reset binding to vcpu0 when the channel is freed. */
442 chn1->state = ECS_FREE;
443 chn1->notify_vcpu_id = 0;
445 out:
446 if ( d2 != NULL )
447 {
448 if ( d1 != d2 )
449 spin_unlock(&d2->evtchn_lock);
450 put_domain(d2);
451 }
453 spin_unlock(&d1->evtchn_lock);
455 return rc;
456 }
459 static long evtchn_close(evtchn_close_t *close)
460 {
461 struct domain *d;
462 long rc;
463 domid_t dom = close->dom;
465 if ( dom == DOMID_SELF )
466 dom = current->domain->domain_id;
467 else if ( !IS_PRIV(current->domain) )
468 return -EPERM;
470 if ( (d = find_domain_by_id(dom)) == NULL )
471 return -ESRCH;
473 rc = __evtchn_close(d, close->port);
475 put_domain(d);
476 return rc;
477 }
480 long evtchn_send(int lport)
481 {
482 struct evtchn *lchn, *rchn;
483 struct domain *ld = current->domain, *rd;
484 int rport, ret = 0;
486 spin_lock(&ld->evtchn_lock);
488 if ( unlikely(!port_is_valid(ld, lport)) )
489 {
490 spin_unlock(&ld->evtchn_lock);
491 return -EINVAL;
492 }
494 lchn = evtchn_from_port(ld, lport);
495 switch ( lchn->state )
496 {
497 case ECS_INTERDOMAIN:
498 rd = lchn->u.interdomain.remote_dom;
499 rport = lchn->u.interdomain.remote_port;
500 rchn = evtchn_from_port(rd, rport);
501 evtchn_set_pending(rd->vcpu[rchn->notify_vcpu_id], rport);
502 break;
503 case ECS_IPI:
504 evtchn_set_pending(ld->vcpu[lchn->notify_vcpu_id], lport);
505 break;
506 default:
507 ret = -EINVAL;
508 }
510 spin_unlock(&ld->evtchn_lock);
512 return ret;
513 }
515 void send_guest_pirq(struct domain *d, int pirq)
516 {
517 int port = d->pirq_to_evtchn[pirq];
518 struct evtchn *chn = evtchn_from_port(d, port);
519 evtchn_set_pending(d->vcpu[chn->notify_vcpu_id], port);
520 }
522 static long evtchn_status(evtchn_status_t *status)
523 {
524 struct domain *d;
525 domid_t dom = status->dom;
526 int port = status->port;
527 struct evtchn *chn;
528 long rc = 0;
530 if ( dom == DOMID_SELF )
531 dom = current->domain->domain_id;
532 else if ( !IS_PRIV(current->domain) )
533 return -EPERM;
535 if ( (d = find_domain_by_id(dom)) == NULL )
536 return -ESRCH;
538 spin_lock(&d->evtchn_lock);
540 if ( !port_is_valid(d, port) )
541 {
542 rc = -EINVAL;
543 goto out;
544 }
546 chn = evtchn_from_port(d, port);
547 switch ( chn->state )
548 {
549 case ECS_FREE:
550 case ECS_RESERVED:
551 status->status = EVTCHNSTAT_closed;
552 break;
553 case ECS_UNBOUND:
554 status->status = EVTCHNSTAT_unbound;
555 status->u.unbound.dom = chn->u.unbound.remote_domid;
556 break;
557 case ECS_INTERDOMAIN:
558 status->status = EVTCHNSTAT_interdomain;
559 status->u.interdomain.dom =
560 chn->u.interdomain.remote_dom->domain_id;
561 status->u.interdomain.port = chn->u.interdomain.remote_port;
562 break;
563 case ECS_PIRQ:
564 status->status = EVTCHNSTAT_pirq;
565 status->u.pirq = chn->u.pirq;
566 break;
567 case ECS_VIRQ:
568 status->status = EVTCHNSTAT_virq;
569 status->u.virq = chn->u.virq;
570 break;
571 case ECS_IPI:
572 status->status = EVTCHNSTAT_ipi;
573 break;
574 default:
575 BUG();
576 }
578 status->vcpu = chn->notify_vcpu_id;
580 out:
581 spin_unlock(&d->evtchn_lock);
582 put_domain(d);
583 return rc;
584 }
586 static long evtchn_bind_vcpu(evtchn_bind_vcpu_t *bind)
587 {
588 struct domain *d = current->domain;
589 int port = bind->port;
590 int vcpu = bind->vcpu;
591 struct evtchn *chn;
592 long rc = 0;
594 if ( (vcpu >= MAX_VIRT_CPUS) || (d->vcpu[vcpu] == NULL) ) {
595 return -EINVAL;
596 }
598 spin_lock(&d->evtchn_lock);
600 if ( !port_is_valid(d, port) )
601 {
602 rc = -EINVAL;
603 goto out;
604 }
606 chn = evtchn_from_port(d, port);
607 switch ( chn->state )
608 {
609 case ECS_UNBOUND:
610 case ECS_INTERDOMAIN:
611 case ECS_PIRQ:
612 chn->notify_vcpu_id = vcpu;
613 break;
614 default:
615 rc = -EINVAL;
616 break;
617 }
619 out:
620 spin_unlock(&d->evtchn_lock);
621 return rc;
622 }
624 long do_event_channel_op(evtchn_op_t *uop)
625 {
626 long rc;
627 evtchn_op_t op;
629 if ( copy_from_user(&op, uop, sizeof(op)) != 0 )
630 return -EFAULT;
632 if (acm_pre_event_channel(&op))
633 return -EACCES;
635 switch ( op.cmd )
636 {
637 case EVTCHNOP_alloc_unbound:
638 rc = evtchn_alloc_unbound(&op.u.alloc_unbound);
639 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
640 rc = -EFAULT; /* Cleaning up here would be a mess! */
641 break;
643 case EVTCHNOP_bind_interdomain:
644 rc = evtchn_bind_interdomain(&op.u.bind_interdomain);
645 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
646 rc = -EFAULT; /* Cleaning up here would be a mess! */
647 break;
649 case EVTCHNOP_bind_virq:
650 rc = evtchn_bind_virq(&op.u.bind_virq);
651 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
652 rc = -EFAULT; /* Cleaning up here would be a mess! */
653 break;
655 case EVTCHNOP_bind_ipi:
656 rc = evtchn_bind_ipi(&op.u.bind_ipi);
657 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
658 rc = -EFAULT; /* Cleaning up here would be a mess! */
659 break;
661 case EVTCHNOP_bind_pirq:
662 rc = evtchn_bind_pirq(&op.u.bind_pirq);
663 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
664 rc = -EFAULT; /* Cleaning up here would be a mess! */
665 break;
667 case EVTCHNOP_close:
668 rc = evtchn_close(&op.u.close);
669 break;
671 case EVTCHNOP_send:
672 rc = evtchn_send(op.u.send.local_port);
673 break;
675 case EVTCHNOP_status:
676 rc = evtchn_status(&op.u.status);
677 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
678 rc = -EFAULT;
679 break;
681 case EVTCHNOP_bind_vcpu:
682 rc = evtchn_bind_vcpu(&op.u.bind_vcpu);
683 break;
685 default:
686 rc = -ENOSYS;
687 break;
688 }
690 return rc;
691 }
694 int evtchn_init(struct domain *d)
695 {
696 spin_lock_init(&d->evtchn_lock);
697 if ( get_free_port(d) != 0 )
698 return -EINVAL;
699 evtchn_from_port(d, 0)->state = ECS_RESERVED;
700 return 0;
701 }
704 void evtchn_destroy(struct domain *d)
705 {
706 int i;
708 for ( i = 0; port_is_valid(d, i); i++ )
709 (void)__evtchn_close(d, i);
711 for ( i = 0; i < NR_EVTCHN_BUCKETS; i++ )
712 if ( d->evtchn[i] != NULL )
713 xfree(d->evtchn[i]);
714 }
716 /*
717 * Local variables:
718 * mode: C
719 * c-set-style: "BSD"
720 * c-basic-offset: 4
721 * tab-width: 4
722 * indent-tabs-mode: nil
723 * End:
724 */