ia64/xen-unstable

annotate xen/common/event_channel.c @ 1133:cd5a94809f8a

bitkeeper revision 1.755 (403f5fffwYqT6Gw88yRJHe04lMgpjg)

Many files:
Further cleanups to the Xen pagetable interface.
author kaf24@scramble.cl.cam.ac.uk
date Fri Feb 27 15:19:27 2004 +0000 (2004-02-27)
parents acd0f2cab313
children ca5de6ca0355
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@954 18 #include <xeno/config.h>
kaf24@954 19 #include <xeno/init.h>
kaf24@954 20 #include <xeno/lib.h>
kaf24@954 21 #include <xeno/errno.h>
kaf24@954 22 #include <xeno/sched.h>
kaf24@954 23 #include <xeno/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@956 28 #define MAX_EVENT_CHANNELS 1024
kaf24@954 29
kaf24@1127 30 static int get_free_port(struct task_struct *p)
kaf24@954 31 {
kaf24@1127 32 int max, port;
kaf24@1127 33 event_channel_t *chn;
kaf24@1127 34
kaf24@1127 35 max = p->max_event_channel;
kaf24@1127 36 chn = p->event_channel;
kaf24@1127 37
kaf24@1127 38 for ( port = 0; port < max; port++ )
kaf24@1127 39 if ( chn[port].state == ECS_FREE )
kaf24@1127 40 break;
kaf24@1127 41
kaf24@1127 42 if ( port == max )
kaf24@1127 43 {
kaf24@1127 44 if ( max == MAX_EVENT_CHANNELS )
kaf24@1127 45 return -ENOSPC;
kaf24@1127 46
kaf24@1127 47 max = (max == 0) ? 4 : (max * 2);
kaf24@1127 48
kaf24@1127 49 chn = kmalloc(max * sizeof(event_channel_t), GFP_KERNEL);
kaf24@1127 50 if ( unlikely(chn == NULL) )
kaf24@1127 51 return -ENOMEM;
kaf24@1127 52
kaf24@1127 53 memset(chn, 0, max * sizeof(event_channel_t));
kaf24@1127 54
kaf24@1127 55 if ( p->event_channel != NULL )
kaf24@1127 56 {
kaf24@1127 57 memcpy(chn, p->event_channel, (max/2) * sizeof(event_channel_t));
kaf24@1127 58 kfree(p->event_channel);
kaf24@1127 59 }
kaf24@1127 60
kaf24@1127 61 p->event_channel = chn;
kaf24@1127 62 p->max_event_channel = max;
kaf24@1127 63 }
kaf24@1127 64
kaf24@1127 65 return port;
kaf24@1127 66 }
kaf24@1127 67
kaf24@1127 68 static inline unsigned long set_event_pending(struct task_struct *p, int port)
kaf24@1127 69 {
kaf24@1127 70 if ( !test_and_set_bit(port, &p->shared_info->event_channel_pend[0]) &&
kaf24@1127 71 !test_and_set_bit(port>>5, &p->shared_info->event_channel_pend_sel) )
kaf24@1127 72 return mark_guest_event(p, _EVENT_EVTCHN);
kaf24@1127 73 return 0;
kaf24@1127 74 }
kaf24@1127 75
kaf24@1127 76 static inline unsigned long set_event_disc(struct task_struct *p, int port)
kaf24@1127 77 {
kaf24@1127 78 if ( !test_and_set_bit(port, &p->shared_info->event_channel_disc[0]) &&
kaf24@1127 79 !test_and_set_bit(port>>5, &p->shared_info->event_channel_disc_sel) )
kaf24@1127 80 return mark_guest_event(p, _EVENT_EVTCHN);
kaf24@1127 81 return 0;
kaf24@1127 82 }
kaf24@1127 83
kaf24@1127 84 static long event_channel_open(evtchn_open_t *open)
kaf24@1127 85 {
kaf24@1129 86 struct task_struct *p1, *p2;
kaf24@1129 87 int port1 = 0, port2 = 0;
kaf24@954 88 unsigned long cpu_mask;
kaf24@1129 89 domid_t dom1 = open->dom1, dom2 = open->dom2;
kaf24@954 90 long rc = 0;
kaf24@954 91
kaf24@1127 92 if ( !IS_PRIV(current) )
kaf24@1127 93 return -EPERM;
kaf24@954 94
kaf24@1129 95 /* 'dom1' may be DOMID_SELF. 'dom2' cannot be.*/
kaf24@1129 96 if ( dom1 == DOMID_SELF )
kaf24@1129 97 dom1 = current->domain;
kaf24@1129 98 if ( dom2 == DOMID_SELF )
kaf24@1129 99 return -EINVAL;
kaf24@1127 100
kaf24@1127 101 /* Event channel must connect distinct domains. */
kaf24@1129 102 if ( dom1 == dom2 )
kaf24@1127 103 return -EINVAL;
kaf24@1127 104
kaf24@1129 105 if ( ((p1 = find_domain_by_id(dom1)) == NULL) ||
kaf24@1129 106 ((p2 = find_domain_by_id(dom2)) == NULL) )
kaf24@1127 107 {
kaf24@1129 108 if ( p1 != NULL )
kaf24@1129 109 put_task_struct(p1);
kaf24@1127 110 return -ESRCH;
kaf24@1127 111 }
kaf24@1127 112
kaf24@1127 113 /* Avoid deadlock by first acquiring lock of domain with smaller id. */
kaf24@1129 114 if ( dom1 < dom2 )
kaf24@954 115 {
kaf24@1129 116 spin_lock(&p1->event_channel_lock);
kaf24@1129 117 spin_lock(&p2->event_channel_lock);
kaf24@954 118 }
kaf24@954 119 else
kaf24@954 120 {
kaf24@1129 121 spin_lock(&p2->event_channel_lock);
kaf24@1129 122 spin_lock(&p1->event_channel_lock);
kaf24@954 123 }
kaf24@954 124
kaf24@1129 125 if ( (port1 = get_free_port(p1)) < 0 )
kaf24@954 126 {
kaf24@1129 127 rc = port1;
kaf24@1127 128 goto out;
kaf24@954 129 }
kaf24@954 130
kaf24@1129 131 if ( (port2 = get_free_port(p2)) < 0 )
kaf24@954 132 {
kaf24@1129 133 rc = port2;
kaf24@1127 134 goto out;
kaf24@1127 135 }
kaf24@954 136
kaf24@1129 137 p1->event_channel[port1].remote_dom = p2;
kaf24@1129 138 p1->event_channel[port1].remote_port = (u16)port2;
kaf24@1129 139 p1->event_channel[port1].state = ECS_CONNECTED;
kaf24@954 140
kaf24@1129 141 p2->event_channel[port2].remote_dom = p1;
kaf24@1129 142 p2->event_channel[port2].remote_port = (u16)port1;
kaf24@1129 143 p2->event_channel[port2].state = ECS_CONNECTED;
kaf24@1127 144
kaf24@1129 145 cpu_mask = set_event_pending(p1, port1);
kaf24@1129 146 cpu_mask |= set_event_pending(p2, port2);
kaf24@1127 147 guest_event_notify(cpu_mask);
kaf24@954 148
kaf24@954 149 out:
kaf24@1129 150 spin_unlock(&p1->event_channel_lock);
kaf24@1129 151 spin_unlock(&p2->event_channel_lock);
kaf24@1127 152
kaf24@1129 153 put_task_struct(p1);
kaf24@1129 154 put_task_struct(p2);
kaf24@1127 155
kaf24@1129 156 open->port1 = port1;
kaf24@1129 157 open->port2 = port2;
kaf24@954 158
kaf24@954 159 return rc;
kaf24@954 160 }
kaf24@954 161
kaf24@954 162
kaf24@1129 163 static long __event_channel_close(struct task_struct *p1, int port1)
kaf24@954 164 {
kaf24@1129 165 struct task_struct *p2 = NULL;
kaf24@1129 166 event_channel_t *chn1, *chn2;
kaf24@1129 167 int port2;
kaf24@1133 168 unsigned long cpu_mask = 0;
kaf24@954 169 long rc = 0;
kaf24@954 170
kaf24@954 171 again:
kaf24@1129 172 spin_lock(&p1->event_channel_lock);
kaf24@954 173
kaf24@1129 174 chn1 = p1->event_channel;
kaf24@954 175
kaf24@1129 176 if ( (port1 < 0) || (port1 >= p1->max_event_channel) ||
kaf24@1129 177 (chn1[port1].state == ECS_FREE) )
kaf24@954 178 {
kaf24@954 179 rc = -EINVAL;
kaf24@954 180 goto out;
kaf24@954 181 }
kaf24@954 182
kaf24@1129 183 if ( chn1[port1].state == ECS_CONNECTED )
kaf24@954 184 {
kaf24@1129 185 if ( p2 == NULL )
kaf24@954 186 {
kaf24@1129 187 p2 = chn1[port1].remote_dom;
kaf24@1129 188 get_task_struct(p2);
kaf24@1127 189
kaf24@1129 190 if ( p1->domain < p2->domain )
kaf24@954 191 {
kaf24@1129 192 spin_lock(&p2->event_channel_lock);
kaf24@954 193 }
kaf24@954 194 else
kaf24@954 195 {
kaf24@1129 196 spin_unlock(&p1->event_channel_lock);
kaf24@1129 197 spin_lock(&p2->event_channel_lock);
kaf24@954 198 goto again;
kaf24@954 199 }
kaf24@954 200 }
kaf24@1129 201 else if ( p2 != chn1[port1].remote_dom )
kaf24@954 202 {
kaf24@954 203 rc = -EINVAL;
kaf24@954 204 goto out;
kaf24@954 205 }
kaf24@954 206
kaf24@1129 207 chn2 = p2->event_channel;
kaf24@1129 208 port2 = chn1[port1].remote_port;
kaf24@954 209
kaf24@1129 210 if ( port2 >= p2->max_event_channel )
kaf24@1127 211 BUG();
kaf24@1129 212 if ( chn2[port2].state != ECS_CONNECTED )
kaf24@1127 213 BUG();
kaf24@1129 214 if ( chn2[port2].remote_dom != p1 )
kaf24@1127 215 BUG();
kaf24@954 216
kaf24@1133 217 chn2[port2].state = ECS_DISCONNECTED;
kaf24@1129 218 chn2[port2].remote_dom = NULL;
kaf24@1129 219 chn2[port2].remote_port = 0xFFFF;
kaf24@1127 220
kaf24@1129 221 cpu_mask |= set_event_disc(p2, port2);
kaf24@954 222 }
kaf24@954 223
kaf24@1129 224 chn1[port1].state = ECS_FREE;
kaf24@1129 225 chn1[port1].remote_dom = NULL;
kaf24@1129 226 chn1[port1].remote_port = 0xFFFF;
kaf24@954 227
kaf24@1133 228 cpu_mask |= set_event_disc(p1, port1);
kaf24@1133 229 guest_event_notify(cpu_mask);
kaf24@1133 230
kaf24@954 231 out:
kaf24@1129 232 spin_unlock(&p1->event_channel_lock);
kaf24@1129 233 put_task_struct(p1);
kaf24@1127 234
kaf24@1129 235 if ( p2 != NULL )
kaf24@954 236 {
kaf24@1129 237 spin_unlock(&p2->event_channel_lock);
kaf24@1129 238 put_task_struct(p2);
kaf24@954 239 }
kaf24@954 240
kaf24@954 241 return rc;
kaf24@954 242 }
kaf24@954 243
kaf24@954 244
kaf24@1127 245 static long event_channel_close(evtchn_close_t *close)
kaf24@1127 246 {
kaf24@1129 247 struct task_struct *p;
kaf24@1127 248 long rc;
kaf24@1129 249 domid_t dom = close->dom;
kaf24@1127 250
kaf24@1129 251 if ( dom == DOMID_SELF )
kaf24@1129 252 dom = current->domain;
kaf24@1127 253 else if ( !IS_PRIV(current) )
kaf24@1127 254 return -EPERM;
kaf24@1127 255
kaf24@1129 256 if ( (p = find_domain_by_id(dom)) == NULL )
kaf24@1127 257 return -ESRCH;
kaf24@1127 258
kaf24@1129 259 rc = __event_channel_close(p, close->port);
kaf24@1127 260
kaf24@1129 261 put_task_struct(p);
kaf24@1127 262 return rc;
kaf24@1127 263 }
kaf24@1127 264
kaf24@1127 265
kaf24@1127 266 static long event_channel_send(int lport)
kaf24@954 267 {
kaf24@954 268 struct task_struct *lp = current, *rp;
kaf24@1127 269 int rport;
kaf24@954 270 unsigned long cpu_mask;
kaf24@954 271
kaf24@954 272 spin_lock(&lp->event_channel_lock);
kaf24@954 273
kaf24@1127 274 if ( unlikely(lport < 0) ||
kaf24@1127 275 unlikely(lport >= lp->max_event_channel) ||
kaf24@1127 276 unlikely(lp->event_channel[lport].state != ECS_CONNECTED) )
kaf24@954 277 {
kaf24@954 278 spin_unlock(&lp->event_channel_lock);
kaf24@954 279 return -EINVAL;
kaf24@954 280 }
kaf24@954 281
kaf24@1127 282 rp = lp->event_channel[lport].remote_dom;
kaf24@1127 283 rport = lp->event_channel[lport].remote_port;
kaf24@1127 284
kaf24@1127 285 get_task_struct(rp);
kaf24@954 286
kaf24@954 287 spin_unlock(&lp->event_channel_lock);
kaf24@954 288
kaf24@1127 289 cpu_mask = set_event_pending(rp, rport);
kaf24@1127 290 guest_event_notify(cpu_mask);
kaf24@954 291
kaf24@954 292 put_task_struct(rp);
kaf24@1127 293
kaf24@954 294 return 0;
kaf24@954 295 }
kaf24@954 296
kaf24@954 297
kaf24@1127 298 static long event_channel_status(evtchn_status_t *status)
kaf24@954 299 {
kaf24@1129 300 struct task_struct *p;
kaf24@1129 301 domid_t dom = status->dom1;
kaf24@1129 302 int port = status->port1;
kaf24@1129 303 event_channel_t *chn;
kaf24@1127 304
kaf24@1129 305 if ( dom == DOMID_SELF )
kaf24@1129 306 dom = current->domain;
kaf24@1127 307 else if ( !IS_PRIV(current) )
kaf24@1127 308 return -EPERM;
kaf24@1127 309
kaf24@1129 310 if ( (p = find_domain_by_id(dom)) == NULL )
kaf24@1127 311 return -ESRCH;
kaf24@954 312
kaf24@1129 313 spin_lock(&p->event_channel_lock);
kaf24@954 314
kaf24@1129 315 chn = p->event_channel;
kaf24@954 316
kaf24@1129 317 if ( (port < 0) || (port >= p->max_event_channel) )
kaf24@954 318 {
kaf24@1129 319 spin_unlock(&p->event_channel_lock);
kaf24@1127 320 return -EINVAL;
kaf24@1127 321 }
kaf24@1127 322
kaf24@1129 323 switch ( chn[port].state )
kaf24@1127 324 {
kaf24@1127 325 case ECS_FREE:
kaf24@1127 326 status->status = EVTCHNSTAT_closed;
kaf24@1127 327 break;
kaf24@1133 328 case ECS_DISCONNECTED:
kaf24@1127 329 status->status = EVTCHNSTAT_disconnected;
kaf24@1127 330 break;
kaf24@1127 331 case ECS_CONNECTED:
kaf24@1127 332 status->status = EVTCHNSTAT_connected;
kaf24@1129 333 status->dom2 = chn[port].remote_dom->domain;
kaf24@1129 334 status->port2 = chn[port].remote_port;
kaf24@1127 335 break;
kaf24@1127 336 default:
kaf24@1127 337 BUG();
kaf24@954 338 }
kaf24@954 339
kaf24@1129 340 spin_unlock(&p->event_channel_lock);
kaf24@1127 341 return 0;
kaf24@954 342 }
kaf24@954 343
kaf24@954 344
kaf24@1127 345 long do_event_channel_op(evtchn_op_t *uop)
kaf24@954 346 {
kaf24@954 347 long rc;
kaf24@1127 348 evtchn_op_t op;
kaf24@954 349
kaf24@1127 350 if ( copy_from_user(&op, uop, sizeof(op)) != 0 )
kaf24@1127 351 return -EFAULT;
kaf24@1127 352
kaf24@1127 353 switch ( op.cmd )
kaf24@954 354 {
kaf24@954 355 case EVTCHNOP_open:
kaf24@1127 356 rc = event_channel_open(&op.u.open);
kaf24@1127 357 if ( copy_to_user(uop, &op, sizeof(op)) != 0 )
kaf24@1127 358 rc = -EFAULT; /* Cleaning up here would be a mess! */
kaf24@954 359 break;
kaf24@954 360
kaf24@954 361 case EVTCHNOP_close:
kaf24@1127 362 rc = event_channel_close(&op.u.close);
kaf24@954 363 break;
kaf24@954 364
kaf24@954 365 case EVTCHNOP_send:
kaf24@1127 366 rc = event_channel_send(op.u.send.local_port);
kaf24@954 367 break;
kaf24@954 368
kaf24@954 369 case EVTCHNOP_status:
kaf24@1127 370 rc = event_channel_status(&op.u.status);
kaf24@1127 371 if ( copy_to_user(uop, &op, sizeof(op)) != 0 )
kaf24@1127 372 rc = -EFAULT;
kaf24@954 373 break;
kaf24@954 374
kaf24@954 375 default:
kaf24@954 376 rc = -ENOSYS;
kaf24@954 377 break;
kaf24@954 378 }
kaf24@954 379
kaf24@954 380 return rc;
kaf24@954 381 }
kaf24@954 382
kaf24@954 383
kaf24@954 384 void destroy_event_channels(struct task_struct *p)
kaf24@954 385 {
kaf24@954 386 int i;
kaf24@954 387 if ( p->event_channel != NULL )
kaf24@954 388 {
kaf24@954 389 for ( i = 0; i < p->max_event_channel; i++ )
kaf24@1127 390 (void)__event_channel_close(p, i);
kaf24@954 391 kfree(p->event_channel);
kaf24@954 392 }
kaf24@954 393 }