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.
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.
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]
22 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
27 * Support routines for managing per-page state.
30 #include <gmem_page.h>
32 #include <gmem_dimm.h>
37 #include <fm/fmd_api.h>
38 #include <sys/fm/protocol.h>
41 page_write(fmd_hdl_t
*hdl
, gmem_page_t
*page
)
43 fmd_buf_write(hdl
, NULL
, page
->page_bufname
, page
,
44 sizeof (gmem_page_pers_t
));
48 gmem_page_free(fmd_hdl_t
*hdl
, gmem_page_t
*page
, int destroy
)
50 gmem_case_t
*cc
= &page
->page_case
;
52 if (cc
->cc_cp
!= NULL
)
53 gmem_case_fini(hdl
, cc
->cc_cp
, destroy
);
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
);
62 fmd_buf_destroy(hdl
, NULL
, page
->page_bufname
);
64 gmem_fmri_fini(hdl
, &page
->page_asru
, destroy
);
66 gmem_list_delete(&gmem
.gm_pages
, page
);
67 fmd_hdl_free(hdl
, page
, sizeof (gmem_page_t
));
71 gmem_page_destroy(fmd_hdl_t
*hdl
, gmem_page_t
*page
)
73 fmd_hdl_debug(hdl
, "destroying the page\n");
74 gmem_page_free(hdl
, page
, FMD_B_TRUE
);
78 page_lookup_by_physaddr(uint64_t pa
)
82 for (page
= gmem_list_next(&gmem
.gm_pages
); page
!= NULL
;
83 page
= gmem_list_next(page
)) {
84 if (page
->page_physbase
== pa
)
92 gmem_page_create(fmd_hdl_t
*hdl
, nvlist_t
*modasru
, uint64_t pa
,
98 pa
= pa
& gmem
.gm_pagemask
;
100 fmd_hdl_debug(hdl
, "page_lookup: creating new page for %llx\n",
102 GMEM_STAT_BUMP(page_creat
);
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
;
110 gmem_bufname(page
->page_bufname
, sizeof (page
->page_bufname
),
111 "page_%llx", (u_longlong_t
)pa
);
113 if (nvlist_dup(modasru
, &asru
, 0) != 0) {
114 fmd_hdl_debug(hdl
, "Page create nvlist dup failed");
118 if (nvlist_alloc(&hsp
, NV_UNIQUE_NAME
, 0) != 0) {
119 fmd_hdl_debug(hdl
, "Page create nvlist alloc failed");
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");
135 gmem_fmri_init(hdl
, &page
->page_asru
, asru
, "page_asru_%llx",
141 gmem_list_append(&gmem
.gm_pages
, page
);
142 page_write(hdl
, page
);
148 gmem_page_lookup(uint64_t pa
)
150 pa
= pa
& gmem
.gm_pagemask
;
152 return (page_lookup_by_physaddr(pa
));
156 page_wrapv0(fmd_hdl_t
*hdl
, gmem_page_pers_t
*pers
, size_t psz
)
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
));
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
);
172 gmem_page_restore(fmd_hdl_t
*hdl
, fmd_case_t
*cp
, gmem_case_ptr_t
*ptr
)
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)
185 fmd_hdl_debug(hdl
, "restoring page from %s\n", ptr
->ptr_name
);
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
));
195 fmd_hdl_abort(hdl
, "page referenced by case %s "
196 "does not exist in saved state\n",
197 fmd_case_uuid(hdl
, cp
));
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
);
207 if ((page
= gmem_buf_read(hdl
, NULL
, ptr
->ptr_name
,
209 fmd_hdl_abort(hdl
, "failed to read page buf %s",
213 fmd_hdl_debug(hdl
, "found %d in version field\n",
216 switch (page
->page_version
) {
217 case CMD_PAGE_VERSION_0
:
218 page
= page_wrapv0(hdl
, (gmem_page_pers_t
*)page
,
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
));
228 gmem_fmri_restore(hdl
, &page
->page_asru
);
230 gmem_list_append(&gmem
.gm_pages
, page
);
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
));
239 fmd_hdl_abort(hdl
, "invalid %s subtype %d\n",
240 ptr
->ptr_name
, ptr
->ptr_subtype
);
248 gmem_page_unusable(fmd_hdl_t
*hdl
, gmem_page_t
*page
)
250 nvlist_t
*asru
= NULL
;
253 if (nvlist_lookup_string(page
->page_asru_nvl
,
254 FM_FMRI_HC_SERIAL_ID
, &sn
) != 0)
258 * get asru in mem scheme from topology
260 asru
= gmem_find_dimm_asru(hdl
, sn
);
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
);
269 if (fmd_nvl_fmri_unusable(hdl
, asru
)) {
282 gmem_page_validate(fmd_hdl_t
*hdl
)
284 gmem_page_t
*page
, *next
;
286 for (page
= gmem_list_next(&gmem
.gm_pages
); page
!= NULL
; page
= next
) {
287 next
= gmem_list_next(page
);
289 if (gmem_page_unusable(hdl
, page
))
290 gmem_page_destroy(hdl
, page
);
295 gmem_page_dirty(fmd_hdl_t
*hdl
, gmem_page_t
*page
)
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
);
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
));
307 gmem_page_fini(fmd_hdl_t
*hdl
)
311 while ((page
= gmem_list_next(&gmem
.gm_pages
)) != NULL
)
312 gmem_page_free(hdl
, page
, FMD_B_FALSE
);
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
)
320 gmem_page_t
*page
= NULL
;
324 page
= gmem_page_lookup(afar
);
326 if (page
->page_flags
& GMEM_F_FAULTING
||
327 gmem_page_unusable(hdl
, page
)) {
329 page
->page_flags
|= GMEM_F_FAULTING
;
333 page
= gmem_page_create(hdl
, fru
, afar
, offset
);
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
);
341 if (nvlist_lookup_nvlist(page
->page_asru_nvl
, FM_FMRI_HC_SPECIFIC
,
343 (void) nvlist_add_nvlist(rsc
, FM_FMRI_HC_SPECIFIC
, hsp
);
345 flt
= fmd_nvl_create_fault(hdl
, GMEM_FAULT_PAGE
, 100, NULL
, fru
, rsc
);
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");
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
);
358 gmem_page_close(fmd_hdl_t
*hdl
, void *arg
)
360 gmem_page_destroy(hdl
, arg
);