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