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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <unistd.h> 30 #include <errno.h> 31 #include <string.h> 32 #include <pthread.h> 33 #include <alloca.h> 34 #include <libnvpair.h> 35 #include <libhotplug.h> 36 #include <libhotplug_impl.h> 37 #include <sys/types.h> 38 #include <sys/sunddi.h> 39 #include <sys/ddi_hp.h> 40 #include <sys/modctl.h> 41 #include "hotplugd_impl.h" 42 43 /* 44 * All operations affecting kernel state are serialized. 45 */ 46 static pthread_mutex_t hotplug_lock = PTHREAD_MUTEX_INITIALIZER; 47 48 /* 49 * Local functions. 50 */ 51 static boolean_t check_rcm_required(hp_node_t, int); 52 static int pack_properties(const char *, ddi_hp_property_t *); 53 static void unpack_properties(ddi_hp_property_t *, char **); 54 static void free_properties(ddi_hp_property_t *); 55 56 /* 57 * changestate() 58 * 59 * Perform a state change operation. 60 * 61 * NOTE: all operations are serialized, using a global lock. 62 */ 63 int 64 changestate(const char *path, const char *connection, int state, uint_t flags, 65 int *old_statep, hp_node_t *resultsp) 66 { 67 hp_node_t root = NULL; 68 char **rsrcs = NULL; 69 boolean_t use_rcm = B_FALSE; 70 int rv; 71 72 hp_dprintf("changestate(path=%s, connection=%s, state=0x%x, " 73 "flags=0x%x)\n", path, connection, state, flags); 74 75 /* Initialize results */ 76 *resultsp = NULL; 77 *old_statep = -1; 78 79 (void) pthread_mutex_lock(&hotplug_lock); 80 81 /* Get an information snapshot, without usage details */ 82 if ((rv = getinfo(path, connection, 0, &root)) != 0) { 83 (void) pthread_mutex_unlock(&hotplug_lock); 84 hp_dprintf("changestate: getinfo() failed (%s)\n", 85 strerror(rv)); 86 return (rv); 87 } 88 89 /* Record current state (used in hotplugd_door.c for auditing) */ 90 *old_statep = hp_state(root); 91 92 /* Check if RCM interactions are required */ 93 use_rcm = check_rcm_required(root, state); 94 95 /* If RCM is required, perform RCM offline */ 96 if (use_rcm) { 97 98 hp_dprintf("changestate: RCM offline is required.\n"); 99 100 /* Get RCM resources */ 101 if ((rv = rcm_resources(root, &rsrcs)) != 0) { 102 hp_dprintf("changestate: rcm_resources() failed.\n"); 103 (void) pthread_mutex_unlock(&hotplug_lock); 104 hp_fini(root); 105 return (rv); 106 } 107 108 /* Request RCM offline */ 109 if ((rsrcs != NULL) && 110 ((rv = rcm_offline(rsrcs, flags, root)) != 0)) { 111 hp_dprintf("changestate: rcm_offline() failed.\n"); 112 rcm_online(rsrcs); 113 (void) pthread_mutex_unlock(&hotplug_lock); 114 free_rcm_resources(rsrcs); 115 *resultsp = root; 116 return (rv); 117 } 118 } 119 120 /* The information snapshot is no longer needed */ 121 hp_fini(root); 122 123 /* Stop now if QUERY flag was specified */ 124 if (flags & HPQUERY) { 125 hp_dprintf("changestate: operation was QUERY only.\n"); 126 rcm_online(rsrcs); 127 (void) pthread_mutex_unlock(&hotplug_lock); 128 free_rcm_resources(rsrcs); 129 return (0); 130 } 131 132 /* Do state change in kernel */ 133 rv = 0; 134 if (modctl(MODHPOPS, MODHPOPS_CHANGE_STATE, path, connection, state)) 135 rv = errno; 136 hp_dprintf("changestate: modctl(MODHPOPS_CHANGE_STATE) = %d.\n", rv); 137 138 /* 139 * If RCM is required, then perform an RCM online or RCM remove 140 * operation. Which depends upon if modctl succeeded or failed. 141 */ 142 if (use_rcm && (rsrcs != NULL)) { 143 144 /* RCM online if failure, or RCM remove if successful */ 145 if (rv == 0) 146 rcm_remove(rsrcs); 147 else 148 rcm_online(rsrcs); 149 150 /* RCM resources no longer required */ 151 free_rcm_resources(rsrcs); 152 } 153 154 (void) pthread_mutex_unlock(&hotplug_lock); 155 156 *resultsp = NULL; 157 return (rv); 158 } 159 160 /* 161 * private_options() 162 * 163 * Implement set/get of bus private options. 164 */ 165 int 166 private_options(const char *path, const char *connection, hp_cmd_t cmd, 167 const char *options, char **resultsp) 168 { 169 ddi_hp_property_t prop; 170 ddi_hp_property_t results; 171 char *values = NULL; 172 int rv; 173 174 hp_dprintf("private_options(path=%s, connection=%s, options='%s')\n", 175 path, connection, options); 176 177 /* Initialize property arguments */ 178 if ((rv = pack_properties(options, &prop)) != 0) { 179 hp_dprintf("private_options: failed to pack properties.\n"); 180 return (rv); 181 } 182 183 /* Initialize results */ 184 (void) memset(&results, 0, sizeof (ddi_hp_property_t)); 185 results.buf_size = HP_PRIVATE_BUF_SZ; 186 results.nvlist_buf = (char *)calloc(1, HP_PRIVATE_BUF_SZ); 187 if (results.nvlist_buf == NULL) { 188 hp_dprintf("private_options: failed to allocate buffer.\n"); 189 free_properties(&prop); 190 return (ENOMEM); 191 } 192 193 /* Lock hotplug */ 194 (void) pthread_mutex_lock(&hotplug_lock); 195 196 /* Perform the command */ 197 rv = 0; 198 if (cmd == HP_CMD_GETPRIVATE) { 199 if (modctl(MODHPOPS, MODHPOPS_BUS_GET, path, connection, 200 &prop, &results)) 201 rv = errno; 202 hp_dprintf("private_options: modctl(MODHPOPS_BUS_GET) = %d\n", 203 rv); 204 } else { 205 if (modctl(MODHPOPS, MODHPOPS_BUS_SET, path, connection, 206 &prop, &results)) 207 rv = errno; 208 hp_dprintf("private_options: modctl(MODHPOPS_BUS_SET) = %d\n", 209 rv); 210 } 211 212 /* Unlock hotplug */ 213 (void) pthread_mutex_unlock(&hotplug_lock); 214 215 /* Parse results */ 216 if (rv == 0) { 217 unpack_properties(&results, &values); 218 *resultsp = values; 219 } 220 221 /* Cleanup */ 222 free_properties(&prop); 223 free_properties(&results); 224 225 return (rv); 226 } 227 228 /* 229 * check_rcm_required() 230 * 231 * Given the root of a changestate operation and the target 232 * state, determine if RCM interactions will be required. 233 */ 234 static boolean_t 235 check_rcm_required(hp_node_t root, int target_state) 236 { 237 /* 238 * RCM is required when transitioning an ENABLED 239 * connector to a non-ENABLED state. 240 */ 241 if ((root->hp_type == HP_NODE_CONNECTOR) && 242 HP_IS_ENABLED(root->hp_state) && !HP_IS_ENABLED(target_state)) 243 return (B_TRUE); 244 245 /* 246 * RCM is required when transitioning an OPERATIONAL 247 * port to a non-OPERATIONAL state. 248 */ 249 if ((root->hp_type == HP_NODE_PORT) && 250 HP_IS_ONLINE(root->hp_state) && HP_IS_OFFLINE(target_state)) 251 return (B_TRUE); 252 253 /* RCM is not required in other cases */ 254 return (B_FALSE); 255 } 256 257 /* 258 * pack_properties() 259 * 260 * Given a specified set/get command and an options string, 261 * construct the structure containing a packed nvlist that 262 * contains the specified options. 263 */ 264 static int 265 pack_properties(const char *options, ddi_hp_property_t *prop) 266 { 267 nvlist_t *nvl; 268 char *buf, *tmp, *name, *value, *next; 269 size_t len; 270 271 /* Initialize results */ 272 (void) memset(prop, 0, sizeof (ddi_hp_property_t)); 273 274 /* Do nothing if options string is empty */ 275 if ((len = strlen(options)) == 0) { 276 hp_dprintf("pack_properties: options string is empty.\n"); 277 return (ENOENT); 278 } 279 280 /* Avoid modifying the input string by using a copy on the stack */ 281 if ((tmp = (char *)alloca(len + 1)) == NULL) { 282 log_err("Failed to allocate buffer for private options.\n"); 283 return (ENOMEM); 284 } 285 (void) strlcpy(tmp, options, len + 1); 286 287 /* Allocate the nvlist */ 288 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { 289 log_err("Failed to allocate private options nvlist.\n"); 290 return (ENOMEM); 291 } 292 293 /* Add each option from the string */ 294 for (name = tmp; name != NULL; name = next) { 295 296 /* Isolate current name/value, and locate the next */ 297 if ((next = strchr(name, ',')) != NULL) { 298 *next = '\0'; 299 next++; 300 } 301 302 /* Split current name/value pair */ 303 if ((value = strchr(name, '=')) != NULL) { 304 *value = '\0'; 305 value++; 306 } else { 307 value = ""; 308 } 309 310 /* Add the option to the nvlist */ 311 if (nvlist_add_string(nvl, name, value) != 0) { 312 log_err("Failed to add private option to nvlist.\n"); 313 nvlist_free(nvl); 314 return (EFAULT); 315 } 316 } 317 318 /* Pack the nvlist */ 319 len = 0; 320 buf = NULL; 321 if (nvlist_pack(nvl, &buf, &len, NV_ENCODE_NATIVE, 0) != 0) { 322 log_err("Failed to pack private options nvlist.\n"); 323 nvlist_free(nvl); 324 return (EFAULT); 325 } 326 327 /* Save results */ 328 prop->nvlist_buf = buf; 329 prop->buf_size = len; 330 331 /* The nvlist is no longer needed */ 332 nvlist_free(nvl); 333 334 return (0); 335 } 336 337 /* 338 * unpack_properties() 339 * 340 * Given a structure possibly containing a packed nvlist of 341 * bus private options, unpack the nvlist and expand its 342 * contents into an options string. 343 */ 344 static void 345 unpack_properties(ddi_hp_property_t *prop, char **optionsp) 346 { 347 nvlist_t *nvl = NULL; 348 nvpair_t *nvp; 349 boolean_t first_flag; 350 char *name, *value, *options; 351 size_t len; 352 353 /* Initialize results */ 354 *optionsp = NULL; 355 356 /* Do nothing if properties do not exist */ 357 if ((prop->nvlist_buf == NULL) || (prop->buf_size == 0)) { 358 hp_dprintf("unpack_properties: no properties exist.\n"); 359 return; 360 } 361 362 /* Unpack the nvlist */ 363 if (nvlist_unpack(prop->nvlist_buf, prop->buf_size, &nvl, 0) != 0) { 364 log_err("Failed to unpack private options nvlist.\n"); 365 return; 366 } 367 368 /* Compute the size of the options string */ 369 for (len = 0, nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) { 370 371 name = nvpair_name(nvp); 372 373 /* Skip the command, and anything not a string */ 374 if ((strcmp(name, "cmd") == 0) || 375 (nvpair_type(nvp) != DATA_TYPE_STRING)) 376 continue; 377 378 (void) nvpair_value_string(nvp, &value); 379 380 /* Account for '=' signs, commas, and terminating NULL */ 381 len += (strlen(name) + strlen(value) + 2); 382 } 383 384 /* Allocate the resulting options string */ 385 if ((options = (char *)calloc(len, sizeof (char))) == NULL) { 386 log_err("Failed to allocate private options string.\n"); 387 nvlist_free(nvl); 388 return; 389 } 390 391 /* Copy name/value pairs into the options string */ 392 first_flag = B_TRUE; 393 for (nvp = NULL; nvp = nvlist_next_nvpair(nvl, nvp); ) { 394 395 name = nvpair_name(nvp); 396 397 /* Skip the command, and anything not a string */ 398 if ((strcmp(name, "cmd") == 0) || 399 (nvpair_type(nvp) != DATA_TYPE_STRING)) 400 continue; 401 402 if (!first_flag) 403 (void) strlcat(options, ",", len); 404 405 (void) strlcat(options, name, len); 406 407 (void) nvpair_value_string(nvp, &value); 408 409 if (strlen(value) > 0) { 410 (void) strlcat(options, "=", len); 411 (void) strlcat(options, value, len); 412 } 413 414 first_flag = B_FALSE; 415 } 416 417 /* The unpacked nvlist is no longer needed */ 418 nvlist_free(nvl); 419 420 /* Save results */ 421 *optionsp = options; 422 } 423 424 /* 425 * free_properties() 426 * 427 * Destroy a structure containing a packed nvlist of bus 428 * private properties. 429 */ 430 static void 431 free_properties(ddi_hp_property_t *prop) 432 { 433 if (prop) { 434 if (prop->nvlist_buf) 435 free(prop->nvlist_buf); 436 (void) memset(prop, 0, sizeof (ddi_hp_property_t)); 437 } 438 } 439