ia64/xen-unstable

view xen/common/event_channel.c @ 5691:88c2d410979f

I updated the vcpu_to_cpu string creation to include a field separator,
which gets rid of the -1 -> # hack and works for cpus > 9.

I ran into some issues with stale vcpu_to_cpu lists when running the
hotplug subprogram. I would take a vcpu offline, and then issue the
command to bring it back and the vcpu_to_cpu list would not have changed
to indicate the the vcpu actually went down. If I injected a xm list -v
(which always showed the correct mapping) then subsequent hotplug
commands would see the state change and fire off the hotplug request. I
don't know that not sending the event when not changing state saves that
much work so I took the state check out and now just send the hotplug
event directly.

> Also the whole hotplug stuff is still missing interrupt re-routing
> when a vcpu is taken down. To do this, we need an evtchn operation to
> change the vcpu affinity of a port by changing notify_vcpu_id.

I don't fully understand all of the mappings that are happening, so this
part of the patch might be way off. In any case, I've added a new
evtchn op to set the notify_vcpu_id field of a channel. I updated the
HOTPLUG_CPU code to use the new routines when bringing cpus up and down.
When taking down a cpu, I route the IPI irq channels to CPU 0, and when
the cpu comes up, it re-routes the channels back to the awakened CPU.

From: Ryan Harper <ryanh@us.ibm.com>
Signed-off-by: ian@xensource.com
author iap10@freefall.cl.cam.ac.uk
date Wed Jul 06 22:23:18 2005 +0000 (2005-07-06)
parents 649cd37aa1ab
children 707fcf42a5ae 579d1e771025 c1a7ed266c7e
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, ipi_vcpu = bind->ipi_vcpu;
288 if ( (ipi_vcpu >= MAX_VIRT_CPUS) || (d->vcpu[ipi_vcpu] == NULL) )
289 return -EINVAL;
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 = ipi_vcpu;
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[chn->notify_vcpu_id], 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 chn1->state = ECS_FREE;
442 out:
443 if ( d2 != NULL )
444 {
445 if ( d1 != d2 )
446 spin_unlock(&d2->evtchn_lock);
447 put_domain(d2);
448 }
450 spin_unlock(&d1->evtchn_lock);
452 return rc;
453 }
456 static long evtchn_close(evtchn_close_t *close)
457 {
458 struct domain *d;
459 long rc;
460 domid_t dom = close->dom;
462 if ( dom == DOMID_SELF )
463 dom = current->domain->domain_id;
464 else if ( !IS_PRIV(current->domain) )
465 return -EPERM;
467 if ( (d = find_domain_by_id(dom)) == NULL )
468 return -ESRCH;
470 rc = __evtchn_close(d, close->port);
472 put_domain(d);
473 return rc;
474 }
477 long evtchn_send(int lport)
478 {
479 struct evtchn *lchn, *rchn;
480 struct domain *ld = current->domain, *rd;
481 int rport, ret = 0;
483 spin_lock(&ld->evtchn_lock);
485 if ( unlikely(!port_is_valid(ld, lport)) )
486 {
487 spin_unlock(&ld->evtchn_lock);
488 return -EINVAL;
489 }
491 lchn = evtchn_from_port(ld, lport);
492 switch ( lchn->state )
493 {
494 case ECS_INTERDOMAIN:
495 rd = lchn->u.interdomain.remote_dom;
496 rport = lchn->u.interdomain.remote_port;
497 rchn = evtchn_from_port(rd, rport);
498 evtchn_set_pending(rd->vcpu[rchn->notify_vcpu_id], rport);
499 break;
500 case ECS_IPI:
501 evtchn_set_pending(ld->vcpu[lchn->notify_vcpu_id], lport);
502 break;
503 default:
504 ret = -EINVAL;
505 }
507 spin_unlock(&ld->evtchn_lock);
509 return ret;
510 }
512 void send_guest_pirq(struct domain *d, int pirq)
513 {
514 int port = d->pirq_to_evtchn[pirq];
515 struct evtchn *chn = evtchn_from_port(d, port);
516 evtchn_set_pending(d->vcpu[chn->notify_vcpu_id], port);
517 }
519 static long evtchn_status(evtchn_status_t *status)
520 {
521 struct domain *d;
522 domid_t dom = status->dom;
523 int port = status->port;
524 struct evtchn *chn;
525 long rc = 0;
527 if ( dom == DOMID_SELF )
528 dom = current->domain->domain_id;
529 else if ( !IS_PRIV(current->domain) )
530 return -EPERM;
532 if ( (d = find_domain_by_id(dom)) == NULL )
533 return -ESRCH;
535 spin_lock(&d->evtchn_lock);
537 if ( !port_is_valid(d, port) )
538 {
539 rc = -EINVAL;
540 goto out;
541 }
543 chn = evtchn_from_port(d, port);
544 switch ( chn->state )
545 {
546 case ECS_FREE:
547 case ECS_RESERVED:
548 status->status = EVTCHNSTAT_closed;
549 break;
550 case ECS_UNBOUND:
551 status->status = EVTCHNSTAT_unbound;
552 status->u.unbound.dom = chn->u.unbound.remote_domid;
553 break;
554 case ECS_INTERDOMAIN:
555 status->status = EVTCHNSTAT_interdomain;
556 status->u.interdomain.dom =
557 chn->u.interdomain.remote_dom->domain_id;
558 status->u.interdomain.port = chn->u.interdomain.remote_port;
559 break;
560 case ECS_PIRQ:
561 status->status = EVTCHNSTAT_pirq;
562 status->u.pirq = chn->u.pirq;
563 break;
564 case ECS_VIRQ:
565 status->status = EVTCHNSTAT_virq;
566 status->u.virq = chn->u.virq;
567 break;
568 case ECS_IPI:
569 status->status = EVTCHNSTAT_ipi;
570 status->u.ipi_vcpu = chn->notify_vcpu_id;
571 break;
572 default:
573 BUG();
574 }
576 out:
577 spin_unlock(&d->evtchn_lock);
578 put_domain(d);
579 return rc;
580 }
582 static long evtchn_rebind(evtchn_rebind_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 spin_lock(&d->evtchn_lock);
592 if ( !port_is_valid(d, port) )
593 {
594 rc = -EINVAL;
595 goto out;
596 }
598 chn = evtchn_from_port(d, port);
599 chn->notify_vcpu_id = vcpu;
601 out:
602 spin_unlock(&d->evtchn_lock);
603 return rc;
604 }
606 long do_event_channel_op(evtchn_op_t *uop)
607 {
608 long rc;
609 evtchn_op_t op;
611 if ( copy_from_user(&op, uop, sizeof(op)) != 0 )
612 return -EFAULT;
614 if (acm_pre_event_channel(&op))
615 return -EACCES;
617 switch ( op.cmd )
618 {
619 case EVTCHNOP_alloc_unbound:
620 rc = evtchn_alloc_unbound(&op.u.alloc_unbound);
621 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
622 rc = -EFAULT; /* Cleaning up here would be a mess! */
623 break;
625 case EVTCHNOP_bind_interdomain:
626 rc = evtchn_bind_interdomain(&op.u.bind_interdomain);
627 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
628 rc = -EFAULT; /* Cleaning up here would be a mess! */
629 break;
631 case EVTCHNOP_bind_virq:
632 rc = evtchn_bind_virq(&op.u.bind_virq);
633 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
634 rc = -EFAULT; /* Cleaning up here would be a mess! */
635 break;
637 case EVTCHNOP_bind_ipi:
638 rc = evtchn_bind_ipi(&op.u.bind_ipi);
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_pirq:
644 rc = evtchn_bind_pirq(&op.u.bind_pirq);
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_close:
650 rc = evtchn_close(&op.u.close);
651 break;
653 case EVTCHNOP_send:
654 rc = evtchn_send(op.u.send.local_port);
655 break;
657 case EVTCHNOP_status:
658 rc = evtchn_status(&op.u.status);
659 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
660 rc = -EFAULT;
661 break;
663 case EVTCHNOP_rebind:
664 rc = evtchn_rebind(&op.u.rebind);
665 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
666 rc = -EFAULT;
667 break;
669 default:
670 rc = -ENOSYS;
671 break;
672 }
674 return rc;
675 }
678 int evtchn_init(struct domain *d)
679 {
680 spin_lock_init(&d->evtchn_lock);
681 if ( get_free_port(d) != 0 )
682 return -EINVAL;
683 evtchn_from_port(d, 0)->state = ECS_RESERVED;
684 return 0;
685 }
688 void evtchn_destroy(struct domain *d)
689 {
690 int i;
692 for ( i = 0; port_is_valid(d, i); i++ )
693 (void)__evtchn_close(d, i);
695 for ( i = 0; i < NR_EVTCHN_BUCKETS; i++ )
696 if ( d->evtchn[i] != NULL )
697 xfree(d->evtchn[i]);
698 }
700 /*
701 * Local variables:
702 * mode: C
703 * c-set-style: "BSD"
704 * c-basic-offset: 4
705 * tab-width: 4
706 * indent-tabs-mode: nil
707 * End:
708 */