1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Machine frame segment driver. This segment driver allows dom0 processes to 29 * map pages of other domains or Xen (e.g. during save/restore). ioctl()s on 30 * the privcmd driver provide the MFN values backing each mapping, and we map 31 * them into the process's address space at this time. Demand-faulting is not 32 * supported by this driver due to the requirements upon some of the ioctl()s. 33 */ 34 35 36 #include <sys/types.h> 37 #include <sys/systm.h> 38 #include <sys/vmsystm.h> 39 #include <sys/mman.h> 40 #include <sys/errno.h> 41 #include <sys/kmem.h> 42 #include <sys/cmn_err.h> 43 #include <sys/vnode.h> 44 #include <sys/conf.h> 45 #include <sys/debug.h> 46 #include <sys/lgrp.h> 47 #include <sys/hypervisor.h> 48 49 #include <vm/page.h> 50 #include <vm/hat.h> 51 #include <vm/as.h> 52 #include <vm/seg.h> 53 54 #include <vm/hat_pte.h> 55 #include <vm/hat_i86.h> 56 #include <vm/seg_mf.h> 57 58 #include <sys/fs/snode.h> 59 60 #define VTOCVP(vp) (VTOS(vp)->s_commonvp) 61 62 typedef struct segmf_mfn_s { 63 mfn_t m_mfn; 64 } segmf_mfn_t; 65 66 /* g_flags */ 67 #define SEGMF_GFLAGS_WR 0x1 68 #define SEGMF_GFLAGS_MAPPED 0x2 69 typedef struct segmf_gref_s { 70 uint64_t g_ptep; 71 grant_ref_t g_gref; 72 uint32_t g_flags; 73 grant_handle_t g_handle; 74 } segmf_gref_t; 75 76 typedef union segmf_mu_u { 77 segmf_mfn_t m; 78 segmf_gref_t g; 79 } segmf_mu_t; 80 81 typedef enum { 82 SEGMF_MAP_EMPTY = 0, 83 SEGMF_MAP_MFN, 84 SEGMF_MAP_GREF 85 } segmf_map_type_t; 86 87 typedef struct segmf_map_s { 88 segmf_map_type_t t_type; 89 segmf_mu_t u; 90 } segmf_map_t; 91 92 struct segmf_data { 93 kmutex_t lock; 94 struct vnode *vp; 95 uchar_t prot; 96 uchar_t maxprot; 97 size_t softlockcnt; 98 domid_t domid; 99 segmf_map_t *map; 100 }; 101 102 static struct seg_ops segmf_ops; 103 104 static int segmf_fault_gref_range(struct seg *seg, caddr_t addr, size_t len); 105 106 static struct segmf_data * 107 segmf_data_zalloc(struct seg *seg) 108 { 109 struct segmf_data *data = kmem_zalloc(sizeof (*data), KM_SLEEP); 110 111 mutex_init(&data->lock, "segmf.lock", MUTEX_DEFAULT, NULL); 112 seg->s_ops = &segmf_ops; 113 seg->s_data = data; 114 return (data); 115 } 116 117 int 118 segmf_create(struct seg *seg, void *args) 119 { 120 struct segmf_crargs *a = args; 121 struct segmf_data *data; 122 struct as *as = seg->s_as; 123 pgcnt_t i, npages = seg_pages(seg); 124 int error; 125 126 hat_map(as->a_hat, seg->s_base, seg->s_size, HAT_MAP); 127 128 data = segmf_data_zalloc(seg); 129 data->vp = specfind(a->dev, VCHR); 130 data->prot = a->prot; 131 data->maxprot = a->maxprot; 132 133 data->map = kmem_alloc(npages * sizeof (segmf_map_t), KM_SLEEP); 134 for (i = 0; i < npages; i++) { 135 data->map[i].t_type = SEGMF_MAP_EMPTY; 136 } 137 138 error = VOP_ADDMAP(VTOCVP(data->vp), 0, as, seg->s_base, seg->s_size, 139 data->prot, data->maxprot, MAP_SHARED, CRED(), NULL); 140 141 if (error != 0) 142 hat_unload(as->a_hat, 143 seg->s_base, seg->s_size, HAT_UNLOAD_UNMAP); 144 return (error); 145 } 146 147 /* 148 * Duplicate a seg and return new segment in newseg. 149 */ 150 static int 151 segmf_dup(struct seg *seg, struct seg *newseg) 152 { 153 struct segmf_data *data = seg->s_data; 154 struct segmf_data *ndata; 155 pgcnt_t npages = seg_pages(newseg); 156 size_t sz; 157 158 ndata = segmf_data_zalloc(newseg); 159 160 VN_HOLD(data->vp); 161 ndata->vp = data->vp; 162 ndata->prot = data->prot; 163 ndata->maxprot = data->maxprot; 164 ndata->domid = data->domid; 165 166 sz = npages * sizeof (segmf_map_t); 167 ndata->map = kmem_alloc(sz, KM_SLEEP); 168 bcopy(data->map, ndata->map, sz); 169 170 return (VOP_ADDMAP(VTOCVP(ndata->vp), 0, newseg->s_as, 171 newseg->s_base, newseg->s_size, ndata->prot, ndata->maxprot, 172 MAP_SHARED, CRED(), NULL)); 173 } 174 175 /* 176 * We only support unmapping the whole segment, and we automatically unlock 177 * what we previously soft-locked. 178 */ 179 static int 180 segmf_unmap(struct seg *seg, caddr_t addr, size_t len) 181 { 182 struct segmf_data *data = seg->s_data; 183 offset_t off; 184 185 if (addr < seg->s_base || addr + len > seg->s_base + seg->s_size || 186 (len & PAGEOFFSET) || ((uintptr_t)addr & PAGEOFFSET)) 187 panic("segmf_unmap"); 188 189 if (addr != seg->s_base || len != seg->s_size) 190 return (ENOTSUP); 191 192 hat_unload(seg->s_as->a_hat, addr, len, 193 HAT_UNLOAD_UNMAP | HAT_UNLOAD_UNLOCK); 194 195 off = (offset_t)seg_page(seg, addr); 196 197 ASSERT(data->vp != NULL); 198 199 (void) VOP_DELMAP(VTOCVP(data->vp), off, seg->s_as, addr, len, 200 data->prot, data->maxprot, MAP_SHARED, CRED(), NULL); 201 202 seg_free(seg); 203 return (0); 204 } 205 206 static void 207 segmf_free(struct seg *seg) 208 { 209 struct segmf_data *data = seg->s_data; 210 pgcnt_t npages = seg_pages(seg); 211 212 kmem_free(data->map, npages * sizeof (segmf_map_t)); 213 VN_RELE(data->vp); 214 mutex_destroy(&data->lock); 215 kmem_free(data, sizeof (*data)); 216 } 217 218 static int segmf_faultpage_debug = 0; 219 /*ARGSUSED*/ 220 static int 221 segmf_faultpage(struct hat *hat, struct seg *seg, caddr_t addr, 222 enum fault_type type, uint_t prot) 223 { 224 struct segmf_data *data = seg->s_data; 225 uint_t hat_flags = HAT_LOAD_NOCONSIST; 226 mfn_t mfn; 227 x86pte_t pte; 228 segmf_map_t *map; 229 uint_t idx; 230 231 232 idx = seg_page(seg, addr); 233 map = &data->map[idx]; 234 ASSERT(map->t_type == SEGMF_MAP_MFN); 235 236 mfn = map->u.m.m_mfn; 237 238 if (type == F_SOFTLOCK) { 239 mutex_enter(&freemem_lock); 240 data->softlockcnt++; 241 mutex_exit(&freemem_lock); 242 hat_flags |= HAT_LOAD_LOCK; 243 } else 244 hat_flags |= HAT_LOAD; 245 246 if (segmf_faultpage_debug > 0) { 247 uprintf("segmf_faultpage: addr %p domid %x mfn %lx prot %x\n", 248 (void *)addr, data->domid, mfn, prot); 249 segmf_faultpage_debug--; 250 } 251 252 /* 253 * Ask the HAT to load a throwaway mapping to page zero, then 254 * overwrite it with our foreign domain mapping. It gets removed 255 * later via hat_unload() 256 */ 257 hat_devload(hat, addr, MMU_PAGESIZE, (pfn_t)0, 258 PROT_READ | HAT_UNORDERED_OK, hat_flags); 259 260 pte = mmu_ptob((x86pte_t)mfn) | PT_VALID | PT_USER | PT_FOREIGN; 261 if (prot & PROT_WRITE) 262 pte |= PT_WRITABLE; 263 264 if (HYPERVISOR_update_va_mapping_otherdomain((uintptr_t)addr, pte, 265 UVMF_INVLPG | UVMF_ALL, data->domid) != 0) { 266 hat_flags = HAT_UNLOAD_UNMAP; 267 268 if (type == F_SOFTLOCK) { 269 hat_flags |= HAT_UNLOAD_UNLOCK; 270 mutex_enter(&freemem_lock); 271 data->softlockcnt--; 272 mutex_exit(&freemem_lock); 273 } 274 275 hat_unload(hat, addr, MMU_PAGESIZE, hat_flags); 276 return (FC_MAKE_ERR(EFAULT)); 277 } 278 279 return (0); 280 } 281 282 static int 283 seg_rw_to_prot(enum seg_rw rw) 284 { 285 switch (rw) { 286 case S_READ: 287 return (PROT_READ); 288 case S_WRITE: 289 return (PROT_WRITE); 290 case S_EXEC: 291 return (PROT_EXEC); 292 case S_OTHER: 293 default: 294 break; 295 } 296 return (PROT_READ | PROT_WRITE | PROT_EXEC); 297 } 298 299 static void 300 segmf_softunlock(struct hat *hat, struct seg *seg, caddr_t addr, size_t len) 301 { 302 struct segmf_data *data = seg->s_data; 303 304 hat_unlock(hat, addr, len); 305 306 mutex_enter(&freemem_lock); 307 ASSERT(data->softlockcnt >= btopr(len)); 308 data->softlockcnt -= btopr(len); 309 mutex_exit(&freemem_lock); 310 311 if (data->softlockcnt == 0) { 312 struct as *as = seg->s_as; 313 314 if (AS_ISUNMAPWAIT(as)) { 315 mutex_enter(&as->a_contents); 316 if (AS_ISUNMAPWAIT(as)) { 317 AS_CLRUNMAPWAIT(as); 318 cv_broadcast(&as->a_cv); 319 } 320 mutex_exit(&as->a_contents); 321 } 322 } 323 } 324 325 static int 326 segmf_fault_range(struct hat *hat, struct seg *seg, caddr_t addr, size_t len, 327 enum fault_type type, enum seg_rw rw) 328 { 329 struct segmf_data *data = seg->s_data; 330 int error = 0; 331 caddr_t a; 332 333 if ((data->prot & seg_rw_to_prot(rw)) == 0) 334 return (FC_PROT); 335 336 /* loop over the address range handling each fault */ 337 338 for (a = addr; a < addr + len; a += PAGESIZE) { 339 error = segmf_faultpage(hat, seg, a, type, data->prot); 340 if (error != 0) 341 break; 342 } 343 344 if (error != 0 && type == F_SOFTLOCK) { 345 size_t done = (size_t)(a - addr); 346 347 /* 348 * Undo what's been done so far. 349 */ 350 if (done > 0) 351 segmf_softunlock(hat, seg, addr, done); 352 } 353 354 return (error); 355 } 356 357 /* 358 * We never demand-fault for seg_mf. 359 */ 360 /*ARGSUSED*/ 361 static int 362 segmf_fault(struct hat *hat, struct seg *seg, caddr_t addr, size_t len, 363 enum fault_type type, enum seg_rw rw) 364 { 365 return (FC_MAKE_ERR(EFAULT)); 366 } 367 368 /*ARGSUSED*/ 369 static int 370 segmf_faulta(struct seg *seg, caddr_t addr) 371 { 372 return (0); 373 } 374 375 /*ARGSUSED*/ 376 static int 377 segmf_setprot(struct seg *seg, caddr_t addr, size_t len, uint_t prot) 378 { 379 return (EINVAL); 380 } 381 382 /*ARGSUSED*/ 383 static int 384 segmf_checkprot(struct seg *seg, caddr_t addr, size_t len, uint_t prot) 385 { 386 return (EINVAL); 387 } 388 389 /*ARGSUSED*/ 390 static int 391 segmf_kluster(struct seg *seg, caddr_t addr, ssize_t delta) 392 { 393 return (-1); 394 } 395 396 /*ARGSUSED*/ 397 static int 398 segmf_sync(struct seg *seg, caddr_t addr, size_t len, int attr, uint_t flags) 399 { 400 return (0); 401 } 402 403 /* 404 * XXPV Hmm. Should we say that mf mapping are "in core?" 405 */ 406 407 /*ARGSUSED*/ 408 static size_t 409 segmf_incore(struct seg *seg, caddr_t addr, size_t len, char *vec) 410 { 411 size_t v; 412 413 for (v = 0, len = (len + PAGEOFFSET) & PAGEMASK; len; 414 len -= PAGESIZE, v += PAGESIZE) 415 *vec++ = 1; 416 return (v); 417 } 418 419 /*ARGSUSED*/ 420 static int 421 segmf_lockop(struct seg *seg, caddr_t addr, 422 size_t len, int attr, int op, ulong_t *lockmap, size_t pos) 423 { 424 return (0); 425 } 426 427 static int 428 segmf_getprot(struct seg *seg, caddr_t addr, size_t len, uint_t *protv) 429 { 430 struct segmf_data *data = seg->s_data; 431 pgcnt_t pgno = seg_page(seg, addr + len) - seg_page(seg, addr) + 1; 432 433 if (pgno != 0) { 434 do 435 protv[--pgno] = data->prot; 436 while (pgno != 0) 437 ; 438 } 439 return (0); 440 } 441 442 static u_offset_t 443 segmf_getoffset(struct seg *seg, caddr_t addr) 444 { 445 return (addr - seg->s_base); 446 } 447 448 /*ARGSUSED*/ 449 static int 450 segmf_gettype(struct seg *seg, caddr_t addr) 451 { 452 return (MAP_SHARED); 453 } 454 455 /*ARGSUSED1*/ 456 static int 457 segmf_getvp(struct seg *seg, caddr_t addr, struct vnode **vpp) 458 { 459 struct segmf_data *data = seg->s_data; 460 461 *vpp = VTOCVP(data->vp); 462 return (0); 463 } 464 465 /*ARGSUSED*/ 466 static int 467 segmf_advise(struct seg *seg, caddr_t addr, size_t len, uint_t behav) 468 { 469 return (0); 470 } 471 472 /*ARGSUSED*/ 473 static void 474 segmf_dump(struct seg *seg) 475 {} 476 477 /*ARGSUSED*/ 478 static int 479 segmf_pagelock(struct seg *seg, caddr_t addr, size_t len, 480 struct page ***ppp, enum lock_type type, enum seg_rw rw) 481 { 482 return (ENOTSUP); 483 } 484 485 /*ARGSUSED*/ 486 static int 487 segmf_setpagesize(struct seg *seg, caddr_t addr, size_t len, uint_t szc) 488 { 489 return (ENOTSUP); 490 } 491 492 static int 493 segmf_getmemid(struct seg *seg, caddr_t addr, memid_t *memid) 494 { 495 struct segmf_data *data = seg->s_data; 496 497 memid->val[0] = (uintptr_t)VTOCVP(data->vp); 498 memid->val[1] = (uintptr_t)seg_page(seg, addr); 499 return (0); 500 } 501 502 /*ARGSUSED*/ 503 static lgrp_mem_policy_info_t * 504 segmf_getpolicy(struct seg *seg, caddr_t addr) 505 { 506 return (NULL); 507 } 508 509 /*ARGSUSED*/ 510 static int 511 segmf_capable(struct seg *seg, segcapability_t capability) 512 { 513 return (0); 514 } 515 516 /* 517 * Add a set of contiguous foreign MFNs to the segment. soft-locking them. The 518 * pre-faulting is necessary due to live migration; in particular we must 519 * return an error in response to IOCTL_PRIVCMD_MMAPBATCH rather than faulting 520 * later on a bad MFN. Whilst this isn't necessary for the other MMAP 521 * ioctl()s, we lock them too, as they should be transitory. 522 */ 523 int 524 segmf_add_mfns(struct seg *seg, caddr_t addr, mfn_t mfn, 525 pgcnt_t pgcnt, domid_t domid) 526 { 527 struct segmf_data *data = seg->s_data; 528 pgcnt_t base; 529 faultcode_t fc; 530 pgcnt_t i; 531 int error = 0; 532 533 if (seg->s_ops != &segmf_ops) 534 return (EINVAL); 535 536 /* 537 * Don't mess with dom0. 538 * 539 * Only allow the domid to be set once for the segment. 540 * After that attempts to add mappings to this segment for 541 * other domains explicitly fails. 542 */ 543 544 if (domid == 0 || domid == DOMID_SELF) 545 return (EACCES); 546 547 mutex_enter(&data->lock); 548 549 if (data->domid == 0) 550 data->domid = domid; 551 552 if (data->domid != domid) { 553 error = EINVAL; 554 goto out; 555 } 556 557 base = seg_page(seg, addr); 558 559 for (i = 0; i < pgcnt; i++) { 560 data->map[base + i].t_type = SEGMF_MAP_MFN; 561 data->map[base + i].u.m.m_mfn = mfn++; 562 } 563 564 fc = segmf_fault_range(seg->s_as->a_hat, seg, addr, 565 pgcnt * MMU_PAGESIZE, F_SOFTLOCK, S_OTHER); 566 567 if (fc != 0) { 568 error = fc_decode(fc); 569 for (i = 0; i < pgcnt; i++) { 570 data->map[base + i].t_type = SEGMF_MAP_EMPTY; 571 } 572 } 573 574 out: 575 mutex_exit(&data->lock); 576 return (error); 577 } 578 579 int 580 segmf_add_grefs(struct seg *seg, caddr_t addr, uint_t flags, 581 grant_ref_t *grefs, uint_t cnt, domid_t domid) 582 { 583 struct segmf_data *data; 584 segmf_map_t *map; 585 faultcode_t fc; 586 uint_t idx; 587 uint_t i; 588 int e; 589 590 if (seg->s_ops != &segmf_ops) 591 return (EINVAL); 592 593 /* 594 * Don't mess with dom0. 595 * 596 * Only allow the domid to be set once for the segment. 597 * After that attempts to add mappings to this segment for 598 * other domains explicitly fails. 599 */ 600 601 if (domid == 0 || domid == DOMID_SELF) 602 return (EACCES); 603 604 data = seg->s_data; 605 idx = seg_page(seg, addr); 606 map = &data->map[idx]; 607 e = 0; 608 609 mutex_enter(&data->lock); 610 611 if (data->domid == 0) 612 data->domid = domid; 613 614 if (data->domid != domid) { 615 e = EINVAL; 616 goto out; 617 } 618 619 /* store away the grefs passed in then fault in the pages */ 620 for (i = 0; i < cnt; i++) { 621 map[i].t_type = SEGMF_MAP_GREF; 622 map[i].u.g.g_gref = grefs[i]; 623 map[i].u.g.g_handle = 0; 624 map[i].u.g.g_flags = 0; 625 if (flags & SEGMF_GREF_WR) { 626 map[i].u.g.g_flags |= SEGMF_GFLAGS_WR; 627 } 628 } 629 fc = segmf_fault_gref_range(seg, addr, cnt); 630 if (fc != 0) { 631 e = fc_decode(fc); 632 for (i = 0; i < cnt; i++) { 633 data->map[i].t_type = SEGMF_MAP_EMPTY; 634 } 635 } 636 637 out: 638 mutex_exit(&data->lock); 639 return (e); 640 } 641 642 int 643 segmf_release_grefs(struct seg *seg, caddr_t addr, uint_t cnt) 644 { 645 gnttab_unmap_grant_ref_t mapop[SEGMF_MAX_GREFS]; 646 struct segmf_data *data; 647 segmf_map_t *map; 648 uint_t idx; 649 long e; 650 int i; 651 int n; 652 653 654 if (cnt > SEGMF_MAX_GREFS) { 655 return (-1); 656 } 657 658 idx = seg_page(seg, addr); 659 data = seg->s_data; 660 map = &data->map[idx]; 661 662 bzero(mapop, sizeof (gnttab_unmap_grant_ref_t) * cnt); 663 664 /* 665 * for each entry which isn't empty and is currently mapped, 666 * set it up for an unmap then mark them empty. 667 */ 668 n = 0; 669 for (i = 0; i < cnt; i++) { 670 ASSERT(map[i].t_type != SEGMF_MAP_MFN); 671 if ((map[i].t_type == SEGMF_MAP_GREF) && 672 (map[i].u.g.g_flags & SEGMF_GFLAGS_MAPPED)) { 673 mapop[n].handle = map[i].u.g.g_handle; 674 mapop[n].host_addr = map[i].u.g.g_ptep; 675 mapop[n].dev_bus_addr = 0; 676 n++; 677 } 678 map[i].t_type = SEGMF_MAP_EMPTY; 679 } 680 681 /* if there's nothing to unmap, just return */ 682 if (n == 0) { 683 return (0); 684 } 685 686 e = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &mapop, n); 687 if (e != 0) { 688 return (-1); 689 } 690 691 return (0); 692 } 693 694 695 void 696 segmf_add_gref_pte(struct seg *seg, caddr_t addr, uint64_t pte_ma) 697 { 698 struct segmf_data *data; 699 uint_t idx; 700 701 idx = seg_page(seg, addr); 702 data = seg->s_data; 703 704 data->map[idx].u.g.g_ptep = pte_ma; 705 } 706 707 708 static int 709 segmf_fault_gref_range(struct seg *seg, caddr_t addr, size_t cnt) 710 { 711 gnttab_map_grant_ref_t mapop[SEGMF_MAX_GREFS]; 712 struct segmf_data *data; 713 segmf_map_t *map; 714 uint_t idx; 715 int e; 716 int i; 717 718 719 if (cnt > SEGMF_MAX_GREFS) { 720 return (-1); 721 } 722 723 data = seg->s_data; 724 idx = seg_page(seg, addr); 725 map = &data->map[idx]; 726 727 bzero(mapop, sizeof (gnttab_map_grant_ref_t) * cnt); 728 729 ASSERT(map->t_type == SEGMF_MAP_GREF); 730 731 /* 732 * map in each page passed in into the user apps AS. We do this by 733 * passing the MA of the actual pte of the mapping to the hypervisor. 734 */ 735 for (i = 0; i < cnt; i++) { 736 mapop[i].host_addr = map[i].u.g.g_ptep; 737 mapop[i].dom = data->domid; 738 mapop[i].ref = map[i].u.g.g_gref; 739 mapop[i].flags = GNTMAP_host_map | GNTMAP_application_map | 740 GNTMAP_contains_pte; 741 if (!(map[i].u.g.g_flags & SEGMF_GFLAGS_WR)) { 742 mapop[i].flags |= GNTMAP_readonly; 743 } 744 } 745 e = xen_map_gref(GNTTABOP_map_grant_ref, mapop, cnt, B_TRUE); 746 if ((e != 0) || (mapop[0].status != GNTST_okay)) { 747 return (FC_MAKE_ERR(EFAULT)); 748 } 749 750 /* save handle for segmf_release_grefs() and mark it as mapped */ 751 for (i = 0; i < cnt; i++) { 752 ASSERT(mapop[i].status == GNTST_okay); 753 map[i].u.g.g_handle = mapop[i].handle; 754 map[i].u.g.g_flags |= SEGMF_GFLAGS_MAPPED; 755 } 756 757 return (0); 758 } 759 760 static struct seg_ops segmf_ops = { 761 segmf_dup, 762 segmf_unmap, 763 segmf_free, 764 segmf_fault, 765 segmf_faulta, 766 segmf_setprot, 767 segmf_checkprot, 768 (int (*)())segmf_kluster, 769 (size_t (*)(struct seg *))NULL, /* swapout */ 770 segmf_sync, 771 segmf_incore, 772 segmf_lockop, 773 segmf_getprot, 774 segmf_getoffset, 775 segmf_gettype, 776 segmf_getvp, 777 segmf_advise, 778 segmf_dump, 779 segmf_pagelock, 780 segmf_setpagesize, 781 segmf_getmemid, 782 segmf_getpolicy, 783 segmf_capable 784 }; 785