ia64/xen-unstable

view xen/common/event_channel.c @ 5707:05b63285047c

Merge.
author sos22@douglas.cl.cam.ac.uk
date Fri Jul 08 17:38:38 2005 +0000 (2005-07-08)
parents 7c3d7c37dfde 1d375ce8e0e0
children 215d8b2f3d94
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 spin_lock(&d->evtchn_lock);
256 /*
257 * Port 0 is the fallback port for VIRQs that haven't been explicitly
258 * bound yet.
259 */
260 if ( ((port = v->virq_to_evtchn[virq]) != 0) ||
261 ((port = get_free_port(d)) < 0) )
262 goto out;
264 chn = evtchn_from_port(d, port);
265 chn->state = ECS_VIRQ;
266 chn->notify_vcpu_id = v->vcpu_id;
267 chn->u.virq = virq;
269 v->virq_to_evtchn[virq] = port;
271 out:
272 spin_unlock(&d->evtchn_lock);
274 if ( port < 0 )
275 return port;
277 bind->port = port;
278 return 0;
279 }
282 static long evtchn_bind_ipi(evtchn_bind_ipi_t *bind)
283 {
284 struct evtchn *chn;
285 struct domain *d = current->domain;
286 int port;
288 spin_lock(&d->evtchn_lock);
290 if ( (port = get_free_port(d)) >= 0 )
291 {
292 chn = evtchn_from_port(d, port);
293 chn->state = ECS_IPI;
294 chn->notify_vcpu_id = current->vcpu_id;
295 }
297 spin_unlock(&d->evtchn_lock);
299 if ( port < 0 )
300 return port;
302 bind->port = port;
303 return 0;
304 }
307 static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind)
308 {
309 struct evtchn *chn;
310 struct domain *d = current->domain;
311 int port, rc, pirq = bind->pirq;
313 if ( pirq >= ARRAY_SIZE(d->pirq_to_evtchn) )
314 return -EINVAL;
316 spin_lock(&d->evtchn_lock);
318 if ( ((rc = port = d->pirq_to_evtchn[pirq]) != 0) ||
319 ((rc = port = get_free_port(d)) < 0) )
320 goto out;
322 chn = evtchn_from_port(d, port);
324 d->pirq_to_evtchn[pirq] = port;
325 rc = pirq_guest_bind(d->vcpu[0], pirq,
326 !!(bind->flags & BIND_PIRQ__WILL_SHARE));
327 if ( rc != 0 )
328 {
329 d->pirq_to_evtchn[pirq] = 0;
330 goto out;
331 }
333 chn->state = ECS_PIRQ;
334 chn->u.pirq = pirq;
336 out:
337 spin_unlock(&d->evtchn_lock);
339 if ( rc < 0 )
340 return rc;
342 bind->port = port;
343 return 0;
344 }
347 static long __evtchn_close(struct domain *d1, int port1)
348 {
349 struct domain *d2 = NULL;
350 struct vcpu *v;
351 struct evtchn *chn1, *chn2;
352 int port2;
353 long rc = 0;
355 again:
356 spin_lock(&d1->evtchn_lock);
358 if ( !port_is_valid(d1, port1) )
359 {
360 rc = -EINVAL;
361 goto out;
362 }
364 chn1 = evtchn_from_port(d1, port1);
365 switch ( chn1->state )
366 {
367 case ECS_FREE:
368 case ECS_RESERVED:
369 rc = -EINVAL;
370 goto out;
372 case ECS_UNBOUND:
373 break;
375 case ECS_PIRQ:
376 if ( (rc = pirq_guest_unbind(d1, chn1->u.pirq)) == 0 )
377 d1->pirq_to_evtchn[chn1->u.pirq] = 0;
378 break;
380 case ECS_VIRQ:
381 for_each_vcpu ( d1, v )
382 if ( v->virq_to_evtchn[chn1->u.virq] == port1 )
383 v->virq_to_evtchn[chn1->u.virq] = 0;
384 break;
386 case ECS_IPI:
387 break;
389 case ECS_INTERDOMAIN:
390 if ( d2 == NULL )
391 {
392 d2 = chn1->u.interdomain.remote_dom;
394 /* If we unlock d1 then we could lose d2. Must get a reference. */
395 if ( unlikely(!get_domain(d2)) )
396 {
397 /*
398 * Failed to obtain a reference. No matter: d2 must be dying
399 * and so will close this event channel for us.
400 */
401 d2 = NULL;
402 goto out;
403 }
405 if ( d1 < d2 )
406 {
407 spin_lock(&d2->evtchn_lock);
408 }
409 else if ( d1 != d2 )
410 {
411 spin_unlock(&d1->evtchn_lock);
412 spin_lock(&d2->evtchn_lock);
413 goto again;
414 }
415 }
416 else if ( d2 != chn1->u.interdomain.remote_dom )
417 {
418 rc = -EINVAL;
419 goto out;
420 }
422 port2 = chn1->u.interdomain.remote_port;
423 BUG_ON(!port_is_valid(d2, port2));
425 chn2 = evtchn_from_port(d2, port2);
426 BUG_ON(chn2->state != ECS_INTERDOMAIN);
427 BUG_ON(chn2->u.interdomain.remote_dom != d1);
429 chn2->state = ECS_UNBOUND;
430 chn2->u.unbound.remote_domid = d1->domain_id;
431 break;
433 default:
434 BUG();
435 }
437 /* Reset binding to vcpu0 when the channel is freed. */
438 chn1->state = ECS_FREE;
439 chn1->notify_vcpu_id = 0;
441 out:
442 if ( d2 != NULL )
443 {
444 if ( d1 != d2 )
445 spin_unlock(&d2->evtchn_lock);
446 put_domain(d2);
447 }
449 spin_unlock(&d1->evtchn_lock);
451 return rc;
452 }
455 static long evtchn_close(evtchn_close_t *close)
456 {
457 struct domain *d;
458 long rc;
459 domid_t dom = close->dom;
461 if ( dom == DOMID_SELF )
462 dom = current->domain->domain_id;
463 else if ( !IS_PRIV(current->domain) )
464 return -EPERM;
466 if ( (d = find_domain_by_id(dom)) == NULL )
467 return -ESRCH;
469 rc = __evtchn_close(d, close->port);
471 put_domain(d);
472 return rc;
473 }
476 long evtchn_send(int lport)
477 {
478 struct evtchn *lchn, *rchn;
479 struct domain *ld = current->domain, *rd;
480 int rport, ret = 0;
482 spin_lock(&ld->evtchn_lock);
484 if ( unlikely(!port_is_valid(ld, lport)) )
485 {
486 spin_unlock(&ld->evtchn_lock);
487 return -EINVAL;
488 }
490 lchn = evtchn_from_port(ld, lport);
491 switch ( lchn->state )
492 {
493 case ECS_INTERDOMAIN:
494 rd = lchn->u.interdomain.remote_dom;
495 rport = lchn->u.interdomain.remote_port;
496 rchn = evtchn_from_port(rd, rport);
497 evtchn_set_pending(rd->vcpu[rchn->notify_vcpu_id], rport);
498 break;
499 case ECS_IPI:
500 evtchn_set_pending(ld->vcpu[lchn->notify_vcpu_id], lport);
501 break;
502 default:
503 ret = -EINVAL;
504 }
506 spin_unlock(&ld->evtchn_lock);
508 return ret;
509 }
511 void send_guest_pirq(struct domain *d, int pirq)
512 {
513 int port = d->pirq_to_evtchn[pirq];
514 struct evtchn *chn = evtchn_from_port(d, port);
515 evtchn_set_pending(d->vcpu[chn->notify_vcpu_id], port);
516 }
518 static long evtchn_status(evtchn_status_t *status)
519 {
520 struct domain *d;
521 domid_t dom = status->dom;
522 int port = status->port;
523 struct evtchn *chn;
524 long rc = 0;
526 if ( dom == DOMID_SELF )
527 dom = current->domain->domain_id;
528 else if ( !IS_PRIV(current->domain) )
529 return -EPERM;
531 if ( (d = find_domain_by_id(dom)) == NULL )
532 return -ESRCH;
534 spin_lock(&d->evtchn_lock);
536 if ( !port_is_valid(d, port) )
537 {
538 rc = -EINVAL;
539 goto out;
540 }
542 chn = evtchn_from_port(d, port);
543 switch ( chn->state )
544 {
545 case ECS_FREE:
546 case ECS_RESERVED:
547 status->status = EVTCHNSTAT_closed;
548 break;
549 case ECS_UNBOUND:
550 status->status = EVTCHNSTAT_unbound;
551 status->u.unbound.dom = chn->u.unbound.remote_domid;
552 break;
553 case ECS_INTERDOMAIN:
554 status->status = EVTCHNSTAT_interdomain;
555 status->u.interdomain.dom =
556 chn->u.interdomain.remote_dom->domain_id;
557 status->u.interdomain.port = chn->u.interdomain.remote_port;
558 break;
559 case ECS_PIRQ:
560 status->status = EVTCHNSTAT_pirq;
561 status->u.pirq = chn->u.pirq;
562 break;
563 case ECS_VIRQ:
564 status->status = EVTCHNSTAT_virq;
565 status->u.virq = chn->u.virq;
566 break;
567 case ECS_IPI:
568 status->status = EVTCHNSTAT_ipi;
569 break;
570 default:
571 BUG();
572 }
574 status->vcpu = chn->notify_vcpu_id;
576 out:
577 spin_unlock(&d->evtchn_lock);
578 put_domain(d);
579 return rc;
580 }
582 static long evtchn_bind_vcpu(evtchn_bind_vcpu_t *bind)
583 {
584 struct domain *d = current->domain;
585 int port = bind->port;
586 int vcpu = bind->vcpu;
587 struct evtchn *chn;
588 long rc = 0;
590 if ( (vcpu >= MAX_VIRT_CPUS) || (d->vcpu[vcpu] == NULL) ) {
591 printf("vcpu %d bad.\n", vcpu);
592 return -EINVAL;
593 }
595 spin_lock(&d->evtchn_lock);
597 if ( !port_is_valid(d, port) )
598 {
599 printf("port %d bad.\n", port);
600 rc = -EINVAL;
601 goto out;
602 }
604 chn = evtchn_from_port(d, port);
605 switch ( chn->state )
606 {
607 case ECS_UNBOUND:
608 case ECS_INTERDOMAIN:
609 case ECS_PIRQ:
610 chn->notify_vcpu_id = vcpu;
611 break;
612 default:
613 printf("evtchn type %d can't be rebound.\n", chn->state);
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 */