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
page_write(fmd_hdl_t * hdl,gmem_page_t * page)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
gmem_page_free(fmd_hdl_t * hdl,gmem_page_t * page,int destroy)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
gmem_page_destroy(fmd_hdl_t * hdl,gmem_page_t * page)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 *
page_lookup_by_physaddr(uint64_t pa)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 *
gmem_page_create(fmd_hdl_t * hdl,nvlist_t * modasru,uint64_t pa,uint64_t offset)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 *
gmem_page_lookup(uint64_t pa)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 *
page_wrapv0(fmd_hdl_t * hdl,gmem_page_pers_t * pers,size_t psz)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 *
gmem_page_restore(fmd_hdl_t * hdl,fmd_case_t * cp,gmem_case_ptr_t * ptr)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
gmem_page_unusable(fmd_hdl_t * hdl,gmem_page_t * page)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
gmem_page_validate(fmd_hdl_t * hdl)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
gmem_page_dirty(fmd_hdl_t * hdl,gmem_page_t * page)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
gmem_page_fini(fmd_hdl_t * hdl)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
gmem_page_fault(fmd_hdl_t * hdl,nvlist_t * fru,nvlist_t * rsc,fmd_event_t * ep,uint64_t afar,uint64_t offset)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
gmem_page_close(fmd_hdl_t * hdl,void * arg)358 gmem_page_close(fmd_hdl_t *hdl, void *arg)
359 {
360 gmem_page_destroy(hdl, arg);
361 }
362