ia64/xen-unstable

annotate xen/common/memory.c @ 10584:4260eb8c0874

kunmap_atomic() must zap the PTE to avoid dangling references
when attempting to free memory back to Xen. We can implement
something more efficient in future.

Also add debug print message if guest tries to free 'in use'
memory. We'll make it a real guest-visible error in future.

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