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
changestate(const char * path,const char * connection,int state,uint_t flags,int * old_statep,hp_node_t * resultsp)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
private_options(const char * path,const char * connection,hp_cmd_t cmd,const char * options,char ** resultsp)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
check_rcm_required(hp_node_t root,int target_state)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
pack_properties(const char * options,ddi_hp_property_t * prop)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
unpack_properties(ddi_hp_property_t * prop,char ** optionsp)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
free_properties(ddi_hp_property_t * prop)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