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 /* 23 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 #include <limits.h> 27 #include <strings.h> 28 #include <string.h> 29 #include <unistd.h> 30 #include <stdio.h> 31 #include <alloca.h> 32 #include <devid.h> 33 #include <sys/stat.h> 34 #include <libnvpair.h> 35 #include <fm/topo_mod.h> 36 #include <fm/fmd_fmri.h> 37 #include <sys/fm/protocol.h> 38 39 #include <topo_method.h> 40 #include <topo_subr.h> 41 #include <dev.h> 42 43 static int dev_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 44 topo_instance_t, void *, void *); 45 static void dev_release(topo_mod_t *, tnode_t *); 46 static int dev_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, 47 nvlist_t *, nvlist_t **); 48 static int dev_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t, 49 nvlist_t *, nvlist_t **); 50 static int dev_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t, 51 nvlist_t *, nvlist_t **); 52 static int dev_fmri_present(topo_mod_t *, tnode_t *, topo_version_t, 53 nvlist_t *, nvlist_t **); 54 static int dev_fmri_replaced(topo_mod_t *, tnode_t *, topo_version_t, 55 nvlist_t *, nvlist_t **); 56 static int dev_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t, 57 nvlist_t *, nvlist_t **); 58 static int dev_fmri_service_state(topo_mod_t *, tnode_t *, topo_version_t, 59 nvlist_t *, nvlist_t **); 60 61 static const topo_method_t dev_methods[] = { 62 { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION, 63 TOPO_STABILITY_INTERNAL, dev_fmri_nvl2str }, 64 { TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION, 65 TOPO_STABILITY_INTERNAL, dev_fmri_str2nvl }, 66 { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION, 67 TOPO_STABILITY_INTERNAL, dev_fmri_create_meth }, 68 { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION, 69 TOPO_STABILITY_INTERNAL, dev_fmri_present }, 70 { TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC, 71 TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL, 72 dev_fmri_replaced }, 73 { TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC, 74 TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL, 75 dev_fmri_unusable }, 76 { TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_DESC, 77 TOPO_METH_SERVICE_STATE_VERSION, TOPO_STABILITY_INTERNAL, 78 dev_fmri_service_state }, 79 { NULL } 80 }; 81 82 static const topo_modops_t dev_ops = 83 { dev_enum, dev_release }; 84 static const topo_modinfo_t dev_info = 85 { "dev", FM_FMRI_SCHEME_DEV, DEV_VERSION, &dev_ops }; 86 87 int 88 dev_init(topo_mod_t *mod, topo_version_t version) 89 { 90 if (getenv("TOPOHCDEBUG")) 91 topo_mod_setdebug(mod); 92 topo_mod_dprintf(mod, "initializing dev builtin\n"); 93 94 if (version != DEV_VERSION) 95 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 96 97 if (topo_mod_register(mod, &dev_info, TOPO_VERSION) != 0) { 98 topo_mod_dprintf(mod, "failed to register dev_info: " 99 "%s\n", topo_mod_errmsg(mod)); 100 return (-1); 101 } 102 103 return (0); 104 } 105 106 void 107 dev_fini(topo_mod_t *mod) 108 { 109 topo_mod_unregister(mod); 110 } 111 112 /*ARGSUSED*/ 113 static int 114 dev_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, 115 topo_instance_t min, topo_instance_t max, void *notused1, void *notused2) 116 { 117 (void) topo_method_register(mod, pnode, dev_methods); 118 return (0); 119 } 120 121 static void 122 dev_release(topo_mod_t *mod, tnode_t *node) 123 { 124 topo_method_unregister_all(mod, node); 125 } 126 127 static ssize_t 128 fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) 129 { 130 char *devid = NULL, *tpl0id = NULL; 131 char *devpath = NULL; 132 ssize_t size = 0; 133 uint8_t version; 134 int err; 135 136 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 137 version > FM_DEV_SCHEME_VERSION) 138 return (-1); 139 140 /* Get devid, if present */ 141 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_ID, &devid); 142 if (err != 0 && err != ENOENT) 143 return (-1); 144 145 /* Get target-port-l0id, if present */ 146 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_TGTPTLUN0, &tpl0id); 147 if (err != 0 && err != ENOENT) 148 return (-1); 149 150 /* There must be a device path present */ 151 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_PATH, &devpath); 152 if (err != 0 || devpath == NULL) 153 return (-1); 154 155 /* 156 * dev:/// 157 * 158 * The dev scheme does not render fmri authority information 159 * in the string form of an fmri. It is meaningless to 160 * transmit a dev scheme fmri outside of the immediate fault 161 * manager. 162 */ 163 topo_fmristr_build(&size, 164 buf, buflen, FM_FMRI_SCHEME_DEV, NULL, ":///"); 165 166 /* device-id part, topo_fmristr_build does nothing if devid is NULL */ 167 topo_fmristr_build(&size, 168 buf, buflen, devid, ":" FM_FMRI_DEV_ID "=", NULL); 169 170 /* target-port-l0id part */ 171 topo_fmristr_build(&size, 172 buf, buflen, tpl0id, ":" FM_FMRI_DEV_TGTPTLUN0 "=", NULL); 173 174 /* 175 * device-path part; the devpath should always start with a / 176 * so you'd think we don't need to add a further / prefix here; 177 * however past implementation has always added the / if 178 * there is a devid component so we continue to do that 179 * so strings match exactly as before. So we can have: 180 * 181 * dev:////pci@0,0/... 182 * dev:///<devid-and-tpl0>//pci@0,0/... 183 * 184 * where <devid-and-tpl0> = 185 * [:devid=<devid>][:target-port-l0id=<tpl0>] 186 */ 187 topo_fmristr_build(&size, buf, buflen, devpath, 188 devid || tpl0id ? "/" : NULL, NULL); 189 190 return (size); 191 } 192 193 /*ARGSUSED*/ 194 static int 195 dev_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version, 196 nvlist_t *nvl, nvlist_t **out) 197 { 198 ssize_t len; 199 char *name = NULL; 200 nvlist_t *fmristr; 201 202 if (version > TOPO_METH_NVL2STR_VERSION) 203 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 204 205 if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 || 206 (name = topo_mod_alloc(mod, len + 1)) == NULL || 207 fmri_nvl2str(nvl, name, len + 1) == 0) { 208 if (name != NULL) 209 topo_mod_free(mod, name, len + 1); 210 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 211 } 212 213 if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) 214 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 215 if (nvlist_add_string(fmristr, "fmri-string", name) != 0) { 216 topo_mod_free(mod, name, len + 1); 217 nvlist_free(fmristr); 218 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 219 } 220 topo_mod_free(mod, name, len + 1); 221 *out = fmristr; 222 223 return (0); 224 } 225 226 /*ARGSUSED*/ 227 static int 228 dev_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version, 229 nvlist_t *in, nvlist_t **out) 230 { 231 char *cur, *devid = NULL, *tpl0id = NULL; 232 char *str, *strcp; 233 nvlist_t *fmri; 234 char *devpath; 235 size_t len; 236 int err; 237 238 if (version > TOPO_METH_STR2NVL_VERSION) 239 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 240 241 if (nvlist_lookup_string(in, "fmri-string", &str) != 0) 242 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 243 244 len = strlen(str); 245 246 /* 247 * We're expecting a string version of a dev scheme FMRI, and 248 * no fmri authority information. 249 * 250 * The shortest legal string would be "dev:////" (len 8) for a string 251 * with no FMRI auth info, no devid or target-port-l0id and 252 * an empty devpath string. 253 */ 254 if (len < 8 || strncmp(str, "dev:///", 7) != 0) 255 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 256 257 strcp = alloca(len + 1); 258 (void) memcpy(strcp, str, len); 259 strcp[len] = '\0'; 260 cur = strcp + 7; /* already parsed "dev:///" */ 261 262 /* 263 * If the first character after the "/" that terminates the (empty) 264 * fmri authority is a colon then we have devid and/or target-port-l0id 265 * info. They could be in either order. 266 * 267 * If not a colon then it must be the / that begins the devpath. 268 */ 269 if (*cur == ':') { 270 char *eos, *part[2]; 271 int i; 272 /* 273 * Look ahead to the "/" that starts the devpath. If not 274 * found or if straight after the : then we're busted. 275 */ 276 eos = devpath = strchr(cur, '/'); 277 if (devpath == NULL || devpath == cur + 1) 278 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 279 280 part[0] = ++cur; 281 282 /* 283 * Replace the initial "/" of the devpath with a NUL 284 * to terminate the string before it. We'll undo this 285 * before rendering devpath. 286 */ 287 *eos = '\0'; 288 289 /* 290 * We should now have a NUL-terminated string matching 291 * foo=<pat1>[:bar=<pat2>] (we stepped over the initial :) 292 * Look for a second colon; if found there must be space 293 * after it for the additional component, but no more colons. 294 */ 295 if ((part[1] = strchr(cur, ':')) != NULL) { 296 if (part[1] + 1 == eos || 297 strchr(part[1] + 1, ':') != NULL) 298 return (topo_mod_seterrno(mod, 299 EMOD_FMRI_MALFORM)); 300 *part[1] = '\0'; /* terminate part[0] */ 301 part[1]++; 302 } 303 304 for (i = 0; i < 2; i++) { 305 char *eq; 306 307 if (!part[i]) 308 continue; 309 310 if ((eq = strchr(part[i], '=')) == NULL || 311 *(eq + 1) == '\0') 312 return (topo_mod_seterrno(mod, 313 EMOD_FMRI_MALFORM)); 314 315 *eq = '\0'; 316 if (strcmp(part[i], FM_FMRI_DEV_ID) == 0) 317 devid = eq + 1; 318 else if (strcmp(part[i], FM_FMRI_DEV_TGTPTLUN0) == 0) 319 tpl0id = eq + 1; 320 else 321 return (topo_mod_seterrno(mod, 322 EMOD_FMRI_MALFORM)); 323 } 324 325 if (devid == NULL && tpl0id == NULL) 326 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 327 328 cur = devpath; /* initial slash is NULled */ 329 } else if (*cur != '/') { 330 /* the device-path should start with a slash */ 331 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 332 } else { 333 devpath = cur; 334 } 335 336 if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) 337 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 338 339 err = nvlist_add_uint8(fmri, FM_VERSION, FM_DEV_SCHEME_VERSION); 340 err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV); 341 342 if (devid != NULL) 343 err |= nvlist_add_string(fmri, FM_FMRI_DEV_ID, devid); 344 345 if (tpl0id != NULL) 346 err |= nvlist_add_string(fmri, FM_FMRI_DEV_TGTPTLUN0, tpl0id); 347 348 if (devid != NULL || tpl0id != NULL) 349 *devpath = '/'; /* we NULed this earlier; put it back */ 350 351 /* step over repeated initial / in the devpath */ 352 while (*(devpath + 1) == '/') 353 devpath++; 354 355 err |= nvlist_add_string(fmri, FM_FMRI_DEV_PATH, devpath); 356 357 if (err != 0) { 358 nvlist_free(fmri); 359 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 360 } 361 362 *out = fmri; 363 364 return (0); 365 } 366 367 /*ARGSUSED*/ 368 static int 369 dev_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version, 370 nvlist_t *in, nvlist_t **out) 371 { 372 uint8_t fmversion; 373 char *devpath = NULL; 374 uint32_t present; 375 char *devid = NULL, *path; 376 ddi_devid_t id; 377 ddi_devid_t matchid; 378 di_node_t dnode; 379 struct stat sb; 380 int len; 381 382 if (version > TOPO_METH_PRESENT_VERSION) 383 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 384 385 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 386 fmversion > FM_DEV_SCHEME_VERSION || 387 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 388 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 389 390 (void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid); 391 392 if (devpath == NULL || strlen(devpath) == 0) 393 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 394 395 /* 396 * stat() the device node in devfs. This will tell us if the device is 397 * present or not. Don't stat the minor, just the whole device. 398 * If the device is present and there is a devid, it must also match. 399 * so di_init that one node. No need for DINFOFORCE. 400 */ 401 len = strlen(devpath) + strlen("/devices") + 1; 402 path = topo_mod_alloc(mod, len); 403 (void) snprintf(path, len, "/devices%s", devpath); 404 if (devid == NULL) { 405 if (stat(path, &sb) != -1) 406 present = 1; 407 else if ((dnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL) 408 present = 0; 409 else { 410 if (di_lookup_node(dnode, devpath) == DI_NODE_NIL) 411 present = 0; 412 else 413 present = 1; 414 di_fini(dnode); 415 } 416 } else { 417 if (stat(path, &sb) == -1) 418 present = 0; 419 else if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) 420 present = 0; 421 else { 422 if ((id = di_devid(dnode)) == NULL || 423 devid_str_decode(devid, &matchid, NULL) != 0) 424 present = 0; 425 else { 426 if (devid_compare(id, matchid) != 0) 427 present = 0; 428 else 429 present = 1; 430 devid_free(matchid); 431 } 432 di_fini(dnode); 433 } 434 } 435 topo_mod_free(mod, path, len); 436 437 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 438 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 439 if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, present) != 0) { 440 nvlist_free(*out); 441 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 442 } 443 444 return (0); 445 } 446 447 /*ARGSUSED*/ 448 static int 449 dev_fmri_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t version, 450 nvlist_t *in, nvlist_t **out) 451 { 452 uint8_t fmversion; 453 char *devpath = NULL; 454 uint32_t rval; 455 char *devid = NULL, *path; 456 ddi_devid_t id; 457 ddi_devid_t matchid; 458 di_node_t dnode; 459 struct stat sb; 460 int len; 461 462 if (version > TOPO_METH_REPLACED_VERSION) 463 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 464 465 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 466 fmversion > FM_DEV_SCHEME_VERSION || 467 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 468 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 469 470 (void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid); 471 472 if (devpath == NULL || strlen(devpath) == 0) 473 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 474 475 /* 476 * stat() the device node in devfs. This will tell us if the device is 477 * present or not. Don't stat the minor, just the whole device. 478 * If the device is present and there is a devid, it must also match. 479 * so di_init that one node. No need for DINFOFORCE. 480 */ 481 len = strlen(devpath) + strlen("/devices") + 1; 482 path = topo_mod_alloc(mod, len); 483 (void) snprintf(path, len, "/devices%s", devpath); 484 if (devid == NULL) { 485 if (stat(path, &sb) != -1) 486 rval = FMD_OBJ_STATE_UNKNOWN; 487 else if ((dnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL) 488 rval = FMD_OBJ_STATE_UNKNOWN; 489 else { 490 if (di_lookup_node(dnode, devpath) == DI_NODE_NIL) 491 rval = FMD_OBJ_STATE_UNKNOWN; 492 else 493 rval = FMD_OBJ_STATE_UNKNOWN; 494 di_fini(dnode); 495 } 496 } else { 497 if (stat(path, &sb) == -1) 498 rval = FMD_OBJ_STATE_UNKNOWN; 499 else if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) 500 rval = FMD_OBJ_STATE_UNKNOWN; 501 else { 502 if ((id = di_devid(dnode)) == NULL || 503 devid_str_decode(devid, &matchid, NULL) != 0) 504 rval = FMD_OBJ_STATE_UNKNOWN; 505 else { 506 if (devid_compare(id, matchid) != 0) 507 rval = FMD_OBJ_STATE_REPLACED; 508 else 509 rval = FMD_OBJ_STATE_STILL_PRESENT; 510 devid_free(matchid); 511 } 512 di_fini(dnode); 513 } 514 } 515 topo_mod_free(mod, path, len); 516 517 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 518 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 519 if (nvlist_add_uint32(*out, TOPO_METH_REPLACED_RET, rval) != 0) { 520 nvlist_free(*out); 521 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 522 } 523 524 return (0); 525 } 526 527 /*ARGSUSED*/ 528 static int 529 dev_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version, 530 nvlist_t *in, nvlist_t **out) 531 { 532 di_node_t dnode; 533 uint8_t fmversion; 534 char *devpath = NULL; 535 uint32_t unusable; 536 uint_t state; 537 538 if (version > TOPO_METH_UNUSABLE_VERSION) 539 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 540 541 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 542 fmversion > FM_DEV_SCHEME_VERSION || 543 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 544 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 545 546 if (devpath == NULL) 547 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 548 549 if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) { 550 if (errno != ENXIO) 551 return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM)); 552 unusable = 1; 553 } else { 554 uint_t retired = di_retired(dnode); 555 state = di_state(dnode); 556 if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN | 557 DI_BUS_QUIESCED | DI_BUS_DOWN))) 558 unusable = 1; 559 else 560 unusable = 0; 561 di_fini(dnode); 562 } 563 564 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 565 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 566 if (nvlist_add_uint32(*out, TOPO_METH_UNUSABLE_RET, unusable) != 0) { 567 nvlist_free(*out); 568 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 569 } 570 571 return (0); 572 } 573 574 /*ARGSUSED*/ 575 static int 576 dev_fmri_service_state(topo_mod_t *mod, tnode_t *node, topo_version_t version, 577 nvlist_t *in, nvlist_t **out) 578 { 579 di_node_t dnode; 580 uint8_t fmversion; 581 char *devpath = NULL; 582 uint32_t service_state; 583 uint_t state; 584 585 if (version > TOPO_METH_SERVICE_STATE_VERSION) 586 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 587 588 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 589 fmversion > FM_DEV_SCHEME_VERSION || 590 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 591 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 592 593 if (devpath == NULL) 594 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 595 596 if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) { 597 if (errno != ENXIO) 598 return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM)); 599 service_state = FMD_SERVICE_STATE_UNUSABLE; 600 } else { 601 uint_t retired = di_retired(dnode); 602 state = di_state(dnode); 603 if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN | 604 DI_BUS_QUIESCED | DI_BUS_DOWN))) 605 service_state = FMD_SERVICE_STATE_UNUSABLE; 606 else if (state & DI_DEVICE_DEGRADED) 607 service_state = FMD_SERVICE_STATE_DEGRADED; 608 else 609 service_state = FMD_SERVICE_STATE_OK; 610 di_fini(dnode); 611 } 612 613 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 614 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 615 if (nvlist_add_uint32(*out, TOPO_METH_SERVICE_STATE_RET, 616 service_state) != 0) { 617 nvlist_free(*out); 618 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 619 } 620 621 return (0); 622 } 623 624 static nvlist_t * 625 dev_fmri_create(topo_mod_t *mp, const char *id, const char *path) 626 { 627 nvlist_t *out = NULL; 628 int e; 629 630 if (topo_mod_nvalloc(mp, &out, NV_UNIQUE_NAME) != 0) { 631 (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 632 return (NULL); 633 } 634 e = nvlist_add_string(out, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV); 635 e |= nvlist_add_uint8(out, FM_VERSION, FM_DEV_SCHEME_VERSION); 636 e |= nvlist_add_string(out, FM_FMRI_DEV_PATH, path); 637 638 if (id != NULL) 639 e |= nvlist_add_string(out, FM_FMRI_DEV_ID, id); 640 641 if (e == 0) 642 return (out); 643 644 topo_mod_dprintf(mp, "construction of dev nvl failed"); 645 (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 646 nvlist_free(out); 647 return (NULL); 648 } 649 650 /*ARGSUSED*/ 651 static int 652 dev_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version, 653 nvlist_t *in, nvlist_t **out) 654 { 655 nvlist_t *args = NULL; 656 char *path, *id = NULL; 657 658 if (version > TOPO_METH_FMRI_VERSION) 659 return (topo_mod_seterrno(mp, EMOD_VER_NEW)); 660 661 if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 || 662 nvlist_lookup_string(args, FM_FMRI_DEV_PATH, &path) != 0) { 663 topo_mod_dprintf(mp, "no path string in method argument\n"); 664 return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 665 } 666 667 (void) nvlist_lookup_string(args, FM_FMRI_DEV_ID, &id); 668 669 if ((*out = dev_fmri_create(mp, id, path)) == NULL) 670 return (-1); 671 return (0); 672 } 673