6659 nvlist_free(NULL) is a no-op
[illumos-gate.git] / usr / src / cmd / fm / modules / sun4v / generic-mem / gmem_dimm.c
blobc64ab7ccfe36c5b7868d1f9359412a66ccb2897d
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
22 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
27 * Support routines for DIMMs.
30 #include <gmem_mem.h>
31 #include <gmem_dimm.h>
32 #include <gmem.h>
33 #include <errno.h>
34 #include <limits.h>
35 #include <string.h>
36 #include <strings.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <fm/fmd_api.h>
40 #include <fm/libtopo.h>
41 #include <sys/fm/protocol.h>
42 #include <sys/mem.h>
43 #include <sys/nvpair.h>
45 nvlist_t *dimm_nvl;
47 typedef struct dimmid {
48 char serial[100];
49 int type;
50 } dimmid_t;
52 static int gmem_find_dimm_chip(nvlist_t *, uint32_t *);
54 nvlist_t *
55 gmem_dimm_fru(gmem_dimm_t *dimm)
57 return (dimm->dimm_asru_nvl);
60 static void
61 gmem_dimm_free(fmd_hdl_t *hdl, gmem_dimm_t *dimm, int destroy)
63 gmem_case_t *cc = &dimm->dimm_case;
64 int i;
65 gmem_mq_t *q;
66 tstamp_t *tsp, *next;
68 if (cc->cc_cp != NULL) {
69 gmem_case_fini(hdl, cc->cc_cp, destroy);
70 if (cc->cc_serdnm != NULL) {
71 if (fmd_serd_exists(hdl, cc->cc_serdnm) &&
72 destroy)
73 fmd_serd_destroy(hdl, cc->cc_serdnm);
74 fmd_hdl_strfree(hdl, cc->cc_serdnm);
78 gmem_fmri_fini(hdl, &dimm->dimm_asru, destroy);
80 for (i = 0; i < GMEM_MAX_CKWDS; i++) {
81 while ((q = gmem_list_next(&dimm->mq_root[i])) != NULL) {
82 if (q->mq_serdnm != NULL) {
83 if (fmd_serd_exists(hdl, q->mq_serdnm))
84 fmd_serd_destroy(hdl, q->mq_serdnm);
85 fmd_hdl_strfree(hdl, q->mq_serdnm);
86 q->mq_serdnm = NULL;
89 for (tsp = gmem_list_next(&q->mq_dupce_tstamp);
90 tsp != NULL; tsp = next) {
91 next = gmem_list_next(tsp);
92 gmem_list_delete(&q->mq_dupce_tstamp,
93 &tsp->ts_l);
94 fmd_hdl_free(hdl, tsp, sizeof (tstamp_t));
97 gmem_list_delete(&dimm->mq_root[i], q);
98 fmd_hdl_free(hdl, q, sizeof (gmem_mq_t));
102 if (destroy)
103 fmd_buf_destroy(hdl, NULL, dimm->dimm_bufname);
105 gmem_list_delete(&gmem.gm_dimms, dimm);
106 fmd_hdl_free(hdl, dimm, sizeof (gmem_dimm_t));
109 void
110 gmem_dimm_destroy(fmd_hdl_t *hdl, gmem_dimm_t *dimm)
112 fmd_stat_destroy(hdl, 1, &(dimm->dimm_retstat));
113 gmem_dimm_free(hdl, dimm, FMD_B_TRUE);
116 static gmem_dimm_t *
117 dimm_lookup_by_serial(const char *serial)
119 gmem_dimm_t *dimm;
121 for (dimm = gmem_list_next(&gmem.gm_dimms); dimm != NULL;
122 dimm = gmem_list_next(dimm)) {
123 if (strcmp(dimm->dimm_serial, serial) == 0)
124 return (dimm);
127 return (NULL);
130 gmem_dimm_t *
131 gmem_dimm_create(fmd_hdl_t *hdl, nvlist_t *asru, nvlist_t *det)
133 gmem_dimm_t *dimm;
134 nvlist_t *fmri;
135 char *serial;
136 uint32_t chip_id;
138 if (nvlist_lookup_string(asru, FM_FMRI_HC_SERIAL_ID, &serial) != 0) {
139 fmd_hdl_debug(hdl, "Unable to get dimm serial\n");
140 return (NULL);
143 if (nvlist_dup(asru, &fmri, 0) != 0) {
144 fmd_hdl_debug(hdl, "dimm create nvlist dup failed");
145 return (NULL);
148 (void) gmem_find_dimm_chip(det, &chip_id);
150 fmd_hdl_debug(hdl, "dimm_create: creating new DIMM serial=%s\n",
151 serial);
152 GMEM_STAT_BUMP(dimm_creat);
154 dimm = fmd_hdl_zalloc(hdl, sizeof (gmem_dimm_t), FMD_SLEEP);
155 dimm->dimm_nodetype = GMEM_NT_DIMM;
156 dimm->dimm_version = GMEM_DIMM_VERSION;
157 dimm->dimm_phys_addr_low = ULLONG_MAX;
158 dimm->dimm_phys_addr_hi = 0;
159 dimm->dimm_syl_error = USHRT_MAX;
160 dimm->dimm_chipid = chip_id;
162 gmem_bufname(dimm->dimm_bufname, sizeof (dimm->dimm_bufname), "dimm_%s",
163 serial);
164 gmem_fmri_init(hdl, &dimm->dimm_asru, fmri, "dimm_asru_%s", serial);
166 nvlist_free(fmri);
168 (void) nvlist_lookup_string(dimm->dimm_asru_nvl, FM_FMRI_HC_SERIAL_ID,
169 (char **)&dimm->dimm_serial);
171 gmem_mem_retirestat_create(hdl, &dimm->dimm_retstat, dimm->dimm_serial,
172 0, GMEM_DIMM_STAT_PREFIX);
174 gmem_list_append(&gmem.gm_dimms, dimm);
175 gmem_dimm_dirty(hdl, dimm);
177 return (dimm);
180 gmem_dimm_t *
181 gmem_dimm_lookup(fmd_hdl_t *hdl, nvlist_t *asru)
183 gmem_dimm_t *dimm;
184 char *serial;
185 int err;
187 err = nvlist_lookup_string(asru, FM_FMRI_HC_SERIAL_ID, &serial);
189 if (err != 0) {
190 fmd_hdl_debug(hdl, "Can't get dimm serial number\n");
191 GMEM_STAT_BUMP(bad_mem_resource);
192 return (NULL);
195 dimm = dimm_lookup_by_serial(serial);
196 return (dimm);
200 static gmem_dimm_t *
201 gmem_dimm_v0tov1(fmd_hdl_t *hdl, gmem_dimm_0_t *old, size_t oldsz)
203 gmem_dimm_t *new;
204 if (oldsz != sizeof (gmem_dimm_0_t)) {
205 fmd_hdl_abort(hdl, "size of state doesn't match size of "
206 "version 0 state (%u bytes).\n", sizeof (gmem_dimm_0_t));
209 new = fmd_hdl_zalloc(hdl, sizeof (gmem_dimm_t), FMD_SLEEP);
210 new->dimm_header = old->dimm0_header;
211 new->dimm_version = GMEM_DIMM_VERSION;
212 new->dimm_asru = old->dimm0_asru;
213 new->dimm_nretired = old->dimm0_nretired;
214 new->dimm_phys_addr_hi = 0;
215 new->dimm_phys_addr_low = ULLONG_MAX;
217 fmd_hdl_free(hdl, old, oldsz);
218 return (new);
221 static gmem_dimm_t *
222 gmem_dimm_wrapv1(fmd_hdl_t *hdl, gmem_dimm_pers_t *pers, size_t psz)
224 gmem_dimm_t *dimm;
226 if (psz != sizeof (gmem_dimm_pers_t)) {
227 fmd_hdl_abort(hdl, "size of state doesn't match size of "
228 "version 0 state (%u bytes).\n", sizeof (gmem_dimm_pers_t));
231 dimm = fmd_hdl_zalloc(hdl, sizeof (gmem_dimm_t), FMD_SLEEP);
232 bcopy(pers, dimm, sizeof (gmem_dimm_pers_t));
233 fmd_hdl_free(hdl, pers, psz);
234 return (dimm);
237 void *
238 gmem_dimm_restore(fmd_hdl_t *hdl, fmd_case_t *cp, gmem_case_ptr_t *ptr)
240 gmem_dimm_t *dimm;
242 for (dimm = gmem_list_next(&gmem.gm_dimms); dimm != NULL;
243 dimm = gmem_list_next(dimm)) {
244 if (strcmp(dimm->dimm_bufname, ptr->ptr_name) == 0)
245 break;
248 if (dimm == NULL) {
249 int migrated = 0;
250 size_t dimmsz;
252 fmd_hdl_debug(hdl, "restoring dimm from %s\n", ptr->ptr_name);
254 if ((dimmsz = fmd_buf_size(hdl, NULL, ptr->ptr_name)) == 0) {
255 fmd_hdl_abort(hdl, "dimm referenced by case %s does "
256 "not exist in saved state\n",
257 fmd_case_uuid(hdl, cp));
258 } else if (dimmsz > GMEM_DIMM_MAXSIZE ||
259 dimmsz < GMEM_DIMM_MINSIZE) {
260 fmd_hdl_abort(hdl, "dimm buffer referenced by case %s "
261 "is out of bounds (is %u bytes, max %u, min %u)\n",
262 fmd_case_uuid(hdl, cp), dimmsz,
263 GMEM_DIMM_MAXSIZE, GMEM_DIMM_MINSIZE);
266 if ((dimm = gmem_buf_read(hdl, NULL, ptr->ptr_name,
267 dimmsz)) == NULL) {
268 fmd_hdl_abort(hdl, "failed to read dimm buf %s",
269 ptr->ptr_name);
272 fmd_hdl_debug(hdl, "found %d in version field\n",
273 dimm->dimm_version);
275 if (GMEM_DIMM_VERSIONED(dimm)) {
277 switch (dimm->dimm_version) {
278 case GMEM_DIMM_VERSION_1:
279 dimm = gmem_dimm_wrapv1(hdl,
280 (gmem_dimm_pers_t *)dimm, dimmsz);
281 break;
282 default:
283 fmd_hdl_abort(hdl, "unknown version (found %d) "
284 "for dimm state referenced by case %s.\n",
285 dimm->dimm_version, fmd_case_uuid(hdl, cp));
286 break;
288 } else {
289 dimm = gmem_dimm_v0tov1(hdl, (gmem_dimm_0_t *)dimm,
290 dimmsz);
291 migrated = 1;
294 if (migrated) {
295 GMEM_STAT_BUMP(dimm_migrat);
296 gmem_dimm_dirty(hdl, dimm);
299 gmem_fmri_restore(hdl, &dimm->dimm_asru);
301 if ((errno = nvlist_lookup_string(dimm->dimm_asru_nvl,
302 FM_FMRI_HC_SERIAL_ID, (char **)&dimm->dimm_serial)) != 0)
303 fmd_hdl_abort(hdl,
304 "failed to retrieve serial from asru");
307 gmem_mem_retirestat_create(hdl, &dimm->dimm_retstat,
308 dimm->dimm_serial, dimm->dimm_nretired,
309 GMEM_DIMM_STAT_PREFIX);
311 gmem_list_append(&gmem.gm_dimms, dimm);
314 switch (ptr->ptr_subtype) {
315 case GMEM_PTR_DIMM_CASE:
316 gmem_mem_case_restore(hdl, &dimm->dimm_case, cp, "dimm",
317 dimm->dimm_serial);
318 break;
319 default:
320 fmd_hdl_abort(hdl, "invalid %s subtype %d\n",
321 ptr->ptr_name, ptr->ptr_subtype);
324 return (dimm);
327 void
328 gmem_dimm_validate(fmd_hdl_t *hdl)
330 gmem_dimm_t *dimm, *next;
332 for (dimm = gmem_list_next(&gmem.gm_dimms); dimm != NULL; dimm = next) {
333 next = gmem_list_next(dimm);
335 if (!gmem_dimm_present(hdl, dimm->dimm_asru_nvl))
336 gmem_dimm_destroy(hdl, dimm);
340 void
341 gmem_dimm_dirty(fmd_hdl_t *hdl, gmem_dimm_t *dimm)
343 if (fmd_buf_size(hdl, NULL, dimm->dimm_bufname) !=
344 sizeof (gmem_dimm_pers_t))
345 fmd_buf_destroy(hdl, NULL, dimm->dimm_bufname);
347 /* No need to rewrite the FMRIs in the dimm - they don't change */
348 fmd_buf_write(hdl, NULL, dimm->dimm_bufname, &dimm->dimm_pers,
349 sizeof (gmem_dimm_pers_t));
352 void
353 gmem_dimm_gc(fmd_hdl_t *hdl)
355 gmem_dimm_validate(hdl);
358 void
359 gmem_dimm_fini(fmd_hdl_t *hdl)
361 gmem_dimm_t *dimm;
363 while ((dimm = gmem_list_next(&gmem.gm_dimms)) != NULL)
364 gmem_dimm_free(hdl, dimm, FMD_B_FALSE);
368 /*ARGSUSED*/
369 static int
370 find_dimm_hc_fmri(topo_hdl_t *thp, tnode_t *node, void *arg)
373 char *topo_sn;
374 dimmid_t *dimmid = (dimmid_t *)arg;
375 nvlist_t *fru = NULL;
376 nvlist_t *rsc = NULL;
377 nvlist_t *asru = NULL;
378 int err;
380 if (topo_node_fru(node, &fru, NULL, &err) < 0)
381 return (TOPO_WALK_NEXT);
383 err = nvlist_lookup_string(fru, FM_FMRI_HC_SERIAL_ID, &topo_sn);
384 if (err != 0) {
385 nvlist_free(fru);
386 return (TOPO_WALK_NEXT);
389 if (strcmp(dimmid->serial, topo_sn) != 0) {
390 nvlist_free(fru);
391 return (TOPO_WALK_NEXT);
394 switch (dimmid->type) {
395 case FINDFRU:
396 (void) nvlist_dup(fru, &dimm_nvl, NV_UNIQUE_NAME);
397 break;
398 case FINDRSC:
399 (void) topo_node_resource(node, &rsc, &err);
400 if (rsc != NULL) {
401 (void) nvlist_dup(rsc, &dimm_nvl,
402 NV_UNIQUE_NAME);
403 nvlist_free(rsc);
405 break;
406 case FINDASRU:
407 (void) topo_node_asru(node, &asru, NULL, &err);
408 if (asru != NULL) {
409 (void) nvlist_dup(asru, &dimm_nvl,
410 NV_UNIQUE_NAME);
411 nvlist_free(asru);
413 break;
414 default:
415 break;
417 nvlist_free(fru);
418 return (TOPO_WALK_TERMINATE);
421 nvlist_t *
422 gmem_find_dimm_by_sn(fmd_hdl_t *hdl, dimmid_t *dimmid) {
423 topo_hdl_t *thp;
424 topo_walk_t *twp;
425 int err;
426 dimm_nvl = NULL;
428 if ((thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION)) == NULL)
429 return (NULL);
431 if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC,
432 find_dimm_hc_fmri, dimmid, &err)) == NULL) {
433 fmd_hdl_topo_rele(hdl, thp);
434 return (NULL);
437 (void) topo_walk_step(twp, TOPO_WALK_CHILD);
438 topo_walk_fini(twp);
439 fmd_hdl_topo_rele(hdl, thp);
440 return (dimm_nvl);
443 nvlist_t *
444 gmem_find_dimm_fru(fmd_hdl_t *hdl, char *sn)
446 dimmid_t fru;
447 (void) strcpy(fru.serial, sn);
448 fru.type = FINDFRU;
449 return (gmem_find_dimm_by_sn(hdl, &fru));
452 nvlist_t *
453 gmem_find_dimm_rsc(fmd_hdl_t *hdl, char *sn)
455 dimmid_t rsc;
456 (void) strcpy(rsc.serial, sn);
457 rsc.type = FINDRSC;
458 return (gmem_find_dimm_by_sn(hdl, &rsc));
461 nvlist_t *
462 gmem_find_dimm_asru(fmd_hdl_t *hdl, char *sn)
464 dimmid_t asru;
465 (void) strcpy(asru.serial, sn);
466 asru.type = FINDASRU;
467 return (gmem_find_dimm_by_sn(hdl, &asru));
471 gmem_dimm_present(fmd_hdl_t *hdl, nvlist_t *asru)
473 char *sn;
474 nvlist_t *dimm = NULL;
476 if (nvlist_lookup_string(asru, FM_FMRI_HC_SERIAL_ID, &sn) != 0) {
477 fmd_hdl_debug(hdl, "Unable to get dimm serial\n");
478 return (0);
480 dimm = gmem_find_dimm_fru(hdl, sn);
481 if (dimm == NULL) {
482 fmd_hdl_debug(hdl, "Dimm sn=%s is not present\n", sn);
483 return (0);
485 nvlist_free(dimm);
486 return (1);
489 static int
490 gmem_find_dimm_chip(nvlist_t *nvl, uint32_t *chip)
493 char *name, *id, *end;
494 nvlist_t **hcl;
495 uint_t n;
496 int i;
497 int rc = 0;
498 *chip = ULONG_MAX;
500 if (nvlist_lookup_nvlist_array(nvl, FM_FMRI_HC_LIST, &hcl, &n) < 0)
501 return (0);
502 for (i = 0; i < n; i++) {
503 (void) nvlist_lookup_string(hcl[i], FM_FMRI_HC_NAME, &name);
504 (void) nvlist_lookup_string(hcl[i], FM_FMRI_HC_ID, &id);
506 if (strcmp(name, "chip") == 0) {
507 *chip = (uint32_t)strtoul(id, &end, 10);
508 rc = 1;
509 break;
512 return (rc);
515 /*ARGSUSED*/
517 gmem_same_datapath_dimms(fmd_hdl_t *hdl, gmem_dimm_t *d1, gmem_dimm_t *d2)
520 if (d1->dimm_chipid == ULONG_MAX || d2->dimm_chipid == ULONG_MAX)
521 return (0);
523 if (d1->dimm_chipid == d2->dimm_chipid)
524 return (1);
526 return (0);
530 gmem_check_symbol_error(fmd_hdl_t *hdl, gmem_dimm_t *d, uint16_t upos)
532 gmem_dimm_t *dimm = NULL, *next = NULL;
534 for (dimm = gmem_list_next(&gmem.gm_dimms); dimm != NULL;
535 dimm = next) {
536 next = gmem_list_next(dimm);
537 if (gmem_same_datapath_dimms(hdl, dimm, d) &&
538 dimm->dimm_syl_error == upos)
539 return (1);
541 return (0);
544 void
545 gmem_save_symbol_error(fmd_hdl_t *hdl, gmem_dimm_t *d, uint16_t upos)
547 gmem_dimm_t *dimm = NULL, *next = NULL;
549 for (dimm = gmem_list_next(&gmem.gm_dimms); dimm != NULL;
550 dimm = next) {
551 next = gmem_list_next(dimm);
552 if (gmem_same_datapath_dimms(hdl, dimm, d))
553 dimm->dimm_syl_error = upos;