/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Support routines for managing state related to memory modules. */ #include #include #include #include #ifdef sun4u #include #endif #ifdef sun4v #include #endif #include #include #include #include #include #include #include #include #include const char * cmd_fmri_get_unum(nvlist_t *fmri) { const char *scheme, *unum; uint8_t vers; if (nvlist_lookup_pairs(fmri, 0, FM_VERSION, DATA_TYPE_UINT8, &vers, FM_FMRI_SCHEME, DATA_TYPE_STRING, &scheme, FM_FMRI_MEM_UNUM, DATA_TYPE_STRING, &unum, NULL) != 0 || vers > FM_MEM_SCHEME_VERSION || strcmp(scheme, FM_FMRI_SCHEME_MEM) != 0) return (NULL); return (unum); } char * cmd_mem_serdnm_create(fmd_hdl_t *hdl, const char *serdbase, const char *unum) { const char *fmt = "%s_%s_serd"; size_t sz = snprintf(NULL, 0, fmt, serdbase, unum) + 1; char *nm = fmd_hdl_alloc(hdl, sz, FMD_SLEEP); (void) snprintf(nm, sz, fmt, serdbase, unum); return (nm); } char * cmd_page_serdnm_create(fmd_hdl_t *hdl, const char *serdbase, uint64_t phys_addr) { const char *fmt = "%s_%llXserd"; size_t sz = snprintf(NULL, 0, fmt, serdbase, phys_addr) + 1; char *nm = fmd_hdl_alloc(hdl, sz, FMD_SLEEP); (void) snprintf(nm, sz, fmt, serdbase, phys_addr); return (nm); } char * cmd_mq_serdnm_create(fmd_hdl_t *hdl, const char *serdbase, uint64_t phys_addr, uint16_t cw, uint16_t pos) { const char *fmt = "%s_%llX_%x_%x_serd"; size_t sz = snprintf(NULL, 0, fmt, serdbase, phys_addr, cw, pos) + 1; char *nm = fmd_hdl_alloc(hdl, sz, FMD_SLEEP); (void) snprintf(nm, sz, fmt, serdbase, phys_addr, cw, pos); return (nm); } void cmd_mem_case_restore(fmd_hdl_t *hdl, cmd_case_t *cc, fmd_case_t *cp, const char *serdbase, const char *unum) { cmd_case_restore(hdl, cc, cp, cmd_mem_serdnm_create(hdl, serdbase, unum)); } void cmd_mem_retirestat_create(fmd_hdl_t *hdl, fmd_stat_t *st, const char *unum, uint64_t value, const char *prefix) { char *c; /* * Prior to Niagara-2, every bank had to have at least two dimms; it * was therefore impossible for the retirestat of a bank to ever have * the same name (strcmp() == 0) as that of a dimm. * * Niagara-2 and VF, in "single channel mode" , retrieve an entire * cache line from a single dimm. We therefore use a different * prefix to name the bank retirestat vs. the dimm retirestat, * or else the DE will abort trying to register a duplicate stat name * with fmd. */ (void) snprintf(st->fmds_name, sizeof (st->fmds_name), "%s%s", prefix, unum); (void) snprintf(st->fmds_desc, sizeof (st->fmds_desc), "retirements for %s", unum); st->fmds_type = FMD_TYPE_UINT64; st->fmds_value.ui64 = value; /* * Sanitize the name of the statistic -- standard unums won't get * by fmd's validity checker. */ for (c = st->fmds_name; *c != '\0'; c++) { if (!isupper(*c) && !islower(*c) && !isdigit(*c) && *c != '-' && *c != '_' && *c != '.') *c = '_'; } (void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, 1, st); } int cmd_mem_thresh_check(fmd_hdl_t *hdl, uint_t nret) { ulong_t npages = cmd_mem_get_phys_pages(hdl); ulong_t wrnpgs; fmd_hdl_debug(hdl, "thresh_check: npages is %lu\n", npages); if (npages == 0) { return (0); } if (cmd.cmd_thresh_abs_sysmem != 0) { wrnpgs = cmd.cmd_thresh_abs_sysmem; } else { /* threshold is in thousandths of a percent */ wrnpgs = npages * cmd.cmd_thresh_tpct_sysmem / 100000; } fmd_hdl_debug(hdl, "thresh_check: nret %u, wrn %lu\n", nret, wrnpgs); return (nret > wrnpgs); } nvlist_t * cmd_mem_fmri_create(const char *unum, char **serids, size_t nserids) { nvlist_t *fmri; if ((errno = nvlist_alloc(&fmri, NV_UNIQUE_NAME, 0)) != 0) return (NULL); if ((errno = nvlist_add_uint8(fmri, FM_VERSION, FM_MEM_SCHEME_VERSION)) != 0 || (errno = nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM)) != 0 || (errno = nvlist_add_string(fmri, FM_FMRI_MEM_UNUM, unum)) != 0) { nvlist_free(fmri); return (NULL); } if ((nserids > 0) && (serids != NULL)) { (void) nvlist_add_string_array(fmri, FM_FMRI_MEM_SERIAL_ID, serids, nserids); } return (fmri); } nvlist_t * cmd_mem_fmri_derive(fmd_hdl_t *hdl, uint64_t afar, uint64_t afsr, uint16_t synd) { mem_name_t mn; nvlist_t *fmri; int fd; if ((fd = open("/dev/mem", O_RDONLY)) < 0) return (NULL); mn.m_addr = afar; mn.m_synd = synd; mn.m_type[0] = afsr; mn.m_type[1] = 0; mn.m_namelen = 100; for (;;) { mn.m_name = fmd_hdl_alloc(hdl, mn.m_namelen, FMD_SLEEP); if (ioctl(fd, MEM_NAME, &mn) == 0) break; fmd_hdl_free(hdl, mn.m_name, mn.m_namelen); if (errno != ENOSPC) { (void) close(fd); return (NULL); } mn.m_namelen *= 2; } (void) close(fd); fmri = cmd_mem_fmri_create(mn.m_name, NULL, 0); fmd_hdl_free(hdl, mn.m_name, mn.m_namelen); return (fmri); } void cmd_iorxefrx_queue(fmd_hdl_t *hdl, cmd_iorxefrx_t *rf) { fmd_hdl_debug(hdl, "queueing IOxE/RxE/FRx for matching\n"); rf->rf_expid = fmd_timer_install(hdl, (void *)CMD_TIMERTYPE_MEM, NULL, cmd.cmd_iorxefrx_window); cmd_list_append(&cmd.cmd_iorxefrx, rf); } void cmd_iorxefrx_free(fmd_hdl_t *hdl, cmd_iorxefrx_t *rf) { /* It's not persisted, so just remove it */ cmd_list_delete(&cmd.cmd_iorxefrx, rf); fmd_hdl_free(hdl, rf, sizeof (cmd_iorxefrx_t)); } void cmd_mem_timeout(fmd_hdl_t *hdl, id_t id) { cmd_iorxefrx_t *rf; for (rf = cmd_list_next(&cmd.cmd_iorxefrx); rf != NULL; rf = cmd_list_next(rf)) { if (rf->rf_expid == id) { fmd_hdl_debug(hdl, "reclaiming iorxefrx tid %d\n", id); cmd_iorxefrx_free(hdl, rf); return; } } } void cmd_mem_gc(fmd_hdl_t *hdl) { cmd_dimm_gc(hdl); cmd_bank_gc(hdl); #ifdef sun4v cmd_branch_gc(hdl); #endif } void cmd_mem_fini(fmd_hdl_t *hdl) { cmd_iorxefrx_t *rf; cmd_dimm_fini(hdl); cmd_bank_fini(hdl); #ifdef sun4v cmd_branch_fini(hdl); #endif while ((rf = cmd_list_next(&cmd.cmd_iorxefrx)) != NULL) cmd_iorxefrx_free(hdl, rf); }