ia64/xen-unstable

annotate xen/common/event_channel.c @ 1235:d2c1f58f2edd

bitkeeper revision 1.825.4.3 (40642185aZwwgLwBAies8HKAne40aw)

Many files:
Adding physirq support to new Xen upcall interface.
.del-physirq.c~e02f2ea038df07fa:
Delete: xenolinux-2.4.25-sparse/arch/xen/kernel/physirq.c
author kaf24@scramble.cl.cam.ac.uk
date Fri Mar 26 12:26:45 2004 +0000 (2004-03-26)
parents fc8ea15b6dcf
children 8b5451a42056 ebfb82dc24e2 e2d412f4b445
rev   line source
kaf24@954 1 /******************************************************************************
kaf24@954 2 * event_channel.c
kaf24@954 3 *
kaf24@954 4 * Event channels between domains.
kaf24@954 5 *
kaf24@1127 6 * Copyright (c) 2003-2004, K A Fraser.
kaf24@954 7 *
kaf24@954 8 * This program is distributed in the hope that it will be useful,
kaf24@954 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
kaf24@954 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
kaf24@954 11 * GNU General Public License for more details.
kaf24@954 12 *
kaf24@954 13 * You should have received a copy of the GNU General Public License
kaf24@954 14 * along with this program; if not, write to the Free Software
kaf24@954 15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
kaf24@954 16 */
kaf24@954 17
kaf24@1210 18 #include <xen/config.h>
kaf24@1210 19 #include <xen/init.h>
kaf24@1210 20 #include <xen/lib.h>
kaf24@1210 21 #include <xen/errno.h>
kaf24@1210 22 #include <xen/sched.h>
kaf24@1210 23 #include <xen/event.h>
kaf24@954 24
kaf24@1127 25 #include <hypervisor-ifs/hypervisor-if.h>
kaf24@1127 26 #include <hypervisor-ifs/event_channel.h>
kaf24@1127 27
kaf24@1218 28 #define INIT_EVENT_CHANNELS 16
kaf24@1218 29 #define MAX_EVENT_CHANNELS 1024
kaf24@954 30
kaf24@1127 31 static int get_free_port(struct task_struct *p)
kaf24@954 32 {
kaf24@1127 33 int max, port;
kaf24@1127 34 event_channel_t *chn;
kaf24@1127 35
kaf24@1127 36 max = p->max_event_channel;
kaf24@1127 37 chn = p->event_channel;
kaf24@1127 38
kaf24@1127 39 for ( port = 0; port < max; port++ )
kaf24@1127 40 if ( chn[port].state == ECS_FREE )
kaf24@1127 41 break;
kaf24@1127 42
kaf24@1127 43 if ( port == max )
kaf24@1127 44 {
kaf24@1127 45 if ( max == MAX_EVENT_CHANNELS )
kaf24@1127 46 return -ENOSPC;
kaf24@1127 47
kaf24@1127 48 max = (max == 0) ? 4 : (max * 2);
kaf24@1127 49
kaf24@1127 50 chn = kmalloc(max * sizeof(event_channel_t), GFP_KERNEL);
kaf24@1127 51 if ( unlikely(chn == NULL) )
kaf24@1127 52 return -ENOMEM;
kaf24@1127 53
kaf24@1127 54 memset(chn, 0, max * sizeof(event_channel_t));
kaf24@1127 55
kaf24@1127 56 if ( p->event_channel != NULL )
kaf24@1127 57 {
kaf24@1127 58 memcpy(chn, p->event_channel, (max/2) * sizeof(event_channel_t));
kaf24@1127 59 kfree(p->event_channel);
kaf24@1127 60 }
kaf24@1127 61
kaf24@1127 62 p->event_channel = chn;
kaf24@1127 63 p->max_event_channel = max;
kaf24@1127 64 }
kaf24@1127 65
kaf24@1127 66 return port;
kaf24@1127 67 }
kaf24@1127 68
kaf24@1218 69 static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind)
kaf24@1127 70 {
kaf24@1129 71 struct task_struct *p1, *p2;
kaf24@1129 72 int port1 = 0, port2 = 0;
kaf24@1218 73 domid_t dom1 = bind->dom1, dom2 = bind->dom2;
kaf24@954 74 long rc = 0;
kaf24@954 75
kaf24@1127 76 if ( !IS_PRIV(current) )
kaf24@1127 77 return -EPERM;
kaf24@954 78
kaf24@1129 79 if ( dom1 == DOMID_SELF )
kaf24@1129 80 dom1 = current->domain;
kaf24@1129 81 if ( dom2 == DOMID_SELF )
kaf24@1135 82 dom2 = current->domain;
kaf24@1127 83
kaf24@1129 84 if ( ((p1 = find_domain_by_id(dom1)) == NULL) ||
kaf24@1129 85 ((p2 = find_domain_by_id(dom2)) == NULL) )
kaf24@1127 86 {
kaf24@1129 87 if ( p1 != NULL )
kaf24@1129 88 put_task_struct(p1);
kaf24@1127 89 return -ESRCH;
kaf24@1127 90 }
kaf24@1127 91
kaf24@1127 92 /* Avoid deadlock by first acquiring lock of domain with smaller id. */
kaf24@1129 93 if ( dom1 < dom2 )
kaf24@954 94 {
kaf24@1129 95 spin_lock(&p1->event_channel_lock);
kaf24@1129 96 spin_lock(&p2->event_channel_lock);
kaf24@954 97 }
kaf24@954 98 else
kaf24@954 99 {
kaf24@1135 100 if ( p1 != p2 )
kaf24@1135 101 spin_lock(&p2->event_channel_lock);
kaf24@1129 102 spin_lock(&p1->event_channel_lock);
kaf24@954 103 }
kaf24@954 104
kaf24@1129 105 if ( (port1 = get_free_port(p1)) < 0 )
kaf24@954 106 {
kaf24@1129 107 rc = port1;
kaf24@1127 108 goto out;
kaf24@954 109 }
kaf24@954 110
kaf24@1129 111 if ( (port2 = get_free_port(p2)) < 0 )
kaf24@954 112 {
kaf24@1129 113 rc = port2;
kaf24@1127 114 goto out;
kaf24@1127 115 }
kaf24@954 116
kaf24@1218 117 p1->event_channel[port1].u.remote.dom = p2;
kaf24@1218 118 p1->event_channel[port1].u.remote.port = (u16)port2;
kaf24@1218 119 p1->event_channel[port1].state = ECS_INTERDOMAIN;
kaf24@954 120
kaf24@1218 121 p2->event_channel[port2].u.remote.dom = p1;
kaf24@1218 122 p2->event_channel[port2].u.remote.port = (u16)port1;
kaf24@1218 123 p2->event_channel[port2].state = ECS_INTERDOMAIN;
kaf24@1127 124
kaf24@1218 125 evtchn_set_pending(p1, port1);
kaf24@1218 126 evtchn_set_pending(p2, port2);
kaf24@954 127
kaf24@954 128 out:
kaf24@1129 129 spin_unlock(&p1->event_channel_lock);
kaf24@1135 130 if ( p1 != p2 )
kaf24@1135 131 spin_unlock(&p2->event_channel_lock);
kaf24@1127 132
kaf24@1129 133 put_task_struct(p1);
kaf24@1129 134 put_task_struct(p2);
kaf24@1127 135
kaf24@1218 136 bind->port1 = port1;
kaf24@1218 137 bind->port2 = port2;
kaf24@954 138
kaf24@954 139 return rc;
kaf24@954 140 }
kaf24@954 141
kaf24@954 142
kaf24@1218 143 static long evtchn_bind_virq(evtchn_bind_virq_t *bind)
kaf24@1218 144 {
kaf24@1218 145 struct task_struct *p = current;
kaf24@1218 146 int virq = bind->virq;
kaf24@1218 147 int port;
kaf24@1218 148
kaf24@1235 149 if ( virq >= ARRAY_SIZE(p->virq_to_evtchn) )
kaf24@1218 150 return -EINVAL;
kaf24@1218 151
kaf24@1218 152 spin_lock(&p->event_channel_lock);
kaf24@1218 153
kaf24@1218 154 /*
kaf24@1218 155 * Port 0 is the fallback port for VIRQs that haven't been explicitly
kaf24@1218 156 * bound yet. The exception is the 'error VIRQ', which is permanently
kaf24@1218 157 * bound to port 0.
kaf24@1218 158 */
kaf24@1218 159 if ( ((port = p->virq_to_evtchn[virq]) != 0) ||
kaf24@1218 160 (virq == VIRQ_ERROR) ||
kaf24@1218 161 ((port = get_free_port(p)) < 0) )
kaf24@1218 162 goto out;
kaf24@1218 163
kaf24@1218 164 p->event_channel[port].state = ECS_VIRQ;
kaf24@1218 165 p->event_channel[port].u.virq = virq;
kaf24@1218 166
kaf24@1218 167 p->virq_to_evtchn[virq] = port;
kaf24@1218 168
kaf24@1218 169 out:
kaf24@1218 170 spin_unlock(&p->event_channel_lock);
kaf24@1218 171
kaf24@1218 172 if ( port < 0 )
kaf24@1218 173 return port;
kaf24@1218 174
kaf24@1218 175 bind->port = port;
kaf24@1218 176 return 0;
kaf24@1218 177 }
kaf24@1218 178
kaf24@1218 179
kaf24@1235 180 static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind)
kaf24@1235 181 {
kaf24@1235 182 struct task_struct *p = current;
kaf24@1235 183 int pirq = bind->pirq;
kaf24@1235 184 int port;
kaf24@1235 185
kaf24@1235 186 if ( pirq >= ARRAY_SIZE(p->pirq_to_evtchn) )
kaf24@1235 187 return -EINVAL;
kaf24@1235 188
kaf24@1235 189 spin_lock(&p->event_channel_lock);
kaf24@1235 190
kaf24@1235 191 if ( ((port = p->pirq_to_evtchn[pirq]) != 0) ||
kaf24@1235 192 ((port = get_free_port(p)) < 0) )
kaf24@1235 193 goto out;
kaf24@1235 194
kaf24@1235 195 p->event_channel[port].state = ECS_PIRQ;
kaf24@1235 196 p->event_channel[port].u.pirq = pirq;
kaf24@1235 197
kaf24@1235 198 p->pirq_to_evtchn[pirq] = port;
kaf24@1235 199
kaf24@1235 200 out:
kaf24@1235 201 spin_unlock(&p->event_channel_lock);
kaf24@1235 202
kaf24@1235 203 if ( port < 0 )
kaf24@1235 204 return port;
kaf24@1235 205
kaf24@1235 206 bind->port = port;
kaf24@1235 207 return 0;
kaf24@1235 208 }
kaf24@1235 209
kaf24@1235 210
kaf24@1218 211 static long __evtchn_close(struct task_struct *p1, int port1)
kaf24@954 212 {
kaf24@1129 213 struct task_struct *p2 = NULL;
kaf24@1129 214 event_channel_t *chn1, *chn2;
kaf24@1129 215 int port2;
kaf24@954 216 long rc = 0;
kaf24@954 217
kaf24@954 218 again:
kaf24@1129 219 spin_lock(&p1->event_channel_lock);
kaf24@954 220
kaf24@1129 221 chn1 = p1->event_channel;
kaf24@954 222
kaf24@1218 223 /* NB. Port 0 is special (VIRQ_ERROR). Never let it be closed. */
kaf24@1218 224 if ( (port1 <= 0) || (port1 >= p1->max_event_channel) )
kaf24@954 225 {
kaf24@954 226 rc = -EINVAL;
kaf24@954 227 goto out;
kaf24@954 228 }
kaf24@954 229
kaf24@1218 230 switch ( chn1[port1].state )
kaf24@954 231 {
kaf24@1218 232 case ECS_FREE:
kaf24@1218 233 rc = -EINVAL;
kaf24@1218 234 goto out;
kaf24@1218 235
kaf24@1218 236 case ECS_UNBOUND:
kaf24@1218 237 break;
kaf24@1218 238
kaf24@1218 239 case ECS_PIRQ:
kaf24@1218 240 p1->pirq_to_evtchn[chn1[port1].u.pirq] = 0;
kaf24@1218 241 break;
kaf24@1218 242
kaf24@1218 243 case ECS_VIRQ:
kaf24@1218 244 p1->virq_to_evtchn[chn1[port1].u.virq] = 0;
kaf24@1218 245 break;
kaf24@1218 246
kaf24@1218 247 case ECS_INTERDOMAIN:
kaf24@1129 248 if ( p2 == NULL )
kaf24@954 249 {
kaf24@1218 250 p2 = chn1[port1].u.remote.dom;
kaf24@1129 251 get_task_struct(p2);
kaf24@1127 252
kaf24@1129 253 if ( p1->domain < p2->domain )
kaf24@954 254 {
kaf24@1129 255 spin_lock(&p2->event_channel_lock);
kaf24@954 256 }
kaf24@1135 257 else if ( p1 != p2 )
kaf24@954 258 {
kaf24@1129 259 spin_unlock(&p1->event_channel_lock);
kaf24@1129 260 spin_lock(&p2->event_channel_lock);
kaf24@954 261 goto again;
kaf24@954 262 }
kaf24@954 263 }
kaf24@1218 264 else if ( p2 != chn1[port1].u.remote.dom )
kaf24@954 265 {
kaf24@954 266 rc = -EINVAL;
kaf24@954 267 goto out;
kaf24@954 268 }
kaf24@954 269
kaf24@1129 270 chn2 = p2->event_channel;
kaf24@1218 271 port2 = chn1[port1].u.remote.port;
kaf24@954 272
kaf24@1129 273 if ( port2 >= p2->max_event_channel )
kaf24@1127 274 BUG();
kaf24@1218 275 if ( chn2[port2].state != ECS_INTERDOMAIN )
kaf24@1127 276 BUG();
kaf24@1218 277 if ( chn2[port2].u.remote.dom != p1 )
kaf24@1127 278 BUG();
kaf24@954 279
kaf24@1218 280 chn2[port2].state = ECS_UNBOUND;
kaf24@1218 281 chn2[port2].u.remote.dom = NULL;
kaf24@1218 282 chn2[port2].u.remote.port = 0xFFFF;
kaf24@1127 283
kaf24@1218 284 evtchn_set_exception(p2, port2);
kaf24@1218 285
kaf24@1218 286 break;
kaf24@1218 287
kaf24@1218 288 default:
kaf24@1218 289 BUG();
kaf24@954 290 }
kaf24@954 291
kaf24@1218 292 chn1[port1].state = ECS_FREE;
kaf24@1218 293 chn1[port1].u.remote.dom = NULL;
kaf24@1218 294 chn1[port1].u.remote.port = 0xFFFF;
kaf24@954 295
kaf24@1218 296 evtchn_set_exception(p1, port1);
kaf24@1133 297
kaf24@954 298 out:
kaf24@1129 299 if ( p2 != NULL )
kaf24@954 300 {
kaf24@1135 301 if ( p1 != p2 )
kaf24@1135 302 spin_unlock(&p2->event_channel_lock);
kaf24@1129 303 put_task_struct(p2);
kaf24@954 304 }
kaf24@954 305
kaf24@1145 306 spin_unlock(&p1->event_channel_lock);
kaf24@1145 307
kaf24@954 308 return rc;
kaf24@954 309 }
kaf24@954 310
kaf24@954 311
kaf24@1218 312 static long evtchn_close(evtchn_close_t *close)
kaf24@1127 313 {
kaf24@1129 314 struct task_struct *p;
kaf24@1127 315 long rc;
kaf24@1129 316 domid_t dom = close->dom;
kaf24@1127 317
kaf24@1129 318 if ( dom == DOMID_SELF )
kaf24@1129 319 dom = current->domain;
kaf24@1127 320 else if ( !IS_PRIV(current) )
kaf24@1127 321 return -EPERM;
kaf24@1127 322
kaf24@1129 323 if ( (p = find_domain_by_id(dom)) == NULL )
kaf24@1127 324 return -ESRCH;
kaf24@1127 325
kaf24@1218 326 rc = __evtchn_close(p, close->port);
kaf24@1127 327
kaf24@1129 328 put_task_struct(p);
kaf24@1127 329 return rc;
kaf24@1127 330 }
kaf24@1127 331
kaf24@1127 332
kaf24@1218 333 static long evtchn_send(int lport)
kaf24@954 334 {
kaf24@954 335 struct task_struct *lp = current, *rp;
kaf24@1127 336 int rport;
kaf24@954 337
kaf24@954 338 spin_lock(&lp->event_channel_lock);
kaf24@954 339
kaf24@1127 340 if ( unlikely(lport < 0) ||
kaf24@1127 341 unlikely(lport >= lp->max_event_channel) ||
kaf24@1218 342 unlikely(lp->event_channel[lport].state != ECS_INTERDOMAIN) )
kaf24@954 343 {
kaf24@954 344 spin_unlock(&lp->event_channel_lock);
kaf24@954 345 return -EINVAL;
kaf24@954 346 }
kaf24@954 347
kaf24@1218 348 rp = lp->event_channel[lport].u.remote.dom;
kaf24@1218 349 rport = lp->event_channel[lport].u.remote.port;
kaf24@1127 350
kaf24@1127 351 get_task_struct(rp);
kaf24@954 352
kaf24@954 353 spin_unlock(&lp->event_channel_lock);
kaf24@954 354
kaf24@1218 355 evtchn_set_pending(rp, rport);
kaf24@954 356
kaf24@954 357 put_task_struct(rp);
kaf24@1127 358
kaf24@954 359 return 0;
kaf24@954 360 }
kaf24@954 361
kaf24@954 362
kaf24@1218 363 static long evtchn_status(evtchn_status_t *status)
kaf24@954 364 {
kaf24@1129 365 struct task_struct *p;
kaf24@1218 366 domid_t dom = status->dom;
kaf24@1218 367 int port = status->port;
kaf24@1129 368 event_channel_t *chn;
kaf24@1127 369
kaf24@1129 370 if ( dom == DOMID_SELF )
kaf24@1129 371 dom = current->domain;
kaf24@1127 372 else if ( !IS_PRIV(current) )
kaf24@1127 373 return -EPERM;
kaf24@1127 374
kaf24@1129 375 if ( (p = find_domain_by_id(dom)) == NULL )
kaf24@1127 376 return -ESRCH;
kaf24@954 377
kaf24@1129 378 spin_lock(&p->event_channel_lock);
kaf24@954 379
kaf24@1129 380 chn = p->event_channel;
kaf24@954 381
kaf24@1129 382 if ( (port < 0) || (port >= p->max_event_channel) )
kaf24@954 383 {
kaf24@1129 384 spin_unlock(&p->event_channel_lock);
kaf24@1127 385 return -EINVAL;
kaf24@1127 386 }
kaf24@1127 387
kaf24@1129 388 switch ( chn[port].state )
kaf24@1127 389 {
kaf24@1127 390 case ECS_FREE:
kaf24@1127 391 status->status = EVTCHNSTAT_closed;
kaf24@1127 392 break;
kaf24@1218 393 case ECS_UNBOUND:
kaf24@1218 394 status->status = EVTCHNSTAT_unbound;
kaf24@1127 395 break;
kaf24@1218 396 case ECS_INTERDOMAIN:
kaf24@1218 397 status->status = EVTCHNSTAT_interdomain;
kaf24@1218 398 status->u.interdomain.dom = chn[port].u.remote.dom->domain;
kaf24@1218 399 status->u.interdomain.port = chn[port].u.remote.port;
kaf24@1218 400 break;
kaf24@1218 401 case ECS_PIRQ:
kaf24@1218 402 status->status = EVTCHNSTAT_pirq;
kaf24@1218 403 status->u.pirq = chn[port].u.pirq;
kaf24@1218 404 break;
kaf24@1218 405 case ECS_VIRQ:
kaf24@1218 406 status->status = EVTCHNSTAT_virq;
kaf24@1218 407 status->u.virq = chn[port].u.virq;
kaf24@1127 408 break;
kaf24@1127 409 default:
kaf24@1127 410 BUG();
kaf24@954 411 }
kaf24@954 412
kaf24@1129 413 spin_unlock(&p->event_channel_lock);
kaf24@1127 414 return 0;
kaf24@954 415 }
kaf24@954 416
kaf24@954 417
kaf24@1127 418 long do_event_channel_op(evtchn_op_t *uop)
kaf24@954 419 {
kaf24@954 420 long rc;
kaf24@1127 421 evtchn_op_t op;
kaf24@954 422
kaf24@1127 423 if ( copy_from_user(&op, uop, sizeof(op)) != 0 )
kaf24@1127 424 return -EFAULT;
kaf24@1127 425
kaf24@1127 426 switch ( op.cmd )
kaf24@954 427 {
kaf24@1218 428 case EVTCHNOP_bind_interdomain:
kaf24@1218 429 rc = evtchn_bind_interdomain(&op.u.bind_interdomain);
kaf24@1235 430 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
kaf24@1218 431 rc = -EFAULT; /* Cleaning up here would be a mess! */
kaf24@1218 432 break;
kaf24@1218 433
kaf24@1218 434 case EVTCHNOP_bind_virq:
kaf24@1218 435 rc = evtchn_bind_virq(&op.u.bind_virq);
kaf24@1235 436 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
kaf24@1235 437 rc = -EFAULT; /* Cleaning up here would be a mess! */
kaf24@1235 438 break;
kaf24@1235 439
kaf24@1235 440 case EVTCHNOP_bind_pirq:
kaf24@1235 441 rc = evtchn_bind_pirq(&op.u.bind_pirq);
kaf24@1235 442 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
kaf24@1127 443 rc = -EFAULT; /* Cleaning up here would be a mess! */
kaf24@954 444 break;
kaf24@954 445
kaf24@954 446 case EVTCHNOP_close:
kaf24@1218 447 rc = evtchn_close(&op.u.close);
kaf24@954 448 break;
kaf24@954 449
kaf24@954 450 case EVTCHNOP_send:
kaf24@1218 451 rc = evtchn_send(op.u.send.local_port);
kaf24@954 452 break;
kaf24@954 453
kaf24@954 454 case EVTCHNOP_status:
kaf24@1218 455 rc = evtchn_status(&op.u.status);
kaf24@1235 456 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
kaf24@1127 457 rc = -EFAULT;
kaf24@954 458 break;
kaf24@954 459
kaf24@954 460 default:
kaf24@954 461 rc = -ENOSYS;
kaf24@954 462 break;
kaf24@954 463 }
kaf24@954 464
kaf24@954 465 return rc;
kaf24@954 466 }
kaf24@954 467
kaf24@954 468
kaf24@1218 469 int init_event_channels(struct task_struct *p)
kaf24@1218 470 {
kaf24@1218 471 spin_lock_init(&p->event_channel_lock);
kaf24@1218 472 p->event_channel = kmalloc(INIT_EVENT_CHANNELS * sizeof(event_channel_t),
kaf24@1218 473 GFP_KERNEL);
kaf24@1218 474 if ( unlikely(p->event_channel == NULL) )
kaf24@1218 475 return -ENOMEM;
kaf24@1218 476 p->max_event_channel = INIT_EVENT_CHANNELS;
kaf24@1218 477 memset(p->event_channel, 0, INIT_EVENT_CHANNELS * sizeof(event_channel_t));
kaf24@1218 478 p->event_channel[0].state = ECS_VIRQ;
kaf24@1218 479 p->event_channel[0].u.virq = VIRQ_ERROR;
kaf24@1218 480 return 0;
kaf24@1218 481 }
kaf24@1218 482
kaf24@1218 483
kaf24@954 484 void destroy_event_channels(struct task_struct *p)
kaf24@954 485 {
kaf24@954 486 int i;
kaf24@954 487 if ( p->event_channel != NULL )
kaf24@954 488 {
kaf24@954 489 for ( i = 0; i < p->max_event_channel; i++ )
kaf24@1218 490 (void)__evtchn_close(p, i);
kaf24@954 491 kfree(p->event_channel);
kaf24@954 492 }
kaf24@954 493 }