debuggers.hg

view xen/common/event_channel.c @ 3329:37cb59b9ddfd

bitkeeper revision 1.1159.1.484 (41c1a3e20WEWxhNQDQK6avGv36pVEA)

Remove per vcpu misdirect virq support.
author cl349@arcadians.cl.cam.ac.uk
date Thu Dec 16 15:04:02 2004 +0000 (2004-12-16)
parents b9ab4345fd1b
children 53a0cc27ab17 cd90fe329149 3609a4de4be5
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>
26 #include <public/xen.h>
27 #include <public/event_channel.h>
29 #define INIT_EVENT_CHANNELS 16
30 #define MAX_EVENT_CHANNELS 1024
31 #define EVENT_CHANNELS_SPREAD 32
34 static int get_free_port(struct exec_domain *ed)
35 {
36 struct domain *d = ed->domain;
37 int max, port;
38 event_channel_t *chn;
40 max = d->max_event_channel;
41 chn = d->event_channel;
43 for ( port = ed->eid * EVENT_CHANNELS_SPREAD; port < max; port++ )
44 if ( chn[port].state == ECS_FREE )
45 break;
47 if ( port >= max )
48 {
49 if ( max == MAX_EVENT_CHANNELS )
50 return -ENOSPC;
52 if ( port == 0 )
53 max = INIT_EVENT_CHANNELS;
54 else
55 max = port + EVENT_CHANNELS_SPREAD;
57 chn = xmalloc(max * sizeof(event_channel_t));
58 if ( unlikely(chn == NULL) )
59 return -ENOMEM;
61 memset(chn, 0, max * sizeof(event_channel_t));
63 if ( d->event_channel != NULL )
64 {
65 memcpy(chn, d->event_channel, d->max_event_channel *
66 sizeof(event_channel_t));
67 xfree(d->event_channel);
68 }
70 d->event_channel = chn;
71 d->max_event_channel = max;
72 }
74 return port;
75 }
78 static long evtchn_alloc_unbound(evtchn_alloc_unbound_t *alloc)
79 {
80 struct domain *d = current->domain;
81 int port;
83 spin_lock(&d->event_channel_lock);
85 if ( (port = get_free_port(current)) >= 0 )
86 {
87 d->event_channel[port].state = ECS_UNBOUND;
88 d->event_channel[port].u.unbound.remote_domid = alloc->dom;
89 }
91 spin_unlock(&d->event_channel_lock);
93 if ( port < 0 )
94 return port;
96 alloc->port = port;
97 return 0;
98 }
101 static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind)
102 {
103 #define ERROR_EXIT(_errno) do { rc = (_errno); goto out; } while ( 0 )
104 struct domain *d1, *d2;
105 struct exec_domain *ed1, *ed2;
106 int port1 = bind->port1, port2 = bind->port2;
107 domid_t dom1 = bind->dom1, dom2 = bind->dom2;
108 long rc = 0;
110 if ( !IS_PRIV(current->domain) && (dom1 != DOMID_SELF) )
111 return -EPERM;
113 if ( (port1 < 0) || (port2 < 0) )
114 return -EINVAL;
116 if ( dom1 == DOMID_SELF )
117 dom1 = current->domain->id;
118 if ( dom2 == DOMID_SELF )
119 dom2 = current->domain->id;
121 if ( ((d1 = find_domain_by_id(dom1)) == NULL) ||
122 ((d2 = find_domain_by_id(dom2)) == NULL) )
123 {
124 if ( d1 != NULL )
125 put_domain(d1);
126 return -ESRCH;
127 }
129 ed1 = d1->exec_domain[0]; /* XXX */
130 ed2 = d2->exec_domain[0]; /* XXX */
132 /* Avoid deadlock by first acquiring lock of domain with smaller id. */
133 if ( d1 < d2 )
134 {
135 spin_lock(&d1->event_channel_lock);
136 spin_lock(&d2->event_channel_lock);
137 }
138 else
139 {
140 if ( d1 != d2 )
141 spin_lock(&d2->event_channel_lock);
142 spin_lock(&d1->event_channel_lock);
143 }
145 /* Obtain, or ensure that we already have, a valid <port1>. */
146 if ( port1 == 0 )
147 {
148 if ( (port1 = get_free_port(ed1)) < 0 )
149 ERROR_EXIT(port1);
150 }
151 else if ( port1 >= d1->max_event_channel )
152 ERROR_EXIT(-EINVAL);
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 tmp = d1->event_channel[port1].state;
159 d1->event_channel[port1].state = ECS_INTERDOMAIN;
160 port2 = get_free_port(ed2);
161 d1->event_channel[port1].state = tmp;
162 if ( port2 < 0 )
163 ERROR_EXIT(port2);
164 }
165 else if ( port2 >= d2->max_event_channel )
166 ERROR_EXIT(-EINVAL);
168 /* Validate <dom1,port1>'s current state. */
169 switch ( d1->event_channel[port1].state )
170 {
171 case ECS_FREE:
172 break;
174 case ECS_UNBOUND:
175 if ( d1->event_channel[port1].u.unbound.remote_domid != dom2 )
176 ERROR_EXIT(-EINVAL);
177 break;
179 case ECS_INTERDOMAIN:
180 if ( d1->event_channel[port1].u.interdomain.remote_dom != ed2 )
181 ERROR_EXIT(-EINVAL);
182 if ( (d1->event_channel[port1].u.interdomain.remote_port != port2) &&
183 (bind->port2 != 0) )
184 ERROR_EXIT(-EINVAL);
185 port2 = d1->event_channel[port1].u.interdomain.remote_port;
186 goto out;
188 default:
189 ERROR_EXIT(-EINVAL);
190 }
192 /* Validate <dom2,port2>'s current state. */
193 switch ( d2->event_channel[port2].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 ( d2->event_channel[port2].u.unbound.remote_domid != dom1 )
202 ERROR_EXIT(-EINVAL);
203 break;
205 case ECS_INTERDOMAIN:
206 if ( d2->event_channel[port2].u.interdomain.remote_dom != ed1 )
207 ERROR_EXIT(-EINVAL);
208 if ( (d2->event_channel[port2].u.interdomain.remote_port != port1) &&
209 (bind->port1 != 0) )
210 ERROR_EXIT(-EINVAL);
211 port1 = d2->event_channel[port2].u.interdomain.remote_port;
212 goto out;
214 default:
215 ERROR_EXIT(-EINVAL);
216 }
218 /*
219 * Everything checked out okay -- bind <dom1,port1> to <dom2,port2>.
220 */
222 d1->event_channel[port1].u.interdomain.remote_dom = ed2;
223 d1->event_channel[port1].u.interdomain.remote_port = (u16)port2;
224 d1->event_channel[port1].state = ECS_INTERDOMAIN;
226 d2->event_channel[port2].u.interdomain.remote_dom = ed1;
227 d2->event_channel[port2].u.interdomain.remote_port = (u16)port1;
228 d2->event_channel[port2].state = ECS_INTERDOMAIN;
230 out:
231 spin_unlock(&d1->event_channel_lock);
232 if ( d1 != d2 )
233 spin_unlock(&d2->event_channel_lock);
235 put_domain(d1);
236 put_domain(d2);
238 bind->port1 = port1;
239 bind->port2 = port2;
241 return rc;
242 #undef ERROR_EXIT
243 }
246 static long evtchn_bind_virq(evtchn_bind_virq_t *bind)
247 {
248 struct exec_domain *ed = current;
249 struct domain *d = ed->domain;
250 int port, virq = bind->virq;
252 if ( virq >= ARRAY_SIZE(ed->virq_to_evtchn) )
253 return -EINVAL;
255 spin_lock(&d->event_channel_lock);
257 /*
258 * Port 0 is the fallback port for VIRQs that haven't been explicitly
259 * bound yet. The exception is the 'misdirect VIRQ', which is permanently
260 * bound to port 0.
261 */
262 if ( ((port = ed->virq_to_evtchn[virq]) != 0) ||
263 (virq == VIRQ_MISDIRECT) ||
264 ((port = get_free_port(ed)) < 0) )
265 goto out;
267 d->event_channel[port].state = ECS_VIRQ;
268 d->event_channel[port].u.virq = virq;
270 ed->virq_to_evtchn[virq] = port;
272 out:
273 spin_unlock(&d->event_channel_lock);
275 if ( port < 0 )
276 return port;
278 bind->port = port;
279 return 0;
280 }
282 static long evtchn_bind_ipi(evtchn_bind_ipi_t *bind)
283 {
284 struct exec_domain *ed = current;
285 struct domain *d = ed->domain;
286 int port, ipi_edom = bind->ipi_edom;
288 spin_lock(&d->event_channel_lock);
290 if ( (port = get_free_port(ed)) >= 0 )
291 {
292 d->event_channel[port].state = ECS_IPI;
293 d->event_channel[port].u.ipi_edom = ipi_edom;
294 }
296 spin_unlock(&d->event_channel_lock);
298 if ( port < 0 )
299 return port;
301 bind->port = port;
302 return 0;
303 }
306 static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind)
307 {
308 struct domain *d = current->domain;
309 int port, rc, pirq = bind->pirq;
311 if ( pirq >= ARRAY_SIZE(d->pirq_to_evtchn) )
312 return -EINVAL;
314 spin_lock(&d->event_channel_lock);
316 if ( ((rc = port = d->pirq_to_evtchn[pirq]) != 0) ||
317 ((rc = port = get_free_port(current)) < 0) )
318 goto out;
320 d->pirq_to_evtchn[pirq] = port;
321 rc = pirq_guest_bind(current, pirq,
322 !!(bind->flags & BIND_PIRQ__WILL_SHARE));
323 if ( rc != 0 )
324 {
325 d->pirq_to_evtchn[pirq] = 0;
326 goto out;
327 }
329 d->event_channel[port].state = ECS_PIRQ;
330 d->event_channel[port].u.pirq = pirq;
332 out:
333 spin_unlock(&d->event_channel_lock);
335 if ( rc < 0 )
336 return rc;
338 bind->port = port;
339 return 0;
340 }
343 static long __evtchn_close(struct domain *d1, int port1)
344 {
345 struct domain *d2 = NULL;
346 struct exec_domain *ed;
347 event_channel_t *chn1, *chn2;
348 int port2;
349 long rc = 0;
351 again:
352 spin_lock(&d1->event_channel_lock);
354 chn1 = d1->event_channel;
356 /* NB. Port 0 is special (VIRQ_MISDIRECT). Never let it be closed. */
357 if ( (port1 <= 0) || (port1 >= d1->max_event_channel) )
358 {
359 rc = -EINVAL;
360 goto out;
361 }
363 switch ( chn1[port1].state )
364 {
365 case ECS_FREE:
366 rc = -EINVAL;
367 goto out;
369 case ECS_UNBOUND:
370 break;
372 case ECS_PIRQ:
373 if ( (rc = pirq_guest_unbind(d1, chn1[port1].u.pirq)) == 0 )
374 d1->pirq_to_evtchn[chn1[port1].u.pirq] = 0;
375 break;
377 case ECS_VIRQ:
378 /* XXX could store exec_domain in chn1[port1].u */
379 for_each_exec_domain(d1, ed)
380 if (ed->virq_to_evtchn[chn1[port1].u.virq] == port1)
381 ed->virq_to_evtchn[chn1[port1].u.virq] = 0;
382 break;
384 case ECS_IPI:
385 break;
387 case ECS_INTERDOMAIN:
388 if ( d2 == NULL )
389 {
390 d2 = chn1[port1].u.interdomain.remote_dom->domain;
392 /* If we unlock d1 then we could lose d2. Must get a reference. */
393 if ( unlikely(!get_domain(d2)) )
394 {
395 /*
396 * Failed to obtain a reference. No matter: d2 must be dying
397 * and so will close this event channel for us.
398 */
399 d2 = NULL;
400 goto out;
401 }
403 if ( d1 < d2 )
404 {
405 spin_lock(&d2->event_channel_lock);
406 }
407 else if ( d1 != d2 )
408 {
409 spin_unlock(&d1->event_channel_lock);
410 spin_lock(&d2->event_channel_lock);
411 goto again;
412 }
413 }
414 else if ( d2 != chn1[port1].u.interdomain.remote_dom->domain )
415 {
416 rc = -EINVAL;
417 goto out;
418 }
420 chn2 = d2->event_channel;
421 port2 = chn1[port1].u.interdomain.remote_port;
423 if ( port2 >= d2->max_event_channel )
424 BUG();
425 if ( chn2[port2].state != ECS_INTERDOMAIN )
426 BUG();
427 if ( chn2[port2].u.interdomain.remote_dom->domain != d1 )
428 BUG();
430 chn2[port2].state = ECS_UNBOUND;
431 chn2[port2].u.unbound.remote_domid = d1->id;
432 break;
434 default:
435 BUG();
436 }
438 chn1[port1].state = ECS_FREE;
440 out:
441 if ( d2 != NULL )
442 {
443 if ( d1 != d2 )
444 spin_unlock(&d2->event_channel_lock);
445 put_domain(d2);
446 }
448 spin_unlock(&d1->event_channel_lock);
450 return rc;
451 }
454 static long evtchn_close(evtchn_close_t *close)
455 {
456 struct domain *d;
457 long rc;
458 domid_t dom = close->dom;
460 if ( dom == DOMID_SELF )
461 dom = current->domain->id;
462 else if ( !IS_PRIV(current->domain) )
463 return -EPERM;
465 if ( (d = find_domain_by_id(dom)) == NULL )
466 return -ESRCH;
468 rc = __evtchn_close(d, close->port);
470 put_domain(d);
471 return rc;
472 }
475 long evtchn_send(int lport)
476 {
477 struct domain *ld = current->domain;
478 struct exec_domain *rd;
479 int rport, ret = 0;
481 spin_lock(&ld->event_channel_lock);
483 if ( unlikely(lport < 0) ||
484 unlikely(lport >= ld->max_event_channel))
485 {
486 spin_unlock(&ld->event_channel_lock);
487 return -EINVAL;
488 }
490 switch ( ld->event_channel[lport].state )
491 {
492 case ECS_INTERDOMAIN:
493 rd = ld->event_channel[lport].u.interdomain.remote_dom;
494 rport = ld->event_channel[lport].u.interdomain.remote_port;
496 evtchn_set_pending(rd, rport);
497 break;
498 case ECS_IPI:
499 rd = ld->exec_domain[ld->event_channel[lport].u.ipi_edom];
500 if ( rd )
501 evtchn_set_pending(rd, lport);
502 else
503 ret = -EINVAL;
504 break;
505 default:
506 ret = -EINVAL;
507 }
509 spin_unlock(&ld->event_channel_lock);
511 return ret;
512 }
515 static long evtchn_status(evtchn_status_t *status)
516 {
517 struct domain *d;
518 domid_t dom = status->dom;
519 int port = status->port;
520 event_channel_t *chn;
521 long rc = 0;
523 if ( dom == DOMID_SELF )
524 dom = current->domain->id;
525 else if ( !IS_PRIV(current->domain) )
526 return -EPERM;
528 if ( (d = find_domain_by_id(dom)) == NULL )
529 return -ESRCH;
531 spin_lock(&d->event_channel_lock);
533 chn = d->event_channel;
535 if ( (port < 0) || (port >= d->max_event_channel) )
536 {
537 rc = -EINVAL;
538 goto out;
539 }
541 switch ( chn[port].state )
542 {
543 case ECS_FREE:
544 status->status = EVTCHNSTAT_closed;
545 break;
546 case ECS_UNBOUND:
547 status->status = EVTCHNSTAT_unbound;
548 status->u.unbound.dom = chn[port].u.unbound.remote_domid;
549 break;
550 case ECS_INTERDOMAIN:
551 status->status = EVTCHNSTAT_interdomain;
552 status->u.interdomain.dom =
553 chn[port].u.interdomain.remote_dom->domain->id;
554 status->u.interdomain.port = chn[port].u.interdomain.remote_port;
555 break;
556 case ECS_PIRQ:
557 status->status = EVTCHNSTAT_pirq;
558 status->u.pirq = chn[port].u.pirq;
559 break;
560 case ECS_VIRQ:
561 status->status = EVTCHNSTAT_virq;
562 status->u.virq = chn[port].u.virq;
563 break;
564 case ECS_IPI:
565 status->status = EVTCHNSTAT_ipi;
566 status->u.ipi_edom = chn[port].u.ipi_edom;
567 break;
568 default:
569 BUG();
570 }
572 out:
573 spin_unlock(&d->event_channel_lock);
574 put_domain(d);
575 return rc;
576 }
579 long do_event_channel_op(evtchn_op_t *uop)
580 {
581 long rc;
582 evtchn_op_t op;
584 if ( copy_from_user(&op, uop, sizeof(op)) != 0 )
585 return -EFAULT;
587 switch ( op.cmd )
588 {
589 case EVTCHNOP_alloc_unbound:
590 rc = evtchn_alloc_unbound(&op.u.alloc_unbound);
591 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
592 rc = -EFAULT; /* Cleaning up here would be a mess! */
593 break;
595 case EVTCHNOP_bind_interdomain:
596 rc = evtchn_bind_interdomain(&op.u.bind_interdomain);
597 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
598 rc = -EFAULT; /* Cleaning up here would be a mess! */
599 break;
601 case EVTCHNOP_bind_virq:
602 rc = evtchn_bind_virq(&op.u.bind_virq);
603 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
604 rc = -EFAULT; /* Cleaning up here would be a mess! */
605 break;
607 case EVTCHNOP_bind_ipi:
608 rc = evtchn_bind_ipi(&op.u.bind_ipi);
609 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
610 rc = -EFAULT; /* Cleaning up here would be a mess! */
611 break;
613 case EVTCHNOP_bind_pirq:
614 rc = evtchn_bind_pirq(&op.u.bind_pirq);
615 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
616 rc = -EFAULT; /* Cleaning up here would be a mess! */
617 break;
619 case EVTCHNOP_close:
620 rc = evtchn_close(&op.u.close);
621 break;
623 case EVTCHNOP_send:
624 rc = evtchn_send(op.u.send.local_port);
625 break;
627 case EVTCHNOP_status:
628 rc = evtchn_status(&op.u.status);
629 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
630 rc = -EFAULT;
631 break;
633 default:
634 rc = -ENOSYS;
635 break;
636 }
638 return rc;
639 }
642 int init_event_channels(struct domain *d)
643 {
644 spin_lock_init(&d->event_channel_lock);
645 /* Call get_free_port to initialize d->event_channel */
646 if ( get_free_port(d->exec_domain[0]) != 0 )
647 return -EINVAL;
648 d->event_channel[0].state = ECS_VIRQ;
649 d->event_channel[0].u.virq = VIRQ_MISDIRECT;
650 return 0;
651 }
654 void destroy_event_channels(struct domain *d)
655 {
656 int i;
657 if ( d->event_channel != NULL )
658 {
659 for ( i = 0; i < d->max_event_channel; i++ )
660 (void)__evtchn_close(d, i);
661 xfree(d->event_channel);
662 }
663 }