ia64/xen-unstable

annotate xen/common/memory.c @ 8871:0d10fac28427

New memory_op subcall XENMEM_translate_gpfn_list.

Allows translation from GPFNs to MFNs for an auto-translated
guest.

Signed-off-by: Keir Fraser <keir@xensource.com>
author kaf24@firebug.cl.cam.ac.uk
date Thu Feb 16 18:52:06 2006 +0100 (2006-02-16)
parents 1346a69694be
children c369d960f96b
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@6486 19 #include <asm/current.h>
kaf24@6486 20 #include <asm/hardirq.h>
kaf24@6486 21 #include <public/memory.h>
kaf24@6486 22
kaf24@8871 23 /*
kaf24@8871 24 * To allow safe resume of do_memory_op() after preemption, we need to know
kaf24@8871 25 * at what point in the page list to resume. For this purpose I steal the
kaf24@8871 26 * high-order bits of the @cmd parameter, which are otherwise unused and zero.
kaf24@8871 27 */
kaf24@8871 28 #define START_EXTENT_SHIFT 4 /* cmd[:4] == start_extent */
kaf24@8871 29
kaf24@6486 30 static long
kaf24@6486 31 increase_reservation(
kaf24@6486 32 struct domain *d,
kaf24@6486 33 unsigned long *extent_list,
kaf24@6486 34 unsigned int nr_extents,
kaf24@6486 35 unsigned int extent_order,
kaf24@6607 36 unsigned int flags,
kaf24@6607 37 int *preempted)
kaf24@6486 38 {
kaf24@8726 39 struct page_info *page;
kaf24@8859 40 unsigned long i, mfn;
kaf24@6486 41
kaf24@6701 42 if ( (extent_list != NULL) &&
kaf24@6701 43 !array_access_ok(extent_list, nr_extents, sizeof(*extent_list)) )
kaf24@6486 44 return 0;
kaf24@6486 45
kaf24@8468 46 if ( (extent_order != 0) &&
kaf24@8468 47 !multipage_allocation_permitted(current->domain) )
kaf24@6486 48 return 0;
kaf24@6486 49
kaf24@6486 50 for ( i = 0; i < nr_extents; i++ )
kaf24@6486 51 {
kaf24@6486 52 if ( hypercall_preempt_check() )
kaf24@6607 53 {
kaf24@6607 54 *preempted = 1;
kaf24@6486 55 return i;
kaf24@6607 56 }
kaf24@6486 57
kaf24@6486 58 if ( unlikely((page = alloc_domheap_pages(
kaf24@6486 59 d, extent_order, flags)) == NULL) )
kaf24@6486 60 {
kaf24@6752 61 DPRINTK("Could not allocate order=%d extent: "
kaf24@8673 62 "id=%d flags=%x (%ld of %d)\n",
kaf24@6752 63 extent_order, d->domain_id, flags, i, nr_extents);
kaf24@6486 64 return i;
kaf24@6486 65 }
kaf24@6486 66
kaf24@6486 67 /* Inform the domain of the new page's machine address. */
kaf24@8859 68 if ( extent_list != NULL )
kaf24@8859 69 {
kaf24@8859 70 mfn = page_to_mfn(page);
kaf24@8859 71 if ( unlikely(__copy_to_user(&extent_list[i], &mfn, sizeof(mfn))) )
kaf24@8859 72 return i;
kaf24@8859 73 }
kaf24@6486 74 }
kaf24@6486 75
kaf24@6486 76 return nr_extents;
kaf24@6486 77 }
sos22@8688 78
kaf24@6486 79 static long
kaf24@8673 80 populate_physmap(
kaf24@8673 81 struct domain *d,
kaf24@8673 82 unsigned long *extent_list,
kaf24@8673 83 unsigned int nr_extents,
kaf24@8673 84 unsigned int extent_order,
kaf24@8673 85 unsigned int flags,
kaf24@8673 86 int *preempted)
kaf24@8673 87 {
kaf24@8726 88 struct page_info *page;
kaf24@8736 89 unsigned long i, j, gpfn, mfn;
kaf24@8673 90
kaf24@8673 91 if ( !array_access_ok(extent_list, nr_extents, sizeof(*extent_list)) )
kaf24@8673 92 return 0;
kaf24@8673 93
kaf24@8673 94 if ( (extent_order != 0) &&
kaf24@8673 95 !multipage_allocation_permitted(current->domain) )
kaf24@8673 96 return 0;
kaf24@8673 97
kaf24@8673 98 for ( i = 0; i < nr_extents; i++ )
kaf24@8673 99 {
kaf24@8673 100 if ( hypercall_preempt_check() )
kaf24@8673 101 {
kaf24@8673 102 *preempted = 1;
sos22@8688 103 goto out;
kaf24@8673 104 }
kaf24@8673 105
kaf24@8859 106 if ( unlikely(__copy_from_user(&gpfn, &extent_list[i], sizeof(gpfn))) )
kaf24@8859 107 goto out;
kaf24@8859 108
kaf24@8673 109 if ( unlikely((page = alloc_domheap_pages(
kaf24@8673 110 d, extent_order, flags)) == NULL) )
kaf24@8673 111 {
kaf24@8673 112 DPRINTK("Could not allocate order=%d extent: "
kaf24@8673 113 "id=%d flags=%x (%ld of %d)\n",
kaf24@8673 114 extent_order, d->domain_id, flags, i, nr_extents);
sos22@8688 115 goto out;
kaf24@8673 116 }
kaf24@8673 117
kaf24@8726 118 mfn = page_to_mfn(page);
kaf24@8673 119
kaf24@8694 120 if ( unlikely(shadow_mode_translate(d)) )
kaf24@8694 121 {
kaf24@8694 122 for ( j = 0; j < (1 << extent_order); j++ )
kaf24@8736 123 guest_physmap_add_page(d, gpfn + j, mfn + j);
sos22@8688 124 }
kaf24@8694 125 else
kaf24@8694 126 {
kaf24@8694 127 for ( j = 0; j < (1 << extent_order); j++ )
kaf24@8736 128 set_gpfn_from_mfn(mfn + j, gpfn + j);
kaf24@8673 129
sos22@8688 130 /* Inform the domain of the new page's machine address. */
kaf24@8859 131 if ( unlikely(__copy_to_user(&extent_list[i], &mfn, sizeof(mfn))) )
sos22@8688 132 goto out;
sos22@8688 133 }
kaf24@8673 134 }
kaf24@8673 135
sos22@8688 136 out:
sos22@8688 137 return i;
kaf24@8673 138 }
kaf24@8673 139
kaf24@8673 140 static long
kaf24@6486 141 decrease_reservation(
kaf24@6486 142 struct domain *d,
kaf24@6486 143 unsigned long *extent_list,
kaf24@6486 144 unsigned int nr_extents,
kaf24@6486 145 unsigned int extent_order,
kaf24@6607 146 unsigned int flags,
kaf24@6607 147 int *preempted)
kaf24@6486 148 {
kaf24@8726 149 struct page_info *page;
kaf24@8726 150 unsigned long i, j, gmfn, mfn;
kaf24@6486 151
kaf24@6486 152 if ( !array_access_ok(extent_list, nr_extents, sizeof(*extent_list)) )
kaf24@6486 153 return 0;
kaf24@6486 154
kaf24@6486 155 for ( i = 0; i < nr_extents; i++ )
kaf24@6486 156 {
kaf24@6486 157 if ( hypercall_preempt_check() )
kaf24@6607 158 {
kaf24@6607 159 *preempted = 1;
kaf24@6486 160 return i;
kaf24@6607 161 }
kaf24@6486 162
kaf24@8859 163 if ( unlikely(__copy_from_user(&gmfn, &extent_list[i], sizeof(gmfn))) )
kaf24@6486 164 return i;
kaf24@6486 165
kaf24@6486 166 for ( j = 0; j < (1 << extent_order); j++ )
kaf24@6486 167 {
kaf24@8726 168 mfn = gmfn_to_mfn(d, gmfn + j);
kaf24@8736 169 if ( unlikely(!mfn_valid(mfn)) )
kaf24@6486 170 {
kaf24@8736 171 DPRINTK("Domain %u page number %lx invalid\n",
kaf24@8736 172 d->domain_id, mfn);
kaf24@6486 173 return i;
kaf24@6486 174 }
kaf24@6486 175
kaf24@8726 176 page = mfn_to_page(mfn);
kaf24@6486 177 if ( unlikely(!get_page(page, d)) )
kaf24@6486 178 {
kaf24@6486 179 DPRINTK("Bad page free for domain %u\n", d->domain_id);
kaf24@6486 180 return i;
kaf24@6486 181 }
kaf24@6486 182
kaf24@6486 183 if ( test_and_clear_bit(_PGT_pinned, &page->u.inuse.type_info) )
kaf24@6486 184 put_page_and_type(page);
kaf24@6486 185
kaf24@6486 186 if ( test_and_clear_bit(_PGC_allocated, &page->count_info) )
kaf24@6486 187 put_page(page);
kaf24@6486 188
kaf24@8726 189 guest_physmap_remove_page(d, gmfn + j, mfn);
kaf24@8694 190
kaf24@6486 191 put_page(page);
kaf24@6486 192 }
kaf24@6486 193 }
kaf24@6486 194
kaf24@6486 195 return nr_extents;
kaf24@6486 196 }
kaf24@6486 197
kaf24@8871 198 static long
kaf24@8871 199 translate_gpfn_list(
kaf24@8871 200 struct xen_translate_gpfn_list *uop, unsigned long *progress)
kaf24@8871 201 {
kaf24@8871 202 struct xen_translate_gpfn_list op;
kaf24@8871 203 unsigned long i, gpfn, mfn;
kaf24@8871 204 struct domain *d;
kaf24@6486 205
kaf24@8871 206 if ( copy_from_user(&op, uop, sizeof(op)) )
kaf24@8871 207 return -EFAULT;
kaf24@8871 208
kaf24@8871 209 /* Is size too large for us to encode a continuation? */
kaf24@8871 210 if ( op.nr_gpfns > (ULONG_MAX >> START_EXTENT_SHIFT) )
kaf24@8871 211 return -EINVAL;
kaf24@8871 212
kaf24@8871 213 if ( !array_access_ok(op.gpfn_list, op.nr_gpfns, sizeof(*op.gpfn_list)) ||
kaf24@8871 214 !array_access_ok(op.mfn_list, op.nr_gpfns, sizeof(*op.mfn_list)) )
kaf24@8871 215 return -EFAULT;
kaf24@8871 216
kaf24@8871 217 if ( op.domid == DOMID_SELF )
kaf24@8871 218 op.domid = current->domain->domain_id;
kaf24@8871 219 else if ( !IS_PRIV(current->domain) )
kaf24@8871 220 return -EPERM;
kaf24@8871 221
kaf24@8871 222 if ( (d = find_domain_by_id(op.domid)) == NULL )
kaf24@8871 223 return -ESRCH;
kaf24@8871 224
kaf24@8871 225 if ( !shadow_mode_translate(d) )
kaf24@8871 226 {
kaf24@8871 227 put_domain(d);
kaf24@8871 228 return -EINVAL;
kaf24@8871 229 }
kaf24@8871 230
kaf24@8871 231 for ( i = *progress; i < op.nr_gpfns; i++ )
kaf24@8871 232 {
kaf24@8871 233 if ( hypercall_preempt_check() )
kaf24@8871 234 {
kaf24@8871 235 put_domain(d);
kaf24@8871 236 *progress = i;
kaf24@8871 237 return -EAGAIN;
kaf24@8871 238 }
kaf24@8871 239
kaf24@8871 240 if ( unlikely(__copy_from_user(&gpfn, &op.gpfn_list[i],
kaf24@8871 241 sizeof(gpfn))) )
kaf24@8871 242 {
kaf24@8871 243 put_domain(d);
kaf24@8871 244 return -EFAULT;
kaf24@8871 245 }
kaf24@8871 246
kaf24@8871 247 mfn = gmfn_to_mfn(d, gpfn);
kaf24@8871 248
kaf24@8871 249 if ( unlikely(__copy_to_user(&op.mfn_list[i], &mfn,
kaf24@8871 250 sizeof(mfn))) )
kaf24@8871 251 {
kaf24@8871 252 put_domain(d);
kaf24@8871 253 return -EFAULT;
kaf24@8871 254 }
kaf24@8871 255 }
kaf24@8871 256
kaf24@8871 257 put_domain(d);
kaf24@8871 258 return 0;
kaf24@8871 259 }
kaf24@8871 260
kaf24@8871 261 long do_memory_op(unsigned long cmd, void *arg)
kaf24@6486 262 {
kaf24@6486 263 struct domain *d;
kaf24@8871 264 int rc, op, flags = 0, preempted = 0;
kaf24@8871 265 unsigned long start_extent, progress;
kaf24@6486 266 struct xen_memory_reservation reservation;
kaf24@7959 267 domid_t domid;
kaf24@6486 268
kaf24@6486 269 op = cmd & ((1 << START_EXTENT_SHIFT) - 1);
kaf24@6486 270
kaf24@6486 271 switch ( op )
kaf24@6486 272 {
kaf24@6486 273 case XENMEM_increase_reservation:
kaf24@6486 274 case XENMEM_decrease_reservation:
kaf24@8673 275 case XENMEM_populate_physmap:
kaf24@6486 276 if ( copy_from_user(&reservation, arg, sizeof(reservation)) )
kaf24@6486 277 return -EFAULT;
kaf24@6486 278
kaf24@8871 279 /* Is size too large for us to encode a continuation? */
kaf24@8871 280 if ( reservation.nr_extents > (ULONG_MAX >> START_EXTENT_SHIFT) )
kaf24@8871 281 return -EINVAL;
kaf24@8871 282
kaf24@6486 283 start_extent = cmd >> START_EXTENT_SHIFT;
kaf24@6486 284 if ( unlikely(start_extent > reservation.nr_extents) )
kaf24@6486 285 return -EINVAL;
kaf24@6486 286
kaf24@6486 287 if ( reservation.extent_start != NULL )
kaf24@6486 288 reservation.extent_start += start_extent;
kaf24@6486 289 reservation.nr_extents -= start_extent;
kaf24@6486 290
kaf24@6701 291 if ( (reservation.address_bits != 0) &&
kaf24@6702 292 (reservation.address_bits <
kaf24@6702 293 (get_order_from_pages(max_page) + PAGE_SHIFT)) )
kaf24@6486 294 {
kaf24@6486 295 if ( reservation.address_bits < 31 )
kaf24@6486 296 return -ENOMEM;
kaf24@6486 297 flags = ALLOC_DOM_DMA;
kaf24@6486 298 }
kaf24@6486 299
kaf24@6486 300 if ( likely(reservation.domid == DOMID_SELF) )
kaf24@6486 301 d = current->domain;
kaf24@6486 302 else if ( !IS_PRIV(current->domain) )
kaf24@6486 303 return -EPERM;
kaf24@6486 304 else if ( (d = find_domain_by_id(reservation.domid)) == NULL )
kaf24@6486 305 return -ESRCH;
kaf24@6486 306
kaf24@8673 307 switch ( op )
kaf24@8673 308 {
kaf24@8673 309 case XENMEM_increase_reservation:
kaf24@8673 310 rc = increase_reservation(
kaf24@8673 311 d,
kaf24@8673 312 reservation.extent_start,
kaf24@8673 313 reservation.nr_extents,
kaf24@8673 314 reservation.extent_order,
kaf24@8673 315 flags,
kaf24@8673 316 &preempted);
kaf24@8673 317 break;
kaf24@8673 318 case XENMEM_decrease_reservation:
kaf24@8673 319 rc = decrease_reservation(
kaf24@8673 320 d,
kaf24@8673 321 reservation.extent_start,
kaf24@8673 322 reservation.nr_extents,
kaf24@8673 323 reservation.extent_order,
kaf24@8673 324 flags,
kaf24@8673 325 &preempted);
kaf24@8673 326 break;
kaf24@8673 327 case XENMEM_populate_physmap:
kaf24@8673 328 default:
kaf24@8673 329 rc = populate_physmap(
kaf24@8673 330 d,
kaf24@8673 331 reservation.extent_start,
kaf24@8673 332 reservation.nr_extents,
kaf24@8673 333 reservation.extent_order,
kaf24@8673 334 flags,
kaf24@8673 335 &preempted);
kaf24@8673 336 break;
kaf24@8673 337 }
kaf24@6486 338
kaf24@6486 339 if ( unlikely(reservation.domid != DOMID_SELF) )
kaf24@6486 340 put_domain(d);
kaf24@6486 341
kaf24@6486 342 rc += start_extent;
kaf24@6486 343
kaf24@6607 344 if ( preempted )
kaf24@6486 345 return hypercall2_create_continuation(
kaf24@6607 346 __HYPERVISOR_memory_op, op | (rc << START_EXTENT_SHIFT), arg);
kaf24@6607 347
kaf24@6486 348 break;
kaf24@6486 349
kaf24@6486 350 case XENMEM_maximum_ram_page:
kaf24@7959 351 rc = max_page;
kaf24@7959 352 break;
kaf24@7959 353
kaf24@7959 354 case XENMEM_current_reservation:
kaf24@7959 355 case XENMEM_maximum_reservation:
kaf24@8859 356 if ( copy_from_user(&domid, (domid_t *)arg, sizeof(domid)) )
kaf24@6486 357 return -EFAULT;
kaf24@7959 358
kaf24@7959 359 if ( likely((domid = (unsigned long)arg) == DOMID_SELF) )
kaf24@7959 360 d = current->domain;
kaf24@7959 361 else if ( !IS_PRIV(current->domain) )
kaf24@7959 362 return -EPERM;
kaf24@7959 363 else if ( (d = find_domain_by_id(domid)) == NULL )
kaf24@7959 364 return -ESRCH;
kaf24@7959 365
kaf24@7959 366 rc = (op == XENMEM_current_reservation) ? d->tot_pages : d->max_pages;
kaf24@7959 367
kaf24@7959 368 if ( unlikely(domid != DOMID_SELF) )
kaf24@7959 369 put_domain(d);
kaf24@7959 370
kaf24@6486 371 break;
kaf24@6486 372
kaf24@8871 373 case XENMEM_translate_gpfn_list:
kaf24@8871 374 progress = cmd >> START_EXTENT_SHIFT;
kaf24@8871 375 rc = translate_gpfn_list(arg, &progress);
kaf24@8871 376 if ( rc == -EAGAIN )
kaf24@8871 377 return hypercall2_create_continuation(
kaf24@8871 378 __HYPERVISOR_memory_op,
kaf24@8871 379 op | (progress << START_EXTENT_SHIFT),
kaf24@8871 380 arg);
kaf24@8871 381 break;
kaf24@8871 382
kaf24@6486 383 default:
kaf24@8059 384 rc = arch_memory_op(op, arg);
kaf24@6486 385 break;
kaf24@6486 386 }
kaf24@6486 387
kaf24@6486 388 return rc;
kaf24@6486 389 }
kaf24@6486 390
kaf24@6486 391 /*
kaf24@6486 392 * Local variables:
kaf24@6486 393 * mode: C
kaf24@6486 394 * c-set-style: "BSD"
kaf24@6486 395 * c-basic-offset: 4
kaf24@6486 396 * tab-width: 4
kaf24@6486 397 * indent-tabs-mode: nil
kaf24@6486 398 * End:
kaf24@6486 399 */