/*ARGSUSED*/ static int fm_ioctl_page_retire(int cmd, nvlist_t *invl, nvlist_t **onvlp) { uint64_t pa; nvlist_t *fmri; int err; if (is_i86xpv) return (ENOTSUP); if ((err = nvlist_lookup_nvlist(invl, FM_PAGE_RETIRE_FMRI, &fmri)) != 0) return (err); if ((err = fm_get_paddr(fmri, &pa)) != 0) return (err); switch (cmd) { case FM_IOC_PAGE_STATUS: return (page_retire_check(pa, NULL)); case FM_IOC_PAGE_RETIRE: return (page_retire(pa, PR_FMA)); case FM_IOC_PAGE_UNRETIRE: return (page_unretire(pa)); } return (ENOTTY); }
/* * Given a PA, execute the given page retire command on it. */ static int mmioctl_page_retire(int cmd, intptr_t data) { extern int page_retire_test(void); uint64_t pa; if (copyin((void *)data, &pa, sizeof (uint64_t))) { return (EFAULT); } switch (cmd) { case MEM_PAGE_ISRETIRED: return (page_retire_check(pa, NULL)); case MEM_PAGE_UNRETIRE: return (page_unretire(pa)); case MEM_PAGE_RETIRE: return (page_retire(pa, PR_FMA)); case MEM_PAGE_RETIRE_MCE: return (page_retire(pa, PR_MCE)); case MEM_PAGE_RETIRE_UE: return (page_retire(pa, PR_UE)); case MEM_PAGE_GETERRORS: { uint64_t page_errors; int rc = page_retire_check(pa, &page_errors); if (copyout(&page_errors, (void *)data, sizeof (uint64_t))) { return (EFAULT); } return (rc); } case MEM_PAGE_RETIRE_TEST: return (page_retire_test()); } return (EINVAL); }
/* * Page retire self-test. For now, it always returns 0. */ int page_retire_test(void) { page_t *first, *pp, *cpp, *cpp2, *lpp; /* * Tests the corner case where a large page can't be retired * because one of the constituent pages is locked. We mark * one page to be retired and try to retire it, and mark the * other page to be retired but don't try to retire it, so * that page_unlock() in the failure path will recurse and try * to retire THAT page. This is the worst possible situation * we can get ourselves into. */ memsegs_lock(0); pp = first = page_first(); do { if (pp->p_szc && PP_PAGEROOT(pp) == pp) { cpp = pp + 1; lpp = PP_ISFREE(pp)? pp : pp + 2; cpp2 = pp + 3; if (!page_trylock(lpp, pp == lpp? SE_EXCL : SE_SHARED)) continue; if (!page_trylock(cpp, SE_EXCL)) { page_unlock(lpp); continue; } page_settoxic(cpp, PR_FMA | PR_BUSY); page_settoxic(cpp2, PR_FMA); page_tryretire(cpp); /* will fail */ page_unlock(lpp); (void) page_retire(cpp->p_pagenum, PR_FMA); (void) page_retire(cpp2->p_pagenum, PR_FMA); } } while ((pp = page_next(pp)) != first); memsegs_unlock(0); return (0); }