ia64/xen-unstable

annotate xen/common/memory.c @ 10314:b3d901ba705d

Represent PFNs with their own type, rather than 'unsigned long'.
('long' changes size and alignment between 32- and 64-bit ABIs.)
Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com>
author kaf24@firebug.cl.cam.ac.uk
date Tue Jun 06 09:48:17 2006 +0100 (2006-06-06)
parents 4e1b8be54311
children ee3d10828937
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,
kaf24@6607 37 unsigned int flags,
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(
kaf24@6486 61 d, extent_order, flags)) == NULL) )
kaf24@6486 62 {
kaf24@6752 63 DPRINTK("Could not allocate order=%d extent: "
kaf24@8673 64 "id=%d flags=%x (%ld of %d)\n",
kaf24@6752 65 extent_order, d->domain_id, flags, 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,
kaf24@9068 87 unsigned int flags,
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(
kaf24@8673 114 d, extent_order, flags)) == NULL) )
kaf24@8673 115 {
kaf24@8673 116 DPRINTK("Could not allocate order=%d extent: "
kaf24@8673 117 "id=%d flags=%x (%ld of %d)\n",
kaf24@8673 118 extent_order, d->domain_id, flags, 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
cl349@9211 173 guest_physmap_remove_page(d, gmfn, mfn);
cl349@9211 174
cl349@9211 175 put_page(page);
cl349@9211 176
cl349@9211 177 return 1;
cl349@9211 178 }
cl349@9211 179
kaf24@8673 180 static long
kaf24@6486 181 decrease_reservation(
kaf24@9068 182 struct domain *d,
kaf24@10314 183 XEN_GUEST_HANDLE(xen_pfn_t) extent_list,
kaf24@6486 184 unsigned int nr_extents,
kaf24@6486 185 unsigned int extent_order,
kaf24@6607 186 unsigned int flags,
kaf24@6607 187 int *preempted)
kaf24@6486 188 {
kaf24@10314 189 unsigned long i, j;
kaf24@10314 190 xen_pfn_t gmfn;
kaf24@6486 191
kaf24@9068 192 if ( !guest_handle_okay(extent_list, nr_extents) )
kaf24@6486 193 return 0;
kaf24@6486 194
kaf24@6486 195 for ( i = 0; i < nr_extents; i++ )
kaf24@6486 196 {
kaf24@6486 197 if ( hypercall_preempt_check() )
kaf24@6607 198 {
kaf24@6607 199 *preempted = 1;
kaf24@6486 200 return i;
kaf24@6607 201 }
kaf24@6486 202
kaf24@9068 203 if ( unlikely(__copy_from_guest_offset(&gmfn, extent_list, i, 1)) )
kaf24@6486 204 return i;
kaf24@6486 205
kaf24@6486 206 for ( j = 0; j < (1 << extent_order); j++ )
kaf24@6486 207 {
cl349@9211 208 if ( !guest_remove_page(d, gmfn + j) )
kaf24@6486 209 return i;
kaf24@6486 210 }
kaf24@6486 211 }
kaf24@6486 212
kaf24@6486 213 return nr_extents;
kaf24@6486 214 }
kaf24@6486 215
kaf24@8871 216 static long
kaf24@8871 217 translate_gpfn_list(
kaf24@9873 218 XEN_GUEST_HANDLE(xen_translate_gpfn_list_t) uop, unsigned long *progress)
kaf24@8871 219 {
kaf24@8871 220 struct xen_translate_gpfn_list op;
kaf24@10314 221 unsigned long i;
kaf24@10314 222 xen_pfn_t gpfn;
kaf24@10314 223 xen_pfn_t mfn;
kaf24@8871 224 struct domain *d;
kaf24@6486 225
kaf24@9068 226 if ( copy_from_guest(&op, uop, 1) )
kaf24@8871 227 return -EFAULT;
kaf24@8871 228
kaf24@8871 229 /* Is size too large for us to encode a continuation? */
kaf24@8871 230 if ( op.nr_gpfns > (ULONG_MAX >> START_EXTENT_SHIFT) )
kaf24@8871 231 return -EINVAL;
kaf24@8871 232
kaf24@9068 233 if ( !guest_handle_okay(op.gpfn_list, op.nr_gpfns) ||
kaf24@9068 234 !guest_handle_okay(op.mfn_list, op.nr_gpfns) )
kaf24@8871 235 return -EFAULT;
kaf24@8871 236
kaf24@8871 237 if ( op.domid == DOMID_SELF )
kaf24@8871 238 op.domid = current->domain->domain_id;
kaf24@8871 239 else if ( !IS_PRIV(current->domain) )
kaf24@8871 240 return -EPERM;
kaf24@8871 241
kaf24@8871 242 if ( (d = find_domain_by_id(op.domid)) == NULL )
kaf24@8871 243 return -ESRCH;
kaf24@8871 244
kaf24@8871 245 if ( !shadow_mode_translate(d) )
kaf24@8871 246 {
kaf24@8871 247 put_domain(d);
kaf24@8871 248 return -EINVAL;
kaf24@8871 249 }
kaf24@8871 250
kaf24@8871 251 for ( i = *progress; i < op.nr_gpfns; i++ )
kaf24@8871 252 {
kaf24@8871 253 if ( hypercall_preempt_check() )
kaf24@8871 254 {
kaf24@8871 255 put_domain(d);
kaf24@8871 256 *progress = i;
kaf24@8871 257 return -EAGAIN;
kaf24@8871 258 }
kaf24@8871 259
kaf24@9068 260 if ( unlikely(__copy_from_guest_offset(&gpfn, op.gpfn_list, i, 1)) )
kaf24@8871 261 {
kaf24@8871 262 put_domain(d);
kaf24@8871 263 return -EFAULT;
kaf24@8871 264 }
kaf24@8871 265
kaf24@8871 266 mfn = gmfn_to_mfn(d, gpfn);
kaf24@8871 267
kaf24@9068 268 if ( unlikely(__copy_to_guest_offset(op.mfn_list, i, &mfn, 1)) )
kaf24@8871 269 {
kaf24@8871 270 put_domain(d);
kaf24@8871 271 return -EFAULT;
kaf24@8871 272 }
kaf24@8871 273 }
kaf24@8871 274
kaf24@8871 275 put_domain(d);
kaf24@8871 276 return 0;
kaf24@8871 277 }
kaf24@8871 278
kaf24@9873 279 long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE(void) arg)
kaf24@6486 280 {
kaf24@6486 281 struct domain *d;
kaf24@8871 282 int rc, op, flags = 0, preempted = 0;
kaf24@8871 283 unsigned long start_extent, progress;
kaf24@6486 284 struct xen_memory_reservation reservation;
kaf24@7959 285 domid_t domid;
kaf24@6486 286
kaf24@6486 287 op = cmd & ((1 << START_EXTENT_SHIFT) - 1);
kaf24@6486 288
kaf24@6486 289 switch ( op )
kaf24@6486 290 {
kaf24@6486 291 case XENMEM_increase_reservation:
kaf24@6486 292 case XENMEM_decrease_reservation:
kaf24@8673 293 case XENMEM_populate_physmap:
kaf24@9068 294 if ( copy_from_guest(&reservation, arg, 1) )
kaf24@6486 295 return -EFAULT;
kaf24@6486 296
kaf24@8871 297 /* Is size too large for us to encode a continuation? */
kaf24@8871 298 if ( reservation.nr_extents > (ULONG_MAX >> START_EXTENT_SHIFT) )
kaf24@8871 299 return -EINVAL;
kaf24@8871 300
kaf24@6486 301 start_extent = cmd >> START_EXTENT_SHIFT;
kaf24@6486 302 if ( unlikely(start_extent > reservation.nr_extents) )
kaf24@6486 303 return -EINVAL;
kaf24@9068 304
kaf24@9068 305 if ( !guest_handle_is_null(reservation.extent_start) )
kaf24@9068 306 guest_handle_add_offset(reservation.extent_start, start_extent);
kaf24@6486 307 reservation.nr_extents -= start_extent;
kaf24@6486 308
kaf24@6701 309 if ( (reservation.address_bits != 0) &&
kaf24@6702 310 (reservation.address_bits <
kaf24@6702 311 (get_order_from_pages(max_page) + PAGE_SHIFT)) )
kaf24@6486 312 {
kaf24@6486 313 if ( reservation.address_bits < 31 )
kaf24@6486 314 return -ENOMEM;
kaf24@6486 315 flags = ALLOC_DOM_DMA;
kaf24@6486 316 }
kaf24@6486 317
kaf24@6486 318 if ( likely(reservation.domid == DOMID_SELF) )
kaf24@6486 319 d = current->domain;
kaf24@6486 320 else if ( !IS_PRIV(current->domain) )
kaf24@6486 321 return -EPERM;
kaf24@6486 322 else if ( (d = find_domain_by_id(reservation.domid)) == NULL )
kaf24@6486 323 return -ESRCH;
kaf24@6486 324
kaf24@8673 325 switch ( op )
kaf24@8673 326 {
kaf24@8673 327 case XENMEM_increase_reservation:
kaf24@8673 328 rc = increase_reservation(
kaf24@8673 329 d,
kaf24@8673 330 reservation.extent_start,
kaf24@8673 331 reservation.nr_extents,
kaf24@8673 332 reservation.extent_order,
kaf24@8673 333 flags,
kaf24@8673 334 &preempted);
kaf24@8673 335 break;
kaf24@8673 336 case XENMEM_decrease_reservation:
kaf24@8673 337 rc = decrease_reservation(
kaf24@8673 338 d,
kaf24@8673 339 reservation.extent_start,
kaf24@8673 340 reservation.nr_extents,
kaf24@8673 341 reservation.extent_order,
kaf24@8673 342 flags,
kaf24@8673 343 &preempted);
kaf24@8673 344 break;
kaf24@8673 345 case XENMEM_populate_physmap:
kaf24@8673 346 default:
kaf24@8673 347 rc = populate_physmap(
kaf24@8673 348 d,
kaf24@8673 349 reservation.extent_start,
kaf24@8673 350 reservation.nr_extents,
kaf24@8673 351 reservation.extent_order,
kaf24@8673 352 flags,
kaf24@8673 353 &preempted);
kaf24@8673 354 break;
kaf24@8673 355 }
kaf24@6486 356
kaf24@6486 357 if ( unlikely(reservation.domid != DOMID_SELF) )
kaf24@6486 358 put_domain(d);
kaf24@6486 359
kaf24@6486 360 rc += start_extent;
kaf24@6486 361
kaf24@6607 362 if ( preempted )
kaf24@9068 363 return hypercall_create_continuation(
kaf24@9068 364 __HYPERVISOR_memory_op, "lh",
kaf24@9068 365 op | (rc << START_EXTENT_SHIFT), arg);
kaf24@6607 366
kaf24@6486 367 break;
kaf24@6486 368
kaf24@6486 369 case XENMEM_maximum_ram_page:
kaf24@7959 370 rc = max_page;
kaf24@7959 371 break;
kaf24@7959 372
kaf24@7959 373 case XENMEM_current_reservation:
kaf24@7959 374 case XENMEM_maximum_reservation:
kaf24@9068 375 if ( copy_from_guest(&domid, arg, 1) )
kaf24@6486 376 return -EFAULT;
kaf24@7959 377
kaf24@9068 378 if ( likely(domid == DOMID_SELF) )
kaf24@7959 379 d = current->domain;
kaf24@7959 380 else if ( !IS_PRIV(current->domain) )
kaf24@7959 381 return -EPERM;
kaf24@7959 382 else if ( (d = find_domain_by_id(domid)) == NULL )
kaf24@7959 383 return -ESRCH;
kaf24@7959 384
kaf24@7959 385 rc = (op == XENMEM_current_reservation) ? d->tot_pages : d->max_pages;
kaf24@7959 386
kaf24@7959 387 if ( unlikely(domid != DOMID_SELF) )
kaf24@7959 388 put_domain(d);
kaf24@7959 389
kaf24@6486 390 break;
kaf24@6486 391
kaf24@8871 392 case XENMEM_translate_gpfn_list:
kaf24@8871 393 progress = cmd >> START_EXTENT_SHIFT;
kaf24@9068 394 rc = translate_gpfn_list(
kaf24@9068 395 guest_handle_cast(arg, xen_translate_gpfn_list_t),
kaf24@9068 396 &progress);
kaf24@8871 397 if ( rc == -EAGAIN )
kaf24@9068 398 return hypercall_create_continuation(
kaf24@9068 399 __HYPERVISOR_memory_op, "lh",
kaf24@9068 400 op | (progress << START_EXTENT_SHIFT), arg);
kaf24@8871 401 break;
kaf24@8871 402
kaf24@6486 403 default:
kaf24@8059 404 rc = arch_memory_op(op, arg);
kaf24@6486 405 break;
kaf24@6486 406 }
kaf24@6486 407
kaf24@6486 408 return rc;
kaf24@6486 409 }
kaf24@6486 410
kaf24@6486 411 /*
kaf24@6486 412 * Local variables:
kaf24@6486 413 * mode: C
kaf24@6486 414 * c-set-style: "BSD"
kaf24@6486 415 * c-basic-offset: 4
kaf24@6486 416 * tab-width: 4
kaf24@6486 417 * indent-tabs-mode: nil
kaf24@6486 418 * End:
kaf24@6486 419 */