ia64/xen-unstable

view xen/common/event_channel.c @ 6681:422fee1de8e7

Remove debug printk.
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author cl349@firebug.cl.cam.ac.uk
date Wed Sep 07 16:52:49 2005 +0000 (2005-09-07)
parents 4309a1fd8447
children b2f4823b6ff0 b35215021b32 9af349b055e5 3233e7ecfa9f
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 if ( port < 0 )
278 return port;
280 bind->port = port;
281 return 0;
282 }
285 static long evtchn_bind_ipi(evtchn_bind_ipi_t *bind)
286 {
287 struct evtchn *chn;
288 struct domain *d = current->domain;
289 int port;
291 spin_lock(&d->evtchn_lock);
293 if ( (port = get_free_port(d)) >= 0 )
294 {
295 chn = evtchn_from_port(d, port);
296 chn->state = ECS_IPI;
297 chn->notify_vcpu_id = current->vcpu_id;
298 }
300 spin_unlock(&d->evtchn_lock);
302 if ( port < 0 )
303 return port;
305 bind->port = port;
306 return 0;
307 }
310 static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind)
311 {
312 struct evtchn *chn;
313 struct domain *d = current->domain;
314 int port, rc, pirq = bind->pirq;
316 if ( pirq >= ARRAY_SIZE(d->pirq_to_evtchn) )
317 return -EINVAL;
319 spin_lock(&d->evtchn_lock);
321 if ( ((rc = port = d->pirq_to_evtchn[pirq]) != 0) ||
322 ((rc = port = get_free_port(d)) < 0) )
323 goto out;
325 chn = evtchn_from_port(d, port);
327 d->pirq_to_evtchn[pirq] = port;
328 rc = pirq_guest_bind(d->vcpu[0], pirq,
329 !!(bind->flags & BIND_PIRQ__WILL_SHARE));
330 if ( rc != 0 )
331 {
332 d->pirq_to_evtchn[pirq] = 0;
333 goto out;
334 }
336 chn->state = ECS_PIRQ;
337 chn->u.pirq = pirq;
339 out:
340 spin_unlock(&d->evtchn_lock);
342 if ( rc < 0 )
343 return rc;
345 bind->port = port;
346 return 0;
347 }
350 static long __evtchn_close(struct domain *d1, int port1)
351 {
352 struct domain *d2 = NULL;
353 struct vcpu *v;
354 struct evtchn *chn1, *chn2;
355 int port2;
356 long rc = 0;
358 again:
359 spin_lock(&d1->evtchn_lock);
361 if ( !port_is_valid(d1, port1) )
362 {
363 rc = -EINVAL;
364 goto out;
365 }
367 chn1 = evtchn_from_port(d1, port1);
368 switch ( chn1->state )
369 {
370 case ECS_FREE:
371 case ECS_RESERVED:
372 rc = -EINVAL;
373 goto out;
375 case ECS_UNBOUND:
376 break;
378 case ECS_PIRQ:
379 if ( (rc = pirq_guest_unbind(d1, chn1->u.pirq)) == 0 )
380 d1->pirq_to_evtchn[chn1->u.pirq] = 0;
381 break;
383 case ECS_VIRQ:
384 for_each_vcpu ( d1, v )
385 if ( v->virq_to_evtchn[chn1->u.virq] == port1 )
386 v->virq_to_evtchn[chn1->u.virq] = 0;
387 break;
389 case ECS_IPI:
390 break;
392 case ECS_INTERDOMAIN:
393 if ( d2 == NULL )
394 {
395 d2 = chn1->u.interdomain.remote_dom;
397 /* If we unlock d1 then we could lose d2. Must get a reference. */
398 if ( unlikely(!get_domain(d2)) )
399 {
400 /*
401 * Failed to obtain a reference. No matter: d2 must be dying
402 * and so will close this event channel for us.
403 */
404 d2 = NULL;
405 goto out;
406 }
408 if ( d1 < d2 )
409 {
410 spin_lock(&d2->evtchn_lock);
411 }
412 else if ( d1 != d2 )
413 {
414 spin_unlock(&d1->evtchn_lock);
415 spin_lock(&d2->evtchn_lock);
416 goto again;
417 }
418 }
419 else if ( d2 != chn1->u.interdomain.remote_dom )
420 {
421 rc = -EINVAL;
422 goto out;
423 }
425 port2 = chn1->u.interdomain.remote_port;
426 BUG_ON(!port_is_valid(d2, port2));
428 chn2 = evtchn_from_port(d2, port2);
429 BUG_ON(chn2->state != ECS_INTERDOMAIN);
430 BUG_ON(chn2->u.interdomain.remote_dom != d1);
432 chn2->state = ECS_UNBOUND;
433 chn2->u.unbound.remote_domid = d1->domain_id;
434 break;
436 default:
437 BUG();
438 }
440 /* Reset binding to vcpu0 when the channel is freed. */
441 chn1->state = ECS_FREE;
442 chn1->notify_vcpu_id = 0;
444 out:
445 if ( d2 != NULL )
446 {
447 if ( d1 != d2 )
448 spin_unlock(&d2->evtchn_lock);
449 put_domain(d2);
450 }
452 spin_unlock(&d1->evtchn_lock);
454 return rc;
455 }
458 static long evtchn_close(evtchn_close_t *close)
459 {
460 struct domain *d;
461 long rc;
462 domid_t dom = close->dom;
464 if ( dom == DOMID_SELF )
465 dom = current->domain->domain_id;
466 else if ( !IS_PRIV(current->domain) )
467 return -EPERM;
469 if ( (d = find_domain_by_id(dom)) == NULL )
470 return -ESRCH;
472 rc = __evtchn_close(d, close->port);
474 put_domain(d);
475 return rc;
476 }
479 long evtchn_send(int lport)
480 {
481 struct evtchn *lchn, *rchn;
482 struct domain *ld = current->domain, *rd;
483 int rport, ret = 0;
485 spin_lock(&ld->evtchn_lock);
487 if ( unlikely(!port_is_valid(ld, lport)) )
488 {
489 spin_unlock(&ld->evtchn_lock);
490 return -EINVAL;
491 }
493 lchn = evtchn_from_port(ld, lport);
494 switch ( lchn->state )
495 {
496 case ECS_INTERDOMAIN:
497 rd = lchn->u.interdomain.remote_dom;
498 rport = lchn->u.interdomain.remote_port;
499 rchn = evtchn_from_port(rd, rport);
500 evtchn_set_pending(rd->vcpu[rchn->notify_vcpu_id], rport);
501 break;
502 case ECS_IPI:
503 evtchn_set_pending(ld->vcpu[lchn->notify_vcpu_id], lport);
504 break;
505 default:
506 ret = -EINVAL;
507 }
509 spin_unlock(&ld->evtchn_lock);
511 return ret;
512 }
514 void send_guest_pirq(struct domain *d, int pirq)
515 {
516 int port = d->pirq_to_evtchn[pirq];
517 struct evtchn *chn = evtchn_from_port(d, port);
518 evtchn_set_pending(d->vcpu[chn->notify_vcpu_id], port);
519 }
521 static long evtchn_status(evtchn_status_t *status)
522 {
523 struct domain *d;
524 domid_t dom = status->dom;
525 int port = status->port;
526 struct evtchn *chn;
527 long rc = 0;
529 if ( dom == DOMID_SELF )
530 dom = current->domain->domain_id;
531 else if ( !IS_PRIV(current->domain) )
532 return -EPERM;
534 if ( (d = find_domain_by_id(dom)) == NULL )
535 return -ESRCH;
537 spin_lock(&d->evtchn_lock);
539 if ( !port_is_valid(d, port) )
540 {
541 rc = -EINVAL;
542 goto out;
543 }
545 chn = evtchn_from_port(d, port);
546 switch ( chn->state )
547 {
548 case ECS_FREE:
549 case ECS_RESERVED:
550 status->status = EVTCHNSTAT_closed;
551 break;
552 case ECS_UNBOUND:
553 status->status = EVTCHNSTAT_unbound;
554 status->u.unbound.dom = chn->u.unbound.remote_domid;
555 break;
556 case ECS_INTERDOMAIN:
557 status->status = EVTCHNSTAT_interdomain;
558 status->u.interdomain.dom =
559 chn->u.interdomain.remote_dom->domain_id;
560 status->u.interdomain.port = chn->u.interdomain.remote_port;
561 break;
562 case ECS_PIRQ:
563 status->status = EVTCHNSTAT_pirq;
564 status->u.pirq = chn->u.pirq;
565 break;
566 case ECS_VIRQ:
567 status->status = EVTCHNSTAT_virq;
568 status->u.virq = chn->u.virq;
569 break;
570 case ECS_IPI:
571 status->status = EVTCHNSTAT_ipi;
572 break;
573 default:
574 BUG();
575 }
577 status->vcpu = chn->notify_vcpu_id;
579 out:
580 spin_unlock(&d->evtchn_lock);
581 put_domain(d);
582 return rc;
583 }
585 static long evtchn_bind_vcpu(evtchn_bind_vcpu_t *bind)
586 {
587 struct domain *d = current->domain;
588 int port = bind->port;
589 int vcpu = bind->vcpu;
590 struct evtchn *chn;
591 long rc = 0;
593 if ( (vcpu >= MAX_VIRT_CPUS) || (d->vcpu[vcpu] == NULL) ) {
594 return -EINVAL;
595 }
597 spin_lock(&d->evtchn_lock);
599 if ( !port_is_valid(d, port) )
600 {
601 rc = -EINVAL;
602 goto out;
603 }
605 chn = evtchn_from_port(d, port);
606 switch ( chn->state )
607 {
608 case ECS_UNBOUND:
609 case ECS_INTERDOMAIN:
610 case ECS_PIRQ:
611 chn->notify_vcpu_id = vcpu;
612 break;
613 default:
614 rc = -EINVAL;
615 break;
616 }
618 out:
619 spin_unlock(&d->evtchn_lock);
620 return rc;
621 }
623 long do_event_channel_op(evtchn_op_t *uop)
624 {
625 long rc;
626 evtchn_op_t op;
628 if ( copy_from_user(&op, uop, sizeof(op)) != 0 )
629 return -EFAULT;
631 if (acm_pre_event_channel(&op))
632 return -EACCES;
634 switch ( op.cmd )
635 {
636 case EVTCHNOP_alloc_unbound:
637 rc = evtchn_alloc_unbound(&op.u.alloc_unbound);
638 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
639 rc = -EFAULT; /* Cleaning up here would be a mess! */
640 break;
642 case EVTCHNOP_bind_interdomain:
643 rc = evtchn_bind_interdomain(&op.u.bind_interdomain);
644 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
645 rc = -EFAULT; /* Cleaning up here would be a mess! */
646 break;
648 case EVTCHNOP_bind_virq:
649 rc = evtchn_bind_virq(&op.u.bind_virq);
650 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
651 rc = -EFAULT; /* Cleaning up here would be a mess! */
652 break;
654 case EVTCHNOP_bind_ipi:
655 rc = evtchn_bind_ipi(&op.u.bind_ipi);
656 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
657 rc = -EFAULT; /* Cleaning up here would be a mess! */
658 break;
660 case EVTCHNOP_bind_pirq:
661 rc = evtchn_bind_pirq(&op.u.bind_pirq);
662 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
663 rc = -EFAULT; /* Cleaning up here would be a mess! */
664 break;
666 case EVTCHNOP_close:
667 rc = evtchn_close(&op.u.close);
668 break;
670 case EVTCHNOP_send:
671 rc = evtchn_send(op.u.send.local_port);
672 break;
674 case EVTCHNOP_status:
675 rc = evtchn_status(&op.u.status);
676 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
677 rc = -EFAULT;
678 break;
680 case EVTCHNOP_bind_vcpu:
681 rc = evtchn_bind_vcpu(&op.u.bind_vcpu);
682 break;
684 default:
685 rc = -ENOSYS;
686 break;
687 }
689 return rc;
690 }
693 int evtchn_init(struct domain *d)
694 {
695 spin_lock_init(&d->evtchn_lock);
696 if ( get_free_port(d) != 0 )
697 return -EINVAL;
698 evtchn_from_port(d, 0)->state = ECS_RESERVED;
699 return 0;
700 }
703 void evtchn_destroy(struct domain *d)
704 {
705 int i;
707 for ( i = 0; port_is_valid(d, i); i++ )
708 (void)__evtchn_close(d, i);
710 for ( i = 0; i < NR_EVTCHN_BUCKETS; i++ )
711 if ( d->evtchn[i] != NULL )
712 xfree(d->evtchn[i]);
713 }
715 /*
716 * Local variables:
717 * mode: C
718 * c-set-style: "BSD"
719 * c-basic-offset: 4
720 * tab-width: 4
721 * indent-tabs-mode: nil
722 * End:
723 */