Print this page
OS-66 Retired devices may still get attached leading to ndi_devi_online errors
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/lib/libdevinfo/devinfo_retire.c
+++ new/usr/src/lib/libdevinfo/devinfo_retire.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
|
↓ open down ↓ |
13 lines elided |
↑ open up ↑ |
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 + * Copyright (c) 2013, Nexenta Systemc, Inc. All rights reserved.
24 25 */
25 26
26 27 #include <libdevinfo.h>
27 28 #include <sys/modctl.h>
28 29 #include <sys/stat.h>
29 30 #include <string.h>
30 31 #include <librcm.h>
31 32 #include <dlfcn.h>
33 +#include <sys/scsi/scsi_address.h>
34 +#include <limits.h>
35 +#include <errno.h>
32 36
33 37 #undef NDEBUG
34 38 #include <assert.h>
35 39
36 40 typedef struct rio_path {
37 41 char rpt_path[PATH_MAX];
38 42 struct rio_path *rpt_next;
39 43 } rio_path_t;
40 44
41 45 typedef struct rcm_arg {
42 46 char *rcm_root;
43 47 di_node_t rcm_node;
44 48 int rcm_supp;
45 49 rcm_handle_t *rcm_handle;
46 50 int rcm_retcode;
47 51 di_retire_t *rcm_dp;
48 52 rio_path_t *rcm_cons_nodes;
49 53 rio_path_t *rcm_rsrc_minors;
50 54 int (*rcm_offline)();
51 55 int (*rcm_online)();
52 56 int (*rcm_remove)();
53 57 } rcm_arg_t;
54 58
55 59 typedef struct selector {
56 60 char *sel_name;
57 61 int (*sel_selector)(di_node_t node, rcm_arg_t *rp);
58 62 } di_selector_t;
59 63
60 64 static void rio_assert(di_retire_t *dp, const char *EXstr, int line,
61 65 const char *file);
62 66
63 67 #define LIBRCM_PATH "/usr/lib/librcm.so"
64 68 #define RIO_ASSERT(d, x) \
65 69 {if (!(x)) rio_assert(d, #x, __LINE__, __FILE__); }
66 70
67 71 static int disk_select(di_node_t node, rcm_arg_t *rp);
68 72 static int nexus_select(di_node_t node, rcm_arg_t *rp);
69 73 static int enclosure_select(di_node_t node, rcm_arg_t *rp);
70 74 static int smp_select(di_node_t node, rcm_arg_t *rp);
71 75
72 76 di_selector_t supported_devices[] = {
73 77 {"disk", disk_select},
74 78 {"nexus", nexus_select},
75 79 {"enclosure", enclosure_select},
76 80 {"smp", smp_select},
77 81 {NULL, NULL}
78 82 };
79 83
80 84 void *
81 85 s_calloc(size_t nelem, size_t elsize, int fail)
82 86 {
83 87 if (fail) {
84 88 errno = ENOMEM;
85 89 return (NULL);
86 90 } else {
87 91 return (calloc(nelem, elsize));
88 92 }
89 93 }
90 94
91 95 static void
92 96 rio_assert(di_retire_t *dp, const char *EXstr, int line, const char *file)
93 97 {
94 98 char buf[PATH_MAX];
95 99
96 100 if (dp->rt_abort == NULL)
97 101 assert(0);
98 102
99 103 (void) snprintf(buf, sizeof (buf),
100 104 "Assertion failed: %s, file %s, line %d\n",
101 105 EXstr, file, line);
102 106 dp->rt_abort(dp->rt_hdl, buf);
103 107 }
104 108
105 109 /*ARGSUSED*/
106 110 static int
107 111 enclosure_minor(di_node_t node, di_minor_t minor, void *arg)
108 112 {
109 113 rcm_arg_t *rp = (rcm_arg_t *)arg;
110 114 di_retire_t *dp = rp->rcm_dp;
111 115
112 116 rp->rcm_supp = 1;
113 117 dp->rt_debug(dp->rt_hdl, "[INFO]: enclosure_minor: "
114 118 "IDed this node as enclosure\n");
115 119 return (DI_WALK_TERMINATE);
116 120 }
117 121
118 122 static int
119 123 enclosure_select(di_node_t node, rcm_arg_t *rp)
120 124 {
121 125 rcm_arg_t rarg;
122 126 di_retire_t *dp = rp->rcm_dp;
123 127
124 128 rarg.rcm_dp = dp;
125 129
126 130 /*
127 131 * Check if this is an enclosure minor. If any one minor is DDI_NT_SGEN
128 132 * or DDI_NT_SCSI_ENCLOSURE we assume it is an enclosure.
129 133 */
130 134 rarg.rcm_supp = 0;
131 135 if (di_walk_minor(node, DDI_NT_SCSI_ENCLOSURE, 0, &rarg,
132 136 enclosure_minor) != 0) {
133 137 dp->rt_debug(dp->rt_hdl, "[INFO]: enclosure_select:"
134 138 "di_walk_minor failed. Returning NOTSUP\n");
135 139 return (0);
136 140 }
137 141 if (di_walk_minor(node, "ddi_generic:scsi", 0, &rarg,
138 142 enclosure_minor) != 0) {
139 143 dp->rt_debug(dp->rt_hdl, "[INFO]: enclosure_select:"
140 144 "di_walk_minor failed. Returning NOTSUP\n");
141 145 return (0);
142 146 }
143 147
144 148 return (rarg.rcm_supp);
145 149 }
146 150
147 151 /*ARGSUSED*/
148 152 static int
149 153 smp_minor(di_node_t node, di_minor_t minor, void *arg)
150 154 {
151 155 rcm_arg_t *rp = (rcm_arg_t *)arg;
152 156 di_retire_t *dp = rp->rcm_dp;
153 157
154 158 rp->rcm_supp = 1;
155 159 dp->rt_debug(dp->rt_hdl, "[INFO]: smp_minor: "
156 160 "IDed this node as smp\n");
157 161 return (DI_WALK_TERMINATE);
158 162 }
159 163
160 164 static int
161 165 smp_select(di_node_t node, rcm_arg_t *rp)
162 166 {
163 167 rcm_arg_t rarg;
164 168 di_retire_t *dp = rp->rcm_dp;
165 169
166 170 rarg.rcm_dp = dp;
167 171
168 172 /*
169 173 * Check if this is an smp minor. If any one minor is DDI_NT_SMP
170 174 * we assume it is an smp.
171 175 */
172 176 rarg.rcm_supp = 0;
173 177 if (di_walk_minor(node, DDI_NT_SMP, 0, &rarg, smp_minor) != 0) {
174 178 dp->rt_debug(dp->rt_hdl, "[INFO]: smp_select:"
175 179 "di_walk_minor failed. Returning NOTSUP\n");
176 180 return (0);
177 181 }
178 182
179 183 return (rarg.rcm_supp);
180 184 }
181 185
182 186 /*ARGSUSED*/
183 187 static int
184 188 disk_minor(di_node_t node, di_minor_t minor, void *arg)
185 189 {
186 190 rcm_arg_t *rp = (rcm_arg_t *)arg;
187 191 di_retire_t *dp = rp->rcm_dp;
188 192
189 193 if (di_minor_spectype(minor) == S_IFBLK) {
190 194 rp->rcm_supp = 1;
191 195 dp->rt_debug(dp->rt_hdl, "[INFO]: disk_minor: is disk minor. "
192 196 "IDed this node as disk\n");
193 197 return (DI_WALK_TERMINATE);
194 198 }
195 199
196 200 dp->rt_debug(dp->rt_hdl, "[INFO]: disk_minor: Not a disk minor. "
197 201 "Continuing minor walk\n");
198 202 return (DI_WALK_CONTINUE);
199 203 }
200 204
201 205 static int
202 206 disk_select(di_node_t node, rcm_arg_t *rp)
203 207 {
204 208 rcm_arg_t rarg;
205 209 di_retire_t *dp = rp->rcm_dp;
206 210
207 211 rarg.rcm_dp = dp;
208 212
209 213 /*
210 214 * Check if this is a disk minor. If any one minor is DDI_NT_BLOCK
211 215 * we assume it is a disk
212 216 */
213 217 rarg.rcm_supp = 0;
214 218 if (di_walk_minor(node, DDI_NT_BLOCK, 0, &rarg, disk_minor) != 0) {
215 219 dp->rt_debug(dp->rt_hdl, "[INFO]: disk_select: di_walk_minor "
216 220 "failed. Returning NOTSUP\n");
217 221 return (0);
218 222 }
219 223
220 224 return (rarg.rcm_supp);
221 225 }
222 226
223 227 static int
224 228 nexus_select(di_node_t node, rcm_arg_t *rp)
225 229 {
226 230 int select;
227 231 char *path;
228 232
229 233 di_retire_t *dp = rp->rcm_dp;
230 234
231 235 path = di_devfs_path(node);
232 236 if (path == NULL) {
233 237 dp->rt_debug(dp->rt_hdl, "[INFO]: nexus_select: "
234 238 "di_devfs_path() is NULL. Returning NOTSUP\n");
235 239 return (0);
236 240 }
237 241
238 242 /*
239 243 * Check if it is a nexus
240 244 */
241 245 if (di_driver_ops(node) & DI_BUS_OPS) {
242 246 dp->rt_debug(dp->rt_hdl, "[INFO]: nexus_select: is nexus %s\n",
243 247 path);
244 248 select = 1;
245 249 } else {
246 250 dp->rt_debug(dp->rt_hdl, "[INFO]: nexus_select: not nexus %s\n",
247 251 path);
248 252 select = 0;
249 253 }
250 254
251 255 di_devfs_path_free(path);
252 256
253 257 return (select);
254 258 }
255 259
256 260 static int
257 261 node_select(di_node_t node, void *arg)
258 262 {
259 263 rcm_arg_t *rp = (rcm_arg_t *)arg;
260 264 di_retire_t *dp;
261 265 int sel;
262 266 int i;
263 267 char *path;
264 268 uint_t state;
265 269
266 270 dp = rp->rcm_dp;
267 271
268 272 /* skip pseudo nodes - we only retire real hardware */
269 273 path = di_devfs_path(node);
270 274 if (strncmp(path, "/pseudo/", strlen("/pseudo/")) == 0 ||
271 275 strcmp(path, "/pseudo") == 0) {
272 276 dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: "
273 277 "pseudo device in subtree - returning NOTSUP: %s\n",
274 278 path);
275 279 rp->rcm_supp = 0;
276 280 di_devfs_path_free(path);
277 281 return (DI_WALK_TERMINATE);
278 282 }
279 283 di_devfs_path_free(path);
280 284
281 285 /*
282 286 * If a device is offline/detached/down it is
283 287 * retireable irrespective of the type of device,
284 288 * presumably the system is able to function without
285 289 * it.
286 290 */
287 291 state = di_state(node);
288 292 if ((state & DI_DRIVER_DETACHED) || (state & DI_DEVICE_OFFLINE) ||
289 293 (state & DI_BUS_DOWN)) {
290 294 dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: device "
291 295 "is offline/detached. Assuming retire supported\n");
292 296 return (DI_WALK_CONTINUE);
293 297 }
294 298
295 299 sel = 0;
296 300 for (i = 0; supported_devices[i].sel_name != NULL; i++) {
297 301 sel = supported_devices[i].sel_selector(node, rp);
298 302 if (sel == 1) {
299 303 dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: "
300 304 "found supported device: %s\n",
301 305 supported_devices[i].sel_name);
302 306 break;
303 307 }
304 308 }
305 309
306 310 if (sel != 1) {
307 311 /*
308 312 * This node is not a supported device. Retire cannot proceed
309 313 */
310 314 dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: found "
311 315 "unsupported device. Returning NOTSUP\n");
312 316 rp->rcm_supp = 0;
313 317 return (DI_WALK_TERMINATE);
314 318 }
315 319
316 320 /*
317 321 * This node is supported. Check other nodes in this subtree.
318 322 */
319 323 dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: This node supported. "
320 324 "Checking other nodes in subtree: %s\n", rp->rcm_root);
321 325 return (DI_WALK_CONTINUE);
322 326 }
323 327
324 328
325 329
326 330 /*
327 331 * when in doubt assume that retire is not supported for this device.
328 332 */
329 333 static int
330 334 retire_supported(rcm_arg_t *rp)
331 335 {
332 336 di_retire_t *dp;
333 337 di_node_t rnode = rp->rcm_node;
334 338
335 339 dp = rp->rcm_dp;
336 340
337 341 /*
338 342 * We should not be here if devinfo snapshot is NULL.
339 343 */
340 344 RIO_ASSERT(dp, rnode != DI_NODE_NIL);
341 345
342 346 /*
343 347 * Note: We initally set supported to 1, then walk the
344 348 * subtree rooted at devpath, allowing each node the
345 349 * opportunity to veto the support. We cannot do things
346 350 * the other way around i.e. assume "not supported" and
347 351 * let individual nodes indicate that they are supported.
348 352 * In the latter case, the supported flag would be set
349 353 * if any one node in the subtree was supported which is
350 354 * not what we want.
351 355 */
352 356 rp->rcm_supp = 1;
353 357 if (di_walk_node(rnode, DI_WALK_CLDFIRST, rp, node_select) != 0) {
354 358 dp->rt_debug(dp->rt_hdl, "[ERROR]: retire_supported: "
355 359 "di_walk_node: failed. Returning NOTSUP\n");
356 360 rp->rcm_supp = 0;
357 361 }
358 362
359 363 if (rp->rcm_supp) {
360 364 dp->rt_debug(dp->rt_hdl, "[INFO]: retire IS supported\n");
361 365 }
362 366
363 367 return (rp->rcm_supp);
364 368 }
365 369
366 370 static void
367 371 rcm_finalize(rcm_arg_t *rp, int retcode)
368 372 {
369 373 rio_path_t *p;
370 374 rio_path_t *tmp;
371 375 int flags = RCM_RETIRE_NOTIFY;
372 376 int retval;
373 377 int error;
374 378 di_retire_t *dp;
375 379
376 380 dp = rp->rcm_dp;
377 381
378 382 RIO_ASSERT(dp, retcode == 0 || retcode == -1);
379 383
380 384 dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_finalize: retcode=%d: dev=%s\n",
381 385 retcode, rp->rcm_root);
382 386
383 387 for (p = rp->rcm_cons_nodes; p; ) {
384 388 tmp = p;
385 389 p = tmp->rpt_next;
386 390 free(tmp);
387 391 }
388 392 rp->rcm_cons_nodes = NULL;
389 393
390 394 dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_finalize: cons_nodes NULL\n");
391 395
392 396 for (p = rp->rcm_rsrc_minors; p; ) {
393 397 tmp = p;
394 398 p = tmp->rpt_next;
395 399 if (retcode == 0) {
396 400 retval = rp->rcm_remove(rp->rcm_handle,
397 401 tmp->rpt_path, flags, NULL);
398 402 error = errno;
399 403 } else {
400 404 RIO_ASSERT(dp, retcode == -1);
401 405 retval = rp->rcm_online(rp->rcm_handle,
402 406 tmp->rpt_path, flags, NULL);
403 407 error = errno;
404 408 }
405 409 if (retval != RCM_SUCCESS) {
406 410 dp->rt_debug(dp->rt_hdl, "[ERROR]: rcm_finalize: "
407 411 "rcm_%s: retval=%d: error=%s: path=%s\n",
408 412 retcode == 0 ? "remove" : "online", retval,
409 413 strerror(error), tmp->rpt_path);
410 414 } else {
411 415 dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_finalize: "
412 416 "rcm_%s: SUCCESS: path=%s\n",
413 417 retcode == 0 ? "remove" : "online", tmp->rpt_path);
414 418 }
415 419 free(tmp);
416 420 }
417 421 rp->rcm_rsrc_minors = NULL;
418 422 }
419 423 /*ARGSUSED*/
420 424 static int
421 425 call_offline(di_node_t node, di_minor_t minor, void *arg)
422 426 {
423 427 rcm_arg_t *rp = (rcm_arg_t *)arg;
424 428 di_retire_t *dp = rp->rcm_dp;
425 429 char *mnp;
426 430 rio_path_t *rpt;
427 431 int retval;
428 432
429 433 mnp = di_devfs_minor_path(minor);
430 434 if (mnp == NULL) {
431 435 dp->rt_debug(dp->rt_hdl, "[ERROR]: di_devfs_minor_path "
432 436 "failed. Returning RCM FAILURE: %s\n", rp->rcm_root);
433 437 rp->rcm_retcode = RCM_FAILURE;
434 438 return (DI_WALK_TERMINATE);
435 439 }
436 440
437 441 rpt = s_calloc(1, sizeof (rio_path_t), 0);
438 442 if (rpt == NULL) {
439 443 dp->rt_debug(dp->rt_hdl, "[ERROR]: calloc failed. "
440 444 "Returning RCM FAILURE: %s\n", rp->rcm_root);
441 445 di_devfs_path_free(mnp);
442 446 rp->rcm_retcode = RCM_FAILURE;
443 447 return (DI_WALK_TERMINATE);
444 448 }
445 449
446 450 (void) snprintf(rpt->rpt_path, sizeof (rpt->rpt_path),
447 451 "/devices%s", mnp);
448 452
449 453 di_devfs_path_free(mnp);
450 454
451 455 retval = rp->rcm_offline(rp->rcm_handle, rpt->rpt_path,
452 456 RCM_RETIRE_REQUEST, NULL);
453 457
454 458 rpt->rpt_next = rp->rcm_rsrc_minors;
455 459 rp->rcm_rsrc_minors = rpt;
456 460
457 461 if (retval == RCM_FAILURE) {
458 462 dp->rt_debug(dp->rt_hdl, "[ERROR]: RCM OFFLINE failed "
459 463 "for: %s\n", rpt->rpt_path);
460 464 rp->rcm_retcode = RCM_FAILURE;
461 465 return (DI_WALK_TERMINATE);
462 466 } else if (retval == RCM_SUCCESS) {
463 467 rp->rcm_retcode = RCM_SUCCESS;
464 468 dp->rt_debug(dp->rt_hdl, "[INFO]: RCM OFFLINE returned "
465 469 "RCM_SUCCESS: %s\n", rpt->rpt_path);
466 470 } else if (retval != RCM_NO_CONSTRAINT) {
467 471 dp->rt_debug(dp->rt_hdl, "[ERROR]: RCM OFFLINE returned "
468 472 "invalid value for: %s\n", rpt->rpt_path);
469 473 rp->rcm_retcode = RCM_FAILURE;
470 474 return (DI_WALK_TERMINATE);
471 475 } else {
472 476 dp->rt_debug(dp->rt_hdl, "[INFO]: RCM OFFLINE returned "
473 477 "RCM_NO_CONSTRAINT: %s\n", rpt->rpt_path);
474 478 }
475 479
476 480 return (DI_WALK_CONTINUE);
477 481 }
478 482
479 483 static int
480 484 offline_one(di_node_t node, void *arg)
481 485 {
482 486 rcm_arg_t *rp = (rcm_arg_t *)arg;
483 487 rio_path_t *rpt;
484 488 di_retire_t *dp = rp->rcm_dp;
485 489 char *path;
486 490
487 491 /*
488 492 * We should already have terminated the walk
489 493 * in case of failure
490 494 */
491 495 RIO_ASSERT(dp, rp->rcm_retcode == RCM_SUCCESS ||
492 496 rp->rcm_retcode == RCM_NO_CONSTRAINT);
493 497
494 498 dp->rt_debug(dp->rt_hdl, "[INFO]: offline_one: entered\n");
495 499
496 500 rp->rcm_retcode = RCM_NO_CONSTRAINT;
497 501
498 502 rpt = s_calloc(1, sizeof (rio_path_t), 0);
499 503 if (rpt == NULL) {
500 504 dp->rt_debug(dp->rt_hdl, "[ERROR]: rio_path_t calloc "
501 505 "failed: error: %s\n", strerror(errno));
502 506 goto fail;
503 507 }
504 508
505 509 path = di_devfs_path(node);
506 510 if (path == NULL) {
507 511 dp->rt_debug(dp->rt_hdl, "[ERROR]: di_devfs_path "
508 512 "failed: error: %s\n", strerror(errno));
509 513 free(rpt);
510 514 goto fail;
511 515 }
512 516
513 517 (void) strlcpy(rpt->rpt_path, path, sizeof (rpt->rpt_path));
514 518
515 519 di_devfs_path_free(path);
516 520
517 521 if (di_walk_minor(node, NULL, 0, rp, call_offline) != 0) {
518 522 dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_minor "
519 523 "failed: error: %s: %s\n", strerror(errno), path);
520 524 free(rpt);
521 525 goto fail;
522 526 }
523 527
524 528 if (rp->rcm_retcode == RCM_FAILURE) {
525 529 dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_minor "
526 530 "returned: RCM_FAILURE: %s\n", rpt->rpt_path);
527 531 free(rpt);
528 532 goto fail;
529 533 } else if (rp->rcm_retcode == RCM_SUCCESS) {
530 534 dp->rt_debug(dp->rt_hdl, "[INFO]: di_walk_minor "
531 535 "returned: RCM_SUCCESS: %s\n", rpt->rpt_path);
532 536 rpt->rpt_next = rp->rcm_cons_nodes;
533 537 rp->rcm_cons_nodes = rpt;
534 538 } else if (rp->rcm_retcode != RCM_NO_CONSTRAINT) {
535 539 dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_minor "
536 540 "returned: unknown RCM error code: %d, %s\n",
537 541 rp->rcm_retcode, rpt->rpt_path);
538 542 free(rpt);
539 543 goto fail;
540 544 } else {
541 545 dp->rt_debug(dp->rt_hdl, "[INFO]: di_walk_minor "
542 546 "returned: RCM_NO_CONSTRAINT: %s\n", rpt->rpt_path);
543 547 free(rpt);
544 548 }
545 549
546 550 /*
547 551 * RCM_SUCCESS or RCM_NO_CONSTRAINT.
548 552 * RCM_SUCCESS implies we overcame a constraint, so keep walking.
549 553 * RCM_NO_CONSTRAINT implies no constraints applied via RCM.
550 554 * Continue walking in the hope that contracts or LDI will
551 555 * apply constraints
552 556 * set retcode to RCM_SUCCESS to show that at least 1 node
553 557 * completely walked
554 558 */
555 559 rp->rcm_retcode = RCM_SUCCESS;
556 560 return (DI_WALK_CONTINUE);
557 561
558 562 fail:
559 563 rp->rcm_retcode = RCM_FAILURE;
560 564 return (DI_WALK_TERMINATE);
561 565 }
562 566
563 567 /*
564 568 * Returns:
565 569 * RCM_SUCCESS: RCM constraints (if any) were applied. The
566 570 * device paths for which constraints were applied is passed
567 571 * back via the pp argument
568 572 *
569 573 * RCM_FAILURE: Either RCM constraints prevent a retire or
570 574 * an error occurred
571 575 */
572 576 static int
573 577 rcm_notify(rcm_arg_t *rp, char **pp, size_t *clen)
574 578 {
575 579 size_t len;
576 580 rio_path_t *p;
577 581 rio_path_t *tmp;
578 582 char *plistp;
579 583 char *s;
580 584 di_retire_t *dp;
581 585 di_node_t rnode;
582 586
583 587 dp = rp->rcm_dp;
584 588
585 589 dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_notify() entered\n");
586 590
587 591 RIO_ASSERT(dp, rp->rcm_root);
588 592
589 593 *pp = NULL;
590 594
591 595 rnode = rp->rcm_node;
592 596 if (rnode == DI_NODE_NIL) {
593 597 dp->rt_debug(dp->rt_hdl, "[ERROR]: devinfo snapshot "
594 598 "NULL. Returning no RCM constraint: %s\n", rp->rcm_root);
595 599 return (RCM_NO_CONSTRAINT);
596 600 }
597 601
598 602 rp->rcm_retcode = RCM_NO_CONSTRAINT;
599 603 rp->rcm_cons_nodes = NULL;
600 604 rp->rcm_rsrc_minors = NULL;
601 605 if (di_walk_node(rnode, DI_WALK_CLDFIRST, rp, offline_one) != 0) {
602 606 dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_node "
603 607 "failed: error: %s: %s\n", strerror(errno), rp->rcm_root);
604 608 /* online is idempotent - safe to online non-offlined nodes */
605 609 rcm_finalize(rp, -1);
606 610 rp->rcm_retcode = RCM_FAILURE;
607 611 goto out;
608 612 }
609 613
610 614 if (rp->rcm_retcode == RCM_FAILURE) {
611 615 dp->rt_debug(dp->rt_hdl, "[ERROR]: walk_node "
612 616 "returned retcode of RCM_FAILURE: %s\n", rp->rcm_root);
613 617 rcm_finalize(rp, -1);
614 618 goto out;
615 619 }
616 620
617 621 if (rp->rcm_retcode == RCM_NO_CONSTRAINT) {
618 622 dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_node "
619 623 " - no nodes walked: RCM_NO_CONSTRAINT: %s\n",
620 624 rp->rcm_root);
621 625 } else {
622 626 dp->rt_debug(dp->rt_hdl, "[INFO]: walk_node: RCM_SUCCESS\n");
623 627 }
624 628
625 629 /*
626 630 * Convert to a sequence of NUL separated strings terminated by '\0'\0'
627 631 */
628 632 for (len = 0, p = rp->rcm_cons_nodes; p; p = p->rpt_next) {
629 633 RIO_ASSERT(dp, p->rpt_path);
630 634 RIO_ASSERT(dp, strlen(p->rpt_path) > 0);
631 635 len += (strlen(p->rpt_path) + 1);
632 636 }
633 637 len++; /* list terminating '\0' */
634 638
635 639 dp->rt_debug(dp->rt_hdl, "[INFO]: len of constraint str = %lu\n", len);
636 640
637 641 plistp = s_calloc(1, len, 0);
638 642 if (plistp == NULL) {
639 643 dp->rt_debug(dp->rt_hdl, "[ERROR]: fail to alloc "
640 644 "constraint list: error: %s: %s\n", strerror(errno),
641 645 rp->rcm_root);
642 646 rcm_finalize(rp, -1);
643 647 rp->rcm_retcode = RCM_FAILURE;
644 648 goto out;
645 649 }
646 650
647 651 for (s = plistp, p = rp->rcm_cons_nodes; p; ) {
648 652 tmp = p;
649 653 p = tmp->rpt_next;
650 654 (void) strcpy(s, tmp->rpt_path);
651 655 s += strlen(s) + 1;
652 656 RIO_ASSERT(dp, s - plistp < len);
653 657 free(tmp);
654 658 }
655 659 rp->rcm_cons_nodes = NULL;
656 660 RIO_ASSERT(dp, s - plistp == len - 1);
657 661 *s = '\0';
658 662
|
↓ open down ↓ |
617 lines elided |
↑ open up ↑ |
659 663 dp->rt_debug(dp->rt_hdl, "[INFO]: constraint str = %p\n", plistp);
660 664
661 665 *pp = plistp;
662 666 *clen = len;
663 667
664 668 rp->rcm_retcode = RCM_SUCCESS;
665 669 out:
666 670 return (rp->rcm_retcode);
667 671 }
668 672
669 -
670 673 /*ARGSUSED*/
671 -int
672 -di_retire_device(char *devpath, di_retire_t *dp, int flags)
674 +static int
675 +do_di_retire_device(char *devpath, di_retire_t *dp, int flags)
673 676 {
674 677 char path[PATH_MAX];
675 678 struct stat sb;
676 679 int retval = EINVAL;
677 680 char *constraint = NULL;
678 681 size_t clen;
679 682 void *librcm_hdl;
680 683 rcm_arg_t rarg = {0};
681 684 int (*librcm_alloc_handle)();
682 685 int (*librcm_free_handle)();
683 686
684 687 if (dp == NULL || dp->rt_debug == NULL || dp->rt_hdl == NULL)
685 688 return (EINVAL);
686 689
687 690 if (devpath == NULL || devpath[0] == '\0') {
688 691 dp->rt_debug(dp->rt_hdl, "[ERROR]: NULL argument(s)\n");
689 692 return (EINVAL);
690 693 }
691 694
692 695 if (devpath[0] != '/' || strlen(devpath) >= PATH_MAX ||
693 696 strncmp(devpath, "/devices/", strlen("/devices/")) == 0 ||
694 697 strstr(devpath, "../devices/") || strrchr(devpath, ':')) {
695 698 dp->rt_debug(dp->rt_hdl, "[ERROR]: invalid devpath: %s\n",
696 699 devpath);
697 700 return (EINVAL);
698 701 }
699 702
700 703 if (flags != 0) {
701 704 dp->rt_debug(dp->rt_hdl, "[ERROR]: flags should be 0: %d\n",
702 705 flags);
703 706 return (EINVAL);
704 707 }
705 708
706 709 /*
707 710 * dlopen rather than link against librcm since libdevinfo
708 711 * resides in / and librcm resides in /usr. The dlopen is
709 712 * safe to do since fmd which invokes the retire code
710 713 * resides on /usr and will not come here until /usr is
711 714 * mounted.
712 715 */
713 716 librcm_hdl = dlopen(LIBRCM_PATH, RTLD_LAZY);
714 717 if (librcm_hdl == NULL) {
715 718 char *errstr = dlerror();
716 719 dp->rt_debug(dp->rt_hdl, "[ERROR]: Cannot dlopen librcm: %s\n",
717 720 errstr ? errstr : "Unknown error");
718 721 return (ENOSYS);
719 722 }
720 723
721 724 librcm_alloc_handle = (int (*)())dlsym(librcm_hdl, "rcm_alloc_handle");
722 725 rarg.rcm_offline = (int (*)())dlsym(librcm_hdl, "rcm_request_offline");
723 726 rarg.rcm_online = (int (*)())dlsym(librcm_hdl, "rcm_notify_online");
724 727 rarg.rcm_remove = (int (*)())dlsym(librcm_hdl, "rcm_notify_remove");
725 728 librcm_free_handle = (int (*)())dlsym(librcm_hdl, "rcm_free_handle");
726 729
727 730 if (librcm_alloc_handle == NULL ||
728 731 rarg.rcm_offline == NULL ||
729 732 rarg.rcm_online == NULL ||
730 733 rarg.rcm_remove == NULL ||
731 734 librcm_free_handle == NULL) {
732 735 dp->rt_debug(dp->rt_hdl, "[ERROR]: dlsym failed\n");
733 736 retval = ENOSYS;
734 737 goto out;
735 738 }
736 739
737 740 /*
738 741 * Take a libdevinfo snapshot here because we cannot do so
739 742 * after device is retired. If device doesn't attach, we retire
740 743 * anyway i.e. it is not fatal.
741 744 */
742 745 rarg.rcm_node = di_init(devpath, DINFOCPYALL);
743 746 if (rarg.rcm_node == DI_NODE_NIL) {
744 747 dp->rt_debug(dp->rt_hdl, "[ERROR]: device doesn't attach, "
745 748 "retiring anyway: %s\n", devpath);
746 749 }
747 750
748 751 rarg.rcm_handle = NULL;
749 752 if (librcm_alloc_handle(NULL, 0, NULL, &rarg.rcm_handle)
750 753 != RCM_SUCCESS) {
751 754 retval = errno;
752 755 dp->rt_debug(dp->rt_hdl, "[ERROR]: failed to alloc "
753 756 "RCM handle. Returning RCM failure: %s\n", devpath);
754 757 rarg.rcm_handle = NULL;
755 758 goto out;
756 759 }
757 760
758 761 rarg.rcm_root = devpath;
759 762 rarg.rcm_dp = dp;
760 763
761 764 /*
762 765 * If device is already detached/nonexistent and cannot be
763 766 * attached, allow retire without checking device type.
764 767 * XXX
765 768 * Else, check if retire is supported for this device type.
766 769 */
767 770 (void) snprintf(path, sizeof (path), "/devices%s", devpath);
768 771 if (stat(path, &sb) == -1 || !S_ISDIR(sb.st_mode)) {
769 772 dp->rt_debug(dp->rt_hdl, "[ERROR]: detached or nonexistent "
770 773 "device. Bypassing retire_supported: %s\n", devpath);
771 774 } else if (!retire_supported(&rarg)) {
772 775 dp->rt_debug(dp->rt_hdl, "[ERROR]: retire not supported for "
773 776 "device type: %s\n", devpath);
774 777 retval = ENOTSUP;
775 778 goto out;
776 779 }
777 780
778 781 clen = 0;
779 782 constraint = NULL;
780 783 retval = rcm_notify(&rarg, &constraint, &clen);
781 784 if (retval == RCM_FAILURE) {
782 785 /* retire not permitted */
783 786 dp->rt_debug(dp->rt_hdl, "[ERROR]: RCM constraints block "
784 787 "retire: %s\n", devpath);
785 788 retval = EBUSY;
786 789 goto out;
787 790 } else if (retval == RCM_SUCCESS) {
788 791 dp->rt_debug(dp->rt_hdl, "[INFO]: RCM constraints applied"
789 792 ": %s\n", devpath);
790 793 } else if (retval == RCM_NO_CONSTRAINT) {
791 794 dp->rt_debug(dp->rt_hdl, "[INFO]: No RCM constraints applied"
792 795 ": %s\n", devpath);
793 796 } else {
794 797 dp->rt_debug(dp->rt_hdl, "[ERROR]: notify returned unknown "
795 798 "return code: %d: %s\n", retval, devpath);
796 799 retval = ESRCH;
797 800 goto out;
798 801 }
799 802
800 803 if (modctl(MODRETIRE, devpath, constraint, clen) != 0) {
801 804 retval = errno;
802 805 dp->rt_debug(dp->rt_hdl, "[ERROR]: retire modctl() failed: "
803 806 "%s: %s\n", devpath, strerror(retval));
804 807 rcm_finalize(&rarg, -1);
805 808 goto out;
806 809 }
807 810
808 811 dp->rt_debug(dp->rt_hdl, "[INFO]: retire modctl() succeeded: %s\n",
809 812 devpath);
810 813
811 814 rcm_finalize(&rarg, 0);
812 815
813 816 retval = 0;
814 817
815 818 out:
816 819 if (rarg.rcm_handle)
817 820 (void) librcm_free_handle(rarg.rcm_handle);
818 821
819 822 RIO_ASSERT(dp, rarg.rcm_cons_nodes == NULL);
820 823 RIO_ASSERT(dp, rarg.rcm_rsrc_minors == NULL);
821 824
822 825 (void) dlclose(librcm_hdl);
|
↓ open down ↓ |
140 lines elided |
↑ open up ↑ |
823 826
824 827 free(constraint);
825 828
826 829 if (rarg.rcm_node != DI_NODE_NIL)
827 830 di_fini(rarg.rcm_node);
828 831
829 832 return (retval);
830 833 }
831 834
832 835 /*ARGSUSED*/
833 -int
834 -di_unretire_device(char *devpath, di_retire_t *dp)
836 +static int
837 +do_di_unretire_device(char *devpath, di_retire_t *dp)
835 838 {
836 839 if (dp == NULL || dp->rt_debug == NULL || dp->rt_hdl == NULL)
837 840 return (EINVAL);
838 841
839 842 if (devpath == NULL || devpath[0] == '\0') {
840 843 dp->rt_debug(dp->rt_hdl, "[ERROR]: NULL devpath\n");
841 844 return (EINVAL);
842 845 }
843 846
844 847 if (devpath[0] != '/' || strlen(devpath) >= PATH_MAX ||
845 848 strncmp(devpath, "/devices/", strlen("/devices/")) == 0 ||
846 849 strstr(devpath, "../devices/") || strrchr(devpath, ':')) {
847 850 dp->rt_debug(dp->rt_hdl, "[ERROR]: invalid devpath: %s\n",
848 851 devpath);
849 852 return (EINVAL);
850 853 }
851 854
852 855 if (modctl(MODUNRETIRE, devpath) != 0) {
|
↓ open down ↓ |
8 lines elided |
↑ open up ↑ |
853 856 int err = errno;
854 857 dp->rt_debug(dp->rt_hdl, "[ERROR]: unretire modctl() failed: "
855 858 "%s: %s\n", devpath, strerror(err));
856 859 return (err);
857 860 }
858 861
859 862 dp->rt_debug(dp->rt_hdl, "[INFO]: unretire modctl() done: %s\n",
860 863 devpath);
861 864
862 865 return (0);
866 +}
867 +
868 +/* Structure that holds physical path instance. */
869 +struct retire_mpath_info {
870 + char *pathname;
871 + struct retire_mpath_info *next;
872 + char nodename[PATH_MAX];
873 +};
874 +
875 +static int
876 +retire_walk_nodes(di_node_t node, void *arg)
877 +{
878 + char *dn = NULL;
879 + struct retire_mpath_info **mpinfo = (struct retire_mpath_info **)arg;
880 + di_node_t pnode;
881 + char *baddr;
882 + di_path_t path;
883 +
884 + if (node == NULL || ((baddr = di_bus_addr(node)) == NULL) ||
885 + baddr[0] == '\0') {
886 + return (DI_WALK_CONTINUE);
887 + }
888 +
889 + if (((dn = strstr((*mpinfo)->pathname, baddr)) == NULL) ||
890 + /* Make sure bus address matches completely. */
891 + (strlen(dn) != strlen(baddr))) {
892 + return (DI_WALK_CONTINUE);
893 + }
894 +
895 + if ((path = di_path_client_next_path(node, DI_PATH_NIL)) == NULL) {
896 + return (DI_WALK_CONTINUE);
897 + }
898 +
899 + for (; path != NULL; path = di_path_client_next_path(node, path)) {
900 + struct retire_mpath_info *ri, *prev;
901 + char *port_id = NULL;
902 + char *p;
903 +
904 + if ((pnode = di_path_phci_node(path)) == DI_NODE_NIL) {
905 + continue;
906 + }
907 +
908 + if ((p = di_devfs_path(pnode)) == NULL) {
909 + continue;
910 + }
911 +
912 + if (di_path_prop_lookup_strings(path,
913 + SCSI_ADDR_PROP_TARGET_PORT, &port_id) == 1) {
914 +
915 + ri = malloc(sizeof (*ri) + PATH_MAX);
916 + if (ri != NULL) {
917 + prev = *mpinfo;
918 +
919 + ri->next = prev;
920 +
921 + /* Preserve nodename */
922 + ri->pathname = prev->pathname;
923 + (void) snprintf(&ri->nodename[0], PATH_MAX,
924 + "%s/disk@%s,0", p, port_id);
925 +
926 + *mpinfo = ri;
927 + }
928 + }
929 +
930 + di_devfs_path_free(p);
931 + }
932 +
933 + return (DI_WALK_CONTINUE);
934 +}
935 +
936 +int
937 +do_di_retire_device_mp(char *devpath, di_retire_t *dp, int flags,
938 + boolean_t retire)
939 +{
940 + int err = 0;
941 + struct retire_mpath_info mpinfo, *pmpinfo, *pcurr;
942 + char *path;
943 + di_node_t root_node;
944 +
945 + /* First, retire the device itself. */
946 + err = retire ?
947 + do_di_retire_device(devpath, dp, flags) :
948 + do_di_unretire_device(devpath, dp);
949 +
950 + if (err != 0) {
951 + dp->rt_debug(dp->rt_hdl, "di_%sretire_device failed to"
952 + " %sretire device: %d %s", retire ? "" : "un",
953 + retire ? "" : "un", err, devpath);
954 + return (err);
955 + }
956 +
957 + /* Next, try to retire all physical paths, if possible. */
958 + root_node = di_init("/", DINFOCPYALL | DINFOPATH | DINFOLYR);
959 + if (root_node == DI_NODE_NIL) {
960 + dp->rt_debug(dp->rt_hdl, "di_%sretire_device can't access"
961 + " device tree, MPxIO checks ignored for %s",
962 + retire ? "" : "un", devpath);
963 + return (0);
964 + }
965 +
966 + /* Obtain multipath information. */
967 + (void) memset(&mpinfo, 0, sizeof (mpinfo));
968 + mpinfo.pathname = devpath;
969 +
970 + pmpinfo = &mpinfo;
971 +
972 + (void) di_walk_node(root_node, DI_WALK_CLDFIRST, &pmpinfo,
973 + retire_walk_nodes);
974 +
975 + /* Next, retire all possible physical paths. */
976 + for (; err == 0 && pmpinfo != &mpinfo; ) {
977 + pcurr = pmpinfo;
978 + pmpinfo = pmpinfo->next;
979 +
980 + path = &pcurr->nodename[0];
981 +
982 + dp->rt_debug(dp->rt_hdl,
983 + "di_%sretire_device %sretiring physical path %s\n",
984 + retire ? "" : "un", retire ? "" : "un", path);
985 +
986 + err = retire ?
987 + do_di_retire_device(path, dp, flags) :
988 + do_di_unretire_device(path, dp);
989 +
990 + if (err != 0)
991 + dp->rt_debug(dp->rt_hdl,
992 + "di_%sretire_device failed to %sretire physical"
993 + " path %s, %d\n", retire ? "" : "un",
994 + retire ? "" : "un", path, err);
995 +
996 + free(pcurr);
997 + }
998 +
999 + return (0);
1000 +}
1001 +
1002 +/*ARGSUSED*/
1003 +int
1004 +di_retire_device(char *devpath, di_retire_t *dp, int flags)
1005 +{
1006 + return (do_di_retire_device_mp(devpath, dp, flags, B_TRUE));
1007 +}
1008 +
1009 +/*ARGSUSED*/
1010 +int
1011 +di_unretire_device(char *devpath, di_retire_t *dp)
1012 +{
1013 + return (do_di_retire_device_mp(devpath, dp, 0, B_FALSE));
863 1014 }
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX