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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Support routines for managing state related to memory modules. 28 */ 29 30 #include <cmd_mem.h> 31 #include <cmd_dimm.h> 32 #include <cmd_bank.h> 33 #include <cmd.h> 34 #ifdef sun4u 35 #include <cmd_dp.h> 36 #endif 37 #ifdef sun4v 38 #include <cmd_branch.h> 39 #endif 40 41 #include <errno.h> 42 #include <strings.h> 43 #include <ctype.h> 44 #include <fcntl.h> 45 #include <unistd.h> 46 #include <fm/fmd_api.h> 47 #include <sys/fm/protocol.h> 48 #include <sys/mem.h> 49 #include <sys/nvpair.h> 50 51 const char * 52 cmd_fmri_get_unum(nvlist_t *fmri) 53 { 54 const char *scheme, *unum; 55 uint8_t vers; 56 57 if (nvlist_lookup_pairs(fmri, 0, 58 FM_VERSION, DATA_TYPE_UINT8, &vers, 59 FM_FMRI_SCHEME, DATA_TYPE_STRING, &scheme, 60 FM_FMRI_MEM_UNUM, DATA_TYPE_STRING, &unum, 61 NULL) != 0 || vers > FM_MEM_SCHEME_VERSION || 62 strcmp(scheme, FM_FMRI_SCHEME_MEM) != 0) 63 return (NULL); 64 65 return (unum); 66 } 67 68 char * 69 cmd_mem_serdnm_create(fmd_hdl_t *hdl, const char *serdbase, const char *unum) 70 { 71 const char *fmt = "%s_%s_serd"; 72 size_t sz = snprintf(NULL, 0, fmt, serdbase, unum) + 1; 73 char *nm = fmd_hdl_alloc(hdl, sz, FMD_SLEEP); 74 (void) snprintf(nm, sz, fmt, serdbase, unum); 75 76 return (nm); 77 } 78 79 char * 80 cmd_page_serdnm_create(fmd_hdl_t *hdl, const char *serdbase, 81 uint64_t phys_addr) 82 { 83 const char *fmt = "%s_%llXserd"; 84 size_t sz = snprintf(NULL, 0, fmt, serdbase, phys_addr) + 1; 85 char *nm = fmd_hdl_alloc(hdl, sz, FMD_SLEEP); 86 (void) snprintf(nm, sz, fmt, serdbase, phys_addr); 87 88 return (nm); 89 } 90 91 char * 92 cmd_mq_serdnm_create(fmd_hdl_t *hdl, const char *serdbase, 93 uint64_t phys_addr, uint16_t cw, uint16_t pos) 94 { 95 const char *fmt = "%s_%llX_%x_%x_serd"; 96 size_t sz = snprintf(NULL, 0, fmt, serdbase, phys_addr, cw, pos) + 1; 97 char *nm = fmd_hdl_alloc(hdl, sz, FMD_SLEEP); 98 (void) snprintf(nm, sz, fmt, serdbase, phys_addr, cw, pos); 99 100 return (nm); 101 } 102 103 void 104 cmd_mem_case_restore(fmd_hdl_t *hdl, cmd_case_t *cc, fmd_case_t *cp, 105 const char *serdbase, const char *unum) 106 { 107 cmd_case_restore(hdl, cc, cp, cmd_mem_serdnm_create(hdl, serdbase, 108 unum)); 109 } 110 111 void 112 cmd_mem_retirestat_create(fmd_hdl_t *hdl, fmd_stat_t *st, const char *unum, 113 uint64_t value, const char *prefix) 114 { 115 char *c; 116 117 /* 118 * Prior to Niagara-2, every bank had to have at least two dimms; it 119 * was therefore impossible for the retirestat of a bank to ever have 120 * the same name (strcmp() == 0) as that of a dimm. 121 * 122 * Niagara-2 and VF, in "single channel mode" , retrieve an entire 123 * cache line from a single dimm. We therefore use a different 124 * prefix to name the bank retirestat vs. the dimm retirestat, 125 * or else the DE will abort trying to register a duplicate stat name 126 * with fmd. 127 */ 128 (void) snprintf(st->fmds_name, sizeof (st->fmds_name), "%s%s", 129 prefix, unum); 130 (void) snprintf(st->fmds_desc, sizeof (st->fmds_desc), 131 "retirements for %s", unum); 132 st->fmds_type = FMD_TYPE_UINT64; 133 st->fmds_value.ui64 = value; 134 135 /* 136 * Sanitize the name of the statistic -- standard unums won't get 137 * by fmd's validity checker. 138 */ 139 for (c = st->fmds_name; *c != '\0'; c++) { 140 if (!isupper(*c) && !islower(*c) && 141 !isdigit(*c) && *c != '-' && *c != '_' && *c != '.') 142 *c = '_'; 143 } 144 145 (void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, 1, st); 146 } 147 148 int 149 cmd_mem_thresh_check(fmd_hdl_t *hdl, uint_t nret) 150 { 151 ulong_t npages = cmd_mem_get_phys_pages(hdl); 152 ulong_t wrnpgs; 153 154 fmd_hdl_debug(hdl, "thresh_check: npages is %lu\n", npages); 155 if (npages == 0) { 156 return (0); 157 } 158 159 if (cmd.cmd_thresh_abs_sysmem != 0) { 160 wrnpgs = cmd.cmd_thresh_abs_sysmem; 161 } else { 162 /* threshold is in thousandths of a percent */ 163 wrnpgs = npages * cmd.cmd_thresh_tpct_sysmem / 100000; 164 } 165 166 fmd_hdl_debug(hdl, "thresh_check: nret %u, wrn %lu\n", nret, wrnpgs); 167 168 return (nret > wrnpgs); 169 } 170 171 nvlist_t * 172 cmd_mem_fmri_create(const char *unum, char **serids, size_t nserids) 173 { 174 nvlist_t *fmri; 175 176 if ((errno = nvlist_alloc(&fmri, NV_UNIQUE_NAME, 0)) != 0) 177 return (NULL); 178 179 if ((errno = nvlist_add_uint8(fmri, FM_VERSION, 180 FM_MEM_SCHEME_VERSION)) != 0 || (errno = nvlist_add_string(fmri, 181 FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM)) != 0 || (errno = 182 nvlist_add_string(fmri, FM_FMRI_MEM_UNUM, unum)) != 0) { 183 nvlist_free(fmri); 184 return (NULL); 185 } 186 187 if ((nserids > 0) && (serids != NULL)) { 188 (void) nvlist_add_string_array(fmri, FM_FMRI_MEM_SERIAL_ID, 189 serids, nserids); 190 } 191 return (fmri); 192 } 193 194 nvlist_t * 195 cmd_mem_fmri_derive(fmd_hdl_t *hdl, uint64_t afar, uint64_t afsr, uint16_t synd) 196 { 197 mem_name_t mn; 198 nvlist_t *fmri; 199 int fd; 200 201 if ((fd = open("/dev/mem", O_RDONLY)) < 0) 202 return (NULL); 203 204 mn.m_addr = afar; 205 mn.m_synd = synd; 206 mn.m_type[0] = afsr; 207 mn.m_type[1] = 0; 208 mn.m_namelen = 100; 209 210 for (;;) { 211 mn.m_name = fmd_hdl_alloc(hdl, mn.m_namelen, FMD_SLEEP); 212 213 if (ioctl(fd, MEM_NAME, &mn) == 0) 214 break; 215 216 fmd_hdl_free(hdl, mn.m_name, mn.m_namelen); 217 218 if (errno != ENOSPC) { 219 (void) close(fd); 220 return (NULL); 221 } 222 223 mn.m_namelen *= 2; 224 } 225 226 (void) close(fd); 227 228 fmri = cmd_mem_fmri_create(mn.m_name, NULL, 0); 229 fmd_hdl_free(hdl, mn.m_name, mn.m_namelen); 230 231 return (fmri); 232 } 233 234 void 235 cmd_iorxefrx_queue(fmd_hdl_t *hdl, cmd_iorxefrx_t *rf) 236 { 237 238 fmd_hdl_debug(hdl, "queueing IOxE/RxE/FRx for matching\n"); 239 240 rf->rf_expid = fmd_timer_install(hdl, (void *)CMD_TIMERTYPE_MEM, NULL, 241 cmd.cmd_iorxefrx_window); 242 cmd_list_append(&cmd.cmd_iorxefrx, rf); 243 } 244 245 void 246 cmd_iorxefrx_free(fmd_hdl_t *hdl, cmd_iorxefrx_t *rf) 247 { 248 /* It's not persisted, so just remove it */ 249 cmd_list_delete(&cmd.cmd_iorxefrx, rf); 250 fmd_hdl_free(hdl, rf, sizeof (cmd_iorxefrx_t)); 251 } 252 253 void 254 cmd_mem_timeout(fmd_hdl_t *hdl, id_t id) 255 { 256 cmd_iorxefrx_t *rf; 257 258 for (rf = cmd_list_next(&cmd.cmd_iorxefrx); rf != NULL; 259 rf = cmd_list_next(rf)) { 260 if (rf->rf_expid == id) { 261 fmd_hdl_debug(hdl, "reclaiming iorxefrx tid %d\n", id); 262 cmd_iorxefrx_free(hdl, rf); 263 return; 264 } 265 } 266 } 267 268 void 269 cmd_mem_gc(fmd_hdl_t *hdl) 270 { 271 cmd_dimm_gc(hdl); 272 cmd_bank_gc(hdl); 273 #ifdef sun4v 274 cmd_branch_gc(hdl); 275 #endif 276 } 277 278 void 279 cmd_mem_fini(fmd_hdl_t *hdl) 280 { 281 cmd_iorxefrx_t *rf; 282 283 cmd_dimm_fini(hdl); 284 cmd_bank_fini(hdl); 285 #ifdef sun4v 286 cmd_branch_fini(hdl); 287 #endif 288 289 while ((rf = cmd_list_next(&cmd.cmd_iorxefrx)) != NULL) 290 cmd_iorxefrx_free(hdl, rf); 291 } 292