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 <sys/types.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <libintl.h>
33 #include <string.h>
34 #include <rcm_module.h>
35 #include <sys/pset.h>
36 
37 #include <pool.h>
38 
39 /*
40  * RCM module ops.
41  */
42 static int pool_register(rcm_handle_t *);
43 static int pool_unregister(rcm_handle_t *);
44 static int pool_get_info(rcm_handle_t *, char *, id_t, uint_t, char **,
45     char **, nvlist_t *, rcm_info_t **);
46 static int pool_request_suspend(rcm_handle_t *, char *, id_t,
47     timespec_t *, uint_t, char **, rcm_info_t **);
48 static int pool_notify_resume(rcm_handle_t *, char *, id_t, uint_t, char **,
49     rcm_info_t **);
50 static int pool_notify_remove(rcm_handle_t *, char *, id_t, uint_t,
51     char **, rcm_info_t **);
52 static int pool_request_offline(rcm_handle_t *, char *, id_t, uint_t,
53     char **, rcm_info_t **);
54 static int pool_notify_online(rcm_handle_t *, char *, id_t, uint_t, char **,
55     rcm_info_t **);
56 static int pool_request_capacity_change(rcm_handle_t *, char *, id_t, uint_t,
57     nvlist_t *, char **, rcm_info_t **);
58 static int pool_notify_capacity_change(rcm_handle_t *, char *, id_t, uint_t,
59     nvlist_t *, char **, rcm_info_t **);
60 
61 /*
62  * Pool-specific callback functions.
63  */
64 static int pset_validate_remove(nvlist_t *, char **);
65 
66 static struct {
67 	const char *rsrc;
68 	int (*capacity_change_cb)(nvlist_t *, char **);
69 } registrations[] = {
70 	{ "SUNW_cpu", pset_validate_remove },
71 	{ NULL, NULL }
72 };
73 
74 static int registered = 0;
75 
76 static struct rcm_mod_ops pool_ops = {
77 	RCM_MOD_OPS_VERSION,
78 	pool_register,
79 	pool_unregister,
80 	pool_get_info,
81 	pool_request_suspend,
82 	pool_notify_resume,
83 	pool_request_offline,
84 	pool_notify_online,
85 	pool_notify_remove,
86 	pool_request_capacity_change,
87 	pool_notify_capacity_change,
88 	NULL
89 };
90 
91 struct rcm_mod_ops *
rcm_mod_init(void)92 rcm_mod_init(void)
93 {
94 	rcm_log_message(RCM_TRACE1, "Pools RCM module created\n");
95 	return (&pool_ops);
96 }
97 
98 
99 int
rcm_mod_fini(void)100 rcm_mod_fini(void)
101 {
102 	rcm_log_message(RCM_TRACE1, "Pools RCM module unloaded\n");
103 	return (RCM_SUCCESS);
104 }
105 
106 const char *
rcm_mod_info(void)107 rcm_mod_info(void)
108 {
109 	return ("Pools RCM module 1.4");
110 }
111 
112 static int
pool_check_pset(pool_conf_t * conf,pool_resource_t * res,processorid_t * del_cpus,char ** errorp)113 pool_check_pset(pool_conf_t *conf, pool_resource_t *res,
114     processorid_t *del_cpus, char **errorp)
115 {
116 	int64_t tmp;
117 	int i, j;
118 	uint_t num_cpus;
119 	uint64_t min_cpus;
120 	uint_t num_found = 0;
121 	processorid_t *cpulist;
122 	psetid_t psetid;
123 	pool_value_t *pval;
124 	pool_elem_t *elem = pool_resource_to_elem(conf, res);
125 
126 	if ((pval = pool_value_alloc()) == NULL)
127 		return (-1);
128 	if (pool_get_property(conf, elem, "pset.min", pval) != POC_UINT) {
129 		rcm_log_message(RCM_ERROR,
130 		    gettext("POOL: cannot find property 'pset.min' in pset\n"));
131 		pool_value_free(pval);
132 		return (-1);
133 	}
134 	(void) pool_value_get_uint64(pval, &min_cpus);
135 	if (pool_get_property(conf, elem, "pset.sys_id", pval) != POC_INT) {
136 		rcm_log_message(RCM_ERROR,
137 		    gettext("POOL: cannot get pset.sys_id\n"));
138 		pool_value_free(pval);
139 		return (-1);
140 	}
141 	(void) pool_value_get_int64(pval, &tmp);
142 	pool_value_free(pval);
143 	psetid = (psetid_t)tmp;
144 	rcm_log_message(RCM_TRACE1, "POOL: checking pset: %d\n", psetid);
145 
146 	rcm_log_message(RCM_TRACE1, "POOL: min_cpus is %llu\n", min_cpus);
147 	if (pset_info(psetid, NULL, &num_cpus, NULL) != 0) {
148 		rcm_log_message(RCM_ERROR,
149 		    gettext("POOL: pset_info(%d) failed: %s\n"), psetid,
150 		    strerror(errno));
151 		return (-1);
152 	}
153 	if ((cpulist = malloc(num_cpus * sizeof (processorid_t))) == NULL) {
154 		rcm_log_message(RCM_ERROR,
155 		    gettext("POOL: malloc failed: %s\n"), strerror(errno));
156 		return (-1);
157 	}
158 	if (pset_info(psetid, NULL, &num_cpus, cpulist) != 0) {
159 		free(cpulist);
160 		rcm_log_message(RCM_ERROR,
161 		    gettext("POOL: pset_info(%d) failed: %s\n"), psetid,
162 		    strerror(errno));
163 		return (-1);
164 	}
165 	for (i = 0; del_cpus[i] != -1; i++)
166 		for (j = 0; j < num_cpus; j++)
167 			if (cpulist[j] == del_cpus[i])
168 				num_found++;
169 	free(cpulist);
170 	if (num_found > 0 && (num_cpus - num_found) < (uint_t)min_cpus) {
171 		int len;
172 		char *errval;
173 		const char *errfmt =
174 		    gettext("POOL: processor set (%1$d) would go "
175 		    "below its minimum value of %2$u\n");
176 
177 		/*
178 		 * We would go below the min value. Fail this request.
179 		 */
180 		len = strlen(errfmt) + 4 * 2; /* 4 digits for psetid and min */
181 		if ((errval = malloc((len + 1) * sizeof (char))) != NULL) {
182 			(void) snprintf(errval, len + 1, errfmt, psetid,
183 			    (uint_t)min_cpus);
184 			*errorp = errval;
185 		}
186 
187 		rcm_log_message(RCM_ERROR, (char *)errfmt, psetid,
188 		    (uint_t)min_cpus);
189 
190 		return (-1);
191 	}
192 	rcm_log_message(RCM_TRACE1, "POOL: pset %d is fine\n", psetid);
193 	return (0);
194 }
195 
196 /*
197  * pset_validate_remove()
198  * 	Check to see if the requested cpu removal would be acceptable.
199  * 	Returns RCM_FAILURE if not.
200  */
201 static int
pset_validate_remove(nvlist_t * nvl,char ** errorp)202 pset_validate_remove(nvlist_t *nvl, char **errorp)
203 {
204 	int error = RCM_SUCCESS;
205 	int32_t old_total, new_total, removed_total;
206 	processorid_t *removed_list = NULL; /* list terminated by (-1). */
207 	processorid_t *old_cpu_list = NULL, *new_cpu_list = NULL;
208 	int i, j;
209 	pool_conf_t *conf;
210 	pool_value_t *pvals[] = { NULL, NULL };
211 	pool_resource_t **res = NULL;
212 	uint_t nelem;
213 	const char *generic_error = gettext("POOL: Error processing request\n");
214 
215 	if ((conf = pool_conf_alloc()) == NULL)
216 		return (RCM_FAILURE);
217 	if (pool_conf_open(conf, pool_dynamic_location(), PO_RDONLY) < 0) {
218 		rcm_log_message(RCM_TRACE1,
219 		    "POOL: failed to parse config file: '%s'\n",
220 		    pool_dynamic_location());
221 		pool_conf_free(conf);
222 		return (RCM_SUCCESS);
223 	}
224 
225 	if ((error = nvlist_lookup_int32(nvl, "old_total", &old_total)) != 0) {
226 		(void) pool_conf_close(conf);
227 		pool_conf_free(conf);
228 		rcm_log_message(RCM_ERROR,
229 		    gettext("POOL: unable to find 'old_total' in nvlist: %s\n"),
230 		    strerror(error));
231 		*errorp = strdup(generic_error);
232 		return (RCM_FAILURE);
233 	}
234 	if ((error = nvlist_lookup_int32(nvl, "new_total", &new_total)) != 0) {
235 		(void) pool_conf_close(conf);
236 		pool_conf_free(conf);
237 		rcm_log_message(RCM_ERROR,
238 		    gettext("POOL: unable to find 'new_total' in nvlist: %s\n"),
239 		    strerror(error));
240 		*errorp = strdup(generic_error);
241 		return (RCM_FAILURE);
242 	}
243 	if (new_total >= old_total) {
244 		(void) pool_conf_close(conf);
245 		pool_conf_free(conf);
246 		/*
247 		 * This doesn't look like a cpu removal.
248 		 */
249 		rcm_log_message(RCM_TRACE1,
250 		    gettext("POOL: 'old_total' (%d) is less than 'new_total' "
251 		    "(%d)\n"), old_total, new_total);
252 		return (RCM_SUCCESS);
253 	}
254 	if ((removed_list = malloc((old_total - new_total + 1) * sizeof (int)))
255 	    == NULL) {
256 		rcm_log_message(RCM_ERROR,
257 		    gettext("POOL: malloc failed: %s\n"), strerror(errno));
258 
259 		error = RCM_FAILURE;
260 		goto out;
261 	}
262 	if ((error = nvlist_lookup_int32_array(nvl, "old_cpu_list",
263 	    &old_cpu_list, &nelem)) != 0) {
264 		rcm_log_message(RCM_ERROR,
265 		    gettext("POOL: 'old_cpu_list' not found in nvlist: %s\n"),
266 		    strerror(error));
267 		error = RCM_FAILURE;
268 		goto out;
269 	}
270 	if ((int32_t)nelem != old_total) {
271 		rcm_log_message(RCM_ERROR,
272 		    gettext("POOL: 'old_cpu_list' size mismatch: %1$d vs "
273 		    "%2$d\n"), nelem, old_total);
274 		error = RCM_FAILURE;
275 		goto out;
276 	}
277 	if ((error = nvlist_lookup_int32_array(nvl, "new_cpu_list",
278 	    &new_cpu_list, &nelem)) != 0) {
279 		rcm_log_message(RCM_ERROR,
280 		    gettext("POOL: 'new_cpu_list' not found in nvlist: %s\n"),
281 		    strerror(error));
282 		error = RCM_FAILURE;
283 		goto out;
284 	}
285 	if (nelem != new_total) {
286 		rcm_log_message(RCM_ERROR,
287 		    gettext("POOL: 'new_cpu_list' size mismatch: %1$d vs "
288 		    "%2$d\n"), nelem, new_total);
289 		error = RCM_FAILURE;
290 		goto out;
291 	}
292 
293 	for (i = 0, removed_total = 0; i < old_total; i++) {
294 		for (j = 0; j < new_total; j++)
295 			if (old_cpu_list[i] == new_cpu_list[j])
296 				break;
297 		if (j == new_total) /* not found in new_cpu_list */
298 			removed_list[removed_total++] = old_cpu_list[i];
299 	}
300 	removed_list[removed_total] = -1;
301 
302 	if (removed_total != (old_total - new_total)) {
303 		rcm_log_message(RCM_ERROR,
304 		    gettext("POOL: error finding removed cpu list\n"));
305 		error = RCM_FAILURE;
306 		goto out;
307 	}
308 	if ((pvals[0] = pool_value_alloc()) == NULL) {
309 		rcm_log_message(RCM_ERROR, gettext("POOL: pool_value_alloc"
310 		    " failed: %s\n"), strerror(errno));
311 		error = RCM_FAILURE;
312 		goto out;
313 	}
314 	/*
315 	 * Look for resources with "'type' = 'pset'"
316 	 */
317 	(void) pool_value_set_name(pvals[0], "type");
318 	(void) pool_value_set_string(pvals[0], "pset");
319 	if ((res = pool_query_resources(conf, &nelem, pvals)) == NULL) {
320 		rcm_log_message(RCM_ERROR,
321 		    gettext("POOL: No psets found in configuration\n"));
322 		pool_value_free(pvals[0]);
323 		error =	 RCM_FAILURE;
324 		goto out;
325 	}
326 	pool_value_free(pvals[0]);
327 	for (i = 0; res[i] != NULL; i++)
328 		/*
329 		 * Ask each pset if removing these cpus would cause it to go
330 		 * below it's minimum value.
331 		 */
332 		if (pool_check_pset(conf, res[i], removed_list, errorp) < 0) {
333 			error = RCM_FAILURE;
334 			break;
335 		}
336 	free(res);
337 out:
338 	if (removed_list)
339 		free(removed_list);
340 	if (conf) {
341 		(void) pool_conf_close(conf);
342 		pool_conf_free(conf);
343 	}
344 
345 	/*
346 	 * Set the error string if not already set.
347 	 */
348 	if (error != RCM_SUCCESS && *errorp == NULL)
349 		*errorp = strdup(generic_error);
350 	return (error);
351 }
352 
353 /*
354  * Returns RCM_SUCCESS in a number of error cases, since RCM_FAILURE would
355  * mean that the capacity change would be disallowed by this module,
356  * which is not what we mean.
357  */
358 static int
pool_request_capacity_change(rcm_handle_t * hdl,char * rsrcname,id_t id,uint_t flags,nvlist_t * nvlist,char ** errorp,rcm_info_t ** dependent_info)359 pool_request_capacity_change(rcm_handle_t *hdl, char *rsrcname, id_t id,
360     uint_t flags, nvlist_t *nvlist, char **errorp, rcm_info_t **dependent_info)
361 {
362 	int i;
363 
364 	*errorp = NULL;
365 	rcm_log_message(RCM_TRACE1,
366 	    "POOL: requesting capacity change for: %s (flag: %d)\n",
367 	    rsrcname, flags);
368 	if (flags & RCM_FORCE) {
369 		rcm_log_message(RCM_TRACE1,
370 		    "POOL: Allowing forced operation to pass through...\n");
371 		return (RCM_SUCCESS);
372 	}
373 	for (i = 0; registrations[i].rsrc != NULL; i++) {
374 		if (strcmp(rsrcname, registrations[i].rsrc) == 0) {
375 			return ((*registrations[i].capacity_change_cb)(nvlist,
376 			    errorp));
377 		}
378 	}
379 
380 	return (RCM_SUCCESS);
381 }
382 
383 static int
pool_notify_capacity_change(rcm_handle_t * hdl,char * rsrcname,id_t id,uint_t flags,nvlist_t * nvlist,char ** info,rcm_info_t ** dependent_info)384 pool_notify_capacity_change(rcm_handle_t *hdl, char *rsrcname, id_t id,
385     uint_t flags, nvlist_t *nvlist, char **info, rcm_info_t **dependent_info)
386 {
387 	rcm_log_message(RCM_TRACE1,
388 	    "POOL: notifying capacity change for: %s (flags: %d)\n",
389 	    rsrcname, flags);
390 	return (RCM_SUCCESS);
391 }
392 
393 static int
pool_register(rcm_handle_t * hdl)394 pool_register(rcm_handle_t *hdl)
395 {
396 	int i;
397 
398 	rcm_log_message(RCM_TRACE1, "Registering Pools RCM module\n");
399 	if (registered)
400 		return (RCM_SUCCESS);
401 	registered++;
402 	for (i = 0; registrations[i].rsrc != NULL; i++) {
403 		if (rcm_register_capacity(hdl, (char *)registrations[i].rsrc,
404 		    0, NULL) != RCM_SUCCESS) {
405 			rcm_log_message(RCM_ERROR,
406 			    gettext("POOL: failed to register capacity "
407 			    "change for '%s'\n"),
408 			    registrations[i].rsrc);
409 		}
410 	}
411 	return (RCM_SUCCESS);
412 }
413 
414 static int
pool_unregister(rcm_handle_t * hdl)415 pool_unregister(rcm_handle_t *hdl)
416 {
417 	int i;
418 
419 	rcm_log_message(RCM_TRACE1, "Pools RCM un-registered\n");
420 	if (registered) {
421 		registered--;
422 		for (i = 0; registrations[i].rsrc != NULL; i++)
423 			if (rcm_unregister_capacity(hdl,
424 			    (char *)registrations[i].rsrc, 0) != RCM_SUCCESS) {
425 				rcm_log_message(RCM_ERROR,
426 				    gettext("POOL: unregister capacity failed "
427 				    "for '%s'\n"), registrations[i].rsrc);
428 			}
429 	}
430 	return (RCM_SUCCESS);
431 }
432 
433 static int
pool_get_info(rcm_handle_t * hdl,char * rsrcname,id_t pid,uint_t flag,char ** infop,char ** errorp,nvlist_t * props,rcm_info_t ** dependent_info)434 pool_get_info(rcm_handle_t *hdl, char *rsrcname, id_t pid, uint_t flag,
435     char **infop, char **errorp, nvlist_t *props, rcm_info_t **dependent_info)
436 {
437 	rcm_log_message(RCM_TRACE1, "POOL: RCM get info: '%s'\n", rsrcname);
438 	if ((*infop = strdup(gettext("POOL: In use by pool(4) subsystem")))
439 	    == NULL) {
440 		rcm_log_message(RCM_ERROR, gettext("POOL: get info(%s) malloc "
441 		    "failure\n"), rsrcname);
442 		*infop = NULL;
443 		*errorp = NULL;
444 		return (RCM_FAILURE);
445 	}
446 	return (RCM_SUCCESS);
447 }
448 
449 
450 static int
pool_request_suspend(rcm_handle_t * hdl,char * rsrcname,id_t id,timespec_t * time,uint_t flags,char ** reason,rcm_info_t ** dependent_info)451 pool_request_suspend(rcm_handle_t *hdl, char *rsrcname,
452     id_t id, timespec_t *time, uint_t flags, char **reason,
453     rcm_info_t **dependent_info)
454 {
455 	rcm_log_message(RCM_TRACE1,
456 	    "POOL: requesting suspend for: %s\n", rsrcname);
457 	return (RCM_SUCCESS);
458 }
459 
460 static int
pool_notify_resume(rcm_handle_t * hdl,char * rsrcname,id_t pid,uint_t flags,char ** reason,rcm_info_t ** dependent_info)461 pool_notify_resume(rcm_handle_t *hdl, char *rsrcname,
462     id_t pid, uint_t flags, char **reason, rcm_info_t **dependent_info)
463 {
464 	rcm_log_message(RCM_TRACE1,
465 	    "POOL: notifying resume of: %s\n", rsrcname);
466 	return (RCM_SUCCESS);
467 }
468 
469 static int
pool_request_offline(rcm_handle_t * hdl,char * rsrcname,id_t pid,uint_t flag,char ** reason,rcm_info_t ** dependent_info)470 pool_request_offline(rcm_handle_t *hdl, char *rsrcname, id_t pid, uint_t flag,
471     char **reason, rcm_info_t **dependent_info)
472 {
473 	rcm_log_message(RCM_TRACE1,
474 	    "POOL: requesting offline for: %s\n", rsrcname);
475 	return (RCM_SUCCESS);
476 }
477 
478 static int
pool_notify_online(rcm_handle_t * hdl,char * rsrcname,id_t pid,uint_t flags,char ** reason,rcm_info_t ** dependent_info)479 pool_notify_online(rcm_handle_t *hdl, char *rsrcname, id_t pid, uint_t flags,
480     char **reason, rcm_info_t **dependent_info)
481 {
482 	rcm_log_message(RCM_TRACE1,
483 	    "POOL: notifying online for: %s\n", rsrcname);
484 	return (RCM_SUCCESS);
485 }
486 static int
pool_notify_remove(rcm_handle_t * hdl,char * rsrcname,id_t pid,uint_t flag,char ** reason,rcm_info_t ** dependent_info)487 pool_notify_remove(rcm_handle_t *hdl, char *rsrcname, id_t pid,
488     uint_t flag, char **reason, rcm_info_t **dependent_info)
489 {
490 	rcm_log_message(RCM_TRACE1,
491 	    "POOL: notifying removal of: %s\n", rsrcname);
492 	return (RCM_SUCCESS);
493 }
494