ia64/xen-unstable

view xen/common/memory.c @ 14727:1ec1affb1b50

xen: No need for shadow_drop_references() in remove_page().

Because:
guest_physmap_remove_page()
-> p2m_remove_page()
-> set_p2m_entry()
-> paging_write_p2m_entry()
-> shadow_write_p2m_entry()

And shadow_write_p2m_entry() calls sh_remove_all_*() fns.

Signed-off-by: Keir Fraser <keir@xensource.com>
author Keir Fraser <keir@xensource.com>
date Wed Apr 04 22:47:20 2007 +0100 (2007-04-04)
parents 2bbd28891160
children 3c28bc13a3f8
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 <xen/hypercall.h>
21 #include <xen/errno.h>
22 #include <asm/current.h>
23 #include <asm/hardirq.h>
24 #include <public/memory.h>
26 struct memop_args {
27 /* INPUT */
28 struct domain *domain; /* Domain to be affected. */
29 XEN_GUEST_HANDLE(xen_pfn_t) extent_list; /* List of extent base addrs. */
30 unsigned int nr_extents; /* Number of extents to allocate or free. */
31 unsigned int extent_order; /* Size of each extent. */
32 unsigned int memflags; /* Allocation flags. */
34 /* INPUT/OUTPUT */
35 unsigned int nr_done; /* Number of extents processed so far. */
36 int preempted; /* Was the hypercall preempted? */
37 };
39 static unsigned int select_local_cpu(struct domain *d)
40 {
41 struct vcpu *v = d->vcpu[0];
42 return (v ? v->processor : 0);
43 }
45 static void increase_reservation(struct memop_args *a)
46 {
47 struct page_info *page;
48 unsigned long i;
49 xen_pfn_t mfn;
50 struct domain *d = a->domain;
51 unsigned int cpu = select_local_cpu(d);
53 if ( !guest_handle_is_null(a->extent_list) &&
54 !guest_handle_okay(a->extent_list, a->nr_extents) )
55 return;
57 if ( (a->extent_order != 0) &&
58 !multipage_allocation_permitted(current->domain) )
59 return;
61 for ( i = a->nr_done; i < a->nr_extents; i++ )
62 {
63 if ( hypercall_preempt_check() )
64 {
65 a->preempted = 1;
66 goto out;
67 }
69 page = __alloc_domheap_pages(d, cpu, a->extent_order, a->memflags);
70 if ( unlikely(page == NULL) )
71 {
72 gdprintk(XENLOG_INFO, "Could not allocate order=%d extent: "
73 "id=%d memflags=%x (%ld of %d)\n",
74 a->extent_order, d->domain_id, a->memflags,
75 i, a->nr_extents);
76 goto out;
77 }
79 /* Inform the domain of the new page's machine address. */
80 if ( !guest_handle_is_null(a->extent_list) )
81 {
82 mfn = page_to_mfn(page);
83 if ( unlikely(__copy_to_guest_offset(a->extent_list, i, &mfn, 1)) )
84 goto out;
85 }
86 }
88 out:
89 a->nr_done = i;
90 }
92 static void populate_physmap(struct memop_args *a)
93 {
94 struct page_info *page;
95 unsigned long i, j;
96 xen_pfn_t gpfn, mfn;
97 struct domain *d = a->domain;
98 unsigned int cpu = select_local_cpu(d);
100 if ( !guest_handle_okay(a->extent_list, a->nr_extents) )
101 return;
103 if ( (a->extent_order != 0) &&
104 !multipage_allocation_permitted(current->domain) )
105 return;
107 for ( i = a->nr_done; i < a->nr_extents; i++ )
108 {
109 if ( hypercall_preempt_check() )
110 {
111 a->preempted = 1;
112 goto out;
113 }
115 if ( unlikely(__copy_from_guest_offset(&gpfn, a->extent_list, i, 1)) )
116 goto out;
118 page = __alloc_domheap_pages(d, cpu, a->extent_order, a->memflags);
119 if ( unlikely(page == NULL) )
120 {
121 gdprintk(XENLOG_INFO, "Could not allocate order=%d extent: "
122 "id=%d memflags=%x (%ld of %d)\n",
123 a->extent_order, d->domain_id, a->memflags,
124 i, a->nr_extents);
125 goto out;
126 }
128 mfn = page_to_mfn(page);
130 if ( unlikely(shadow_mode_translate(d)) )
131 {
132 for ( j = 0; j < (1 << a->extent_order); j++ )
133 guest_physmap_add_page(d, gpfn + j, mfn + j);
134 }
135 else
136 {
137 for ( j = 0; j < (1 << a->extent_order); j++ )
138 set_gpfn_from_mfn(mfn + j, gpfn + j);
140 /* Inform the domain of the new page's machine address. */
141 if ( unlikely(__copy_to_guest_offset(a->extent_list, i, &mfn, 1)) )
142 goto out;
143 }
144 }
146 out:
147 a->nr_done = i;
148 }
150 int guest_remove_page(struct domain *d, unsigned long gmfn)
151 {
152 struct page_info *page;
153 unsigned long mfn;
155 mfn = gmfn_to_mfn(d, gmfn);
156 if ( unlikely(!mfn_valid(mfn)) )
157 {
158 gdprintk(XENLOG_INFO, "Domain %u page number %lx invalid\n",
159 d->domain_id, gmfn);
160 return 0;
161 }
163 page = mfn_to_page(mfn);
164 if ( unlikely(!get_page(page, d)) )
165 {
166 gdprintk(XENLOG_INFO, "Bad page free for domain %u\n", d->domain_id);
167 return 0;
168 }
170 if ( test_and_clear_bit(_PGT_pinned, &page->u.inuse.type_info) )
171 put_page_and_type(page);
173 if ( test_and_clear_bit(_PGC_allocated, &page->count_info) )
174 put_page(page);
176 guest_physmap_remove_page(d, gmfn, mfn);
178 put_page(page);
180 return 1;
181 }
183 static void decrease_reservation(struct memop_args *a)
184 {
185 unsigned long i, j;
186 xen_pfn_t gmfn;
188 if ( !guest_handle_okay(a->extent_list, a->nr_extents) )
189 return;
191 for ( i = a->nr_done; i < a->nr_extents; i++ )
192 {
193 if ( hypercall_preempt_check() )
194 {
195 a->preempted = 1;
196 goto out;
197 }
199 if ( unlikely(__copy_from_guest_offset(&gmfn, a->extent_list, i, 1)) )
200 goto out;
202 for ( j = 0; j < (1 << a->extent_order); j++ )
203 if ( !guest_remove_page(a->domain, gmfn + j) )
204 goto out;
205 }
207 out:
208 a->nr_done = i;
209 }
211 static long translate_gpfn_list(
212 XEN_GUEST_HANDLE(xen_translate_gpfn_list_t) uop, unsigned long *progress)
213 {
214 struct xen_translate_gpfn_list op;
215 unsigned long i;
216 xen_pfn_t gpfn;
217 xen_pfn_t mfn;
218 struct domain *d;
220 if ( copy_from_guest(&op, uop, 1) )
221 return -EFAULT;
223 /* Is size too large for us to encode a continuation? */
224 if ( op.nr_gpfns > (ULONG_MAX >> MEMOP_EXTENT_SHIFT) )
225 return -EINVAL;
227 if ( !guest_handle_okay(op.gpfn_list, op.nr_gpfns) ||
228 !guest_handle_okay(op.mfn_list, op.nr_gpfns) )
229 return -EFAULT;
231 if ( op.domid == DOMID_SELF )
232 op.domid = current->domain->domain_id;
233 else if ( !IS_PRIV(current->domain) )
234 return -EPERM;
236 if ( (d = rcu_lock_domain_by_id(op.domid)) == NULL )
237 return -ESRCH;
239 if ( !shadow_mode_translate(d) )
240 {
241 rcu_unlock_domain(d);
242 return -EINVAL;
243 }
245 for ( i = *progress; i < op.nr_gpfns; i++ )
246 {
247 if ( hypercall_preempt_check() )
248 {
249 rcu_unlock_domain(d);
250 *progress = i;
251 return -EAGAIN;
252 }
254 if ( unlikely(__copy_from_guest_offset(&gpfn, op.gpfn_list, i, 1)) )
255 {
256 rcu_unlock_domain(d);
257 return -EFAULT;
258 }
260 mfn = gmfn_to_mfn(d, gpfn);
262 if ( unlikely(__copy_to_guest_offset(op.mfn_list, i, &mfn, 1)) )
263 {
264 rcu_unlock_domain(d);
265 return -EFAULT;
266 }
267 }
269 rcu_unlock_domain(d);
270 return 0;
271 }
273 static long memory_exchange(XEN_GUEST_HANDLE(xen_memory_exchange_t) arg)
274 {
275 struct xen_memory_exchange exch;
276 LIST_HEAD(in_chunk_list);
277 LIST_HEAD(out_chunk_list);
278 unsigned long in_chunk_order, out_chunk_order;
279 xen_pfn_t gpfn, gmfn, mfn;
280 unsigned long i, j, k;
281 unsigned int memflags = 0, cpu;
282 long rc = 0;
283 struct domain *d;
284 struct page_info *page;
286 if ( copy_from_guest(&exch, arg, 1) )
287 return -EFAULT;
289 /* Various sanity checks. */
290 if ( (exch.nr_exchanged > exch.in.nr_extents) ||
291 /* Input and output domain identifiers match? */
292 (exch.in.domid != exch.out.domid) ||
293 /* Sizes of input and output lists do not overflow a long? */
294 ((~0UL >> exch.in.extent_order) < exch.in.nr_extents) ||
295 ((~0UL >> exch.out.extent_order) < exch.out.nr_extents) ||
296 /* Sizes of input and output lists match? */
297 ((exch.in.nr_extents << exch.in.extent_order) !=
298 (exch.out.nr_extents << exch.out.extent_order)) )
299 {
300 rc = -EINVAL;
301 goto fail_early;
302 }
304 /* Only privileged guests can allocate multi-page contiguous extents. */
305 if ( ((exch.in.extent_order != 0) || (exch.out.extent_order != 0)) &&
306 !multipage_allocation_permitted(current->domain) )
307 {
308 rc = -EPERM;
309 goto fail_early;
310 }
312 if ( (exch.out.address_bits != 0) &&
313 (exch.out.address_bits <
314 (get_order_from_pages(max_page) + PAGE_SHIFT)) )
315 {
316 if ( exch.out.address_bits <= PAGE_SHIFT )
317 {
318 rc = -ENOMEM;
319 goto fail_early;
320 }
321 memflags = MEMF_bits(exch.out.address_bits);
322 }
324 if ( exch.in.extent_order <= exch.out.extent_order )
325 {
326 in_chunk_order = exch.out.extent_order - exch.in.extent_order;
327 out_chunk_order = 0;
328 }
329 else
330 {
331 in_chunk_order = 0;
332 out_chunk_order = exch.in.extent_order - exch.out.extent_order;
333 }
335 /*
336 * Only support exchange on calling domain right now. Otherwise there are
337 * tricky corner cases to consider (e.g., dying domain).
338 */
339 if ( unlikely(exch.in.domid != DOMID_SELF) )
340 {
341 rc = IS_PRIV(current->domain) ? -EINVAL : -EPERM;
342 goto fail_early;
343 }
344 d = current->domain;
346 cpu = select_local_cpu(d);
348 for ( i = (exch.nr_exchanged >> in_chunk_order);
349 i < (exch.in.nr_extents >> in_chunk_order);
350 i++ )
351 {
352 if ( hypercall_preempt_check() )
353 {
354 exch.nr_exchanged = i << in_chunk_order;
355 if ( copy_field_to_guest(arg, &exch, nr_exchanged) )
356 return -EFAULT;
357 return hypercall_create_continuation(
358 __HYPERVISOR_memory_op, "lh", XENMEM_exchange, arg);
359 }
361 /* Steal a chunk's worth of input pages from the domain. */
362 for ( j = 0; j < (1UL << in_chunk_order); j++ )
363 {
364 if ( unlikely(__copy_from_guest_offset(
365 &gmfn, exch.in.extent_start, (i<<in_chunk_order)+j, 1)) )
366 {
367 rc = -EFAULT;
368 goto fail;
369 }
371 for ( k = 0; k < (1UL << exch.in.extent_order); k++ )
372 {
373 mfn = gmfn_to_mfn(d, gmfn + k);
374 if ( unlikely(!mfn_valid(mfn)) )
375 {
376 rc = -EINVAL;
377 goto fail;
378 }
380 page = mfn_to_page(mfn);
382 if ( unlikely(steal_page(d, page, MEMF_no_refcount)) )
383 {
384 rc = -EINVAL;
385 goto fail;
386 }
388 list_add(&page->list, &in_chunk_list);
389 }
390 }
392 /* Allocate a chunk's worth of anonymous output pages. */
393 for ( j = 0; j < (1UL << out_chunk_order); j++ )
394 {
395 page = __alloc_domheap_pages(
396 NULL, cpu, exch.out.extent_order, memflags);
397 if ( unlikely(page == NULL) )
398 {
399 rc = -ENOMEM;
400 goto fail;
401 }
403 list_add(&page->list, &out_chunk_list);
404 }
406 /*
407 * Success! Beyond this point we cannot fail for this chunk.
408 */
410 /* Destroy final reference to each input page. */
411 while ( !list_empty(&in_chunk_list) )
412 {
413 page = list_entry(in_chunk_list.next, struct page_info, list);
414 list_del(&page->list);
415 if ( !test_and_clear_bit(_PGC_allocated, &page->count_info) )
416 BUG();
417 mfn = page_to_mfn(page);
418 guest_physmap_remove_page(d, mfn_to_gmfn(d, mfn), mfn);
419 put_page(page);
420 }
422 /* Assign each output page to the domain. */
423 j = 0;
424 while ( !list_empty(&out_chunk_list) )
425 {
426 page = list_entry(out_chunk_list.next, struct page_info, list);
427 list_del(&page->list);
428 if ( assign_pages(d, page, exch.out.extent_order,
429 MEMF_no_refcount) )
430 BUG();
432 /* Note that we ignore errors accessing the output extent list. */
433 (void)__copy_from_guest_offset(
434 &gpfn, exch.out.extent_start, (i<<out_chunk_order)+j, 1);
436 mfn = page_to_mfn(page);
437 if ( unlikely(shadow_mode_translate(d)) )
438 {
439 for ( k = 0; k < (1UL << exch.out.extent_order); k++ )
440 guest_physmap_add_page(d, gpfn + k, mfn + k);
441 }
442 else
443 {
444 for ( k = 0; k < (1UL << exch.out.extent_order); k++ )
445 set_gpfn_from_mfn(mfn + k, gpfn + k);
446 (void)__copy_to_guest_offset(
447 exch.out.extent_start, (i<<out_chunk_order)+j, &mfn, 1);
448 }
450 j++;
451 }
452 BUG_ON(j != (1UL << out_chunk_order));
453 }
455 exch.nr_exchanged = exch.in.nr_extents;
456 if ( copy_field_to_guest(arg, &exch, nr_exchanged) )
457 rc = -EFAULT;
458 return rc;
460 /*
461 * Failed a chunk! Free any partial chunk work. Tell caller how many
462 * chunks succeeded.
463 */
464 fail:
465 /* Reassign any input pages we managed to steal. */
466 while ( !list_empty(&in_chunk_list) )
467 {
468 page = list_entry(in_chunk_list.next, struct page_info, list);
469 list_del(&page->list);
470 if ( assign_pages(d, page, 0, MEMF_no_refcount) )
471 BUG();
472 }
474 /* Free any output pages we managed to allocate. */
475 while ( !list_empty(&out_chunk_list) )
476 {
477 page = list_entry(out_chunk_list.next, struct page_info, list);
478 list_del(&page->list);
479 free_domheap_pages(page, exch.out.extent_order);
480 }
482 exch.nr_exchanged = i << in_chunk_order;
484 fail_early:
485 if ( copy_field_to_guest(arg, &exch, nr_exchanged) )
486 rc = -EFAULT;
487 return rc;
488 }
490 long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE(void) arg)
491 {
492 struct domain *d;
493 int rc, op;
494 unsigned long start_extent, progress;
495 struct xen_memory_reservation reservation;
496 struct memop_args args;
497 domid_t domid;
499 op = cmd & MEMOP_CMD_MASK;
501 switch ( op )
502 {
503 case XENMEM_increase_reservation:
504 case XENMEM_decrease_reservation:
505 case XENMEM_populate_physmap:
506 start_extent = cmd >> MEMOP_EXTENT_SHIFT;
508 if ( copy_from_guest(&reservation, arg, 1) )
509 return start_extent;
511 /* Is size too large for us to encode a continuation? */
512 if ( reservation.nr_extents > (ULONG_MAX >> MEMOP_EXTENT_SHIFT) )
513 return start_extent;
515 if ( unlikely(start_extent > reservation.nr_extents) )
516 return start_extent;
518 args.extent_list = reservation.extent_start;
519 args.nr_extents = reservation.nr_extents;
520 args.extent_order = reservation.extent_order;
521 args.nr_done = start_extent;
522 args.preempted = 0;
523 args.memflags = 0;
525 if ( (reservation.address_bits != 0) &&
526 (reservation.address_bits <
527 (get_order_from_pages(max_page) + PAGE_SHIFT)) )
528 {
529 if ( reservation.address_bits <= PAGE_SHIFT )
530 return start_extent;
531 args.memflags = MEMF_bits(reservation.address_bits);
532 }
534 if ( likely(reservation.domid == DOMID_SELF) )
535 d = current->domain;
536 else if ( !IS_PRIV(current->domain) ||
537 ((d = rcu_lock_domain_by_id(reservation.domid)) == NULL) )
538 return start_extent;
539 args.domain = d;
541 switch ( op )
542 {
543 case XENMEM_increase_reservation:
544 increase_reservation(&args);
545 break;
546 case XENMEM_decrease_reservation:
547 decrease_reservation(&args);
548 break;
549 default: /* XENMEM_populate_physmap */
550 populate_physmap(&args);
551 break;
552 }
554 if ( unlikely(reservation.domid != DOMID_SELF) )
555 rcu_unlock_domain(d);
557 rc = args.nr_done;
559 if ( args.preempted )
560 return hypercall_create_continuation(
561 __HYPERVISOR_memory_op, "lh",
562 op | (rc << MEMOP_EXTENT_SHIFT), arg);
564 break;
566 case XENMEM_exchange:
567 rc = memory_exchange(guest_handle_cast(arg, xen_memory_exchange_t));
568 break;
570 case XENMEM_maximum_ram_page:
571 rc = max_page;
572 break;
574 case XENMEM_current_reservation:
575 case XENMEM_maximum_reservation:
576 case XENMEM_maximum_gpfn:
577 if ( copy_from_guest(&domid, arg, 1) )
578 return -EFAULT;
580 if ( likely(domid == DOMID_SELF) )
581 d = current->domain;
582 else if ( !IS_PRIV(current->domain) )
583 return -EPERM;
584 else if ( (d = rcu_lock_domain_by_id(domid)) == NULL )
585 return -ESRCH;
587 switch ( op )
588 {
589 case XENMEM_current_reservation:
590 rc = d->tot_pages;
591 break;
592 case XENMEM_maximum_reservation:
593 rc = d->max_pages;
594 break;
595 default:
596 ASSERT(op == XENMEM_maximum_gpfn);
597 rc = domain_get_maximum_gpfn(d);
598 break;
599 }
601 if ( unlikely(domid != DOMID_SELF) )
602 rcu_unlock_domain(d);
604 break;
606 case XENMEM_translate_gpfn_list:
607 progress = cmd >> MEMOP_EXTENT_SHIFT;
608 rc = translate_gpfn_list(
609 guest_handle_cast(arg, xen_translate_gpfn_list_t),
610 &progress);
611 if ( rc == -EAGAIN )
612 return hypercall_create_continuation(
613 __HYPERVISOR_memory_op, "lh",
614 op | (progress << MEMOP_EXTENT_SHIFT), arg);
615 break;
617 default:
618 rc = arch_memory_op(op, arg);
619 break;
620 }
622 return rc;
623 }
625 /*
626 * Local variables:
627 * mode: C
628 * c-set-style: "BSD"
629 * c-basic-offset: 4
630 * tab-width: 4
631 * indent-tabs-mode: nil
632 * End:
633 */