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