direct-io.hg

view xen/common/memory.c @ 10427:716e365377f5

[XEN] Use correct xen_pfn_t type in implementation of XENMEM_exchange.
Avoids compile errors on ppc.
Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com>
author kaf24@firebug.cl.cam.ac.uk
date Sat Jun 17 08:31:53 2006 +0100 (2006-06-17)
parents ee3d10828937
children 4260eb8c0874
line source
1 /******************************************************************************
2 * memory.c
3 *
4 * Code to handle memory-related requests.
5 *
6 * Copyright (c) 2003-2004, B Dragovic
7 * Copyright (c) 2003-2005, K A Fraser
8 */
10 #include <xen/config.h>
11 #include <xen/types.h>
12 #include <xen/lib.h>
13 #include <xen/mm.h>
14 #include <xen/perfc.h>
15 #include <xen/sched.h>
16 #include <xen/event.h>
17 #include <xen/shadow.h>
18 #include <xen/iocap.h>
19 #include <xen/guest_access.h>
20 #include <asm/current.h>
21 #include <asm/hardirq.h>
22 #include <public/memory.h>
24 /*
25 * To allow safe resume of do_memory_op() after preemption, we need to know
26 * at what point in the page list to resume. For this purpose I steal the
27 * high-order bits of the @cmd parameter, which are otherwise unused and zero.
28 */
29 #define START_EXTENT_SHIFT 4 /* cmd[:4] == start_extent */
31 static long
32 increase_reservation(
33 struct domain *d,
34 XEN_GUEST_HANDLE(xen_pfn_t) extent_list,
35 unsigned int nr_extents,
36 unsigned int extent_order,
37 unsigned int memflags,
38 int *preempted)
39 {
40 struct page_info *page;
41 unsigned long i;
42 xen_pfn_t mfn;
44 if ( !guest_handle_is_null(extent_list) &&
45 !guest_handle_okay(extent_list, nr_extents) )
46 return 0;
48 if ( (extent_order != 0) &&
49 !multipage_allocation_permitted(current->domain) )
50 return 0;
52 for ( i = 0; i < nr_extents; i++ )
53 {
54 if ( hypercall_preempt_check() )
55 {
56 *preempted = 1;
57 return i;
58 }
60 if ( unlikely((page = alloc_domheap_pages(
61 d, extent_order, memflags)) == NULL) )
62 {
63 DPRINTK("Could not allocate order=%d extent: "
64 "id=%d memflags=%x (%ld of %d)\n",
65 extent_order, d->domain_id, memflags, i, nr_extents);
66 return i;
67 }
69 /* Inform the domain of the new page's machine address. */
70 if ( !guest_handle_is_null(extent_list) )
71 {
72 mfn = page_to_mfn(page);
73 if ( unlikely(__copy_to_guest_offset(extent_list, i, &mfn, 1)) )
74 return i;
75 }
76 }
78 return nr_extents;
79 }
81 static long
82 populate_physmap(
83 struct domain *d,
84 XEN_GUEST_HANDLE(xen_pfn_t) extent_list,
85 unsigned int nr_extents,
86 unsigned int extent_order,
87 unsigned int memflags,
88 int *preempted)
89 {
90 struct page_info *page;
91 unsigned long i, j;
92 xen_pfn_t gpfn;
93 xen_pfn_t mfn;
95 if ( !guest_handle_okay(extent_list, nr_extents) )
96 return 0;
98 if ( (extent_order != 0) &&
99 !multipage_allocation_permitted(current->domain) )
100 return 0;
102 for ( i = 0; i < nr_extents; i++ )
103 {
104 if ( hypercall_preempt_check() )
105 {
106 *preempted = 1;
107 goto out;
108 }
110 if ( unlikely(__copy_from_guest_offset(&gpfn, extent_list, i, 1)) )
111 goto out;
113 if ( unlikely((page = alloc_domheap_pages(
114 d, extent_order, memflags)) == NULL) )
115 {
116 DPRINTK("Could not allocate order=%d extent: "
117 "id=%d memflags=%x (%ld of %d)\n",
118 extent_order, d->domain_id, memflags, i, nr_extents);
119 goto out;
120 }
122 mfn = page_to_mfn(page);
124 if ( unlikely(shadow_mode_translate(d)) )
125 {
126 for ( j = 0; j < (1 << extent_order); j++ )
127 guest_physmap_add_page(d, gpfn + j, mfn + j);
128 }
129 else
130 {
131 for ( j = 0; j < (1 << extent_order); j++ )
132 set_gpfn_from_mfn(mfn + j, gpfn + j);
134 /* Inform the domain of the new page's machine address. */
135 if ( unlikely(__copy_to_guest_offset(extent_list, i, &mfn, 1)) )
136 goto out;
137 }
138 }
140 out:
141 return i;
142 }
144 int
145 guest_remove_page(
146 struct domain *d,
147 unsigned long gmfn)
148 {
149 struct page_info *page;
150 unsigned long mfn;
152 mfn = gmfn_to_mfn(d, gmfn);
153 if ( unlikely(!mfn_valid(mfn)) )
154 {
155 DPRINTK("Domain %u page number %lx invalid\n",
156 d->domain_id, mfn);
157 return 0;
158 }
160 page = mfn_to_page(mfn);
161 if ( unlikely(!get_page(page, d)) )
162 {
163 DPRINTK("Bad page free for domain %u\n", d->domain_id);
164 return 0;
165 }
167 if ( test_and_clear_bit(_PGT_pinned, &page->u.inuse.type_info) )
168 put_page_and_type(page);
170 if ( test_and_clear_bit(_PGC_allocated, &page->count_info) )
171 put_page(page);
173 guest_physmap_remove_page(d, gmfn, mfn);
175 put_page(page);
177 return 1;
178 }
180 static long
181 decrease_reservation(
182 struct domain *d,
183 XEN_GUEST_HANDLE(xen_pfn_t) extent_list,
184 unsigned int nr_extents,
185 unsigned int extent_order,
186 int *preempted)
187 {
188 unsigned long i, j;
189 xen_pfn_t gmfn;
191 if ( !guest_handle_okay(extent_list, nr_extents) )
192 return 0;
194 for ( i = 0; i < nr_extents; i++ )
195 {
196 if ( hypercall_preempt_check() )
197 {
198 *preempted = 1;
199 return i;
200 }
202 if ( unlikely(__copy_from_guest_offset(&gmfn, extent_list, i, 1)) )
203 return i;
205 for ( j = 0; j < (1 << extent_order); j++ )
206 {
207 if ( !guest_remove_page(d, gmfn + j) )
208 return i;
209 }
210 }
212 return nr_extents;
213 }
215 static long
216 translate_gpfn_list(
217 XEN_GUEST_HANDLE(xen_translate_gpfn_list_t) uop, unsigned long *progress)
218 {
219 struct xen_translate_gpfn_list op;
220 unsigned long i;
221 xen_pfn_t gpfn;
222 xen_pfn_t mfn;
223 struct domain *d;
225 if ( copy_from_guest(&op, uop, 1) )
226 return -EFAULT;
228 /* Is size too large for us to encode a continuation? */
229 if ( op.nr_gpfns > (ULONG_MAX >> START_EXTENT_SHIFT) )
230 return -EINVAL;
232 if ( !guest_handle_okay(op.gpfn_list, op.nr_gpfns) ||
233 !guest_handle_okay(op.mfn_list, op.nr_gpfns) )
234 return -EFAULT;
236 if ( op.domid == DOMID_SELF )
237 op.domid = current->domain->domain_id;
238 else if ( !IS_PRIV(current->domain) )
239 return -EPERM;
241 if ( (d = find_domain_by_id(op.domid)) == NULL )
242 return -ESRCH;
244 if ( !shadow_mode_translate(d) )
245 {
246 put_domain(d);
247 return -EINVAL;
248 }
250 for ( i = *progress; i < op.nr_gpfns; i++ )
251 {
252 if ( hypercall_preempt_check() )
253 {
254 put_domain(d);
255 *progress = i;
256 return -EAGAIN;
257 }
259 if ( unlikely(__copy_from_guest_offset(&gpfn, op.gpfn_list, i, 1)) )
260 {
261 put_domain(d);
262 return -EFAULT;
263 }
265 mfn = gmfn_to_mfn(d, gpfn);
267 if ( unlikely(__copy_to_guest_offset(op.mfn_list, i, &mfn, 1)) )
268 {
269 put_domain(d);
270 return -EFAULT;
271 }
272 }
274 put_domain(d);
275 return 0;
276 }
278 static long
279 memory_exchange(XEN_GUEST_HANDLE(xen_memory_exchange_t) arg)
280 {
281 struct xen_memory_exchange exch;
282 LIST_HEAD(in_chunk_list);
283 LIST_HEAD(out_chunk_list);
284 unsigned long in_chunk_order, out_chunk_order;
285 xen_pfn_t gpfn, gmfn, mfn;
286 unsigned long i, j, k;
287 unsigned int memflags = 0;
288 long rc = 0;
289 struct domain *d;
290 struct page_info *page;
292 if ( copy_from_guest(&exch, arg, 1) )
293 return -EFAULT;
295 /* Various sanity checks. */
296 if ( (exch.nr_exchanged > exch.in.nr_extents) ||
297 /* Input and output domain identifiers match? */
298 (exch.in.domid != exch.out.domid) ||
299 /* Sizes of input and output lists do not overflow a long? */
300 ((~0UL >> exch.in.extent_order) < exch.in.nr_extents) ||
301 ((~0UL >> exch.out.extent_order) < exch.out.nr_extents) ||
302 /* Sizes of input and output lists match? */
303 ((exch.in.nr_extents << exch.in.extent_order) !=
304 (exch.out.nr_extents << exch.out.extent_order)) )
305 {
306 rc = -EINVAL;
307 goto fail_early;
308 }
310 /* Only privileged guests can allocate multi-page contiguous extents. */
311 if ( ((exch.in.extent_order != 0) || (exch.out.extent_order != 0)) &&
312 !multipage_allocation_permitted(current->domain) )
313 {
314 rc = -EPERM;
315 goto fail_early;
316 }
318 if ( (exch.out.address_bits != 0) &&
319 (exch.out.address_bits <
320 (get_order_from_pages(max_page) + PAGE_SHIFT)) )
321 {
322 if ( exch.out.address_bits < 31 )
323 {
324 rc = -ENOMEM;
325 goto fail_early;
326 }
327 memflags = MEMF_dma;
328 }
330 guest_handle_add_offset(exch.in.extent_start, exch.nr_exchanged);
331 exch.in.nr_extents -= exch.nr_exchanged;
333 if ( exch.in.extent_order <= exch.out.extent_order )
334 {
335 in_chunk_order = exch.out.extent_order - exch.in.extent_order;
336 out_chunk_order = 0;
337 guest_handle_add_offset(
338 exch.out.extent_start, exch.nr_exchanged >> in_chunk_order);
339 exch.out.nr_extents -= exch.nr_exchanged >> in_chunk_order;
340 }
341 else
342 {
343 in_chunk_order = 0;
344 out_chunk_order = exch.in.extent_order - exch.out.extent_order;
345 guest_handle_add_offset(
346 exch.out.extent_start, exch.nr_exchanged << out_chunk_order);
347 exch.out.nr_extents -= exch.nr_exchanged << out_chunk_order;
348 }
350 /*
351 * Only support exchange on calling domain right now. Otherwise there are
352 * tricky corner cases to consider (e.g., DOMF_dying domain).
353 */
354 if ( unlikely(exch.in.domid != DOMID_SELF) )
355 {
356 rc = IS_PRIV(current->domain) ? -EINVAL : -EPERM;
357 goto fail_early;
358 }
359 d = current->domain;
361 for ( i = 0; i < (exch.in.nr_extents >> in_chunk_order); i++ )
362 {
363 if ( hypercall_preempt_check() )
364 {
365 exch.nr_exchanged += i << in_chunk_order;
366 if ( copy_field_to_guest(arg, &exch, nr_exchanged) )
367 return -EFAULT;
368 return hypercall_create_continuation(
369 __HYPERVISOR_memory_op, "lh", XENMEM_exchange, arg);
370 }
372 /* Steal a chunk's worth of input pages from the domain. */
373 for ( j = 0; j < (1UL << in_chunk_order); j++ )
374 {
375 if ( unlikely(__copy_from_guest_offset(
376 &gmfn, exch.in.extent_start, (i<<in_chunk_order)+j, 1)) )
377 {
378 rc = -EFAULT;
379 goto fail;
380 }
382 for ( k = 0; k < (1UL << exch.in.extent_order); k++ )
383 {
384 mfn = gmfn_to_mfn(d, gmfn + k);
385 if ( unlikely(!mfn_valid(mfn)) )
386 {
387 rc = -EINVAL;
388 goto fail;
389 }
391 page = mfn_to_page(mfn);
393 if ( unlikely(steal_page(d, page, MEMF_no_refcount)) )
394 {
395 rc = -EINVAL;
396 goto fail;
397 }
399 list_add(&page->list, &in_chunk_list);
400 }
401 }
403 /* Allocate a chunk's worth of anonymous output pages. */
404 for ( j = 0; j < (1UL << out_chunk_order); j++ )
405 {
406 page = alloc_domheap_pages(
407 NULL, exch.out.extent_order, memflags);
408 if ( unlikely(page == NULL) )
409 {
410 rc = -ENOMEM;
411 goto fail;
412 }
414 list_add(&page->list, &out_chunk_list);
415 }
417 /*
418 * Success! Beyond this point we cannot fail for this chunk.
419 */
421 /* Destroy final reference to each input page. */
422 while ( !list_empty(&in_chunk_list) )
423 {
424 page = list_entry(in_chunk_list.next, struct page_info, list);
425 list_del(&page->list);
426 if ( !test_and_clear_bit(_PGC_allocated, &page->count_info) )
427 BUG();
428 mfn = page_to_mfn(page);
429 guest_physmap_remove_page(d, mfn_to_gmfn(d, mfn), mfn);
430 put_page(page);
431 }
433 /* Assign each output page to the domain. */
434 j = 0;
435 while ( !list_empty(&out_chunk_list) )
436 {
437 page = list_entry(out_chunk_list.next, struct page_info, list);
438 list_del(&page->list);
439 if ( assign_pages(d, page, exch.out.extent_order,
440 MEMF_no_refcount) )
441 BUG();
443 /* Note that we ignore errors accessing the output extent list. */
444 (void)__copy_from_guest_offset(
445 &gpfn, exch.out.extent_start, (i<<out_chunk_order)+j, 1);
447 mfn = page_to_mfn(page);
448 if ( unlikely(shadow_mode_translate(d)) )
449 {
450 for ( k = 0; k < (1UL << exch.out.extent_order); k++ )
451 guest_physmap_add_page(d, gpfn + k, mfn + k);
452 }
453 else
454 {
455 for ( k = 0; k < (1UL << exch.out.extent_order); k++ )
456 set_gpfn_from_mfn(mfn + k, gpfn + k);
457 (void)__copy_to_guest_offset(
458 exch.out.extent_start, (i<<out_chunk_order)+j, &mfn, 1);
459 }
461 j++;
462 }
463 BUG_ON(j != (1UL << out_chunk_order));
464 }
466 exch.nr_exchanged += exch.in.nr_extents;
467 if ( copy_field_to_guest(arg, &exch, nr_exchanged) )
468 rc = -EFAULT;
469 return rc;
471 /*
472 * Failed a chunk! Free any partial chunk work. Tell caller how many
473 * chunks succeeded.
474 */
475 fail:
476 /* Reassign any input pages we managed to steal. */
477 while ( !list_empty(&in_chunk_list) )
478 {
479 page = list_entry(in_chunk_list.next, struct page_info, list);
480 list_del(&page->list);
481 if ( assign_pages(d, page, 0, MEMF_no_refcount) )
482 BUG();
483 }
485 /* Free any output pages we managed to allocate. */
486 while ( !list_empty(&out_chunk_list) )
487 {
488 page = list_entry(out_chunk_list.next, struct page_info, list);
489 list_del(&page->list);
490 free_domheap_pages(page, exch.out.extent_order);
491 }
493 exch.nr_exchanged += i << in_chunk_order;
495 fail_early:
496 if ( copy_field_to_guest(arg, &exch, nr_exchanged) )
497 rc = -EFAULT;
498 return rc;
499 }
501 long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE(void) arg)
502 {
503 struct domain *d;
504 int rc, op, preempted = 0;
505 unsigned int memflags = 0;
506 unsigned long start_extent, progress;
507 struct xen_memory_reservation reservation;
508 domid_t domid;
510 op = cmd & ((1 << START_EXTENT_SHIFT) - 1);
512 switch ( op )
513 {
514 case XENMEM_increase_reservation:
515 case XENMEM_decrease_reservation:
516 case XENMEM_populate_physmap:
517 start_extent = cmd >> START_EXTENT_SHIFT;
519 if ( copy_from_guest(&reservation, arg, 1) )
520 return start_extent;
522 /* Is size too large for us to encode a continuation? */
523 if ( reservation.nr_extents > (ULONG_MAX >> START_EXTENT_SHIFT) )
524 return start_extent;
526 if ( unlikely(start_extent > reservation.nr_extents) )
527 return start_extent;
529 if ( !guest_handle_is_null(reservation.extent_start) )
530 guest_handle_add_offset(reservation.extent_start, start_extent);
531 reservation.nr_extents -= start_extent;
533 if ( (reservation.address_bits != 0) &&
534 (reservation.address_bits <
535 (get_order_from_pages(max_page) + PAGE_SHIFT)) )
536 {
537 if ( reservation.address_bits < 31 )
538 return start_extent;
539 memflags = MEMF_dma;
540 }
542 if ( likely(reservation.domid == DOMID_SELF) )
543 d = current->domain;
544 else if ( !IS_PRIV(current->domain) ||
545 ((d = find_domain_by_id(reservation.domid)) == NULL) )
546 return start_extent;
548 switch ( op )
549 {
550 case XENMEM_increase_reservation:
551 rc = increase_reservation(
552 d,
553 reservation.extent_start,
554 reservation.nr_extents,
555 reservation.extent_order,
556 memflags,
557 &preempted);
558 break;
559 case XENMEM_decrease_reservation:
560 rc = decrease_reservation(
561 d,
562 reservation.extent_start,
563 reservation.nr_extents,
564 reservation.extent_order,
565 &preempted);
566 break;
567 case XENMEM_populate_physmap:
568 default:
569 rc = populate_physmap(
570 d,
571 reservation.extent_start,
572 reservation.nr_extents,
573 reservation.extent_order,
574 memflags,
575 &preempted);
576 break;
577 }
579 if ( unlikely(reservation.domid != DOMID_SELF) )
580 put_domain(d);
582 rc += start_extent;
584 if ( preempted )
585 return hypercall_create_continuation(
586 __HYPERVISOR_memory_op, "lh",
587 op | (rc << START_EXTENT_SHIFT), arg);
589 break;
591 case XENMEM_exchange:
592 rc = memory_exchange(guest_handle_cast(arg, xen_memory_exchange_t));
593 break;
595 case XENMEM_maximum_ram_page:
596 rc = max_page;
597 break;
599 case XENMEM_current_reservation:
600 case XENMEM_maximum_reservation:
601 if ( copy_from_guest(&domid, arg, 1) )
602 return -EFAULT;
604 if ( likely(domid == DOMID_SELF) )
605 d = current->domain;
606 else if ( !IS_PRIV(current->domain) )
607 return -EPERM;
608 else if ( (d = find_domain_by_id(domid)) == NULL )
609 return -ESRCH;
611 rc = (op == XENMEM_current_reservation) ? d->tot_pages : d->max_pages;
613 if ( unlikely(domid != DOMID_SELF) )
614 put_domain(d);
616 break;
618 case XENMEM_translate_gpfn_list:
619 progress = cmd >> START_EXTENT_SHIFT;
620 rc = translate_gpfn_list(
621 guest_handle_cast(arg, xen_translate_gpfn_list_t),
622 &progress);
623 if ( rc == -EAGAIN )
624 return hypercall_create_continuation(
625 __HYPERVISOR_memory_op, "lh",
626 op | (progress << START_EXTENT_SHIFT), arg);
627 break;
629 default:
630 rc = arch_memory_op(op, arg);
631 break;
632 }
634 return rc;
635 }
637 /*
638 * Local variables:
639 * mode: C
640 * c-set-style: "BSD"
641 * c-basic-offset: 4
642 * tab-width: 4
643 * indent-tabs-mode: nil
644 * End:
645 */