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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
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"
44 * All operations affecting kernel state are serialized.
46 static pthread_mutex_t hotplug_lock
= PTHREAD_MUTEX_INITIALIZER
;
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
*);
59 * Perform a state change operation.
61 * NOTE: all operations are serialized, using a global lock.
64 changestate(const char *path
, const char *connection
, int state
, uint_t flags
,
65 int *old_statep
, hp_node_t
*resultsp
)
67 hp_node_t root
= NULL
;
69 boolean_t use_rcm
= B_FALSE
;
72 dprintf("changestate(path=%s, connection=%s, state=0x%x, flags=0x%x)\n",
73 path
, connection
, state
, flags
);
75 /* Initialize results */
79 (void) pthread_mutex_lock(&hotplug_lock
);
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
));
88 /* Record current state (used in hotplugd_door.c for auditing) */
89 *old_statep
= hp_state(root
);
91 /* Check if RCM interactions are required */
92 use_rcm
= check_rcm_required(root
, state
);
94 /* If RCM is required, perform RCM offline */
97 dprintf("changestate: RCM offline is required.\n");
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
);
107 /* Request RCM offline */
108 if ((rsrcs
!= NULL
) &&
109 ((rv
= rcm_offline(rsrcs
, flags
, root
)) != 0)) {
110 dprintf("changestate: rcm_offline() failed.\n");
112 (void) pthread_mutex_unlock(&hotplug_lock
);
113 free_rcm_resources(rsrcs
);
119 /* The information snapshot is no longer needed */
122 /* Stop now if QUERY flag was specified */
123 if (flags
& HPQUERY
) {
124 dprintf("changestate: operation was QUERY only.\n");
126 (void) pthread_mutex_unlock(&hotplug_lock
);
127 free_rcm_resources(rsrcs
);
131 /* Do state change in kernel */
133 if (modctl(MODHPOPS
, MODHPOPS_CHANGE_STATE
, path
, connection
, state
))
135 dprintf("changestate: modctl(MODHPOPS_CHANGE_STATE) = %d.\n", rv
);
138 * If RCM is required, then perform an RCM online or RCM remove
139 * operation. Which depends upon if modctl succeeded or failed.
141 if (use_rcm
&& (rsrcs
!= NULL
)) {
143 /* RCM online if failure, or RCM remove if successful */
149 /* RCM resources no longer required */
150 free_rcm_resources(rsrcs
);
153 (void) pthread_mutex_unlock(&hotplug_lock
);
162 * Implement set/get of bus private options.
165 private_options(const char *path
, const char *connection
, hp_cmd_t cmd
,
166 const char *options
, char **resultsp
)
168 ddi_hp_property_t prop
;
169 ddi_hp_property_t results
;
173 dprintf("private_options(path=%s, connection=%s, options='%s')\n",
174 path
, connection
, options
);
176 /* Initialize property arguments */
177 if ((rv
= pack_properties(options
, &prop
)) != 0) {
178 dprintf("private_options: failed to pack properties.\n");
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
);
193 (void) pthread_mutex_lock(&hotplug_lock
);
195 /* Perform the command */
197 if (cmd
== HP_CMD_GETPRIVATE
) {
198 if (modctl(MODHPOPS
, MODHPOPS_BUS_GET
, path
, connection
,
201 dprintf("private_options: modctl(MODHPOPS_BUS_GET) = %d\n", rv
);
203 if (modctl(MODHPOPS
, MODHPOPS_BUS_SET
, path
, connection
,
206 dprintf("private_options: modctl(MODHPOPS_BUS_SET) = %d\n", rv
);
210 (void) pthread_mutex_unlock(&hotplug_lock
);
214 unpack_properties(&results
, &values
);
219 free_properties(&prop
);
220 free_properties(&results
);
226 * check_rcm_required()
228 * Given the root of a changestate operation and the target
229 * state, determine if RCM interactions will be required.
232 check_rcm_required(hp_node_t root
, int target_state
)
235 * RCM is required when transitioning an ENABLED
236 * connector to a non-ENABLED state.
238 if ((root
->hp_type
== HP_NODE_CONNECTOR
) &&
239 HP_IS_ENABLED(root
->hp_state
) && !HP_IS_ENABLED(target_state
))
243 * RCM is required when transitioning an OPERATIONAL
244 * port to a non-OPERATIONAL state.
246 if ((root
->hp_type
== HP_NODE_PORT
) &&
247 HP_IS_ONLINE(root
->hp_state
) && HP_IS_OFFLINE(target_state
))
250 /* RCM is not required in other cases */
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.
262 pack_properties(const char *options
, ddi_hp_property_t
*prop
)
265 char *buf
, *tmp
, *name
, *value
, *next
;
268 /* Initialize results */
269 (void) memset(prop
, 0, sizeof (ddi_hp_property_t
));
271 /* Do nothing if options string is empty */
272 if ((len
= strlen(options
)) == 0) {
273 dprintf("pack_properties: options string is empty.\n");
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");
282 (void) strlcpy(tmp
, options
, len
+ 1);
284 /* Allocate the nvlist */
285 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME
, 0) != 0) {
286 log_err("Failed to allocate private options nvlist.\n");
290 /* Add each option from the string */
291 for (name
= tmp
; name
!= NULL
; name
= next
) {
293 /* Isolate current name/value, and locate the next */
294 if ((next
= strchr(name
, ',')) != NULL
) {
299 /* Split current name/value pair */
300 if ((value
= strchr(name
, '=')) != NULL
) {
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");
315 /* Pack the nvlist */
318 if (nvlist_pack(nvl
, &buf
, &len
, NV_ENCODE_NATIVE
, 0) != 0) {
319 log_err("Failed to pack private options nvlist.\n");
325 prop
->nvlist_buf
= buf
;
326 prop
->buf_size
= len
;
328 /* The nvlist is no longer needed */
335 * unpack_properties()
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.
342 unpack_properties(ddi_hp_property_t
*prop
, char **optionsp
)
344 nvlist_t
*nvl
= NULL
;
346 boolean_t first_flag
;
347 char *name
, *value
, *options
;
350 /* Initialize results */
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");
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");
365 /* Compute the size of the options string */
366 for (len
= 0, nvp
= NULL
; nvp
= nvlist_next_nvpair(nvl
, nvp
); ) {
368 name
= nvpair_name(nvp
);
370 /* Skip the command, and anything not a string */
371 if ((strcmp(name
, "cmd") == 0) ||
372 (nvpair_type(nvp
) != DATA_TYPE_STRING
))
375 (void) nvpair_value_string(nvp
, &value
);
377 /* Account for '=' signs, commas, and terminating NULL */
378 len
+= (strlen(name
) + strlen(value
) + 2);
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");
388 /* Copy name/value pairs into the options string */
390 for (nvp
= NULL
; nvp
= nvlist_next_nvpair(nvl
, nvp
); ) {
392 name
= nvpair_name(nvp
);
394 /* Skip the command, and anything not a string */
395 if ((strcmp(name
, "cmd") == 0) ||
396 (nvpair_type(nvp
) != DATA_TYPE_STRING
))
400 (void) strlcat(options
, ",", len
);
402 (void) strlcat(options
, name
, len
);
404 (void) nvpair_value_string(nvp
, &value
);
406 if (strlen(value
) > 0) {
407 (void) strlcat(options
, "=", len
);
408 (void) strlcat(options
, value
, len
);
411 first_flag
= B_FALSE
;
414 /* The unpacked nvlist is no longer needed */
424 * Destroy a structure containing a packed nvlist of bus
425 * private properties.
428 free_properties(ddi_hp_property_t
*prop
)
431 free(prop
->nvlist_buf
);
432 (void) memset(prop
, 0, sizeof (ddi_hp_property_t
));