debuggers.hg

view xen/common/grant_table.c @ 3129:e0351a3744a5

bitkeeper revision 1.1159.187.4 (41a471c8NjyQJy-vepqpb8H7LdzHzA)

Allow preemption of long-running hypercalls for softirq processing.
author kaf24@scramble.cl.cam.ac.uk
date Wed Nov 24 11:34:32 2004 +0000 (2004-11-24)
parents ed0f5b1a41ba
children 2754a2ed61c3 2fae9947de6f b013a6b30d9e
line source
1 /******************************************************************************
2 * common/grant_table.c
3 *
4 * Mechanism for granting foreign access to page frames, and receiving
5 * page-ownership transfers.
6 *
7 * Copyright (c) 2004 K A Fraser
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
24 #include <xen/config.h>
25 #include <xen/sched.h>
27 #define PIN_FAIL(_rc, _f, _a...) \
28 do { \
29 DPRINTK( _f, ## _a ); \
30 rc = (_rc); \
31 goto fail; \
32 } while ( 0 )
34 static inline int
35 get_maptrack_handle(
36 grant_table_t *t)
37 {
38 unsigned int h;
39 if ( unlikely((h = t->maptrack_head) == NR_MAPTRACK_ENTRIES) )
40 return -1;
41 t->maptrack_head = t->maptrack[h].ref_and_flags >> MAPTRACK_REF_SHIFT;
42 return h;
43 }
45 static inline void
46 put_maptrack_handle(
47 grant_table_t *t, int handle)
48 {
49 t->maptrack[handle].ref_and_flags = t->maptrack_head << MAPTRACK_REF_SHIFT;
50 t->maptrack_head = handle;
51 }
53 static void
54 __gnttab_map_grant_ref(
55 gnttab_map_grant_ref_t *uop)
56 {
57 domid_t dom, sdom;
58 grant_ref_t ref;
59 struct domain *ld, *rd;
60 u16 flags, sflags;
61 int handle;
62 active_grant_entry_t *act;
63 grant_entry_t *sha;
64 s16 rc = 0;
65 unsigned long frame;
67 /*
68 * We bound the number of times we retry CMPXCHG on memory locations that
69 * we share with a guest OS. The reason is that the guest can modify that
70 * location at a higher rate than we can read-modify-CMPXCHG, so the guest
71 * could cause us to livelock. There are a few cases where it is valid for
72 * the guest to race our updates (e.g., to change the GTF_readonly flag),
73 * so we allow a few retries before failing.
74 */
75 int retries = 0;
77 ld = current;
79 /* Bitwise-OR avoids short-circuiting which screws control flow. */
80 if ( unlikely(__get_user(dom, &uop->dom) |
81 __get_user(ref, &uop->ref) |
82 __get_user(flags, &uop->flags)) )
83 {
84 DPRINTK("Fault while reading gnttab_map_grant_ref_t.\n");
85 return; /* don't set status */
86 }
88 if ( unlikely(ref >= NR_GRANT_ENTRIES) ||
89 unlikely((flags & (GNTMAP_device_map|GNTMAP_host_map)) == 0) )
90 {
91 DPRINTK("Bad ref (%d) or flags (%x).\n", ref, flags);
92 (void)__put_user(GNTST_bad_gntref, &uop->handle);
93 return;
94 }
96 if ( unlikely((rd = find_domain_by_id(dom)) == NULL) ||
97 unlikely(ld == rd) )
98 {
99 if ( rd != NULL )
100 put_domain(rd);
101 DPRINTK("Could not find domain %d\n", dom);
102 (void)__put_user(GNTST_bad_domain, &uop->handle);
103 return;
104 }
106 if ( unlikely((handle = get_maptrack_handle(ld->grant_table)) == -1) )
107 {
108 put_domain(rd);
109 DPRINTK("No more map handles available\n");
110 (void)__put_user(GNTST_no_device_space, &uop->handle);
111 return;
112 }
114 act = &rd->grant_table->active[ref];
115 sha = &rd->grant_table->shared[ref];
117 spin_lock(&rd->grant_table->lock);
119 if ( act->pin == 0 )
120 {
121 /* CASE 1: Activating a previously inactive entry. */
123 sflags = sha->flags;
124 sdom = sha->domid;
126 for ( ; ; )
127 {
128 u32 scombo, prev_scombo, new_scombo;
130 if ( unlikely((sflags & GTF_type_mask) != GTF_permit_access) ||
131 unlikely(sdom != ld->id) )
132 PIN_FAIL(GNTST_general_error,
133 "Bad flags (%x) or dom (%d). (NB. expected dom %d)\n",
134 sflags, sdom, ld->id);
136 /* Merge two 16-bit values into a 32-bit combined update. */
137 /* NB. Endianness! */
138 prev_scombo = scombo = ((u32)sdom << 16) | (u32)sflags;
140 new_scombo = scombo | GTF_reading;
141 if ( !(flags & GNTMAP_readonly) )
142 {
143 new_scombo |= GTF_writing;
144 if ( unlikely(sflags & GTF_readonly) )
145 PIN_FAIL(GNTST_general_error,
146 "Attempt to write-pin a r/o grant entry.\n");
147 }
149 /* NB. prev_scombo is updated in place to seen value. */
150 if ( unlikely(cmpxchg_user((u32 *)&sha->flags,
151 prev_scombo,
152 new_scombo)) )
153 PIN_FAIL(GNTST_general_error,
154 "Fault while modifying shared flags and domid.\n");
156 /* Did the combined update work (did we see what we expected?). */
157 if ( likely(prev_scombo == scombo) )
158 break;
160 if ( retries++ == 4 )
161 PIN_FAIL(GNTST_general_error,
162 "Shared grant entry is unstable.\n");
164 /* Didn't see what we expected. Split out the seen flags & dom. */
165 /* NB. Endianness! */
166 sflags = (u16)prev_scombo;
167 sdom = (u16)(prev_scombo >> 16);
168 }
170 /* rmb(); */ /* not on x86 */
171 frame = sha->frame;
172 if ( unlikely(!pfn_is_ram(frame)) ||
173 unlikely(!((flags & GNTMAP_readonly) ?
174 get_page(&frame_table[frame], rd) :
175 get_page_and_type(&frame_table[frame], rd,
176 PGT_writable_page))) )
177 {
178 clear_bit(_GTF_writing, &sha->flags);
179 clear_bit(_GTF_reading, &sha->flags);
180 PIN_FAIL(GNTST_general_error,
181 "Could not pin the granted frame!\n");
182 }
184 if ( flags & GNTMAP_device_map )
185 act->pin += (flags & GNTMAP_readonly) ?
186 GNTPIN_devr_inc : GNTPIN_devw_inc;
187 if ( flags & GNTMAP_host_map )
188 act->pin += (flags & GNTMAP_readonly) ?
189 GNTPIN_hstr_inc : GNTPIN_hstw_inc;
190 act->domid = sdom;
191 act->frame = frame;
192 }
193 else
194 {
195 /* CASE 2: Active modications to an already active entry. */
197 /*
198 * A cheesy check for possible pin-count overflow.
199 * A more accurate check cannot be done with a single comparison.
200 */
201 if ( (act->pin & 0x80808080U) != 0 )
202 PIN_FAIL(ENOSPC, "Risk of counter overflow %08x\n", act->pin);
204 if ( !(flags & GNTMAP_readonly) &&
205 !((sflags = sha->flags) & GTF_writing) )
206 {
207 for ( ; ; )
208 {
209 u16 prev_sflags;
211 if ( unlikely(sflags & GTF_readonly) )
212 PIN_FAIL(GNTST_general_error,
213 "Attempt to write-pin a r/o grant entry.\n");
215 prev_sflags = sflags;
217 /* NB. prev_sflags is updated in place to seen value. */
218 if ( unlikely(cmpxchg_user(&sha->flags, prev_sflags,
219 prev_sflags | GTF_writing)) )
220 PIN_FAIL(GNTST_general_error,
221 "Fault while modifying shared flags.\n");
223 if ( likely(prev_sflags == sflags) )
224 break;
226 if ( retries++ == 4 )
227 PIN_FAIL(GNTST_general_error,
228 "Shared grant entry is unstable.\n");
230 sflags = prev_sflags;
231 }
233 if ( unlikely(!get_page_type(&frame_table[act->frame],
234 PGT_writable_page)) )
235 {
236 clear_bit(_GTF_writing, &sha->flags);
237 PIN_FAIL(GNTST_general_error,
238 "Attempt to write-pin a unwritable page.\n");
239 }
240 }
242 if ( flags & GNTMAP_device_map )
243 act->pin += (flags & GNTMAP_readonly) ?
244 GNTPIN_devr_inc : GNTPIN_devw_inc;
245 if ( flags & GNTMAP_host_map )
246 act->pin += (flags & GNTMAP_readonly) ?
247 GNTPIN_hstr_inc : GNTPIN_hstw_inc;
248 }
250 ld->grant_table->maptrack[handle].domid = dom;
251 ld->grant_table->maptrack[handle].ref_and_flags =
252 (ref << MAPTRACK_REF_SHIFT) | (flags & MAPTRACK_GNTMAP_MASK);
254 /* Unchecked and unconditional. */
255 (void)__put_user(handle, &uop->handle);
256 (void)__put_user(act->frame, &uop->dev_bus_addr);
258 spin_unlock(&rd->grant_table->lock);
259 put_domain(rd);
260 return;
262 fail:
263 (void)__put_user(rc, &uop->handle);
264 spin_unlock(&rd->grant_table->lock);
265 put_domain(rd);
266 put_maptrack_handle(ld->grant_table, handle);
267 }
269 static long
270 gnttab_map_grant_ref(
271 gnttab_map_grant_ref_t *uop, unsigned int count)
272 {
273 int i;
274 for ( i = 0; i < count; i++ )
275 __gnttab_map_grant_ref(&uop[i]);
276 return 0;
277 }
279 static void
280 __gnttab_unmap_grant_ref(
281 gnttab_unmap_grant_ref_t *uop)
282 {
283 domid_t dom;
284 grant_ref_t ref;
285 u16 handle;
286 struct domain *ld, *rd;
288 active_grant_entry_t *act;
289 grant_entry_t *sha;
290 grant_mapping_t *map;
291 s16 rc = 0;
292 unsigned long frame, virt;
294 ld = current;
296 /* Bitwise-OR avoids short-circuiting which screws control flow. */
297 if ( unlikely(__get_user(virt, &uop->host_virt_addr) |
298 __get_user(frame, &uop->dev_bus_addr) |
299 __get_user(handle, &uop->handle)) )
300 {
301 DPRINTK("Fault while reading gnttab_unmap_grant_ref_t.\n");
302 return; /* don't set status */
303 }
305 map = &ld->grant_table->maptrack[handle];
307 if ( unlikely(handle >= NR_MAPTRACK_ENTRIES) ||
308 unlikely(!(map->ref_and_flags & MAPTRACK_GNTMAP_MASK)) )
309 {
310 DPRINTK("Bad handle (%d).\n", handle);
311 (void)__put_user(GNTST_bad_handle, &uop->status);
312 return;
313 }
315 dom = map->domid;
316 ref = map->ref_and_flags >> MAPTRACK_REF_SHIFT;
318 if ( unlikely((rd = find_domain_by_id(dom)) == NULL) ||
319 unlikely(ld == rd) )
320 {
321 if ( rd != NULL )
322 put_domain(rd);
323 DPRINTK("Could not find domain %d\n", dom);
324 (void)__put_user(GNTST_bad_domain, &uop->status);
325 return;
326 }
328 act = &rd->grant_table->active[ref];
329 sha = &rd->grant_table->shared[ref];
331 spin_lock(&rd->grant_table->lock);
333 if ( frame != 0 )
334 {
335 if ( unlikely(frame != act->frame) )
336 PIN_FAIL(GNTST_general_error,
337 "Bad frame number doesn't match gntref.\n");
338 if ( map->ref_and_flags & GNTMAP_device_map )
339 act->pin -= (map->ref_and_flags & GNTMAP_readonly) ?
340 GNTPIN_devr_inc : GNTPIN_devw_inc;
341 }
342 else
343 {
344 frame = act->frame;
345 }
347 if ( (virt != 0) && (map->ref_and_flags & GNTMAP_host_map) )
348 {
349 act->pin -= (map->ref_and_flags & GNTMAP_readonly) ?
350 GNTPIN_hstr_inc : GNTPIN_hstw_inc;
351 }
353 if ( ((act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) == 0) &&
354 !(map->ref_and_flags & GNTMAP_readonly) )
355 {
356 put_page_type(&frame_table[frame]);
357 clear_bit(_GTF_writing, &sha->flags);
358 }
360 if ( act->pin == 0 )
361 {
362 put_page(&frame_table[frame]);
363 clear_bit(_GTF_reading, &sha->flags);
364 }
366 fail:
367 (void)__put_user(rc, &uop->status);
368 spin_unlock(&rd->grant_table->lock);
369 put_domain(rd);
370 }
372 static long
373 gnttab_unmap_grant_ref(
374 gnttab_unmap_grant_ref_t *uop, unsigned int count)
375 {
376 int i;
377 for ( i = 0; i < count; i++ )
378 __gnttab_unmap_grant_ref(&uop[i]);
379 return 0;
380 }
382 static long
383 gnttab_setup_table(
384 gnttab_setup_table_t *uop, unsigned int count)
385 {
386 gnttab_setup_table_t op;
387 struct domain *d;
389 if ( count != 1 )
390 return -EINVAL;
392 if ( unlikely(copy_from_user(&op, uop, sizeof(op)) != 0) )
393 {
394 DPRINTK("Fault while reading gnttab_setup_table_t.\n");
395 return -EFAULT;
396 }
398 if ( unlikely(op.nr_frames > 1) )
399 {
400 DPRINTK("Xen only supports one grant-table frame per domain.\n");
401 (void)put_user(GNTST_general_error, &uop->status);
402 return 0;
403 }
405 if ( op.dom == DOMID_SELF )
406 {
407 op.dom = current->id;
408 }
409 else if ( unlikely(!IS_PRIV(current)) )
410 {
411 (void)put_user(GNTST_permission_denied, &uop->status);
412 return 0;
413 }
415 if ( unlikely((d = find_domain_by_id(op.dom)) == NULL) )
416 {
417 DPRINTK("Bad domid %d.\n", op.dom);
418 (void)put_user(GNTST_bad_domain, &uop->status);
419 return 0;
420 }
422 if ( op.nr_frames == 1 )
423 {
424 ASSERT(d->grant_table != NULL);
425 (void)put_user(GNTST_okay, &uop->status);
426 (void)put_user(virt_to_phys(d->grant_table) >> PAGE_SHIFT,
427 &uop->frame_list[0]);
428 }
430 put_domain(d);
431 return 0;
432 }
434 long
435 do_grant_table_op(
436 unsigned int cmd, void *uop, unsigned int count)
437 {
438 long rc;
440 /* XXX stubbed out XXX */
441 return -ENOSYS;
443 if ( count > 512 )
444 return -EINVAL;
446 switch ( cmd )
447 {
448 case GNTTABOP_map_grant_ref:
449 if ( unlikely(!access_ok(VERIFY_WRITE, uop,
450 count * sizeof(gnttab_map_grant_ref_t))) )
451 return -EFAULT;
452 rc = gnttab_map_grant_ref((gnttab_map_grant_ref_t *)uop, count);
453 break;
454 case GNTTABOP_unmap_grant_ref:
455 if ( unlikely(!access_ok(VERIFY_WRITE, uop,
456 count * sizeof(gnttab_unmap_grant_ref_t))) )
457 return -EFAULT;
458 rc = gnttab_unmap_grant_ref((gnttab_unmap_grant_ref_t *)uop, count);
459 break;
460 case GNTTABOP_setup_table:
461 rc = gnttab_setup_table((gnttab_setup_table_t *)uop, count);
462 break;
463 default:
464 rc = -ENOSYS;
465 break;
466 }
468 return rc;
469 }
471 int
472 gnttab_check_unmap(
473 struct domain *rd, struct domain *ld, unsigned long frame, int readonly)
474 {
475 return 0;
476 }
478 int
479 gnttab_prepare_for_transfer(
480 struct domain *rd, struct domain *ld, grant_ref_t ref)
481 {
482 grant_table_t *t;
483 grant_entry_t *e;
484 domid_t sdom;
485 u16 sflags;
486 u32 scombo, prev_scombo;
487 int retries = 0;
489 if ( unlikely((t = rd->grant_table) == NULL) ||
490 unlikely(ref >= NR_GRANT_ENTRIES) )
491 {
492 DPRINTK("Dom %d has no g.t., or ref is bad (%d).\n", rd->id, ref);
493 return 0;
494 }
496 spin_lock(&t->lock);
498 e = &t->shared[ref];
500 sflags = e->flags;
501 sdom = e->domid;
503 for ( ; ; )
504 {
505 if ( unlikely(sflags != GTF_accept_transfer) ||
506 unlikely(sdom != ld->id) )
507 {
508 DPRINTK("Bad flags (%x) or dom (%d). (NB. expected dom %d)\n",
509 sflags, sdom, ld->id);
510 goto fail;
511 }
513 /* Merge two 16-bit values into a 32-bit combined update. */
514 /* NB. Endianness! */
515 prev_scombo = scombo = ((u32)sdom << 16) | (u32)sflags;
517 /* NB. prev_scombo is updated in place to seen value. */
518 if ( unlikely(cmpxchg_user((u32 *)&e->flags, prev_scombo,
519 prev_scombo | GTF_transfer_committed)) )
520 {
521 DPRINTK("Fault while modifying shared flags and domid.\n");
522 goto fail;
523 }
525 /* Did the combined update work (did we see what we expected?). */
526 if ( likely(prev_scombo == scombo) )
527 break;
529 if ( retries++ == 4 )
530 {
531 DPRINTK("Shared grant entry is unstable.\n");
532 goto fail;
533 }
535 /* Didn't see what we expected. Split out the seen flags & dom. */
536 /* NB. Endianness! */
537 sflags = (u16)prev_scombo;
538 sdom = (u16)(prev_scombo >> 16);
539 }
541 spin_unlock(&t->lock);
542 return 1;
544 fail:
545 spin_unlock(&t->lock);
546 return 0;
547 }
549 void
550 gnttab_notify_transfer(
551 struct domain *rd, grant_ref_t ref, unsigned long frame)
552 {
553 wmb(); /* Ensure that the reassignment is globally visible. */
554 rd->grant_table->shared[ref].frame = frame;
555 }
557 int
558 grant_table_create(
559 struct domain *d)
560 {
561 grant_table_t *t;
562 int i;
564 if ( (t = xmalloc(sizeof(*t))) == NULL )
565 goto no_mem;
567 /* Simple stuff. */
568 memset(t, 0, sizeof(*t));
569 spin_lock_init(&t->lock);
571 /* Active grant table. */
572 if ( (t->active = xmalloc(sizeof(active_grant_entry_t) *
573 NR_GRANT_ENTRIES)) == NULL )
574 goto no_mem;
575 memset(t->active, 0, sizeof(active_grant_entry_t) * NR_GRANT_ENTRIES);
577 if ( (t->maptrack = (void *)alloc_xenheap_page()) == NULL )
578 goto no_mem;
579 memset(t->maptrack, 0, PAGE_SIZE);
580 for ( i = 0; i < NR_MAPTRACK_ENTRIES; i++ )
581 t->maptrack[i].ref_and_flags = (i+1) << MAPTRACK_REF_SHIFT;
583 /* Shared grant table. */
584 if ( (t->shared = (void *)alloc_xenheap_page()) == NULL )
585 goto no_mem;
586 memset(t->shared, 0, PAGE_SIZE);
587 SHARE_PFN_WITH_DOMAIN(virt_to_page(t->shared), d);
589 /* Okay, install the structure. */
590 wmb(); /* avoid races with lock-free access to d->grant_table */
591 d->grant_table = t;
592 return 0;
594 no_mem:
595 if ( t != NULL )
596 {
597 if ( t->active != NULL )
598 xfree(t->active);
599 if ( t->maptrack != NULL )
600 free_xenheap_page((unsigned long)t->maptrack);
601 xfree(t);
602 }
603 return -ENOMEM;
604 }
606 void
607 grant_table_destroy(
608 struct domain *d)
609 {
610 grant_table_t *t;
612 if ( (t = d->grant_table) != NULL )
613 {
614 /* Free memory relating to this grant table. */
615 d->grant_table = NULL;
616 free_xenheap_page((unsigned long)t->shared);
617 free_xenheap_page((unsigned long)t->maptrack);
618 xfree(t->active);
619 xfree(t);
620 }
621 }
623 void
624 grant_table_init(
625 void)
626 {
627 /* Nothing. */
628 }