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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 26 /* 27 * Support routines for managing per-page state. 28 */ 29 30 #include <gmem_page.h> 31 #include <gmem_mem.h> 32 #include <gmem_dimm.h> 33 #include <gmem.h> 34 35 #include <errno.h> 36 #include <strings.h> 37 #include <fm/fmd_api.h> 38 #include <sys/fm/protocol.h> 39 40 static void 41 page_write(fmd_hdl_t *hdl, gmem_page_t *page) 42 { 43 fmd_buf_write(hdl, NULL, page->page_bufname, page, 44 sizeof (gmem_page_pers_t)); 45 } 46 47 static void 48 gmem_page_free(fmd_hdl_t *hdl, gmem_page_t *page, int destroy) 49 { 50 gmem_case_t *cc = &page->page_case; 51 52 if (cc->cc_cp != NULL) 53 gmem_case_fini(hdl, cc->cc_cp, destroy); 54 55 if (cc->cc_serdnm != NULL) { 56 if (fmd_serd_exists(hdl, cc->cc_serdnm) && destroy) 57 fmd_serd_destroy(hdl, cc->cc_serdnm); 58 fmd_hdl_strfree(hdl, cc->cc_serdnm); 59 } 60 61 if (destroy) 62 fmd_buf_destroy(hdl, NULL, page->page_bufname); 63 64 gmem_fmri_fini(hdl, &page->page_asru, destroy); 65 66 gmem_list_delete(&gmem.gm_pages, page); 67 fmd_hdl_free(hdl, page, sizeof (gmem_page_t)); 68 } 69 70 void 71 gmem_page_destroy(fmd_hdl_t *hdl, gmem_page_t *page) 72 { 73 fmd_hdl_debug(hdl, "destroying the page\n"); 74 gmem_page_free(hdl, page, FMD_B_TRUE); 75 } 76 77 static gmem_page_t * 78 page_lookup_by_physaddr(uint64_t pa) 79 { 80 gmem_page_t *page; 81 82 for (page = gmem_list_next(&gmem.gm_pages); page != NULL; 83 page = gmem_list_next(page)) { 84 if (page->page_physbase == pa) 85 return (page); 86 } 87 88 return (NULL); 89 } 90 91 gmem_page_t * 92 gmem_page_create(fmd_hdl_t *hdl, nvlist_t *modasru, uint64_t pa, 93 uint64_t offset) 94 { 95 gmem_page_t *page; 96 nvlist_t *asru, *hsp; 97 98 pa = pa & gmem.gm_pagemask; 99 100 fmd_hdl_debug(hdl, "page_lookup: creating new page for %llx\n", 101 (u_longlong_t)pa); 102 GMEM_STAT_BUMP(page_creat); 103 104 page = fmd_hdl_zalloc(hdl, sizeof (gmem_page_t), FMD_SLEEP); 105 page->page_nodetype = GMEM_NT_PAGE; 106 page->page_version = CMD_PAGE_VERSION; 107 page->page_physbase = pa; 108 page->page_offset = offset; 109 110 gmem_bufname(page->page_bufname, sizeof (page->page_bufname), 111 "page_%llx", (u_longlong_t)pa); 112 113 if (nvlist_dup(modasru, &asru, 0) != 0) { 114 fmd_hdl_debug(hdl, "Page create nvlist dup failed"); 115 return (NULL); 116 } 117 118 if (nvlist_alloc(&hsp, NV_UNIQUE_NAME, 0) != 0) { 119 fmd_hdl_debug(hdl, "Page create nvlist alloc failed"); 120 nvlist_free(asru); 121 return (NULL); 122 } 123 124 if (nvlist_add_uint64(hsp, FM_FMRI_MEM_PHYSADDR, 125 page->page_physbase) != 0 || 126 nvlist_add_uint64(hsp, FM_FMRI_HC_SPECIFIC_OFFSET, 127 page->page_offset) != 0 || 128 nvlist_add_nvlist(asru, FM_FMRI_HC_SPECIFIC, hsp) != 0) { 129 fmd_hdl_debug(hdl, "Page create failed to build page fmri"); 130 nvlist_free(asru); 131 nvlist_free(hsp); 132 return (NULL); 133 } 134 135 gmem_fmri_init(hdl, &page->page_asru, asru, "page_asru_%llx", 136 (u_longlong_t)pa); 137 138 nvlist_free(asru); 139 nvlist_free(hsp); 140 141 gmem_list_append(&gmem.gm_pages, page); 142 page_write(hdl, page); 143 144 return (page); 145 } 146 147 gmem_page_t * 148 gmem_page_lookup(uint64_t pa) 149 { 150 pa = pa & gmem.gm_pagemask; 151 152 return (page_lookup_by_physaddr(pa)); 153 } 154 155 static gmem_page_t * 156 page_wrapv0(fmd_hdl_t *hdl, gmem_page_pers_t *pers, size_t psz) 157 { 158 gmem_page_t *page; 159 160 if (psz != sizeof (gmem_page_pers_t)) { 161 fmd_hdl_abort(hdl, "size of state doesn't match size of " 162 "version 0 state (%u bytes).\n", sizeof (gmem_page_pers_t)); 163 } 164 165 page = fmd_hdl_zalloc(hdl, sizeof (gmem_page_t), FMD_SLEEP); 166 bcopy(pers, page, sizeof (gmem_page_pers_t)); 167 fmd_hdl_free(hdl, pers, psz); 168 return (page); 169 } 170 171 void * 172 gmem_page_restore(fmd_hdl_t *hdl, fmd_case_t *cp, gmem_case_ptr_t *ptr) 173 { 174 gmem_page_t *page; 175 176 for (page = gmem_list_next(&gmem.gm_pages); page != NULL; 177 page = gmem_list_next(page)) { 178 if (strcmp(page->page_bufname, ptr->ptr_name) == 0) 179 break; 180 } 181 182 if (page == NULL) { 183 size_t pagesz; 184 185 fmd_hdl_debug(hdl, "restoring page from %s\n", ptr->ptr_name); 186 187 if ((pagesz = fmd_buf_size(hdl, NULL, ptr->ptr_name)) == 0) { 188 if (fmd_case_solved(hdl, cp) || 189 fmd_case_closed(hdl, cp)) { 190 fmd_hdl_debug(hdl, "page %s from case %s not " 191 "found. Case is already solved or closed\n", 192 ptr->ptr_name, fmd_case_uuid(hdl, cp)); 193 return (NULL); 194 } else { 195 fmd_hdl_abort(hdl, "page referenced by case %s " 196 "does not exist in saved state\n", 197 fmd_case_uuid(hdl, cp)); 198 } 199 } else if (pagesz > CMD_PAGE_MAXSIZE || 200 pagesz < CMD_PAGE_MINSIZE) { 201 fmd_hdl_abort(hdl, "page buffer referenced by case %s " 202 "is out of bounds (is %u bytes, max %u, min %u)\n", 203 fmd_case_uuid(hdl, cp), pagesz, 204 CMD_PAGE_MAXSIZE, CMD_PAGE_MINSIZE); 205 } 206 207 if ((page = gmem_buf_read(hdl, NULL, ptr->ptr_name, 208 pagesz)) == NULL) { 209 fmd_hdl_abort(hdl, "failed to read page buf %s", 210 ptr->ptr_name); 211 } 212 213 fmd_hdl_debug(hdl, "found %d in version field\n", 214 page->page_version); 215 216 switch (page->page_version) { 217 case CMD_PAGE_VERSION_0: 218 page = page_wrapv0(hdl, (gmem_page_pers_t *)page, 219 pagesz); 220 break; 221 default: 222 fmd_hdl_abort(hdl, "unknown version (found %d) " 223 "for page state referenced by case %s.\n", 224 page->page_version, fmd_case_uuid(hdl, cp)); 225 break; 226 } 227 228 gmem_fmri_restore(hdl, &page->page_asru); 229 230 gmem_list_append(&gmem.gm_pages, page); 231 } 232 233 switch (ptr->ptr_subtype) { 234 case GMEM_PTR_PAGE_CASE: 235 gmem_case_restore(hdl, &page->page_case, cp, 236 gmem_page_serdnm_create(hdl, "page", page->page_physbase)); 237 break; 238 default: 239 fmd_hdl_abort(hdl, "invalid %s subtype %d\n", 240 ptr->ptr_name, ptr->ptr_subtype); 241 } 242 243 return (page); 244 } 245 246 /*ARGSUSED*/ 247 int 248 gmem_page_unusable(fmd_hdl_t *hdl, gmem_page_t *page) 249 { 250 nvlist_t *asru = NULL; 251 char *sn; 252 253 if (nvlist_lookup_string(page->page_asru_nvl, 254 FM_FMRI_HC_SERIAL_ID, &sn) != 0) 255 return (1); 256 257 /* 258 * get asru in mem scheme from topology 259 */ 260 asru = gmem_find_dimm_asru(hdl, sn); 261 if (asru == NULL) 262 return (1); 263 264 (void) nvlist_add_string_array(asru, FM_FMRI_MEM_SERIAL_ID, &sn, 1); 265 (void) nvlist_add_uint64(asru, FM_FMRI_MEM_PHYSADDR, 266 page->page_physbase); 267 (void) nvlist_add_uint64(asru, FM_FMRI_MEM_OFFSET, page->page_offset); 268 269 if (fmd_nvl_fmri_unusable(hdl, asru)) { 270 nvlist_free(asru); 271 return (1); 272 } 273 274 nvlist_free(asru); 275 276 return (0); 277 } 278 279 280 /*ARGSUSED*/ 281 void 282 gmem_page_validate(fmd_hdl_t *hdl) 283 { 284 gmem_page_t *page, *next; 285 286 for (page = gmem_list_next(&gmem.gm_pages); page != NULL; page = next) { 287 next = gmem_list_next(page); 288 289 if (gmem_page_unusable(hdl, page)) 290 gmem_page_destroy(hdl, page); 291 } 292 } 293 294 void 295 gmem_page_dirty(fmd_hdl_t *hdl, gmem_page_t *page) 296 { 297 if (fmd_buf_size(hdl, NULL, page->page_bufname) != 298 sizeof (gmem_page_pers_t)) 299 fmd_buf_destroy(hdl, NULL, page->page_bufname); 300 301 /* No need to rewrite the FMRIs in the page - they don't change */ 302 fmd_buf_write(hdl, NULL, page->page_bufname, &page->page_pers, 303 sizeof (gmem_page_pers_t)); 304 } 305 306 void 307 gmem_page_fini(fmd_hdl_t *hdl) 308 { 309 gmem_page_t *page; 310 311 while ((page = gmem_list_next(&gmem.gm_pages)) != NULL) 312 gmem_page_free(hdl, page, FMD_B_FALSE); 313 } 314 315 316 int 317 gmem_page_fault(fmd_hdl_t *hdl, nvlist_t *fru, nvlist_t *rsc, 318 fmd_event_t *ep, uint64_t afar, uint64_t offset) 319 { 320 gmem_page_t *page = NULL; 321 const char *uuid; 322 nvlist_t *flt, *hsp; 323 324 page = gmem_page_lookup(afar); 325 if (page != NULL) { 326 if (page->page_flags & GMEM_F_FAULTING || 327 gmem_page_unusable(hdl, page)) { 328 nvlist_free(rsc); 329 page->page_flags |= GMEM_F_FAULTING; 330 return (0); 331 } 332 } else { 333 page = gmem_page_create(hdl, fru, afar, offset); 334 } 335 336 page->page_flags |= GMEM_F_FAULTING; 337 if (page->page_case.cc_cp == NULL) 338 page->page_case.cc_cp = gmem_case_create(hdl, 339 &page->page_header, GMEM_PTR_PAGE_CASE, &uuid); 340 341 if (nvlist_lookup_nvlist(page->page_asru_nvl, FM_FMRI_HC_SPECIFIC, 342 &hsp) == 0) 343 (void) nvlist_add_nvlist(rsc, FM_FMRI_HC_SPECIFIC, hsp); 344 345 flt = fmd_nvl_create_fault(hdl, GMEM_FAULT_PAGE, 100, NULL, fru, rsc); 346 nvlist_free(rsc); 347 348 if (nvlist_add_boolean_value(flt, FM_SUSPECT_MESSAGE, B_FALSE) != 0) 349 fmd_hdl_abort(hdl, "failed to add no-message member to fault"); 350 351 fmd_case_add_ereport(hdl, page->page_case.cc_cp, ep); 352 fmd_case_add_suspect(hdl, page->page_case.cc_cp, flt); 353 fmd_case_solve(hdl, page->page_case.cc_cp); 354 return (1); 355 } 356 357 void 358 gmem_page_close(fmd_hdl_t *hdl, void *arg) 359 { 360 gmem_page_destroy(hdl, arg); 361 } 362