ia64/xen-unstable

view xen/common/event_channel.c @ 5701:707fcf42a5ae

Merge.
author sos22@douglas.cl.cam.ac.uk
date Fri Jul 08 12:22:18 2005 +0000 (2005-07-08)
parents f3f845297821 88c2d410979f
children 579d1e771025
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->notify_vcpu_id = 0;
224 chn1->state = ECS_INTERDOMAIN;
226 chn2->u.interdomain.remote_dom = d1;
227 chn2->u.interdomain.remote_port = (u16)port1;
228 chn2->notify_vcpu_id = 0;
229 chn2->state = ECS_INTERDOMAIN;
231 out:
232 spin_unlock(&d1->evtchn_lock);
233 if ( d1 != d2 )
234 spin_unlock(&d2->evtchn_lock);
236 put_domain(d1);
237 put_domain(d2);
239 bind->port1 = port1;
240 bind->port2 = port2;
242 return rc;
243 }
246 static long evtchn_bind_virq(evtchn_bind_virq_t *bind)
247 {
248 struct evtchn *chn;
249 struct vcpu *v = current;
250 struct domain *d = v->domain;
251 int port, virq = bind->virq;
253 if ( virq >= ARRAY_SIZE(v->virq_to_evtchn) )
254 return -EINVAL;
256 spin_lock(&d->evtchn_lock);
258 /*
259 * Port 0 is the fallback port for VIRQs that haven't been explicitly
260 * bound yet.
261 */
262 if ( ((port = v->virq_to_evtchn[virq]) != 0) ||
263 ((port = get_free_port(d)) < 0) )
264 goto out;
266 chn = evtchn_from_port(d, port);
267 chn->state = ECS_VIRQ;
268 chn->notify_vcpu_id = v->vcpu_id;
269 chn->u.virq = virq;
271 v->virq_to_evtchn[virq] = port;
273 out:
274 spin_unlock(&d->evtchn_lock);
276 if ( port < 0 )
277 return port;
279 bind->port = port;
280 return 0;
281 }
284 static long evtchn_bind_ipi(evtchn_bind_ipi_t *bind)
285 {
286 struct evtchn *chn;
287 struct domain *d = current->domain;
288 int port, ipi_vcpu = bind->ipi_vcpu;
290 if ( (ipi_vcpu >= MAX_VIRT_CPUS) || (d->vcpu[ipi_vcpu] == NULL) )
291 return -EINVAL;
293 spin_lock(&d->evtchn_lock);
295 if ( (port = get_free_port(d)) >= 0 )
296 {
297 chn = evtchn_from_port(d, port);
298 chn->state = ECS_IPI;
299 chn->notify_vcpu_id = ipi_vcpu;
300 }
302 spin_unlock(&d->evtchn_lock);
304 if ( port < 0 )
305 return port;
307 bind->port = port;
308 return 0;
309 }
312 static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind)
313 {
314 struct evtchn *chn;
315 struct domain *d = current->domain;
316 int port, rc, pirq = bind->pirq;
318 if ( pirq >= ARRAY_SIZE(d->pirq_to_evtchn) )
319 return -EINVAL;
321 spin_lock(&d->evtchn_lock);
323 if ( ((rc = port = d->pirq_to_evtchn[pirq]) != 0) ||
324 ((rc = port = get_free_port(d)) < 0) )
325 goto out;
327 chn = evtchn_from_port(d, port);
329 chn->notify_vcpu_id = 0;
331 d->pirq_to_evtchn[pirq] = port;
332 rc = pirq_guest_bind(d->vcpu[0], pirq,
333 !!(bind->flags & BIND_PIRQ__WILL_SHARE));
334 if ( rc != 0 )
335 {
336 d->pirq_to_evtchn[pirq] = 0;
337 goto out;
338 }
340 chn->state = ECS_PIRQ;
341 chn->u.pirq = pirq;
343 out:
344 spin_unlock(&d->evtchn_lock);
346 if ( rc < 0 )
347 return rc;
349 bind->port = port;
350 return 0;
351 }
354 static long __evtchn_close(struct domain *d1, int port1)
355 {
356 struct domain *d2 = NULL;
357 struct vcpu *v;
358 struct evtchn *chn1, *chn2;
359 int port2;
360 long rc = 0;
362 again:
363 spin_lock(&d1->evtchn_lock);
365 if ( !port_is_valid(d1, port1) )
366 {
367 rc = -EINVAL;
368 goto out;
369 }
371 chn1 = evtchn_from_port(d1, port1);
372 switch ( chn1->state )
373 {
374 case ECS_FREE:
375 case ECS_RESERVED:
376 rc = -EINVAL;
377 goto out;
379 case ECS_UNBOUND:
380 break;
382 case ECS_PIRQ:
383 if ( (rc = pirq_guest_unbind(d1, chn1->u.pirq)) == 0 )
384 d1->pirq_to_evtchn[chn1->u.pirq] = 0;
385 break;
387 case ECS_VIRQ:
388 for_each_vcpu ( d1, v )
389 if ( v->virq_to_evtchn[chn1->u.virq] == port1 )
390 v->virq_to_evtchn[chn1->u.virq] = 0;
391 break;
393 case ECS_IPI:
394 break;
396 case ECS_INTERDOMAIN:
397 if ( d2 == NULL )
398 {
399 d2 = chn1->u.interdomain.remote_dom;
401 /* If we unlock d1 then we could lose d2. Must get a reference. */
402 if ( unlikely(!get_domain(d2)) )
403 {
404 /*
405 * Failed to obtain a reference. No matter: d2 must be dying
406 * and so will close this event channel for us.
407 */
408 d2 = NULL;
409 goto out;
410 }
412 if ( d1 < d2 )
413 {
414 spin_lock(&d2->evtchn_lock);
415 }
416 else if ( d1 != d2 )
417 {
418 spin_unlock(&d1->evtchn_lock);
419 spin_lock(&d2->evtchn_lock);
420 goto again;
421 }
422 }
423 else if ( d2 != chn1->u.interdomain.remote_dom )
424 {
425 rc = -EINVAL;
426 goto out;
427 }
429 port2 = chn1->u.interdomain.remote_port;
430 BUG_ON(!port_is_valid(d2, port2));
432 chn2 = evtchn_from_port(d2, port2);
433 BUG_ON(chn2->state != ECS_INTERDOMAIN);
434 BUG_ON(chn2->u.interdomain.remote_dom != d1);
436 chn2->state = ECS_UNBOUND;
437 chn2->u.unbound.remote_domid = d1->domain_id;
438 break;
440 default:
441 BUG();
442 }
444 chn1->state = ECS_FREE;
446 out:
447 if ( d2 != NULL )
448 {
449 if ( d1 != d2 )
450 spin_unlock(&d2->evtchn_lock);
451 put_domain(d2);
452 }
454 spin_unlock(&d1->evtchn_lock);
456 return rc;
457 }
460 static long evtchn_close(evtchn_close_t *close)
461 {
462 struct domain *d;
463 long rc;
464 domid_t dom = close->dom;
466 if ( dom == DOMID_SELF )
467 dom = current->domain->domain_id;
468 else if ( !IS_PRIV(current->domain) )
469 return -EPERM;
471 if ( (d = find_domain_by_id(dom)) == NULL )
472 return -ESRCH;
474 rc = __evtchn_close(d, close->port);
476 put_domain(d);
477 return rc;
478 }
481 long evtchn_send(int lport)
482 {
483 struct evtchn *lchn, *rchn;
484 struct domain *ld = current->domain, *rd;
485 int rport, ret = 0;
487 spin_lock(&ld->evtchn_lock);
489 if ( unlikely(!port_is_valid(ld, lport)) )
490 {
491 spin_unlock(&ld->evtchn_lock);
492 return -EINVAL;
493 }
495 lchn = evtchn_from_port(ld, lport);
496 switch ( lchn->state )
497 {
498 case ECS_INTERDOMAIN:
499 rd = lchn->u.interdomain.remote_dom;
500 rport = lchn->u.interdomain.remote_port;
501 rchn = evtchn_from_port(rd, rport);
502 evtchn_set_pending(rd->vcpu[rchn->notify_vcpu_id], rport);
503 break;
504 case ECS_IPI:
505 evtchn_set_pending(ld->vcpu[lchn->notify_vcpu_id], lport);
506 break;
507 default:
508 ret = -EINVAL;
509 }
511 spin_unlock(&ld->evtchn_lock);
513 return ret;
514 }
516 void send_guest_pirq(struct domain *d, int pirq)
517 {
518 int port = d->pirq_to_evtchn[pirq];
519 struct evtchn *chn = evtchn_from_port(d, port);
520 evtchn_set_pending(d->vcpu[chn->notify_vcpu_id], port);
521 }
523 static long evtchn_status(evtchn_status_t *status)
524 {
525 struct domain *d;
526 domid_t dom = status->dom;
527 int port = status->port;
528 struct evtchn *chn;
529 long rc = 0;
531 if ( dom == DOMID_SELF )
532 dom = current->domain->domain_id;
533 else if ( !IS_PRIV(current->domain) )
534 return -EPERM;
536 if ( (d = find_domain_by_id(dom)) == NULL )
537 return -ESRCH;
539 spin_lock(&d->evtchn_lock);
541 if ( !port_is_valid(d, port) )
542 {
543 rc = -EINVAL;
544 goto out;
545 }
547 chn = evtchn_from_port(d, port);
548 switch ( chn->state )
549 {
550 case ECS_FREE:
551 case ECS_RESERVED:
552 status->status = EVTCHNSTAT_closed;
553 break;
554 case ECS_UNBOUND:
555 status->status = EVTCHNSTAT_unbound;
556 status->u.unbound.dom = chn->u.unbound.remote_domid;
557 break;
558 case ECS_INTERDOMAIN:
559 status->status = EVTCHNSTAT_interdomain;
560 status->u.interdomain.dom =
561 chn->u.interdomain.remote_dom->domain_id;
562 status->u.interdomain.port = chn->u.interdomain.remote_port;
563 break;
564 case ECS_PIRQ:
565 status->status = EVTCHNSTAT_pirq;
566 status->u.pirq = chn->u.pirq;
567 break;
568 case ECS_VIRQ:
569 status->status = EVTCHNSTAT_virq;
570 status->u.virq = chn->u.virq;
571 break;
572 case ECS_IPI:
573 status->status = EVTCHNSTAT_ipi;
574 status->u.ipi_vcpu = chn->notify_vcpu_id;
575 break;
576 default:
577 BUG();
578 }
580 out:
581 spin_unlock(&d->evtchn_lock);
582 put_domain(d);
583 return rc;
584 }
586 static long evtchn_rebind(evtchn_rebind_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 spin_lock(&d->evtchn_lock);
596 if ( !port_is_valid(d, port) )
597 {
598 rc = -EINVAL;
599 goto out;
600 }
602 chn = evtchn_from_port(d, port);
603 chn->notify_vcpu_id = vcpu;
605 out:
606 spin_unlock(&d->evtchn_lock);
607 return rc;
608 }
610 long do_event_channel_op(evtchn_op_t *uop)
611 {
612 long rc;
613 evtchn_op_t op;
615 if ( copy_from_user(&op, uop, sizeof(op)) != 0 )
616 return -EFAULT;
618 if (acm_pre_event_channel(&op))
619 return -EACCES;
621 switch ( op.cmd )
622 {
623 case EVTCHNOP_alloc_unbound:
624 rc = evtchn_alloc_unbound(&op.u.alloc_unbound);
625 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
626 rc = -EFAULT; /* Cleaning up here would be a mess! */
627 break;
629 case EVTCHNOP_bind_interdomain:
630 rc = evtchn_bind_interdomain(&op.u.bind_interdomain);
631 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
632 rc = -EFAULT; /* Cleaning up here would be a mess! */
633 break;
635 case EVTCHNOP_bind_virq:
636 rc = evtchn_bind_virq(&op.u.bind_virq);
637 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
638 rc = -EFAULT; /* Cleaning up here would be a mess! */
639 break;
641 case EVTCHNOP_bind_ipi:
642 rc = evtchn_bind_ipi(&op.u.bind_ipi);
643 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
644 rc = -EFAULT; /* Cleaning up here would be a mess! */
645 break;
647 case EVTCHNOP_bind_pirq:
648 rc = evtchn_bind_pirq(&op.u.bind_pirq);
649 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
650 rc = -EFAULT; /* Cleaning up here would be a mess! */
651 break;
653 case EVTCHNOP_close:
654 rc = evtchn_close(&op.u.close);
655 break;
657 case EVTCHNOP_send:
658 rc = evtchn_send(op.u.send.local_port);
659 break;
661 case EVTCHNOP_status:
662 rc = evtchn_status(&op.u.status);
663 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
664 rc = -EFAULT;
665 break;
667 case EVTCHNOP_rebind:
668 rc = evtchn_rebind(&op.u.rebind);
669 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
670 rc = -EFAULT;
671 break;
673 default:
674 rc = -ENOSYS;
675 break;
676 }
678 return rc;
679 }
682 int evtchn_init(struct domain *d)
683 {
684 spin_lock_init(&d->evtchn_lock);
685 if ( get_free_port(d) != 0 )
686 return -EINVAL;
687 evtchn_from_port(d, 0)->state = ECS_RESERVED;
688 return 0;
689 }
692 void evtchn_destroy(struct domain *d)
693 {
694 int i;
696 for ( i = 0; port_is_valid(d, i); i++ )
697 (void)__evtchn_close(d, i);
699 for ( i = 0; i < NR_EVTCHN_BUCKETS; i++ )
700 if ( d->evtchn[i] != NULL )
701 xfree(d->evtchn[i]);
702 }
704 /*
705 * Local variables:
706 * mode: C
707 * c-set-style: "BSD"
708 * c-basic-offset: 4
709 * tab-width: 4
710 * indent-tabs-mode: nil
711 * End:
712 */