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