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 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <cmd_mem.h> 29 #include <cmd_bank.h> 30 #include <cmd_dimm.h> 31 #include <cmd.h> 32 33 #include <errno.h> 34 #include <string.h> 35 #include <strings.h> 36 #include <fcntl.h> 37 #include <unistd.h> 38 #include <fm/fmd_api.h> 39 #include <sys/fm/protocol.h> 40 #include <sys/mem.h> 41 #include <sys/nvpair.h> 42 43 void 44 cmd_bank_add_dimm(fmd_hdl_t *hdl, cmd_bank_t *bank, cmd_dimm_t *dimm) 45 { 46 cmd_bank_memb_t *bm; 47 48 fmd_hdl_debug(hdl, "attaching dimm %s to bank %s\n", dimm->dimm_unum, 49 bank->bank_unum); 50 51 dimm->dimm_bank = bank; 52 53 bm = fmd_hdl_zalloc(hdl, sizeof (cmd_bank_memb_t), FMD_SLEEP); 54 bm->bm_dimm = dimm; 55 cmd_list_append(&bank->bank_dimms, bm); 56 } 57 58 void 59 cmd_bank_remove_dimm(fmd_hdl_t *hdl, cmd_bank_t *bank, cmd_dimm_t *dimm) 60 { 61 cmd_bank_memb_t *bm; 62 63 fmd_hdl_debug(hdl, "detaching dimm %s from bank %s\n", dimm->dimm_unum, 64 bank->bank_unum); 65 66 for (bm = cmd_list_next(&bank->bank_dimms); bm != NULL; 67 bm = cmd_list_next(bm)) { 68 if (bm->bm_dimm != dimm) 69 continue; 70 71 cmd_list_delete(&bank->bank_dimms, bm); 72 dimm->dimm_bank = NULL; 73 fmd_hdl_free(hdl, bm, sizeof (cmd_bank_memb_t)); 74 return; 75 } 76 77 fmd_hdl_abort(hdl, "attempt to disconnect dimm from non-parent bank\n"); 78 } 79 80 static void 81 bank_dimmlist_create(fmd_hdl_t *hdl, cmd_bank_t *bank) 82 { 83 cmd_dimm_t *dimm; 84 85 for (dimm = cmd_list_next(&cmd.cmd_dimms); dimm != NULL; 86 dimm = cmd_list_next(dimm)) { 87 if (fmd_nvl_fmri_contains(hdl, bank->bank_asru_nvl, 88 dimm->dimm_asru_nvl)) 89 cmd_bank_add_dimm(hdl, bank, dimm); 90 } 91 } 92 93 static void 94 bank_dimmlist_free(fmd_hdl_t *hdl, cmd_bank_t *bank) 95 { 96 cmd_bank_memb_t *bm; 97 98 while ((bm = cmd_list_next(&bank->bank_dimms)) != NULL) { 99 cmd_list_delete(&bank->bank_dimms, bm); 100 bm->bm_dimm->dimm_bank = NULL; 101 fmd_hdl_free(hdl, bm, sizeof (cmd_bank_memb_t)); 102 } 103 } 104 105 nvlist_t * 106 cmd_bank_fru(cmd_bank_t *bank) 107 { 108 return (bank->bank_asru_nvl); 109 } 110 111 nvlist_t * 112 cmd_bank_create_fault(fmd_hdl_t *hdl, cmd_bank_t *bank, const char *fltnm, 113 uint_t cert) 114 { 115 return (cmd_nvl_create_fault(hdl, fltnm, cert, bank->bank_asru_nvl, 116 bank->bank_asru_nvl, NULL)); 117 } 118 119 static void 120 bank_free(fmd_hdl_t *hdl, cmd_bank_t *bank, int destroy) 121 { 122 if (bank->bank_case.cc_cp != NULL) 123 cmd_case_fini(hdl, bank->bank_case.cc_cp, destroy); 124 125 bank_dimmlist_free(hdl, bank); 126 cmd_fmri_fini(hdl, &bank->bank_asru, destroy); 127 128 if (destroy) 129 fmd_buf_destroy(hdl, NULL, bank->bank_bufname); 130 cmd_list_delete(&cmd.cmd_banks, bank); 131 fmd_hdl_free(hdl, bank, sizeof (cmd_bank_t)); 132 } 133 134 void 135 cmd_bank_destroy(fmd_hdl_t *hdl, cmd_bank_t *bank) 136 { 137 fmd_stat_destroy(hdl, 1, &(bank->bank_retstat)); 138 bank_free(hdl, bank, FMD_B_TRUE); 139 } 140 141 static cmd_bank_t * 142 bank_lookup_by_unum(const char *unum) 143 { 144 cmd_bank_t *bank; 145 146 for (bank = cmd_list_next(&cmd.cmd_banks); bank != NULL; 147 bank = cmd_list_next(bank)) { 148 if (strcmp(bank->bank_unum, unum) == 0) 149 return (bank); 150 } 151 152 return (NULL); 153 } 154 155 cmd_bank_t * 156 cmd_bank_create(fmd_hdl_t *hdl, nvlist_t *asru) 157 { 158 cmd_bank_t *bank; 159 const char *unum; 160 161 if (!fmd_nvl_fmri_present(hdl, asru)) { 162 fmd_hdl_debug(hdl, "dimm_lookup: discarding old ereport\n"); 163 return (NULL); 164 } 165 166 if ((unum = cmd_fmri_get_unum(asru)) == NULL) { 167 CMD_STAT_BUMP(bad_mem_asru); 168 return (NULL); 169 } 170 171 fmd_hdl_debug(hdl, "bank_create: creating new bank %s\n", unum); 172 CMD_STAT_BUMP(bank_creat); 173 174 bank = fmd_hdl_zalloc(hdl, sizeof (cmd_bank_t), FMD_SLEEP); 175 bank->bank_nodetype = CMD_NT_BANK; 176 bank->bank_version = CMD_BANK_VERSION; 177 178 cmd_bufname(bank->bank_bufname, sizeof (bank->bank_bufname), "bank_%s", 179 unum); 180 cmd_fmri_init(hdl, &bank->bank_asru, asru, "bank_asru_%s", unum); 181 182 (void) nvlist_lookup_string(bank->bank_asru_nvl, FM_FMRI_MEM_UNUM, 183 (char **)&bank->bank_unum); 184 185 bank_dimmlist_create(hdl, bank); 186 187 cmd_mem_retirestat_create(hdl, &bank->bank_retstat, bank->bank_unum, 0, 188 CMD_BANK_STAT_PREFIX); 189 190 cmd_list_append(&cmd.cmd_banks, bank); 191 cmd_bank_dirty(hdl, bank); 192 193 return (bank); 194 } 195 196 cmd_bank_t * 197 cmd_bank_lookup(fmd_hdl_t *hdl, nvlist_t *asru) 198 { 199 cmd_bank_t *bank; 200 const char *unum; 201 202 if ((unum = cmd_fmri_get_unum(asru)) == NULL) { 203 CMD_STAT_BUMP(bad_mem_asru); 204 return (NULL); 205 } 206 207 bank = bank_lookup_by_unum(unum); 208 209 if (bank != NULL && !fmd_nvl_fmri_present(hdl, bank->bank_asru_nvl)) { 210 fmd_hdl_debug(hdl, "bank_lookup: discarding old bank\n"); 211 cmd_bank_destroy(hdl, bank); 212 return (NULL); 213 } 214 215 return (bank); 216 } 217 218 static cmd_bank_t * 219 bank_v0tov1(fmd_hdl_t *hdl, cmd_bank_0_t *old, size_t oldsz) 220 { 221 cmd_bank_t *new; 222 223 if (oldsz != sizeof (cmd_bank_0_t)) { 224 fmd_hdl_abort(hdl, "size of state doesn't match size of " 225 "version 0 state (%u bytes).\n", sizeof (cmd_bank_0_t)); 226 } 227 228 new = fmd_hdl_zalloc(hdl, sizeof (cmd_bank_t), FMD_SLEEP); 229 new->bank_header = old->bank0_header; 230 new->bank_version = CMD_BANK_VERSION; 231 new->bank_asru = old->bank0_asru; 232 new->bank_nretired = old->bank0_nretired; 233 234 fmd_hdl_free(hdl, old, oldsz); 235 return (new); 236 } 237 238 static cmd_bank_t * 239 bank_wrapv1(fmd_hdl_t *hdl, cmd_bank_pers_t *pers, size_t psz) 240 { 241 cmd_bank_t *bank; 242 243 if (psz != sizeof (cmd_bank_pers_t)) { 244 fmd_hdl_abort(hdl, "size of state doesn't match size of " 245 "version 1 state (%u bytes).\n", sizeof (cmd_bank_pers_t)); 246 } 247 248 bank = fmd_hdl_zalloc(hdl, sizeof (cmd_bank_t), FMD_SLEEP); 249 bcopy(pers, bank, sizeof (cmd_bank_pers_t)); 250 fmd_hdl_free(hdl, pers, psz); 251 return (bank); 252 } 253 254 void * 255 cmd_bank_restore(fmd_hdl_t *hdl, fmd_case_t *cp, cmd_case_ptr_t *ptr) 256 { 257 cmd_bank_t *bank; 258 259 for (bank = cmd_list_next(&cmd.cmd_banks); bank != NULL; 260 bank = cmd_list_next(bank)) { 261 if (strcmp(bank->bank_bufname, ptr->ptr_name) == 0) 262 break; 263 } 264 265 if (bank == NULL) { 266 int migrated = 0; 267 size_t banksz; 268 269 fmd_hdl_debug(hdl, "restoring bank from %s\n", ptr->ptr_name); 270 271 if ((banksz = fmd_buf_size(hdl, NULL, ptr->ptr_name)) == 0) { 272 fmd_hdl_abort(hdl, "bank referenced by case %s does " 273 "not exist in saved state\n", 274 fmd_case_uuid(hdl, cp)); 275 } else if (banksz > CMD_BANK_MAXSIZE || 276 banksz < CMD_BANK_MINSIZE) { 277 fmd_hdl_abort(hdl, "bank buffer referenced by case %s " 278 "is out of bounds (is %u bytes, max %u, min %u)\n", 279 fmd_case_uuid(hdl, cp), banksz, 280 CMD_BANK_MAXSIZE, CMD_BANK_MAXSIZE); 281 } 282 283 if ((bank = cmd_buf_read(hdl, NULL, ptr->ptr_name, 284 banksz)) == NULL) { 285 fmd_hdl_abort(hdl, "failed to read bank buf %s", 286 ptr->ptr_name); 287 } 288 289 fmd_hdl_debug(hdl, "found %d in version field\n", 290 bank->bank_version); 291 292 if (CMD_BANK_VERSIONED(bank)) { 293 switch (bank->bank_version) { 294 case CMD_BANK_VERSION_1: 295 bank = bank_wrapv1(hdl, (cmd_bank_pers_t *)bank, 296 banksz); 297 break; 298 default: 299 fmd_hdl_abort(hdl, "unknown version (found %d) " 300 "for bank state referenced by case %s.\n", 301 bank->bank_version, fmd_case_uuid(hdl, cp)); 302 break; 303 } 304 } else { 305 bank = bank_v0tov1(hdl, (cmd_bank_0_t *)bank, banksz); 306 migrated = 1; 307 } 308 309 if (migrated) { 310 CMD_STAT_BUMP(bank_migrat); 311 cmd_bank_dirty(hdl, bank); 312 } 313 314 cmd_fmri_restore(hdl, &bank->bank_asru); 315 316 if ((errno = nvlist_lookup_string(bank->bank_asru_nvl, 317 FM_FMRI_MEM_UNUM, (char **)&bank->bank_unum)) != 0) 318 fmd_hdl_abort(hdl, "failed to retrieve nuum from asru"); 319 320 bank_dimmlist_create(hdl, bank); 321 322 cmd_mem_retirestat_create(hdl, &bank->bank_retstat, 323 bank->bank_unum, bank->bank_nretired, CMD_BANK_STAT_PREFIX); 324 325 cmd_list_append(&cmd.cmd_banks, bank); 326 } 327 328 switch (ptr->ptr_subtype) { 329 case BUG_PTR_BANK_CASE: 330 fmd_hdl_debug(hdl, "recovering from out of order page ptr\n"); 331 cmd_case_redirect(hdl, cp, CMD_PTR_BANK_CASE); 332 /*FALLTHROUGH*/ 333 case CMD_PTR_BANK_CASE: 334 cmd_mem_case_restore(hdl, &bank->bank_case, cp, "bank", 335 bank->bank_unum); 336 break; 337 default: 338 fmd_hdl_abort(hdl, "invalid %s subtype %d\n", 339 ptr->ptr_name, ptr->ptr_subtype); 340 } 341 342 return (bank); 343 } 344 345 void 346 cmd_bank_validate(fmd_hdl_t *hdl) 347 { 348 cmd_bank_t *bank, *next; 349 350 for (bank = cmd_list_next(&cmd.cmd_banks); bank != NULL; bank = next) { 351 next = cmd_list_next(bank); 352 353 if (!fmd_nvl_fmri_present(hdl, bank->bank_asru_nvl)) 354 cmd_bank_destroy(hdl, bank); 355 } 356 } 357 358 void 359 cmd_bank_dirty(fmd_hdl_t *hdl, cmd_bank_t *bank) 360 { 361 if (fmd_buf_size(hdl, NULL, bank->bank_bufname) != 362 sizeof (cmd_bank_pers_t)) 363 fmd_buf_destroy(hdl, NULL, bank->bank_bufname); 364 365 /* No need to rewrite the FMRIs in the bank - they don't change */ 366 fmd_buf_write(hdl, NULL, bank->bank_bufname, &bank->bank_pers, 367 sizeof (cmd_bank_pers_t)); 368 } 369 370 void 371 cmd_bank_gc(fmd_hdl_t *hdl) 372 { 373 cmd_bank_validate(hdl); 374 } 375 376 void 377 cmd_bank_fini(fmd_hdl_t *hdl) 378 { 379 cmd_bank_t *bank; 380 381 while ((bank = cmd_list_next(&cmd.cmd_banks)) != NULL) 382 bank_free(hdl, bank, FMD_B_FALSE); 383 } 384