1 0 stevel /* 2 0 stevel * CDDL HEADER START 3 0 stevel * 4 0 stevel * The contents of this file are subject to the terms of the 5 6640 cth * Common Development and Distribution License (the "License"). 6 6640 cth * You may not use this file except in compliance with the License. 7 0 stevel * 8 0 stevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 0 stevel * or http://www.opensolaris.org/os/licensing. 10 0 stevel * See the License for the specific language governing permissions 11 0 stevel * and limitations under the License. 12 0 stevel * 13 0 stevel * When distributing Covered Code, include this CDDL HEADER in each 14 0 stevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 0 stevel * If applicable, add the following below this CDDL HEADER, with the 16 0 stevel * fields enclosed by brackets "[]" replaced with your own identifying 17 0 stevel * information: Portions Copyright [yyyy] [name of copyright owner] 18 0 stevel * 19 0 stevel * CDDL HEADER END 20 0 stevel */ 21 0 stevel /* 22 6640 cth * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 0 stevel * Use is subject to license terms. 24 0 stevel */ 25 0 stevel 26 0 stevel #pragma ident "%Z%%M% %I% %E% SMI" 27 0 stevel 28 0 stevel /* 29 0 stevel * RCM module supporting multiplexed I/O controllers (MPxIO). 30 0 stevel */ 31 0 stevel #include <stdlib.h> 32 0 stevel #include <stdarg.h> 33 0 stevel #include <unistd.h> 34 0 stevel #include <assert.h> 35 0 stevel #include <syslog.h> 36 0 stevel #include <string.h> 37 0 stevel #include <synch.h> 38 0 stevel #include <libintl.h> 39 0 stevel #include <locale.h> 40 0 stevel #include <ctype.h> 41 0 stevel #include <errno.h> 42 0 stevel #include <libdevinfo.h> 43 0 stevel #include <sys/types.h> 44 0 stevel #include "rcm_module.h" 45 0 stevel 46 0 stevel #define MPXIO_PROP_NAME "mpxio-component" 47 0 stevel #define MPXIO_PROP_CLIENT "client" 48 0 stevel 49 0 stevel #define CMD_GETINFO 0 50 0 stevel #define CMD_OFFLINE 1 51 0 stevel #define CMD_ONLINE 2 52 0 stevel #define CMD_REMOVE 3 53 0 stevel 54 0 stevel #define CACHE_NEW 0 55 0 stevel #define CACHE_REFERENCED 1 56 0 stevel #define CACHE_STALE 2 57 0 stevel 58 0 stevel #define MPXIO_MSG_CACHEFAIL gettext("Internal analysis failure.") 59 0 stevel #define MPXIO_MSG_LASTPATH gettext("Last path to busy resources.") 60 0 stevel #define MPXIO_MSG_USAGE gettext("SCSI Multipathing PHCI (%s)") 61 0 stevel #define MPXIO_MSG_USAGEUNKNOWN gettext("SCSI Multipathing PHCI (<unknown>)") 62 0 stevel 63 0 stevel typedef struct { 64 0 stevel char *path; 65 0 stevel di_path_state_t state; 66 0 stevel } phci_t; 67 0 stevel 68 0 stevel typedef struct phci_list { 69 0 stevel phci_t phci; 70 0 stevel int referenced; 71 0 stevel struct phci_list *next; 72 0 stevel } phci_list_t; 73 0 stevel 74 0 stevel typedef struct group { 75 0 stevel int offline; 76 0 stevel int nphcis; 77 0 stevel int nclients; 78 0 stevel phci_t *phcis; 79 0 stevel char **clients; 80 0 stevel struct group *next; 81 0 stevel } group_t; 82 0 stevel 83 0 stevel static int mpxio_register(rcm_handle_t *); 84 0 stevel static int mpxio_unregister(rcm_handle_t *); 85 0 stevel static int mpxio_getinfo(rcm_handle_t *, char *, id_t, uint_t, char **, char **, 86 0 stevel nvlist_t *, rcm_info_t **); 87 0 stevel static int mpxio_suspend(rcm_handle_t *, char *, id_t, timespec_t *, uint_t, 88 0 stevel char **, rcm_info_t **); 89 0 stevel static int mpxio_resume(rcm_handle_t *, char *, id_t, uint_t, char **, 90 0 stevel rcm_info_t **); 91 0 stevel static int mpxio_offline(rcm_handle_t *, char *, id_t, uint_t, char **, 92 0 stevel rcm_info_t **); 93 0 stevel static int mpxio_online(rcm_handle_t *, char *, id_t, uint_t, char **, 94 0 stevel rcm_info_t **); 95 0 stevel static int mpxio_remove(rcm_handle_t *, char *, id_t, uint_t, char **, 96 0 stevel rcm_info_t **); 97 0 stevel static int get_nclients(di_node_t, void *); 98 0 stevel static int build_groups(di_node_t, void *); 99 0 stevel static void refresh_regs(rcm_handle_t *); 100 0 stevel static int get_affected_clients(rcm_handle_t *, char *, int, int, char ***); 101 0 stevel static int detect_client_change(rcm_handle_t *, int, int, group_t *, char *); 102 0 stevel static int merge_clients(int *, char ***, group_t *); 103 0 stevel static phci_list_t *lookup_phci(char *); 104 0 stevel static int is_client(di_node_t); 105 0 stevel static char *get_rsrcname(di_node_t); 106 0 stevel static char *s_state(di_path_state_t); 107 0 stevel static int compare_phci(const void *, const void *); 108 0 stevel static void free_grouplist(); 109 0 stevel static void free_group(group_t *); 110 0 stevel static void free_clients(int, char **); 111 0 stevel static void free_phcis(int, phci_t *); 112 0 stevel 113 0 stevel static struct rcm_mod_ops mpxio_ops = 114 0 stevel { 115 0 stevel RCM_MOD_OPS_VERSION, 116 0 stevel mpxio_register, 117 0 stevel mpxio_unregister, 118 0 stevel mpxio_getinfo, 119 0 stevel mpxio_suspend, 120 0 stevel mpxio_resume, 121 0 stevel mpxio_offline, 122 0 stevel mpxio_online, 123 0 stevel mpxio_remove, 124 0 stevel NULL, 125 0 stevel NULL, 126 0 stevel NULL 127 0 stevel }; 128 0 stevel 129 0 stevel static group_t *group_list; 130 0 stevel static phci_list_t *reg_list; 131 0 stevel static mutex_t mpxio_lock; 132 0 stevel 133 0 stevel extern int errno; 134 0 stevel 135 0 stevel /* 136 0 stevel * Return the mod-ops vector for initialization. 137 0 stevel */ 138 0 stevel struct rcm_mod_ops * 139 0 stevel rcm_mod_init() 140 0 stevel { 141 0 stevel rcm_log_message(RCM_TRACE1, "MPXIO: rcm_mod_init()\n"); 142 0 stevel 143 0 stevel return (&mpxio_ops); 144 0 stevel } 145 0 stevel 146 0 stevel /* 147 0 stevel * Return name and version number for mod_info. 148 0 stevel */ 149 0 stevel const char * 150 0 stevel rcm_mod_info() 151 0 stevel { 152 0 stevel rcm_log_message(RCM_TRACE1, "MPXIO: rcm_mod_info()\n"); 153 0 stevel 154 7202 vikram return (gettext("RCM MPxIO module 1.6")); 155 0 stevel } 156 0 stevel 157 0 stevel /* 158 0 stevel * Destroy the cache and mutex lock when being unloaded. 159 0 stevel */ 160 0 stevel int 161 0 stevel rcm_mod_fini() 162 0 stevel { 163 0 stevel phci_list_t *reg; 164 0 stevel phci_list_t *next; 165 0 stevel 166 0 stevel rcm_log_message(RCM_TRACE1, "MPXIO: rcm_mod_fini()\n"); 167 0 stevel 168 0 stevel /* Free the cache of MPxIO group information */ 169 0 stevel free_grouplist(); 170 0 stevel 171 0 stevel /* Free the cache of registrants */ 172 0 stevel reg = reg_list; 173 0 stevel while (reg) { 174 0 stevel next = reg->next; 175 0 stevel free(reg->phci.path); 176 0 stevel free(reg); 177 0 stevel reg = next; 178 0 stevel } 179 0 stevel 180 0 stevel /* Destroy the mutex for locking the caches */ 181 0 stevel (void) mutex_destroy(&mpxio_lock); 182 0 stevel 183 0 stevel return (RCM_SUCCESS); 184 0 stevel } 185 0 stevel 186 0 stevel /* 187 0 stevel * During each register callback: totally rebuild the group list from a new 188 0 stevel * libdevinfo snapshot, and then update the registrants. 189 0 stevel */ 190 0 stevel static int 191 0 stevel mpxio_register(rcm_handle_t *hdl) 192 0 stevel { 193 0 stevel int nclients = 0; 194 0 stevel di_node_t devroot; 195 0 stevel 196 0 stevel rcm_log_message(RCM_TRACE1, "MPXIO: register()\n"); 197 0 stevel 198 0 stevel (void) mutex_lock(&mpxio_lock); 199 0 stevel 200 0 stevel /* Destroy the previous group list */ 201 0 stevel free_grouplist(); 202 0 stevel 203 0 stevel /* Get a current libdevinfo snapshot */ 204 0 stevel if ((devroot = di_init("/", DINFOCPYALL | DINFOPATH)) == DI_NODE_NIL) { 205 0 stevel rcm_log_message(RCM_ERROR, 206 0 stevel "MPXIO: libdevinfo initialization failed (%s).\n", 207 0 stevel strerror(errno)); 208 0 stevel (void) mutex_unlock(&mpxio_lock); 209 0 stevel return (RCM_FAILURE); 210 0 stevel } 211 0 stevel 212 0 stevel /* 213 0 stevel * First count the total number of clients. This'll be a useful 214 0 stevel * upper bound when allocating client arrays within each group. 215 0 stevel */ 216 0 stevel (void) di_walk_node(devroot, DI_WALK_CLDFIRST, &nclients, get_nclients); 217 0 stevel 218 0 stevel rcm_log_message(RCM_TRACE2, gettext("MPXIO: found %d clients.\n"), 219 0 stevel nclients); 220 0 stevel 221 0 stevel /* 222 0 stevel * Then walk the libdevinfo snapshot, building up the new group list 223 0 stevel * along the way. Pass in the total number of clients (from above) to 224 0 stevel * assist in group construction. 225 0 stevel */ 226 0 stevel (void) di_walk_node(devroot, DI_WALK_CLDFIRST, &nclients, build_groups); 227 0 stevel 228 0 stevel /* Now with a new group list constructed, refresh the registrants */ 229 0 stevel refresh_regs(hdl); 230 0 stevel 231 0 stevel /* Free the libdevinfo snapshot */ 232 0 stevel di_fini(devroot); 233 0 stevel 234 0 stevel (void) mutex_unlock(&mpxio_lock); 235 0 stevel 236 0 stevel return (0); 237 0 stevel } 238 0 stevel 239 0 stevel /* 240 0 stevel * Unregister all PHCIs and mark the whole registrants list as stale. 241 0 stevel */ 242 0 stevel static int 243 0 stevel mpxio_unregister(rcm_handle_t *hdl) 244 0 stevel { 245 0 stevel phci_list_t *reg; 246 0 stevel 247 0 stevel rcm_log_message(RCM_TRACE1, "MPXIO: unregister()\n"); 248 0 stevel 249 0 stevel (void) mutex_lock(&mpxio_lock); 250 0 stevel 251 0 stevel for (reg = reg_list; reg != NULL; reg = reg->next) { 252 0 stevel (void) rcm_unregister_interest(hdl, reg->phci.path, 0); 253 0 stevel reg->referenced = CACHE_STALE; 254 0 stevel } 255 0 stevel 256 0 stevel (void) mutex_unlock(&mpxio_lock); 257 0 stevel 258 0 stevel return (RCM_SUCCESS); 259 0 stevel } 260 0 stevel 261 0 stevel /* 262 0 stevel * To return usage information, just lookup the PHCI in the cache and return 263 0 stevel * a string identifying that it's a PHCI and describing its cached MPxIO state. 264 0 stevel * Recurse with the cached list of disks if dependents are to be included. 265 0 stevel */ 266 0 stevel static int 267 0 stevel mpxio_getinfo(rcm_handle_t *hdl, char *rsrc, id_t id, uint_t flags, 268 0 stevel char **infostr, char **errstr, nvlist_t *props, rcm_info_t **infop) 269 0 stevel { 270 0 stevel size_t len; 271 0 stevel int rv = RCM_SUCCESS; 272 0 stevel char *buf = NULL; 273 0 stevel char **clients = NULL; 274 0 stevel phci_list_t *reg; 275 0 stevel char c; 276 0 stevel 277 0 stevel rcm_log_message(RCM_TRACE1, "MPXIO: getinfo(%s)\n", rsrc); 278 0 stevel 279 0 stevel *infostr = NULL; 280 0 stevel *errstr = NULL; 281 0 stevel 282 0 stevel (void) mutex_lock(&mpxio_lock); 283 0 stevel 284 0 stevel if ((reg = lookup_phci(rsrc)) == NULL) { 285 0 stevel *errstr = strdup(MPXIO_MSG_CACHEFAIL); 286 0 stevel (void) mutex_unlock(&mpxio_lock); 287 0 stevel return (RCM_FAILURE); 288 0 stevel } 289 0 stevel 290 0 stevel len = snprintf(&c, 1, MPXIO_MSG_USAGE, s_state(reg->phci.state)); 291 0 stevel buf = calloc(len + 1, sizeof (char)); 292 0 stevel if ((buf == NULL) || (snprintf(buf, len + 1, MPXIO_MSG_USAGE, 293 0 stevel s_state(reg->phci.state)) > len + 1)) { 294 0 stevel *infostr = strdup(MPXIO_MSG_USAGEUNKNOWN); 295 0 stevel *errstr = strdup(gettext("Cannot construct usage string.")); 296 0 stevel (void) mutex_unlock(&mpxio_lock); 297 0 stevel if (buf) 298 0 stevel free(buf); 299 0 stevel return (RCM_FAILURE); 300 0 stevel } 301 0 stevel *infostr = buf; 302 0 stevel 303 0 stevel if (flags & RCM_INCLUDE_DEPENDENT) { 304 0 stevel rcm_log_message(RCM_TRACE2, "MPXIO: getting clients\n"); 305 0 stevel if (get_affected_clients(hdl, rsrc, CMD_GETINFO, flags, 306 0 stevel &clients) < 0) { 307 0 stevel *errstr = strdup(gettext("Cannot lookup clients.")); 308 0 stevel (void) mutex_unlock(&mpxio_lock); 309 0 stevel return (RCM_FAILURE); 310 0 stevel } 311 0 stevel if (clients) { 312 0 stevel rv = rcm_get_info_list(hdl, clients, flags, infop); 313 0 stevel free(clients); 314 0 stevel } else { 315 0 stevel rcm_log_message(RCM_TRACE2, "MPXIO: none found\n"); 316 0 stevel } 317 0 stevel } 318 0 stevel 319 0 stevel (void) mutex_unlock(&mpxio_lock); 320 0 stevel return (rv); 321 0 stevel } 322 0 stevel 323 0 stevel /* 324 0 stevel * Nothing is implemented for suspend operations. 325 0 stevel */ 326 0 stevel static int 327 0 stevel mpxio_suspend(rcm_handle_t *hdl, char *rsrc, id_t id, timespec_t *interval, 328 0 stevel uint_t flags, char **errstr, rcm_info_t **infop) 329 0 stevel { 330 0 stevel rcm_log_message(RCM_TRACE1, "MPXIO: suspend(%s)\n", rsrc); 331 0 stevel 332 0 stevel return (RCM_SUCCESS); 333 0 stevel } 334 0 stevel 335 0 stevel /* 336 0 stevel * Nothing is implemented for resume operations. 337 0 stevel */ 338 0 stevel static int 339 0 stevel mpxio_resume(rcm_handle_t *hdl, char *rsrc, id_t id, uint_t flags, 340 0 stevel char **errstr, rcm_info_t **infop) 341 0 stevel { 342 0 stevel rcm_log_message(RCM_TRACE1, "MPXIO: resume(%s)\n", rsrc); 343 0 stevel 344 0 stevel return (RCM_SUCCESS); 345 0 stevel } 346 0 stevel 347 0 stevel /* 348 0 stevel * MPxIO has no policy against offlining. If disks will be affected, then 349 0 stevel * base the return value for this request on the results of offlining the 350 0 stevel * list of disks. Otherwise succeed. 351 0 stevel */ 352 0 stevel static int 353 0 stevel mpxio_offline(rcm_handle_t *hdl, char *rsrc, id_t id, uint_t flags, 354 0 stevel char **errstr, rcm_info_t **infop) 355 0 stevel { 356 0 stevel char **clients = NULL; 357 0 stevel int rv = RCM_SUCCESS; 358 0 stevel 359 0 stevel rcm_log_message(RCM_TRACE1, "MPXIO: offline(%s)\n", rsrc); 360 0 stevel 361 0 stevel (void) mutex_lock(&mpxio_lock); 362 0 stevel 363 0 stevel if (get_affected_clients(hdl, rsrc, CMD_OFFLINE, flags, &clients) < 0) { 364 0 stevel *errstr = strdup(gettext("Cannot lookup clients.")); 365 0 stevel (void) mutex_unlock(&mpxio_lock); 366 0 stevel return (RCM_FAILURE); 367 0 stevel } 368 0 stevel 369 0 stevel if (clients) { 370 0 stevel rv = rcm_request_offline_list(hdl, clients, flags, infop); 371 0 stevel if (rv != RCM_SUCCESS) 372 0 stevel *errstr = strdup(MPXIO_MSG_LASTPATH); 373 0 stevel free(clients); 374 0 stevel } 375 0 stevel 376 0 stevel (void) mutex_unlock(&mpxio_lock); 377 0 stevel 378 0 stevel return (rv); 379 0 stevel } 380 0 stevel 381 0 stevel /* 382 0 stevel * If disks are affected, then they are probably offline and we need to 383 0 stevel * propagate this online notification to them. 384 0 stevel */ 385 0 stevel static int 386 0 stevel mpxio_online(rcm_handle_t *hdl, char *rsrc, id_t id, uint_t flags, 387 0 stevel char **errstr, rcm_info_t **infop) 388 0 stevel { 389 0 stevel char **clients; 390 0 stevel int rv = RCM_SUCCESS; 391 0 stevel 392 0 stevel rcm_log_message(RCM_TRACE1, "MPXIO: online(%s)\n", rsrc); 393 0 stevel 394 0 stevel (void) mutex_lock(&mpxio_lock); 395 0 stevel 396 0 stevel if (get_affected_clients(hdl, rsrc, CMD_ONLINE, flags, &clients) < 0) { 397 0 stevel *errstr = strdup(gettext("Cannot lookup clients.")); 398 0 stevel (void) mutex_unlock(&mpxio_lock); 399 0 stevel return (RCM_FAILURE); 400 0 stevel } 401 0 stevel 402 0 stevel if (clients) { 403 0 stevel rv = rcm_notify_online_list(hdl, clients, flags, infop); 404 0 stevel free(clients); 405 0 stevel } 406 0 stevel 407 0 stevel (void) mutex_unlock(&mpxio_lock); 408 0 stevel 409 0 stevel return (rv); 410 0 stevel } 411 0 stevel 412 0 stevel /* 413 0 stevel * If clients are affected, then they are probably offline and we need to 414 0 stevel * propagate this removal notification to them. We can also remove the 415 0 stevel * cache entry for this PHCI. If that leaves its group empty, then the 416 0 stevel * group will be removed during the next register callback. 417 0 stevel */ 418 0 stevel static int 419 0 stevel mpxio_remove(rcm_handle_t *hdl, char *rsrc, id_t id, uint_t flags, 420 0 stevel char **errstr, rcm_info_t **infop) 421 0 stevel { 422 0 stevel char **clients; 423 0 stevel int rv = RCM_SUCCESS; 424 0 stevel 425 0 stevel rcm_log_message(RCM_TRACE1, "MPXIO: remove(%s)\n", rsrc); 426 0 stevel 427 0 stevel (void) mutex_lock(&mpxio_lock); 428 0 stevel 429 0 stevel if (get_affected_clients(hdl, rsrc, CMD_REMOVE, flags, &clients) < 0) { 430 0 stevel *errstr = strdup(gettext("Cannot lookup clients.")); 431 0 stevel (void) mutex_unlock(&mpxio_lock); 432 0 stevel return (RCM_FAILURE); 433 0 stevel } 434 0 stevel 435 0 stevel if (clients) { 436 0 stevel rv = rcm_notify_remove_list(hdl, clients, flags, infop); 437 0 stevel free(clients); 438 0 stevel } 439 0 stevel 440 0 stevel (void) mutex_unlock(&mpxio_lock); 441 0 stevel 442 0 stevel return (rv); 443 0 stevel } 444 0 stevel 445 0 stevel 446 0 stevel /* 447 0 stevel * Returns a string representation of a given libdevinfo path state. 448 0 stevel */ 449 0 stevel static char * 450 0 stevel s_state(di_path_state_t state) 451 0 stevel { 452 0 stevel switch (state) { 453 0 stevel case DI_PATH_STATE_ONLINE: 454 0 stevel return ("online"); 455 0 stevel case DI_PATH_STATE_OFFLINE: 456 0 stevel return ("offline"); 457 0 stevel case DI_PATH_STATE_STANDBY: 458 0 stevel return ("standby"); 459 0 stevel case DI_PATH_STATE_FAULT: 460 0 stevel return ("faulted"); 461 0 stevel default: 462 0 stevel return ("<unknown>"); 463 0 stevel } 464 0 stevel } 465 0 stevel 466 0 stevel static int 467 0 stevel get_affected_clients(rcm_handle_t *hdl, char *rsrc, int cmd, int flags, 468 0 stevel char ***clientsp) 469 0 stevel { 470 0 stevel int nclients = 0; 471 0 stevel phci_t phci; 472 0 stevel group_t *group; 473 0 stevel char **clients = NULL; 474 0 stevel 475 0 stevel /* Build a dummy phci_t for use with bsearch(). */ 476 0 stevel phci.path = rsrc; 477 0 stevel 478 0 stevel /* Analyze the effects upon each group. */ 479 0 stevel for (group = group_list; group != NULL; group = group->next) { 480 0 stevel 481 0 stevel /* If the PHCI isn't in the group, then no effects. Skip. */ 482 0 stevel if (bsearch(&phci, group->phcis, group->nphcis, sizeof (phci_t), 483 0 stevel compare_phci) == NULL) 484 0 stevel continue; 485 0 stevel 486 0 stevel /* 487 0 stevel * Merge in the clients. All clients are merged in for getinfo 488 0 stevel * operations. Otherwise it's contingent upon a state change 489 0 stevel * being transferred to the clients as a result of changing 490 0 stevel * the PHCI's state. 491 0 stevel */ 492 0 stevel if ((cmd == CMD_GETINFO) || 493 0 stevel detect_client_change(hdl, cmd, flags, group, rsrc)) { 494 0 stevel if (merge_clients(&nclients, &clients, group) < 0) { 495 0 stevel free_clients(nclients, clients); 496 0 stevel return (-1); 497 0 stevel } 498 0 stevel } 499 0 stevel } 500 0 stevel 501 0 stevel /* Return the array of affected disks */ 502 0 stevel *clientsp = clients; 503 0 stevel return (0); 504 0 stevel } 505 0 stevel 506 0 stevel /* 507 0 stevel * Iterates through the members of a PHCI list, returning the entry 508 0 stevel * corresponding to the named PHCI resource. Returns NULL when the lookup 509 0 stevel * fails. 510 0 stevel */ 511 0 stevel static phci_list_t * 512 0 stevel lookup_phci(char *rsrc) 513 0 stevel { 514 0 stevel phci_list_t *reg; 515 0 stevel 516 0 stevel for (reg = reg_list; reg != NULL; reg = reg->next) { 517 0 stevel if (strcmp(reg->phci.path, rsrc) == 0) 518 0 stevel return (reg); 519 0 stevel } 520 0 stevel 521 0 stevel return (NULL); 522 0 stevel } 523 0 stevel 524 0 stevel /* 525 0 stevel * Tests whether or not an operation on a specific PHCI resource would affect 526 0 stevel * the array of client devices attached to the PHCI's MPxIO group. 527 0 stevel * 528 0 stevel * Returns: 1 if clients would be affected, 0 if not. 529 0 stevel */ 530 0 stevel static int 531 0 stevel detect_client_change(rcm_handle_t *hdl, int cmd, int flags, group_t *group, 532 0 stevel char *rsrc) 533 0 stevel { 534 0 stevel int i; 535 0 stevel int state; 536 0 stevel 537 0 stevel /* 538 0 stevel * Perform a full set analysis on the set of redundant PHCIs. When 539 0 stevel * there are no unaffected and online PHCIs, then changing the state 540 0 stevel * of the named PHCI results in a client state change. 541 0 stevel */ 542 0 stevel for (i = 0; i < group->nphcis; i++) { 543 0 stevel 544 0 stevel /* Filter the named resource out of the analysis */ 545 0 stevel if (strcmp(group->phcis[i].path, rsrc) == 0) 546 0 stevel continue; 547 0 stevel 548 0 stevel /* 549 0 stevel * If we find a path that's in the ONLINE or STANDBY state 550 0 stevel * that would be left over in the system after completing 551 0 stevel * whatever DR or hotplugging operation is in progress, then 552 0 stevel * return a 0. 553 0 stevel */ 554 0 stevel if ((group->phcis[i].state == DI_PATH_STATE_ONLINE) || 555 0 stevel (group->phcis[i].state == DI_PATH_STATE_STANDBY)) { 556 0 stevel if (rcm_get_rsrcstate(hdl, group->phcis[i].path, &state) 557 0 stevel != RCM_SUCCESS) { 558 0 stevel rcm_log_message(RCM_ERROR, 559 0 stevel "MPXIO: Failed to query resource state\n"); 560 0 stevel continue; 561 0 stevel } 562 0 stevel rcm_log_message(RCM_TRACE2, "MPXIO: state of %s: %d\n", 563 0 stevel group->phcis[i].path, state); 564 0 stevel if (state == RCM_STATE_ONLINE) { 565 0 stevel return (0); 566 0 stevel } 567 0 stevel } 568 0 stevel } 569 0 stevel 570 0 stevel /* 571 0 stevel * The analysis above didn't find a redundant path to take over. So 572 0 stevel * report that the state of the client resources will change. 573 0 stevel */ 574 0 stevel return (1); 575 0 stevel } 576 0 stevel 577 0 stevel /* 578 0 stevel * Merges the client disks connected to a particular MPxIO group in with a 579 0 stevel * previous array of disk clients. The result is to adjust the 'nclients' 580 0 stevel * value with the new count of disks in the array, and to adjust the 'disks' 581 0 stevel * value to be a larger array of disks including its original contents along 582 0 stevel * with the current group's contents merged in. 583 0 stevel */ 584 0 stevel static int 585 0 stevel merge_clients(int *nclients, char ***clientsp, group_t *group) 586 0 stevel { 587 0 stevel int i; 588 0 stevel int old_nclients; 589 0 stevel char **clients_new; 590 0 stevel 591 0 stevel if (group->nclients) { 592 0 stevel old_nclients = *nclients; 593 0 stevel *nclients += group->nclients; 594 0 stevel clients_new = realloc(*clientsp, 595 0 stevel ((*nclients) + 1) * sizeof (char *)); 596 0 stevel if (clients_new == NULL) { 597 0 stevel rcm_log_message(RCM_ERROR, 598 0 stevel "MPXIO: cannot reallocate client array (%s).\n", 599 0 stevel strerror(errno)); 600 0 stevel return (-1); 601 0 stevel } 602 0 stevel for (i = old_nclients; i < (*nclients); i++) { 603 0 stevel /* 604 0 stevel * Don't allocate space for individual disks in the 605 0 stevel * merged list. Just make references to the previously 606 0 stevel * allocated strings in the group_t structs themselves. 607 0 stevel */ 608 0 stevel clients_new[i] = group->clients[i - old_nclients]; 609 0 stevel } 610 0 stevel clients_new[(*nclients)] = NULL; 611 0 stevel *clientsp = clients_new; 612 0 stevel } 613 0 stevel 614 0 stevel return (0); 615 0 stevel } 616 0 stevel 617 0 stevel /* 618 0 stevel * A libdevinfo di_walk_node() callback. It's passed an integer pointer as an 619 0 stevel * argument, and it increments the integer each time it encounters an MPxIO 620 0 stevel * client. By initializing the integer to zero and doing a libdevinfo walk with 621 0 stevel * this function, the total count of MPxIO clients in the system can be found. 622 0 stevel */ 623 0 stevel static int 624 0 stevel get_nclients(di_node_t dinode, void *arg) 625 0 stevel { 626 0 stevel int *nclients = arg; 627 0 stevel 628 0 stevel if (is_client(dinode)) 629 0 stevel (*nclients)++; 630 0 stevel 631 0 stevel return (DI_WALK_CONTINUE); 632 0 stevel } 633 0 stevel 634 0 stevel /* 635 0 stevel * Tests a libdevinfo node to determine if it's an MPxIO client. 636 0 stevel * 637 0 stevel * Returns: non-zero for true, 0 for false. 638 0 stevel */ 639 0 stevel static int 640 0 stevel is_client(di_node_t dinode) 641 0 stevel { 642 6640 cth return (di_path_client_next_path(dinode, DI_PATH_NIL) != DI_PATH_NIL); 643 0 stevel } 644 0 stevel 645 0 stevel /* 646 0 stevel * After a new group_list has been constructed, this refreshes the RCM 647 0 stevel * registrations and the reg_list contents. It uses a clock like algorithm 648 0 stevel * with reference bits in the reg_list to know which registrants are new or 649 0 stevel * old. 650 0 stevel */ 651 0 stevel static void 652 0 stevel refresh_regs(rcm_handle_t *hdl) 653 0 stevel { 654 0 stevel int i; 655 0 stevel group_t *group; 656 0 stevel phci_list_t *reg; 657 0 stevel phci_list_t *prev_reg; 658 0 stevel 659 0 stevel /* 660 0 stevel * First part of the clock-like algorithm: clear reference bits. 661 0 stevel */ 662 0 stevel for (reg = reg_list; reg != NULL; reg = reg->next) 663 0 stevel reg->referenced = CACHE_STALE; 664 0 stevel 665 0 stevel /* 666 0 stevel * Second part of the clock-like algorithm: set the reference bits 667 0 stevel * on every registrant that's still active. (Also add new list nodes 668 0 stevel * for new registrants.) 669 0 stevel */ 670 0 stevel for (group = group_list; group != NULL; group = group->next) { 671 0 stevel for (i = 0; i < group->nphcis; i++) { 672 0 stevel 673 0 stevel /* 674 0 stevel * If already stale in the registrants list, just set 675 0 stevel * its reference bit to REFERENCED and update its state. 676 0 stevel */ 677 0 stevel if ((reg = lookup_phci(group->phcis[i].path)) != NULL) { 678 0 stevel if (reg->referenced == CACHE_STALE) 679 0 stevel reg->referenced = CACHE_REFERENCED; 680 0 stevel reg->phci.state = group->phcis[i].state; 681 0 stevel continue; 682 0 stevel } 683 0 stevel 684 0 stevel /* 685 0 stevel * Otherwise, build a new list node and mark it NEW. 686 0 stevel */ 687 0 stevel reg = (phci_list_t *)calloc(1, sizeof (*reg)); 688 0 stevel if (reg == NULL) { 689 0 stevel rcm_log_message(RCM_ERROR, 690 0 stevel "MPXIO: cannot allocate phci_list (%s).\n", 691 0 stevel strerror(errno)); 692 0 stevel continue; 693 0 stevel } 694 0 stevel reg->phci.path = strdup(group->phcis[i].path); 695 0 stevel if (reg->phci.path == NULL) { 696 0 stevel free(reg); 697 0 stevel rcm_log_message(RCM_ERROR, 698 0 stevel "MPXIO: cannot allocate phci path (%s).\n", 699 0 stevel strerror(errno)); 700 0 stevel continue; 701 0 stevel } 702 0 stevel reg->phci.state = group->phcis[i].state; 703 0 stevel reg->referenced = CACHE_NEW; 704 0 stevel 705 0 stevel /* Link it at the head of reg_list */ 706 0 stevel reg->next = reg_list; 707 0 stevel reg_list = reg; 708 0 stevel } 709 0 stevel } 710 0 stevel 711 0 stevel /* 712 0 stevel * Final part of the clock algorithm: unregister stale entries, and 713 0 stevel * register new entries. Stale entries get removed from the list. 714 0 stevel */ 715 0 stevel reg = reg_list; 716 0 stevel prev_reg = NULL; 717 0 stevel while (reg) { 718 0 stevel 719 0 stevel /* Unregister and remove stale entries. */ 720 0 stevel if (reg->referenced == CACHE_STALE) { 721 0 stevel (void) rcm_unregister_interest(hdl, reg->phci.path, 0); 722 0 stevel free(reg->phci.path); 723 0 stevel if (prev_reg == NULL) { 724 0 stevel reg_list = reg->next; 725 0 stevel free(reg); 726 0 stevel reg = reg_list; 727 0 stevel } else { 728 0 stevel prev_reg->next = reg->next; 729 0 stevel free(reg); 730 0 stevel reg = prev_reg->next; 731 0 stevel } 732 0 stevel continue; 733 0 stevel } 734 0 stevel 735 0 stevel /* Register new entries. */ 736 0 stevel if (reg->referenced == CACHE_NEW) { 737 0 stevel if (rcm_register_interest(hdl, reg->phci.path, 0, NULL) 738 0 stevel != RCM_SUCCESS) { 739 0 stevel rcm_log_message(RCM_ERROR, 740 0 stevel "MPXIO: failed to register %s (%s).\n", 741 0 stevel reg->phci.path, strerror(errno)); 742 0 stevel } 743 0 stevel } 744 0 stevel 745 0 stevel prev_reg = reg; 746 0 stevel reg = reg->next; 747 0 stevel } 748 0 stevel } 749 0 stevel 750 0 stevel 751 0 stevel /* 752 0 stevel * A libdevinfo di_walk_node() callback that builds up the MPxIO group list. 753 0 stevel * 754 0 stevel * Every node encountered that's a client node is added into a group's client 755 0 stevel * list. Whenever a group doesn't already exist with a matching set of 756 0 stevel * related PHCIs, then a new group is constructed and put at the head of the 757 0 stevel * group list. 758 0 stevel */ 759 0 stevel static int 760 0 stevel build_groups(di_node_t dinode, void *arg) 761 0 stevel { 762 0 stevel int i = 0; 763 0 stevel int nphcis = 0; 764 0 stevel int *nclients = (int *)arg; 765 0 stevel phci_t *phcis; 766 0 stevel group_t *group; 767 0 stevel di_node_t phcinode; 768 0 stevel di_path_t dipath = DI_PATH_NIL; 769 0 stevel 770 0 stevel /* Safety check */ 771 0 stevel if (nclients == NULL) 772 0 stevel return (DI_WALK_TERMINATE); 773 0 stevel 774 0 stevel /* 775 0 stevel * Build a sorted array of PHCIs pertaining to the client. 776 0 stevel */ 777 6640 cth while ((dipath = 778 6640 cth di_path_client_next_path(dinode, dipath)) != DI_PATH_NIL) 779 0 stevel nphcis++; 780 0 stevel 781 0 stevel /* Skip non-clients. */ 782 0 stevel if (nphcis == 0) 783 0 stevel return (DI_WALK_CONTINUE); 784 0 stevel 785 0 stevel if ((phcis = (phci_t *)calloc(nphcis, sizeof (phci_t))) == NULL) { 786 0 stevel rcm_log_message(RCM_ERROR, 787 0 stevel "MPXIO: failed to allocate client's PHCIs (%s).\n", 788 0 stevel strerror(errno)); 789 0 stevel return (DI_WALK_TERMINATE); 790 0 stevel } 791 6640 cth while ((dipath = 792 6640 cth di_path_client_next_path(dinode, dipath)) != DI_PATH_NIL) { 793 0 stevel phcinode = di_path_phci_node(dipath); 794 0 stevel if (phcinode == DI_NODE_NIL) { 795 0 stevel free_phcis(i, phcis); /* free preceeding PHCIs */ 796 0 stevel rcm_log_message(RCM_ERROR, 797 0 stevel "MPXIO: client appears to have no PHCIs.\n"); 798 0 stevel return (DI_WALK_TERMINATE); 799 0 stevel } 800 0 stevel if ((phcis[i].path = get_rsrcname(phcinode)) == NULL) { 801 0 stevel free_phcis(i, phcis); 802 0 stevel return (DI_WALK_TERMINATE); 803 0 stevel } 804 0 stevel phcis[i].state = di_path_state(dipath); 805 0 stevel i++; 806 0 stevel } 807 0 stevel qsort(phcis, nphcis, sizeof (phci_t), compare_phci); 808 0 stevel 809 0 stevel /* 810 0 stevel * Compare that PHCI set to each existing group's set. We just add 811 0 stevel * the client to the group and exit successfully once a match is made. 812 0 stevel * Falling out of this loop means no match was found. 813 0 stevel */ 814 0 stevel for (group = group_list; group != NULL; group = group->next) { 815 0 stevel 816 0 stevel /* There is no match if the number of PHCIs is inequal */ 817 0 stevel if (nphcis != group->nphcis) 818 0 stevel continue; 819 0 stevel 820 0 stevel /* Compare the PHCIs linearly (which is okay; they're sorted) */ 821 0 stevel for (i = 0; i < nphcis; i++) 822 0 stevel if (strcmp(phcis[i].path, group->phcis[i].path) != 0) 823 0 stevel break; 824 0 stevel 825 0 stevel /* 826 0 stevel * If the loop above completed, we have a match. Add the client 827 0 stevel * to the group's disk array in that case, and return 828 0 stevel * successfully. 829 0 stevel */ 830 0 stevel if (i == nphcis) { 831 0 stevel free_phcis(nphcis, phcis); 832 0 stevel if ((group->clients[group->nclients] = 833 0 stevel get_rsrcname(dinode)) == NULL) 834 0 stevel return (DI_WALK_TERMINATE); 835 0 stevel group->nclients++; 836 0 stevel return (DI_WALK_CONTINUE); 837 0 stevel } 838 0 stevel } 839 0 stevel 840 0 stevel /* The loop above didn't find a match. So build a new group. */ 841 0 stevel if ((group = (group_t *)calloc(1, sizeof (*group))) == NULL) { 842 0 stevel rcm_log_message(RCM_ERROR, 843 0 stevel "MPXIO: failed to allocate PHCI group (%s).\n", 844 0 stevel strerror(errno)); 845 0 stevel free_phcis(nphcis, phcis); 846 0 stevel return (DI_WALK_TERMINATE); 847 0 stevel } 848 0 stevel if ((group->clients = (char **)calloc(*nclients, sizeof (char *))) == 849 0 stevel NULL) { 850 0 stevel free(group); 851 0 stevel free_phcis(nphcis, phcis); 852 0 stevel return (DI_WALK_TERMINATE); 853 0 stevel } 854 0 stevel group->nphcis = nphcis; 855 0 stevel group->phcis = phcis; 856 0 stevel if ((group->clients[0] = get_rsrcname(dinode)) == NULL) { 857 0 stevel free_group(group); 858 0 stevel return (DI_WALK_TERMINATE); 859 0 stevel } 860 0 stevel group->nclients = 1; 861 0 stevel 862 0 stevel /* Link the group into the group list and return successfully. */ 863 0 stevel group->next = group_list; 864 0 stevel group_list = group; 865 0 stevel return (DI_WALK_CONTINUE); 866 0 stevel } 867 0 stevel 868 0 stevel /* 869 0 stevel * For bsearch() and qsort(). Returns the results of a strcmp() on the names 870 0 stevel * of two phci_t's. 871 0 stevel */ 872 0 stevel static int 873 0 stevel compare_phci(const void *arg1, const void *arg2) 874 0 stevel { 875 0 stevel phci_t *p1 = (phci_t *)arg1; 876 0 stevel phci_t *p2 = (phci_t *)arg2; 877 0 stevel 878 0 stevel if ((p1 == NULL) || (p2 == NULL)) { 879 0 stevel if (p1 != NULL) 880 0 stevel return (-1); 881 0 stevel else if (p2 != NULL) 882 0 stevel return (1); 883 0 stevel return (0); 884 0 stevel } 885 0 stevel 886 0 stevel return (strcmp(p1->path, p2->path)); 887 0 stevel } 888 0 stevel 889 0 stevel /* 890 0 stevel * Free the whole list of group's in the global group_list. 891 0 stevel */ 892 0 stevel static void 893 0 stevel free_grouplist() 894 0 stevel { 895 0 stevel group_t *group = group_list; 896 0 stevel group_t *next; 897 0 stevel 898 0 stevel while (group) { 899 0 stevel next = group->next; 900 0 stevel free_group(group); 901 0 stevel group = next; 902 0 stevel } 903 0 stevel 904 0 stevel group_list = NULL; 905 0 stevel } 906 0 stevel 907 0 stevel /* 908 0 stevel * Free the contents of a single group_t. 909 0 stevel */ 910 0 stevel static void 911 0 stevel free_group(group_t *group) 912 0 stevel { 913 0 stevel if (group) { 914 0 stevel free_phcis(group->nphcis, group->phcis); 915 0 stevel free_clients(group->nclients, group->clients); 916 0 stevel free(group); 917 0 stevel } 918 0 stevel } 919 0 stevel 920 0 stevel /* 921 0 stevel * Free an array of clients. 922 0 stevel */ 923 0 stevel static void 924 0 stevel free_clients(int nclients, char **clients) 925 0 stevel { 926 0 stevel int i; 927 0 stevel 928 0 stevel if (clients != NULL) { 929 0 stevel if (nclients > 0) { 930 0 stevel for (i = 0; i < nclients; i++) 931 0 stevel if (clients[i]) 932 0 stevel free(clients[i]); 933 0 stevel } 934 0 stevel free(clients); 935 0 stevel } 936 0 stevel } 937 0 stevel 938 0 stevel /* 939 0 stevel * Free an array of phci_t's. 940 0 stevel */ 941 0 stevel static void 942 0 stevel free_phcis(int nphcis, phci_t *phcis) 943 0 stevel { 944 0 stevel int i; 945 0 stevel 946 0 stevel if ((phcis != NULL) && (nphcis > 0)) { 947 0 stevel for (i = 0; i < nphcis; i++) 948 0 stevel if (phcis[i].path) 949 0 stevel free(phcis[i].path); 950 0 stevel free(phcis); 951 0 stevel } 952 0 stevel } 953 0 stevel 954 0 stevel /* 955 0 stevel * Converts a libdevinfo node into a /devices path. Caller must free results. 956 0 stevel */ 957 0 stevel static char * 958 0 stevel get_rsrcname(di_node_t dinode) 959 0 stevel { 960 0 stevel int len; 961 0 stevel char *rsrcname; 962 0 stevel char *devfspath; 963 0 stevel char name[MAXPATHLEN]; 964 0 stevel 965 0 stevel if ((devfspath = di_devfs_path(dinode)) == NULL) { 966 0 stevel rcm_log_message(RCM_ERROR, "MPXIO: resource has null path.\n"); 967 0 stevel return (NULL); 968 0 stevel } 969 0 stevel 970 0 stevel len = snprintf(name, sizeof (name), "/devices%s", devfspath); 971 0 stevel di_devfs_path_free(devfspath); 972 0 stevel if (len >= sizeof (name)) { 973 0 stevel rcm_log_message(RCM_ERROR, "MPXIO: resource path too long.\n"); 974 0 stevel return (NULL); 975 0 stevel } 976 0 stevel 977 0 stevel if ((rsrcname = strdup(name)) == NULL) 978 0 stevel rcm_log_message(RCM_ERROR, 979 0 stevel "MPXIO: failed to allocate resource name (%s).\n", 980 0 stevel strerror(errno)); 981 0 stevel 982 0 stevel return (rsrcname); 983 0 stevel } 984