dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / hotplugd / hotplugd_impl.c
blob003d65dec20eca278f4d8f3f76fca9a7d9235b9b
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
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"
44 * All operations affecting kernel state are serialized.
46 static pthread_mutex_t hotplug_lock = PTHREAD_MUTEX_INITIALIZER;
49 * Local functions.
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 *);
57 * changestate()
59 * Perform a state change operation.
61 * NOTE: all operations are serialized, using a global lock.
63 int
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;
68 char **rsrcs = NULL;
69 boolean_t use_rcm = B_FALSE;
70 int rv;
72 dprintf("changestate(path=%s, connection=%s, state=0x%x, flags=0x%x)\n",
73 path, connection, state, flags);
75 /* Initialize results */
76 *resultsp = NULL;
77 *old_statep = -1;
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));
85 return (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 */
95 if (use_rcm) {
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);
103 hp_fini(root);
104 return (rv);
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);
119 /* The information snapshot is no longer needed */
120 hp_fini(root);
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);
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);
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 */
144 if (rv == 0)
145 rcm_remove(rsrcs);
146 else
147 rcm_online(rsrcs);
149 /* RCM resources no longer required */
150 free_rcm_resources(rsrcs);
153 (void) pthread_mutex_unlock(&hotplug_lock);
155 *resultsp = NULL;
156 return (rv);
160 * private_options()
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;
170 char *values = NULL;
171 int rv;
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");
179 return (rv);
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);
192 /* Lock hotplug */
193 (void) pthread_mutex_lock(&hotplug_lock);
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);
209 /* Unlock hotplug */
210 (void) pthread_mutex_unlock(&hotplug_lock);
212 /* Parse results */
213 if (rv == 0) {
214 unpack_properties(&results, &values);
215 *resultsp = values;
218 /* Cleanup */
219 free_properties(&prop);
220 free_properties(&results);
222 return (rv);
226 * check_rcm_required()
228 * Given the root of a changestate operation and the target
229 * state, determine if RCM interactions will be required.
231 static boolean_t
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))
240 return (B_TRUE);
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))
248 return (B_TRUE);
250 /* RCM is not required in other cases */
251 return (B_FALSE);
255 * pack_properties()
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.
261 static int
262 pack_properties(const char *options, ddi_hp_property_t *prop)
264 nvlist_t *nvl;
265 char *buf, *tmp, *name, *value, *next;
266 size_t len;
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");
274 return (ENOENT);
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);
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");
287 return (ENOMEM);
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) {
295 *next = '\0';
296 next++;
299 /* Split current name/value pair */
300 if ((value = strchr(name, '=')) != NULL) {
301 *value = '\0';
302 value++;
303 } else {
304 value = "";
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);
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);
324 /* Save results */
325 prop->nvlist_buf = buf;
326 prop->buf_size = len;
328 /* The nvlist is no longer needed */
329 nvlist_free(nvl);
331 return (0);
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.
341 static void
342 unpack_properties(ddi_hp_property_t *prop, char **optionsp)
344 nvlist_t *nvl = NULL;
345 nvpair_t *nvp;
346 boolean_t first_flag;
347 char *name, *value, *options;
348 size_t len;
350 /* Initialize results */
351 *optionsp = NULL;
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;
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;
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))
373 continue;
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");
384 nvlist_free(nvl);
385 return;
388 /* Copy name/value pairs into the options string */
389 first_flag = B_TRUE;
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))
397 continue;
399 if (!first_flag)
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 */
415 nvlist_free(nvl);
417 /* Save results */
418 *optionsp = options;
422 * free_properties()
424 * Destroy a structure containing a packed nvlist of bus
425 * private properties.
427 static void
428 free_properties(ddi_hp_property_t *prop)
430 if (prop) {
431 free(prop->nvlist_buf);
432 (void) memset(prop, 0, sizeof (ddi_hp_property_t));