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 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <cmd_mem.h>
27 #include <cmd_branch.h>
28 #include <cmd_dimm.h>
29 #include <cmd.h>
30 #include <cmd_hc_sun4v.h>
31
32 #include <errno.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <fm/fmd_api.h>
38 #include <fm/libtopo.h>
39 #include <sys/fm/protocol.h>
40 #include <sys/mem.h>
41 #include <sys/nvpair.h>
42
43 #define BUF_SIZE 120
44 #define LEN_CMP 6
45
46 /*
47 * mbd_label: If a DIMM associated with this branch is located on a memory
48 * expansion board or riser board, return (pointer to) the label of that board;
49 * otherwise return NULL.
50 * We assume that there will be at most one such board for any branch.
51 */
52
53 char *
mbd_label(fmd_hdl_t * hdl,cmd_branch_t * branch,const char * nacname)54 mbd_label(fmd_hdl_t *hdl, cmd_branch_t *branch, const char *nacname)
55 {
56 cmd_dimm_t *dimm;
57 cmd_branch_memb_t *bm;
58 char *p;
59 size_t s;
60
61 for (bm = cmd_list_next(&branch->branch_dimms); bm != NULL;
62 bm = cmd_list_next(bm)) {
63 dimm = bm->dimm;
64 if ((p = strstr(dimm->dimm_unum, nacname)) != NULL) {
65 p = strchr(p, '/'); /* include instance number */
66 s = p - dimm->dimm_unum;
67 p = fmd_hdl_zalloc(hdl, s+1, FMD_SLEEP);
68 (void) strncpy(p, dimm->dimm_unum, s);
69 *(p + s) = '\0';
70 return (p);
71 }
72 }
73 return (NULL);
74 }
75
76 void
cmd_branch_add_dimm(fmd_hdl_t * hdl,cmd_branch_t * branch,cmd_dimm_t * dimm)77 cmd_branch_add_dimm(fmd_hdl_t *hdl, cmd_branch_t *branch, cmd_dimm_t *dimm)
78 {
79 cmd_branch_memb_t *bm;
80
81 if (dimm == NULL)
82 return;
83
84 fmd_hdl_debug(hdl, "Attaching dimm %s to branch %s\n",
85 dimm->dimm_unum, branch->branch_unum);
86 bm = fmd_hdl_zalloc(hdl, sizeof (cmd_branch_memb_t), FMD_SLEEP);
87 bm->dimm = dimm;
88 cmd_list_append(&branch->branch_dimms, bm);
89 }
90
91 void
cmd_branch_remove_dimm(fmd_hdl_t * hdl,cmd_branch_t * branch,cmd_dimm_t * dimm)92 cmd_branch_remove_dimm(fmd_hdl_t *hdl, cmd_branch_t *branch, cmd_dimm_t *dimm)
93 {
94 cmd_branch_memb_t *bm;
95
96 fmd_hdl_debug(hdl, "Detaching dimm %s from branch %s\n",
97 dimm->dimm_unum, branch->branch_unum);
98
99 for (bm = cmd_list_next(&branch->branch_dimms); bm != NULL;
100 bm = cmd_list_next(bm)) {
101 if (bm->dimm == dimm) {
102 cmd_list_delete(&branch->branch_dimms, bm);
103 fmd_hdl_free(hdl, bm, sizeof (cmd_branch_memb_t));
104 return;
105 }
106 }
107
108 fmd_hdl_abort(hdl,
109 "Attempt to disconnect dimm from non-parent branch\n");
110 }
111
112 static cmd_dimm_t *
branch_dimm_create(fmd_hdl_t * hdl,char * dimm_unum,char ** serids,size_t nserids)113 branch_dimm_create(fmd_hdl_t *hdl, char *dimm_unum, char **serids,
114 size_t nserids)
115 {
116 nvlist_t *fmri;
117 cmd_dimm_t *dimm;
118
119 fmri = cmd_mem_fmri_create(dimm_unum, serids, nserids);
120
121 if (fmri != NULL && (fmd_nvl_fmri_expand(hdl, fmri) == 0)) {
122 dimm = cmd_dimm_create(hdl, fmri);
123 if (dimm != NULL) {
124 nvlist_free(fmri);
125 return (dimm);
126 }
127 }
128
129 nvlist_free(fmri);
130 return (NULL);
131 }
132
133 static fmd_hdl_t *br_hdl; /* for use by callbacks */
134 static int br_dimmcount;
135 static nvlist_t *br_memb_nvl;
136
137 /*ARGSUSED*/
138 static int
branch_dimm_cb(topo_hdl_t * thp,tnode_t * node,void * arg)139 branch_dimm_cb(topo_hdl_t *thp, tnode_t *node, void *arg)
140 {
141 char *lbl, *p, *q;
142 char cx[BUF_SIZE], cy[BUF_SIZE];
143 nvlist_t *rsrc;
144 int err;
145 cmd_branch_t *branch = (cmd_branch_t *)arg;
146 cmd_dimm_t *dimm;
147 size_t nserids;
148 char **serids;
149
150 if (topo_node_resource(node, &rsrc, &err) < 0)
151 return (TOPO_WALK_NEXT); /* no label, try next */
152
153 if ((nvlist_lookup_string(rsrc, FM_FMRI_MEM_UNUM, &lbl) != 0) ||
154 (nvlist_lookup_string_array(rsrc, FM_FMRI_MEM_SERIAL_ID,
155 &serids, &nserids) != 0)) {
156 nvlist_free(rsrc);
157 return (TOPO_WALK_NEXT);
158 }
159
160 /*
161 * Massage the unum of the candidate DIMM as follows:
162 * a) remove any trailing J number. Use result for cmd_dimm_t.
163 * b) for branch membership purposes only, remove reference to
164 * a riser card (MR%d) if one exists.
165 */
166 if ((p = strstr(lbl, "/J")) != NULL) {
167 (void) strncpy(cx, lbl, p - lbl);
168 cx[p - lbl] = '\0';
169 } else {
170 (void) strcpy(cx, lbl);
171 }
172 (void) strcpy(cy, cx);
173 if ((p = strstr(cy, "/MR")) != NULL) {
174 if ((q = strchr(p + 1, '/')) != NULL)
175 (void) strcpy(p, q);
176 else
177 *p = '\0';
178 }
179
180 /*
181 * For benefit of Batoka-like platforms, start comparison with
182 * "CMP", so that any leading "MEM" or "CPU" makes no difference.
183 */
184
185 p = strstr(branch->branch_unum, "CMP");
186 q = strstr(cy, "CMP");
187
188 if ((p != NULL) && (q != NULL) && strncmp(p, q, strlen(p)) == 0) {
189 dimm = branch_dimm_create(br_hdl, cx, serids, nserids);
190 if (dimm != NULL)
191 cmd_branch_add_dimm(br_hdl, branch, dimm);
192 }
193 nvlist_free(rsrc);
194 return (TOPO_WALK_NEXT);
195 }
196
197
198 /*
199 * The cmd_dimm_t structure created for a DIMM in a branch never has a
200 * Jxxx in its unum; the cmd_dimm_t structure created for a DIMM containing
201 * a page, or in a bank (i.e. for ECC errors)-always-has a Jxxx in its
202 * unum. Therefore the set of cmd_dimm_t's created for a branch is always
203 * disjoint from the set of cmd_dimm_t's created for pages and/or banks, so
204 * the cmd_dimm_create will never link a 'branch' cmd_dimm_t into bank.
205 * Faulting a DIMM for ECC will not prevent subsequent faulting of "same"
206 * dimm for FBR/FBU and vice versa
207 */
208 static int
branch_dimmlist_create(fmd_hdl_t * hdl,cmd_branch_t * branch)209 branch_dimmlist_create(fmd_hdl_t *hdl, cmd_branch_t *branch)
210 {
211 topo_hdl_t *thp;
212 topo_walk_t *twp;
213 int err, dimm_count;
214 cmd_list_t *bp;
215
216 if ((thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION)) == NULL)
217 return (0);
218 if ((twp = topo_walk_init(thp,
219 FM_FMRI_SCHEME_MEM, branch_dimm_cb, branch, &err))
220 == NULL) {
221 fmd_hdl_topo_rele(hdl, thp);
222 return (0);
223 }
224 br_hdl = hdl;
225 (void) topo_walk_step(twp, TOPO_WALK_CHILD);
226 topo_walk_fini(twp);
227 fmd_hdl_topo_rele(hdl, thp);
228
229 for (dimm_count = 0, bp = cmd_list_next(&branch->branch_dimms);
230 bp != NULL; bp = cmd_list_next(bp), dimm_count++)
231 ;
232 return (dimm_count);
233 }
234
235 /*ARGSUSED*/
236 static int
fru_by_label_cb(topo_hdl_t * thp,tnode_t * node,void * arg)237 fru_by_label_cb(topo_hdl_t *thp, tnode_t *node, void *arg)
238 {
239 char *lbl;
240 int err;
241 char *target = (char *)arg;
242
243 if (topo_node_label(node, &lbl, &err) < 0)
244 return (TOPO_WALK_NEXT); /* no label, try next */
245
246 if ((strcmp(target, lbl) == 0) &&
247 (topo_node_fru(node, &br_memb_nvl, NULL, &err) == 0)) {
248 topo_hdl_strfree(thp, lbl);
249 return (TOPO_WALK_TERMINATE);
250 }
251 topo_hdl_strfree(thp, lbl);
252 return (TOPO_WALK_NEXT);
253 }
254
255 static nvlist_t *
fru_by_label(fmd_hdl_t * hdl,const char * target)256 fru_by_label(fmd_hdl_t *hdl, const char *target)
257 {
258 topo_hdl_t *thp;
259 topo_walk_t *twp;
260 int err;
261
262 br_memb_nvl = NULL;
263 if (((thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION)) != NULL) &&
264 ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC,
265 fru_by_label_cb, (void *)target, &err)) != NULL)) {
266 br_hdl = hdl;
267 (void) topo_walk_step(twp, TOPO_WALK_CHILD);
268 topo_walk_fini(twp);
269 }
270 fmd_hdl_topo_rele(hdl, thp);
271 return (br_memb_nvl);
272 }
273
274 static void
add_bdflt_to_case(fmd_hdl_t * hdl,char * label,const char * fltnm,uint8_t board_cert,fmd_case_t * cp)275 add_bdflt_to_case(fmd_hdl_t *hdl, char *label, const char *fltnm,
276 uint8_t board_cert, fmd_case_t *cp)
277 {
278 nvlist_t *memb_nvl, *flt;
279
280 memb_nvl = fru_by_label(hdl, label);
281 if (memb_nvl != NULL) {
282 flt = cmd_nvl_create_fault(hdl, fltnm, board_cert,
283 memb_nvl, memb_nvl, NULL);
284 flt = cmd_fault_add_location(hdl, flt, label);
285 if (flt != NULL) {
286 fmd_case_add_suspect(hdl, cp, flt);
287 }
288 nvlist_free(memb_nvl);
289 }
290 }
291
292 /*
293 * For t5440, the memory channel goes like this:
294 * VF -> cpuboard -> D0 -> motherboard -> memboard -> D[1..3]
295 * If there is a dimm on the memory board, the memory board,
296 * motherboard, cpuboard, and dimms are in the suspect list.
297 * If there is no dimm on the memory board, the cpu board and
298 * the dimms are in the suspect list
299 * The board certainty = total board certainty / number of
300 * the faulty boards in the suspect list.
301 */
302 void
cmd_branch_create_fault(fmd_hdl_t * hdl,cmd_branch_t * branch,const char * fltnm,nvlist_t * asru)303 cmd_branch_create_fault(fmd_hdl_t *hdl, cmd_branch_t *branch,
304 const char *fltnm, nvlist_t *asru)
305 {
306 nvlist_t *flt;
307 cmd_branch_memb_t *bm;
308 cmd_dimm_t *dimm;
309 int dimm_count = 0;
310 uint_t cert = 0;
311 uint_t board_cert = 0;
312 char *fruloc = NULL, *membd_label;
313
314 /* attach the dimms to the branch */
315 dimm_count = branch_dimmlist_create(hdl, branch);
316
317 if ((membd_label = mbd_label(hdl, branch, "MEM")) != NULL) {
318 board_cert = CMD_BOARDS_CERT / 3; /* CPU, MEM, MB */
319
320 /*
321 * Batoka with memory expansion. CPU expansion board will
322 * be added below. Add memory expansion board and motherboard
323 * FRUs here.
324 */
325
326 add_bdflt_to_case(hdl, membd_label, fltnm, board_cert,
327 branch->branch_case.cc_cp);
328 fmd_hdl_strfree(hdl, membd_label);
329 add_bdflt_to_case(hdl, "MB", fltnm, board_cert,
330 branch->branch_case.cc_cp);
331
332 } else if ((membd_label = mbd_label(hdl, branch, "MR")) != NULL) {
333
334 board_cert = CMD_BOARDS_CERT / 2; /* MB, MR */
335
336 /*
337 * Maramba or similar platform with mezzanine board.
338 * Motherboard FRU will be added below. Add the mezzanine
339 * board here.
340 */
341
342 add_bdflt_to_case(hdl, membd_label, fltnm, board_cert,
343 branch->branch_case.cc_cp);
344 fmd_hdl_strfree(hdl, membd_label);
345 } else {
346 board_cert = CMD_BOARDS_CERT; /* only MB or CPU */
347 }
348
349 /*
350 * The code which follows adds to the suspect list the FRU which
351 * contains the ereport 'detector'. This can be either a CPU
352 * expansion board (Batoka), or motherboard (Huron, Maramba, or
353 * derivative).
354 */
355
356 fruloc = cmd_getfru_loc(hdl, asru);
357 flt = cmd_boardfru_create_fault(hdl, asru, fltnm, board_cert, fruloc);
358 if (flt != NULL)
359 fmd_case_add_suspect(hdl, branch->branch_case.cc_cp, flt);
360
361 if (dimm_count != 0)
362 cert = (100 - CMD_BOARDS_CERT) / dimm_count;
363
364 /* create dimm faults */
365 for (bm = cmd_list_next(&branch->branch_dimms); bm != NULL;
366 bm = cmd_list_next(bm)) {
367 dimm = bm->dimm;
368 if (dimm != NULL) {
369 dimm->dimm_flags |= CMD_MEM_F_FAULTING;
370 cmd_dimm_dirty(hdl, dimm);
371 flt = cmd_dimm_create_fault(hdl, dimm, fltnm, cert);
372 fmd_case_add_suspect(hdl, branch->branch_case.cc_cp,
373 flt);
374 }
375 }
376 if (fruloc != NULL)
377 fmd_hdl_strfree(hdl, fruloc);
378 }
379
380 cmd_branch_t *
cmd_branch_create(fmd_hdl_t * hdl,nvlist_t * asru)381 cmd_branch_create(fmd_hdl_t *hdl, nvlist_t *asru)
382 {
383 cmd_branch_t *branch;
384 const char *b_unum;
385
386 if ((b_unum = cmd_fmri_get_unum(asru)) == NULL) {
387 CMD_STAT_BUMP(bad_mem_asru);
388 return (NULL);
389 }
390
391 fmd_hdl_debug(hdl, "branch_create: creating new branch %s\n", b_unum);
392 CMD_STAT_BUMP(branch_creat);
393
394 branch = fmd_hdl_zalloc(hdl, sizeof (cmd_branch_t), FMD_SLEEP);
395 branch->branch_nodetype = CMD_NT_BRANCH;
396 branch->branch_version = CMD_BRANCH_VERSION;
397
398 cmd_bufname(branch->branch_bufname, sizeof (branch->branch_bufname),
399 "branch_%s", b_unum);
400 cmd_fmri_init(hdl, &branch->branch_asru, asru, "branch_asru_%s",
401 b_unum);
402
403 (void) nvlist_lookup_string(branch->branch_asru_nvl, FM_FMRI_MEM_UNUM,
404 (char **)&branch->branch_unum);
405
406 cmd_list_append(&cmd.cmd_branches, branch);
407 cmd_branch_dirty(hdl, branch);
408
409 return (branch);
410 }
411
412 cmd_branch_t *
cmd_branch_lookup_by_unum(fmd_hdl_t * hdl,const char * unum)413 cmd_branch_lookup_by_unum(fmd_hdl_t *hdl, const char *unum)
414 {
415 cmd_branch_t *branch;
416
417 fmd_hdl_debug(hdl, "branch_lookup: dimm_unum %s", unum);
418 /*
419 * fbr/fbu unum dimm does not have a J number
420 */
421 if (strstr(unum, "J") != NULL)
422 return (NULL);
423
424 for (branch = cmd_list_next(&cmd.cmd_branches); branch != NULL;
425 branch = cmd_list_next(branch)) {
426 if (strcmp(branch->branch_unum, unum) == 0)
427 return (branch);
428 }
429
430 fmd_hdl_debug(hdl, "branch_lookup_by_unum: no branch is found\n");
431 return (NULL);
432 }
433
434 cmd_branch_t *
cmd_branch_lookup(fmd_hdl_t * hdl,nvlist_t * asru)435 cmd_branch_lookup(fmd_hdl_t *hdl, nvlist_t *asru)
436 {
437 cmd_branch_t *branch;
438 const char *unum;
439
440 if ((unum = cmd_fmri_get_unum(asru)) == NULL) {
441 CMD_STAT_BUMP(bad_mem_asru);
442 return (NULL);
443 }
444
445 for (branch = cmd_list_next(&cmd.cmd_branches); branch != NULL;
446 branch = cmd_list_next(branch)) {
447 if (strcmp(branch->branch_unum, unum) == 0)
448 return (branch);
449 }
450
451 fmd_hdl_debug(hdl, "cmd_branch_lookup: discarding old \n");
452 return (NULL);
453 }
454
455 static cmd_branch_t *
branch_wrapv0(fmd_hdl_t * hdl,cmd_branch_pers_t * pers,size_t psz)456 branch_wrapv0(fmd_hdl_t *hdl, cmd_branch_pers_t *pers, size_t psz)
457 {
458 cmd_branch_t *branch;
459
460 if (psz != sizeof (cmd_branch_pers_t)) {
461 fmd_hdl_abort(hdl, "size of state doesn't match size of "
462 "version 0 state (%u bytes).\n",
463 sizeof (cmd_branch_pers_t));
464 }
465
466 branch = fmd_hdl_zalloc(hdl, sizeof (cmd_branch_t), FMD_SLEEP);
467 bcopy(pers, branch, sizeof (cmd_branch_pers_t));
468 fmd_hdl_free(hdl, pers, psz);
469 return (branch);
470 }
471
472 void *
cmd_branch_restore(fmd_hdl_t * hdl,fmd_case_t * cp,cmd_case_ptr_t * ptr)473 cmd_branch_restore(fmd_hdl_t *hdl, fmd_case_t *cp, cmd_case_ptr_t *ptr)
474 {
475 cmd_branch_t *branch;
476 size_t branchsz;
477
478
479 for (branch = cmd_list_next(&cmd.cmd_branches); branch != NULL;
480 branch = cmd_list_next(branch)) {
481 if (strcmp(branch->branch_bufname, ptr->ptr_name) == 0)
482 break;
483 }
484
485 if (branch == NULL) {
486 fmd_hdl_debug(hdl, "restoring branch from %s\n", ptr->ptr_name);
487
488 if ((branchsz = fmd_buf_size(hdl, NULL, ptr->ptr_name)) == 0) {
489 fmd_hdl_abort(hdl, "branch referenced by case %s does "
490 "not exist in saved state\n",
491 fmd_case_uuid(hdl, cp));
492 } else if (branchsz > CMD_BRANCH_MAXSIZE ||
493 branchsz < CMD_BRANCH_MINSIZE) {
494 fmd_hdl_abort(hdl,
495 "branch buffer referenced by case %s "
496 "is out of bounds (is %u bytes, max %u, min %u)\n",
497 fmd_case_uuid(hdl, cp), branchsz,
498 CMD_BRANCH_MAXSIZE, CMD_BRANCH_MINSIZE);
499 }
500
501 if ((branch = cmd_buf_read(hdl, NULL, ptr->ptr_name,
502 branchsz)) == NULL) {
503 fmd_hdl_abort(hdl, "failed to read branch buf %s",
504 ptr->ptr_name);
505 }
506
507 fmd_hdl_debug(hdl, "found %d in version field\n",
508 branch->branch_version);
509
510 switch (branch->branch_version) {
511 case CMD_BRANCH_VERSION_0:
512 branch = branch_wrapv0(hdl,
513 (cmd_branch_pers_t *)branch, branchsz);
514 break;
515 default:
516 fmd_hdl_abort(hdl, "unknown version (found %d) "
517 "for branch state referenced by case %s.\n",
518 branch->branch_version, fmd_case_uuid(hdl,
519 cp));
520 break;
521 }
522
523 cmd_fmri_restore(hdl, &branch->branch_asru);
524
525 if ((errno = nvlist_lookup_string(branch->branch_asru_nvl,
526 FM_FMRI_MEM_UNUM, (char **)&branch->branch_unum)) != 0)
527 fmd_hdl_abort(hdl, "failed to retrieve unum from asru");
528
529
530 cmd_list_append(&cmd.cmd_branches, branch);
531 }
532
533 switch (ptr->ptr_subtype) {
534 case CMD_PTR_BRANCH_CASE:
535 cmd_mem_case_restore(hdl, &branch->branch_case, cp, "branch",
536 branch->branch_unum);
537 break;
538 default:
539 fmd_hdl_abort(hdl, "invalid %s subtype %d\n",
540 ptr->ptr_name, ptr->ptr_subtype);
541 }
542
543 return (branch);
544 }
545
546 void
cmd_branch_dirty(fmd_hdl_t * hdl,cmd_branch_t * branch)547 cmd_branch_dirty(fmd_hdl_t *hdl, cmd_branch_t *branch)
548 {
549 if (fmd_buf_size(hdl, NULL, branch->branch_bufname) !=
550 sizeof (cmd_branch_pers_t))
551 fmd_buf_destroy(hdl, NULL, branch->branch_bufname);
552
553 /* No need to rewrite the FMRIs in the branch - they don't change */
554 fmd_buf_write(hdl, NULL, branch->branch_bufname, &branch->branch_pers,
555 sizeof (cmd_branch_pers_t));
556 }
557
558 static void
branch_dimmlist_free(fmd_hdl_t * hdl,cmd_branch_t * branch)559 branch_dimmlist_free(fmd_hdl_t *hdl, cmd_branch_t *branch)
560 {
561 cmd_branch_memb_t *bm;
562
563 while ((bm = cmd_list_next(&branch->branch_dimms)) != NULL) {
564 cmd_list_delete(&branch->branch_dimms, bm);
565 fmd_hdl_free(hdl, bm, sizeof (cmd_branch_memb_t));
566 }
567 }
568
569 static void
branch_free(fmd_hdl_t * hdl,cmd_branch_t * branch,int destroy)570 branch_free(fmd_hdl_t *hdl, cmd_branch_t *branch, int destroy)
571 {
572 fmd_hdl_debug(hdl, "Free branch %s\n", branch->branch_unum);
573 if (branch->branch_case.cc_cp != NULL) {
574 if (destroy) {
575 if (branch->branch_case.cc_serdnm != NULL) {
576 fmd_serd_destroy(hdl,
577 branch->branch_case.cc_serdnm);
578 fmd_hdl_strfree(hdl,
579 branch->branch_case.cc_serdnm);
580 branch->branch_case.cc_serdnm = NULL;
581 }
582 }
583 cmd_case_fini(hdl, branch->branch_case.cc_cp, destroy);
584 }
585
586 branch_dimmlist_free(hdl, branch);
587 cmd_fmri_fini(hdl, &branch->branch_asru, destroy);
588
589 if (destroy)
590 fmd_buf_destroy(hdl, NULL, branch->branch_bufname);
591 cmd_list_delete(&cmd.cmd_branches, branch);
592 fmd_hdl_free(hdl, branch, sizeof (cmd_branch_t));
593 }
594
595 void
cmd_branch_destroy(fmd_hdl_t * hdl,cmd_branch_t * branch)596 cmd_branch_destroy(fmd_hdl_t *hdl, cmd_branch_t *branch)
597 {
598 branch_free(hdl, branch, FMD_B_TRUE);
599 }
600
601 /*ARGSUSED*/
602 static int
branch_exist_cb(topo_hdl_t * thp,tnode_t * node,void * arg)603 branch_exist_cb(topo_hdl_t *thp, tnode_t *node, void *arg)
604 {
605 char *lbl, *p, *q;
606 char cy[BUF_SIZE];
607 nvlist_t *rsrc;
608 int err;
609
610 cmd_branch_t *branch = (cmd_branch_t *)arg;
611
612 if (topo_node_resource(node, &rsrc, &err) < 0)
613 return (TOPO_WALK_NEXT); /* no label, try next */
614
615 if (nvlist_lookup_string(rsrc, "unum", &lbl) != 0) {
616 nvlist_free(rsrc);
617 return (TOPO_WALK_NEXT);
618 }
619 /*
620 * for branch membership purposes only, remove reference to
621 * a riser card (MR%d) if one exists.
622 */
623 (void) strcpy(cy, lbl);
624 if ((p = strstr(cy, "/MR")) != NULL) {
625 if ((q = strchr(p + 1, '/')) != NULL)
626 (void) strcpy(p, q);
627 else
628 *p = '\0';
629 }
630 if (strncmp(branch->branch_unum, cy,
631 strlen(branch->branch_unum)) == 0) {
632 br_dimmcount++;
633 nvlist_free(rsrc);
634 return (TOPO_WALK_TERMINATE);
635 }
636 nvlist_free(rsrc);
637 return (TOPO_WALK_NEXT);
638 }
639
640 static int
branch_exist(fmd_hdl_t * hdl,cmd_branch_t * branch)641 branch_exist(fmd_hdl_t *hdl, cmd_branch_t *branch)
642 {
643 topo_hdl_t *thp;
644 topo_walk_t *twp;
645 int err;
646
647 if ((thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION)) == NULL)
648 return (0);
649 if ((twp = topo_walk_init(thp,
650 FM_FMRI_SCHEME_MEM, branch_exist_cb, branch, &err))
651 == NULL) {
652 fmd_hdl_topo_rele(hdl, thp);
653 return (0);
654 }
655 br_dimmcount = 0;
656 (void) topo_walk_step(twp, TOPO_WALK_CHILD);
657 topo_walk_fini(twp);
658 fmd_hdl_topo_rele(hdl, thp);
659
660 return (br_dimmcount);
661 }
662
663 /*
664 * If the case has been solved, don't need to check the dimmlist
665 * If the case has not been solved, the branch is valid if there is least one
666 * existing dimm in the branch
667 */
668 void
cmd_branch_validate(fmd_hdl_t * hdl)669 cmd_branch_validate(fmd_hdl_t *hdl)
670 {
671 cmd_branch_t *branch, *next;
672
673 fmd_hdl_debug(hdl, "cmd_branch_validate\n");
674
675 for (branch = cmd_list_next(&cmd.cmd_branches); branch != NULL;
676 branch = next) {
677 next = cmd_list_next(branch);
678 if (branch->branch_case.cc_cp != NULL &&
679 fmd_case_solved(hdl, branch->branch_case.cc_cp))
680 continue;
681 if (branch_exist(hdl, branch))
682 continue;
683 cmd_branch_destroy(hdl, branch);
684 }
685 }
686
687 void
cmd_branch_gc(fmd_hdl_t * hdl)688 cmd_branch_gc(fmd_hdl_t *hdl)
689 {
690 fmd_hdl_debug(hdl, "cmd_branch_gc\n");
691 cmd_branch_validate(hdl);
692 }
693
694 void
cmd_branch_fini(fmd_hdl_t * hdl)695 cmd_branch_fini(fmd_hdl_t *hdl)
696 {
697 cmd_branch_t *branch;
698 fmd_hdl_debug(hdl, "cmd_branch_fini\n");
699
700 while ((branch = cmd_list_next(&cmd.cmd_branches)) != NULL)
701 branch_free(hdl, branch, FMD_B_FALSE);
702 }
703