xref: /titanic_41/usr/src/uts/sun4v/io/dr_cpu.c (revision 4a9fd251254a5d3b654717d88da3fc34a79bba8c)
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 /*
28  * sun4v CPU DR Module
29  */
30 
31 #include <sys/modctl.h>
32 #include <sys/processor.h>
33 #include <sys/cpuvar.h>
34 #include <sys/cpupart.h>
35 #include <sys/sunddi.h>
36 #include <sys/sunndi.h>
37 #include <sys/note.h>
38 #include <sys/sysevent/dr.h>
39 #include <sys/hypervisor_api.h>
40 #include <sys/mach_descrip.h>
41 #include <sys/mdesc.h>
42 #include <sys/ds.h>
43 #include <sys/drctl.h>
44 #include <sys/dr_util.h>
45 #include <sys/dr_cpu.h>
46 #include <sys/promif.h>
47 #include <sys/machsystm.h>
48 
49 
50 static struct modlmisc modlmisc = {
51 	&mod_miscops,
52 	"sun4v CPU DR"
53 };
54 
55 static struct modlinkage modlinkage = {
56 	MODREV_1,
57 	(void *)&modlmisc,
58 	NULL
59 };
60 
61 typedef int (*fn_t)(processorid_t, int *, boolean_t);
62 
63 /*
64  * Global DS Handle
65  */
66 static ds_svc_hdl_t ds_handle;
67 
68 /*
69  * Supported DS Capability Versions
70  */
71 static ds_ver_t		dr_cpu_vers[] = { { 1, 1 }, { 1, 0 } };
72 #define	DR_CPU_NVERS	(sizeof (dr_cpu_vers) / sizeof (dr_cpu_vers[0]))
73 
74 static ds_ver_t		version;
75 
76 /*
77  * DS Capability Description
78  */
79 static ds_capability_t dr_cpu_cap = {
80 	DR_CPU_DS_ID,		/* svc_id */
81 	dr_cpu_vers,		/* vers */
82 	DR_CPU_NVERS		/* nvers */
83 };
84 
85 #define	DRCPU_VERS_EQ(_maj, _min) \
86 	((version.major == (_maj)) && (version.minor == (_min)))
87 
88 #define	DRCPU_VERS_GTEQ(_maj, _min) \
89 	((version.major > (_maj)) ||					\
90 	((version.major == (_maj)) && (version.minor >= (_min))))
91 
92 /*
93  * DS Callbacks
94  */
95 static void dr_cpu_reg_handler(ds_cb_arg_t, ds_ver_t *, ds_svc_hdl_t);
96 static void dr_cpu_unreg_handler(ds_cb_arg_t arg);
97 static void dr_cpu_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
98 
99 /*
100  * DS Client Ops Vector
101  */
102 static ds_clnt_ops_t dr_cpu_ops = {
103 	dr_cpu_reg_handler,	/* ds_reg_cb */
104 	dr_cpu_unreg_handler,	/* ds_unreg_cb */
105 	dr_cpu_data_handler,	/* ds_data_cb */
106 	NULL			/* cb_arg */
107 };
108 
109 /*
110  * Operation Results
111  *
112  * Used internally to gather results while an operation on a
113  * list of CPUs is in progress. In particular, it is used to
114  * keep track of which CPUs have already failed so that they are
115  * not processed further, and the manner in which they failed.
116  */
117 typedef struct {
118 	uint32_t	cpuid;
119 	uint32_t	result;
120 	uint32_t	status;
121 	char		*string;
122 } dr_cpu_res_t;
123 
124 #define	DR_CPU_MAX_ERR_LEN	64	/* maximum error string length */
125 
126 /*
127  * Internal Functions
128  */
129 static int dr_cpu_init(void);
130 static int dr_cpu_fini(void);
131 
132 static int dr_cpu_list_wrk(dr_cpu_hdr_t *, dr_cpu_hdr_t **, int *);
133 static int dr_cpu_list_status(dr_cpu_hdr_t *, dr_cpu_hdr_t **, int *);
134 
135 static int dr_cpu_unconfigure(processorid_t, int *status, boolean_t force);
136 static int dr_cpu_configure(processorid_t, int *status, boolean_t force);
137 static int dr_cpu_status(processorid_t, int *status);
138 
139 static void dr_cpu_check_cpus(dr_cpu_hdr_t *req, dr_cpu_res_t *res);
140 static void dr_cpu_check_psrset(uint32_t *cpuids, dr_cpu_res_t *res, int nres);
141 static int dr_cpu_check_bound_thr(cpu_t *cp, dr_cpu_res_t *res);
142 
143 static dr_cpu_res_t *dr_cpu_res_array_init(dr_cpu_hdr_t *, drctl_rsrc_t *, int);
144 static void dr_cpu_res_array_fini(dr_cpu_res_t *res, int nres);
145 static size_t dr_cpu_pack_response(dr_cpu_hdr_t *req, dr_cpu_res_t *res,
146     dr_cpu_hdr_t **respp);
147 
148 static int dr_cpu_probe(processorid_t newcpuid);
149 static int dr_cpu_deprobe(processorid_t cpuid);
150 
151 static dev_info_t *dr_cpu_find_node(processorid_t cpuid);
152 static mde_cookie_t dr_cpu_find_node_md(processorid_t, md_t *, mde_cookie_t *);
153 
154 int
_init(void)155 _init(void)
156 {
157 	int	status;
158 
159 	/* check that CPU DR is enabled */
160 	if (dr_is_disabled(DR_TYPE_CPU)) {
161 		cmn_err(CE_CONT, "!CPU DR is disabled\n");
162 		return (-1);
163 	}
164 
165 	if ((status = dr_cpu_init()) != 0) {
166 		cmn_err(CE_NOTE, "CPU DR initialization failed");
167 		return (status);
168 	}
169 
170 	if ((status = mod_install(&modlinkage)) != 0) {
171 		(void) dr_cpu_fini();
172 	}
173 
174 	return (status);
175 }
176 
177 int
_info(struct modinfo * modinfop)178 _info(struct modinfo *modinfop)
179 {
180 	return (mod_info(&modlinkage, modinfop));
181 }
182 
183 int dr_cpu_allow_unload;
184 
185 int
_fini(void)186 _fini(void)
187 {
188 	int	status;
189 
190 	if (dr_cpu_allow_unload == 0)
191 		return (EBUSY);
192 
193 	if ((status = mod_remove(&modlinkage)) == 0) {
194 		(void) dr_cpu_fini();
195 	}
196 
197 	return (status);
198 }
199 
200 static int
dr_cpu_init(void)201 dr_cpu_init(void)
202 {
203 	int	rv;
204 
205 	if ((rv = ds_cap_init(&dr_cpu_cap, &dr_cpu_ops)) != 0) {
206 		cmn_err(CE_NOTE, "ds_cap_init failed: %d", rv);
207 		return (-1);
208 	}
209 
210 	return (0);
211 }
212 
213 static int
dr_cpu_fini(void)214 dr_cpu_fini(void)
215 {
216 	int	rv;
217 
218 	if ((rv = ds_cap_fini(&dr_cpu_cap)) != 0) {
219 		cmn_err(CE_NOTE, "ds_cap_fini failed: %d", rv);
220 		return (-1);
221 	}
222 
223 	return (0);
224 }
225 
226 static void
dr_cpu_reg_handler(ds_cb_arg_t arg,ds_ver_t * ver,ds_svc_hdl_t hdl)227 dr_cpu_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
228 {
229 	DR_DBG_CPU("reg_handler: arg=0x%p, ver=%d.%d, hdl=0x%lx\n", arg,
230 	    ver->major, ver->minor, hdl);
231 
232 	version.major = ver->major;
233 	version.minor = ver->minor;
234 	ds_handle = hdl;
235 }
236 
237 static void
dr_cpu_unreg_handler(ds_cb_arg_t arg)238 dr_cpu_unreg_handler(ds_cb_arg_t arg)
239 {
240 	DR_DBG_CPU("unreg_handler: arg=0x%p\n", arg);
241 
242 	ds_handle = DS_INVALID_HDL;
243 }
244 
245 static void
dr_cpu_data_handler(ds_cb_arg_t arg,void * buf,size_t buflen)246 dr_cpu_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
247 {
248 	_NOTE(ARGUNUSED(arg))
249 
250 	dr_cpu_hdr_t	*req = buf;
251 	dr_cpu_hdr_t	err_resp;
252 	dr_cpu_hdr_t	*resp = &err_resp;
253 	int		resp_len = 0;
254 	int		rv;
255 
256 	/*
257 	 * Sanity check the message
258 	 */
259 	if (buflen < sizeof (dr_cpu_hdr_t)) {
260 		DR_DBG_CPU("incoming message short: expected at least %ld "
261 		    "bytes, received %ld\n", sizeof (dr_cpu_hdr_t), buflen);
262 		goto done;
263 	}
264 
265 	if (req == NULL) {
266 		DR_DBG_CPU("empty message: expected at least %ld bytes\n",
267 		    sizeof (dr_cpu_hdr_t));
268 		goto done;
269 	}
270 
271 	DR_DBG_CPU("incoming request:\n");
272 	DR_DBG_DUMP_MSG(buf, buflen);
273 
274 	if (req->num_records > NCPU) {
275 		DR_DBG_CPU("CPU list too long: %d when %d is the maximum\n",
276 		    req->num_records, NCPU);
277 		goto done;
278 	}
279 
280 	if (req->num_records == 0) {
281 		DR_DBG_CPU("No CPU specified for operation\n");
282 		goto done;
283 	}
284 
285 	/*
286 	 * Process the command
287 	 */
288 	switch (req->msg_type) {
289 	case DR_CPU_CONFIGURE:
290 	case DR_CPU_UNCONFIGURE:
291 	case DR_CPU_FORCE_UNCONFIG:
292 		if ((rv = dr_cpu_list_wrk(req, &resp, &resp_len)) != 0) {
293 			DR_DBG_CPU("%s%s failed (%d)\n",
294 			    (req->msg_type == DR_CPU_CONFIGURE) ?
295 			    "CPU configure" : "CPU unconfigure",
296 			    (req->msg_type == DR_CPU_FORCE_UNCONFIG) ?
297 			    " (forced)" : "", rv);
298 		}
299 		break;
300 
301 	case DR_CPU_STATUS:
302 		if ((rv = dr_cpu_list_status(req, &resp, &resp_len)) != 0)
303 			DR_DBG_CPU("CPU status failed (%d)\n", rv);
304 		break;
305 
306 	default:
307 		cmn_err(CE_NOTE, "unsupported DR operation (%d)",
308 		    req->msg_type);
309 		break;
310 	}
311 
312 done:
313 	/* check if an error occurred */
314 	if (resp == &err_resp) {
315 		resp->req_num = (req) ? req->req_num : 0;
316 		resp->msg_type = DR_CPU_ERROR;
317 		resp->num_records = 0;
318 		resp_len = sizeof (dr_cpu_hdr_t);
319 	}
320 
321 	DR_DBG_CPU("outgoing response:\n");
322 	DR_DBG_DUMP_MSG(resp, resp_len);
323 
324 	/* send back the response */
325 	if (ds_cap_send(ds_handle, resp, resp_len) != 0) {
326 		DR_DBG_CPU("ds_send failed\n");
327 	}
328 
329 	/* free any allocated memory */
330 	if (DRCPU_VERS_GTEQ(1, 1) || (resp != &err_resp)) {
331 		DR_DBG_KMEM("%s: free addr %p size %d\n",
332 		    __func__, (void *)resp, resp_len);
333 		kmem_free(resp, resp_len);
334 	}
335 }
336 
337 /*
338  * Create a response message which consists of a header followed
339  * by the error string passed in.
340  */
341 static size_t
dr_cpu_err_resp(dr_cpu_hdr_t * req,dr_cpu_hdr_t ** respp,char * msg)342 dr_cpu_err_resp(dr_cpu_hdr_t *req, dr_cpu_hdr_t **respp, char *msg)
343 {
344 	size_t size;
345 	dr_cpu_hdr_t *resp;
346 
347 	ASSERT((msg != NULL) && (strlen(msg) > 0));
348 
349 	size = sizeof (*req) + strlen(msg) + 1;
350 	resp = kmem_alloc(size, KM_SLEEP);
351 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
352 	    __func__, (void *)resp, size);
353 
354 	resp->req_num = req->req_num;
355 	resp->msg_type = DR_CPU_ERROR;
356 	resp->num_records = 0;
357 
358 	(void) strcpy((char *)(resp) + sizeof (*resp), msg);
359 
360 	*respp = resp;
361 
362 	return (size);
363 }
364 
365 /*
366  * Common routine to config or unconfig multiple cpus.  The unconfig
367  * case checks with the OS to see if the removal of cpus will be
368  * permitted, but can be overridden by the "force" version of the
369  * command.  Otherwise, the logic for both cases is identical.
370  *
371  * Note: Do not modify result buffer or length on error.
372  */
373 static int
dr_cpu_list_wrk(dr_cpu_hdr_t * req,dr_cpu_hdr_t ** resp,int * resp_len)374 dr_cpu_list_wrk(dr_cpu_hdr_t *req, dr_cpu_hdr_t **resp, int *resp_len)
375 {
376 	int		rv;
377 	int		idx;
378 	int		count;
379 	fn_t		dr_fn;
380 	int		se_hint;
381 	boolean_t	force = B_FALSE;
382 	uint32_t	*req_cpus;
383 	dr_cpu_res_t	*res;
384 	int		drctl_cmd;
385 	int		drctl_flags = 0;
386 	drctl_rsrc_t	*drctl_req;
387 	size_t		drctl_req_len;
388 	drctl_resp_t	*drctl_resp;
389 	drctl_rsrc_t	*drctl_rsrc;
390 	size_t		drctl_resp_len = 0;
391 	drctl_cookie_t	drctl_res_ck;
392 
393 	ASSERT((req != NULL) && (req->num_records != 0));
394 
395 	count = req->num_records;
396 
397 	/*
398 	 * Extract all information that is specific
399 	 * to the various types of operations.
400 	 */
401 	switch (req->msg_type) {
402 	case DR_CPU_CONFIGURE:
403 		dr_fn = dr_cpu_configure;
404 		drctl_cmd = DRCTL_CPU_CONFIG_REQUEST;
405 		se_hint = SE_HINT_INSERT;
406 		break;
407 	case DR_CPU_FORCE_UNCONFIG:
408 		drctl_flags = DRCTL_FLAG_FORCE;
409 		force = B_TRUE;
410 		_NOTE(FALLTHROUGH)
411 	case DR_CPU_UNCONFIGURE:
412 		dr_fn = dr_cpu_unconfigure;
413 		drctl_cmd = DRCTL_CPU_UNCONFIG_REQUEST;
414 		se_hint = SE_HINT_REMOVE;
415 		break;
416 	default:
417 		/* Programming error if we reach this. */
418 		cmn_err(CE_NOTE,
419 		    "%s: bad msg_type %d\n", __func__, req->msg_type);
420 		ASSERT(0);
421 		return (-1);
422 	}
423 
424 	/* the incoming array of cpuids to operate on */
425 	req_cpus = DR_CPU_CMD_CPUIDS(req);
426 
427 	/* allocate drctl request msg based on incoming resource count */
428 	drctl_req_len = sizeof (drctl_rsrc_t) * count;
429 	drctl_req = kmem_zalloc(drctl_req_len, KM_SLEEP);
430 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
431 	    __func__, (void *)drctl_req, drctl_req_len);
432 
433 	/* copy the cpuids for the drctl call from the incoming request msg */
434 	for (idx = 0; idx < count; idx++)
435 		drctl_req[idx].res_cpu_id = req_cpus[idx];
436 
437 	rv = drctl_config_init(drctl_cmd, drctl_flags, drctl_req,
438 	    count, &drctl_resp, &drctl_resp_len, &drctl_res_ck);
439 
440 	ASSERT((drctl_resp != NULL) && (drctl_resp_len != 0));
441 
442 	if (rv != 0) {
443 		DR_DBG_CPU("%s: drctl_config_init "
444 		    "returned: %d\n", __func__, rv);
445 
446 		if (DRCPU_VERS_EQ(1, 0)) {
447 			rv = -1;
448 		} else {
449 			ASSERT(DRCPU_VERS_GTEQ(1, 1));
450 			ASSERT(drctl_resp->resp_type == DRCTL_RESP_ERR);
451 
452 			*resp_len = dr_cpu_err_resp(req,
453 			    resp, drctl_resp->resp_err_msg);
454 		}
455 
456 		DR_DBG_KMEM("%s: free addr %p size %ld\n",
457 		    __func__, (void *)drctl_resp, drctl_resp_len);
458 		kmem_free(drctl_resp, drctl_resp_len);
459 		DR_DBG_KMEM("%s: free addr %p size %ld\n",
460 		    __func__, (void *)drctl_req, drctl_req_len);
461 		kmem_free(drctl_req, drctl_req_len);
462 
463 		return (rv);
464 	}
465 
466 	ASSERT(drctl_resp->resp_type == DRCTL_RESP_OK);
467 
468 	drctl_rsrc = drctl_resp->resp_resources;
469 
470 	/* create the result scratch array */
471 	res = dr_cpu_res_array_init(req, drctl_rsrc, count);
472 
473 	/*
474 	 * For unconfigure, check if there are any conditions
475 	 * that will cause the operation to fail. These are
476 	 * performed before the actual unconfigure attempt so
477 	 * that a meaningful error message can be generated.
478 	 */
479 	if (req->msg_type != DR_CPU_CONFIGURE)
480 		dr_cpu_check_cpus(req, res);
481 
482 	/* perform the specified operation on each of the CPUs */
483 	for (idx = 0; idx < count; idx++) {
484 		int result;
485 		int status;
486 
487 		/*
488 		 * If no action will be taken against the current
489 		 * CPU, update the drctl resource information to
490 		 * ensure that it gets recovered properly during
491 		 * the drctl fini() call.
492 		 */
493 		if (res[idx].result != DR_CPU_RES_OK) {
494 			drctl_req[idx].status = DRCTL_STATUS_CONFIG_FAILURE;
495 			continue;
496 		}
497 
498 		/* call the function to perform the actual operation */
499 		result = (*dr_fn)(req_cpus[idx], &status, force);
500 
501 		/* save off results of the operation */
502 		res[idx].result = result;
503 		res[idx].status = status;
504 
505 		/* save result for drctl fini() reusing init() msg memory */
506 		drctl_req[idx].status = (result != DR_CPU_RES_OK) ?
507 		    DRCTL_STATUS_CONFIG_FAILURE : DRCTL_STATUS_CONFIG_SUCCESS;
508 
509 		DR_DBG_CPU("%s: cpuid %d status %d result %d off '%s'\n",
510 		    __func__, req_cpus[idx], drctl_req[idx].status, result,
511 		    (res[idx].string) ? res[idx].string : "");
512 	}
513 
514 	if ((rv = drctl_config_fini(&drctl_res_ck, drctl_req, count)) != 0)
515 		DR_DBG_CPU("%s: drctl_config_fini "
516 		    "returned: %d\n", __func__, rv);
517 
518 	/*
519 	 * Operation completed without any fatal errors.
520 	 * Pack the response for transmission.
521 	 */
522 	*resp_len = dr_cpu_pack_response(req, res, resp);
523 
524 	/* notify interested parties about the operation */
525 	dr_generate_event(DR_TYPE_CPU, se_hint);
526 
527 	/*
528 	 * Deallocate any scratch memory.
529 	 */
530 	DR_DBG_KMEM("%s: free addr %p size %ld\n",
531 	    __func__, (void *)drctl_resp, drctl_resp_len);
532 	kmem_free(drctl_resp, drctl_resp_len);
533 	DR_DBG_KMEM("%s: free addr %p size %ld\n",
534 	    __func__, (void *)drctl_req, drctl_req_len);
535 	kmem_free(drctl_req, drctl_req_len);
536 
537 	dr_cpu_res_array_fini(res, count);
538 
539 	return (0);
540 }
541 
542 /*
543  * Allocate and initialize a result array based on the initial
544  * drctl operation. A valid result array is always returned.
545  */
546 static dr_cpu_res_t *
dr_cpu_res_array_init(dr_cpu_hdr_t * req,drctl_rsrc_t * rsrc,int nrsrc)547 dr_cpu_res_array_init(dr_cpu_hdr_t *req, drctl_rsrc_t *rsrc, int nrsrc)
548 {
549 	int		idx;
550 	dr_cpu_res_t	*res;
551 	char		*err_str;
552 	size_t		err_len;
553 
554 	/* allocate zero filled buffer to initialize fields */
555 	res = kmem_zalloc(nrsrc * sizeof (dr_cpu_res_t), KM_SLEEP);
556 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
557 	    __func__, (void *)res, nrsrc * sizeof (dr_cpu_res_t));
558 
559 	/*
560 	 * Fill in the result information for each resource.
561 	 */
562 	for (idx = 0; idx < nrsrc; idx++) {
563 		res[idx].cpuid = rsrc[idx].res_cpu_id;
564 		res[idx].result = DR_CPU_RES_OK;
565 
566 		if (rsrc[idx].status == DRCTL_STATUS_ALLOW)
567 			continue;
568 
569 		/*
570 		 * Update the state information for this CPU.
571 		 */
572 		res[idx].result = DR_CPU_RES_BLOCKED;
573 		res[idx].status = (req->msg_type == DR_CPU_CONFIGURE) ?
574 		    DR_CPU_STAT_UNCONFIGURED : DR_CPU_STAT_CONFIGURED;
575 
576 		/*
577 		 * If an error string exists, copy it out of the
578 		 * message buffer. This eliminates any dependency
579 		 * on the memory allocated for the message buffer
580 		 * itself.
581 		 */
582 		if (rsrc[idx].offset != NULL) {
583 			err_str = (char *)rsrc + rsrc[idx].offset;
584 			err_len = strlen(err_str) + 1;
585 
586 			res[idx].string = kmem_alloc(err_len, KM_SLEEP);
587 			DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
588 			    __func__, (void *)(res[idx].string), err_len);
589 			bcopy(err_str, res[idx].string, err_len);
590 		}
591 	}
592 
593 	return (res);
594 }
595 
596 static void
dr_cpu_res_array_fini(dr_cpu_res_t * res,int nres)597 dr_cpu_res_array_fini(dr_cpu_res_t *res, int nres)
598 {
599 	int	idx;
600 	size_t	str_len;
601 
602 	for (idx = 0; idx < nres; idx++) {
603 		/* deallocate the error string if present */
604 		if (res[idx].string) {
605 			str_len = strlen(res[idx].string) + 1;
606 			DR_DBG_KMEM("%s: free addr %p size %ld\n",
607 			    __func__, (void *)(res[idx].string), str_len);
608 			kmem_free(res[idx].string, str_len);
609 		}
610 	}
611 
612 	/* deallocate the result array itself */
613 	DR_DBG_KMEM("%s: free addr %p size %ld\n",
614 	    __func__, (void *)res, sizeof (dr_cpu_res_t) * nres);
615 	kmem_free(res, sizeof (dr_cpu_res_t) * nres);
616 }
617 
618 /*
619  * Allocate and pack a response message for transmission based
620  * on the specified result array. A valid response message and
621  * valid size information is always returned.
622  */
623 static size_t
dr_cpu_pack_response(dr_cpu_hdr_t * req,dr_cpu_res_t * res,dr_cpu_hdr_t ** respp)624 dr_cpu_pack_response(dr_cpu_hdr_t *req, dr_cpu_res_t *res, dr_cpu_hdr_t **respp)
625 {
626 	int		idx;
627 	dr_cpu_hdr_t	*resp;
628 	dr_cpu_stat_t	*resp_stat;
629 	size_t		resp_len;
630 	uint32_t	curr_off;
631 	caddr_t		curr_str;
632 	size_t		str_len;
633 	size_t		stat_len;
634 	int		nstat = req->num_records;
635 
636 	/*
637 	 * Calculate the size of the response message
638 	 * and allocate an appropriately sized buffer.
639 	 */
640 	resp_len = 0;
641 
642 	/* add the header size */
643 	resp_len += sizeof (dr_cpu_hdr_t);
644 
645 	/* add the stat array size */
646 	stat_len = sizeof (dr_cpu_stat_t) * nstat;
647 	resp_len += stat_len;
648 
649 	/* add the size of any error strings */
650 	for (idx = 0; idx < nstat; idx++) {
651 		if (res[idx].string != NULL) {
652 			resp_len += strlen(res[idx].string) + 1;
653 		}
654 	}
655 
656 	/* allocate the message buffer */
657 	resp = kmem_zalloc(resp_len, KM_SLEEP);
658 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
659 	    __func__, (void *)resp, resp_len);
660 
661 	/*
662 	 * Fill in the header information.
663 	 */
664 	resp->req_num = req->req_num;
665 	resp->msg_type = DR_CPU_OK;
666 	resp->num_records = nstat;
667 
668 	/*
669 	 * Fill in the stat information.
670 	 */
671 	resp_stat = DR_CPU_RESP_STATS(resp);
672 
673 	/* string offsets start immediately after stat array */
674 	curr_off = sizeof (dr_cpu_hdr_t) + stat_len;
675 	curr_str = (char *)resp_stat + stat_len;
676 
677 	for (idx = 0; idx < nstat; idx++) {
678 		resp_stat[idx].cpuid = res[idx].cpuid;
679 		resp_stat[idx].result = res[idx].result;
680 		resp_stat[idx].status = res[idx].status;
681 
682 		if (res[idx].string != NULL) {
683 			/* copy over the error string */
684 			str_len = strlen(res[idx].string) + 1;
685 			bcopy(res[idx].string, curr_str, str_len);
686 			resp_stat[idx].string_off = curr_off;
687 
688 			curr_off += str_len;
689 			curr_str += str_len;
690 		}
691 	}
692 
693 	/* buffer should be exactly filled */
694 	ASSERT(curr_off == resp_len);
695 
696 	*respp = resp;
697 	return (resp_len);
698 }
699 
700 /*
701  * Check for conditions that will prevent a CPU from being offlined.
702  * This provides the opportunity to generate useful information to
703  * help diagnose the failure rather than letting the offline attempt
704  * fail in a more generic way.
705  */
706 static void
dr_cpu_check_cpus(dr_cpu_hdr_t * req,dr_cpu_res_t * res)707 dr_cpu_check_cpus(dr_cpu_hdr_t *req, dr_cpu_res_t *res)
708 {
709 	int		idx;
710 	cpu_t		*cp;
711 	uint32_t	*cpuids;
712 
713 	ASSERT((req->msg_type == DR_CPU_UNCONFIGURE) ||
714 	    (req->msg_type == DR_CPU_FORCE_UNCONFIG));
715 
716 	DR_DBG_CPU("dr_cpu_check_cpus...\n");
717 
718 	/* array of cpuids start just after the header */
719 	cpuids = DR_CPU_CMD_CPUIDS(req);
720 
721 	mutex_enter(&cpu_lock);
722 
723 	/*
724 	 * Always check processor set membership first. The
725 	 * last CPU in a processor set will fail to offline
726 	 * even if the operation if forced, so any failures
727 	 * should always be reported.
728 	 */
729 	dr_cpu_check_psrset(cpuids, res, req->num_records);
730 
731 	/* process each cpu that is part of the request */
732 	for (idx = 0; idx < req->num_records; idx++) {
733 
734 		/* nothing to check if the CPU has already failed */
735 		if (res[idx].result != DR_CPU_RES_OK)
736 			continue;
737 
738 		if ((cp = cpu_get(cpuids[idx])) == NULL)
739 			continue;
740 
741 		/*
742 		 * Only check if there are bound threads if the
743 		 * operation is not a forced unconfigure. In a
744 		 * forced request, threads are automatically
745 		 * unbound before they are offlined.
746 		 */
747 		if (req->msg_type == DR_CPU_UNCONFIGURE) {
748 			/*
749 			 * The return value is only interesting if other
750 			 * checks are added to this loop and a decision
751 			 * is needed on whether to continue checking.
752 			 */
753 			(void) dr_cpu_check_bound_thr(cp, &res[idx]);
754 		}
755 	}
756 
757 	mutex_exit(&cpu_lock);
758 }
759 
760 /*
761  * Examine the processor set configuration for the specified
762  * CPUs and see if the unconfigure operation would result in
763  * trying to remove the last CPU in any processor set.
764  */
765 static void
dr_cpu_check_psrset(uint32_t * cpuids,dr_cpu_res_t * res,int nres)766 dr_cpu_check_psrset(uint32_t *cpuids, dr_cpu_res_t *res, int nres)
767 {
768 	int		cpu_idx;
769 	int		set_idx;
770 	cpu_t		*cp;
771 	cpupart_t	*cpp;
772 	char		err_str[DR_CPU_MAX_ERR_LEN];
773 	size_t		err_len;
774 	struct {
775 		cpupart_t	*cpp;
776 		int		ncpus;
777 	} *psrset;
778 
779 	ASSERT(MUTEX_HELD(&cpu_lock));
780 
781 	/*
782 	 * Allocate a scratch array to count the CPUs in
783 	 * the various processor sets. A CPU always belongs
784 	 * to exactly one processor set, so by definition,
785 	 * the scratch array never needs to be larger than
786 	 * the number of CPUs.
787 	 */
788 	psrset = kmem_zalloc(sizeof (*psrset) * nres, KM_SLEEP);
789 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
790 	    __func__, (void *)psrset, sizeof (*psrset) * nres);
791 
792 	for (cpu_idx = 0; cpu_idx < nres; cpu_idx++) {
793 
794 		/* skip any CPUs that have already failed */
795 		if (res[cpu_idx].result != DR_CPU_RES_OK)
796 			continue;
797 
798 		if ((cp = cpu_get(cpuids[cpu_idx])) == NULL)
799 			continue;
800 
801 		cpp = cp->cpu_part;
802 
803 		/* lookup the set this CPU belongs to */
804 		for (set_idx = 0; set_idx < nres; set_idx++) {
805 
806 			/* matching set found */
807 			if (cpp == psrset[set_idx].cpp)
808 				break;
809 
810 			/* set not found, start a new entry */
811 			if (psrset[set_idx].cpp == NULL) {
812 				psrset[set_idx].cpp = cpp;
813 				psrset[set_idx].ncpus = cpp->cp_ncpus;
814 				break;
815 			}
816 		}
817 
818 		ASSERT(set_idx != nres);
819 
820 		/*
821 		 * Remove the current CPU from the set total but only
822 		 * generate an error for the last CPU. The correct CPU
823 		 * will get the error because the unconfigure attempts
824 		 * will occur in the same order in which the CPUs are
825 		 * examined in this loop.  The cp_ncpus field of a
826 		 * cpupart_t counts only online cpus, so it is safe
827 		 * to remove an offline cpu without testing ncpus.
828 		 */
829 		if (cpu_is_offline(cp))
830 			continue;
831 
832 		if (--psrset[set_idx].ncpus == 0) {
833 			/*
834 			 * Fill in the various pieces of information
835 			 * to report that the operation will fail.
836 			 */
837 			res[cpu_idx].result = DR_CPU_RES_BLOCKED;
838 			res[cpu_idx].status = DR_CPU_STAT_CONFIGURED;
839 
840 			(void) snprintf(err_str, DR_CPU_MAX_ERR_LEN,
841 			    "last online cpu in processor set %d", cpp->cp_id);
842 
843 			err_len = strlen(err_str) + 1;
844 
845 			res[cpu_idx].string = kmem_alloc(err_len, KM_SLEEP);
846 			DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
847 			    __func__, (void *)(res[cpu_idx].string), err_len);
848 			bcopy(err_str, res[cpu_idx].string, err_len);
849 
850 			DR_DBG_CPU("cpu %d: %s\n", cpuids[cpu_idx], err_str);
851 		}
852 	}
853 
854 	DR_DBG_KMEM("%s: free addr %p size %ld\n",
855 	    __func__, (void *)psrset, sizeof (*psrset) * nres);
856 	kmem_free(psrset, sizeof (*psrset) * nres);
857 }
858 
859 /*
860  * Check if any threads are bound to the specified CPU. If the
861  * condition is true, DR_CPU_RES_BLOCKED is returned and an error
862  * string is generated and placed in the specified result structure.
863  * Otherwise, DR_CPU_RES_OK is returned.
864  */
865 static int
dr_cpu_check_bound_thr(cpu_t * cp,dr_cpu_res_t * res)866 dr_cpu_check_bound_thr(cpu_t *cp, dr_cpu_res_t *res)
867 {
868 	int		nbound;
869 	proc_t		*pp;
870 	kthread_t	*tp;
871 	char		err_str[DR_CPU_MAX_ERR_LEN];
872 	size_t		err_len;
873 
874 	/*
875 	 * Error string allocation makes an assumption
876 	 * that no blocking condition has been identified.
877 	 */
878 	ASSERT(res->result == DR_CPU_RES_OK);
879 	ASSERT(res->string == NULL);
880 
881 	ASSERT(MUTEX_HELD(&cpu_lock));
882 
883 	mutex_enter(&pidlock);
884 
885 	nbound = 0;
886 
887 	/*
888 	 * Walk the active processes, checking if each
889 	 * thread belonging to the process is bound.
890 	 */
891 	for (pp = practive; (pp != NULL) && (nbound <= 1); pp = pp->p_next) {
892 		mutex_enter(&pp->p_lock);
893 
894 		tp = pp->p_tlist;
895 
896 		if ((tp == NULL) || (pp->p_flag & SSYS)) {
897 			mutex_exit(&pp->p_lock);
898 			continue;
899 		}
900 
901 		do {
902 			if (tp->t_bind_cpu != cp->cpu_id)
903 				continue;
904 
905 			/*
906 			 * Update the running total of bound
907 			 * threads. Continue the search until
908 			 * it can be determined if more than
909 			 * one thread is bound to the CPU.
910 			 */
911 			if (++nbound > 1)
912 				break;
913 
914 		} while ((tp = tp->t_forw) != pp->p_tlist);
915 
916 		mutex_exit(&pp->p_lock);
917 	}
918 
919 	mutex_exit(&pidlock);
920 
921 	if (nbound) {
922 		/*
923 		 * Threads are bound to the CPU. Fill in
924 		 * various pieces of information to report
925 		 * that the operation will fail.
926 		 */
927 		res->result = DR_CPU_RES_BLOCKED;
928 		res->status = DR_CPU_STAT_CONFIGURED;
929 
930 		(void) snprintf(err_str, DR_CPU_MAX_ERR_LEN, "cpu has bound "
931 		    "thread%s", (nbound > 1) ? "s" : "");
932 
933 		err_len = strlen(err_str) + 1;
934 
935 		res->string = kmem_alloc(err_len, KM_SLEEP);
936 		DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
937 		    __func__, (void *)(res->string), err_len);
938 		bcopy(err_str, res->string, err_len);
939 
940 		DR_DBG_CPU("cpu %d: %s\n", cp->cpu_id, err_str);
941 	}
942 
943 	return (res->result);
944 }
945 
946 /*
947  * Do not modify result buffer or length on error.
948  */
949 static int
dr_cpu_list_status(dr_cpu_hdr_t * req,dr_cpu_hdr_t ** resp,int * resp_len)950 dr_cpu_list_status(dr_cpu_hdr_t *req, dr_cpu_hdr_t **resp, int *resp_len)
951 {
952 	int		idx;
953 	int		result;
954 	int		status;
955 	int		rlen;
956 	uint32_t	*cpuids;
957 	dr_cpu_hdr_t	*rp;
958 	dr_cpu_stat_t	*stat;
959 	md_t		*mdp = NULL;
960 	int		num_nodes;
961 	int		listsz;
962 	mde_cookie_t	*listp = NULL;
963 	mde_cookie_t	cpunode;
964 	boolean_t	walk_md = B_FALSE;
965 
966 	/* the incoming array of cpuids to configure */
967 	cpuids = DR_CPU_CMD_CPUIDS(req);
968 
969 	/* allocate a response message */
970 	rlen = sizeof (dr_cpu_hdr_t);
971 	rlen += req->num_records * sizeof (dr_cpu_stat_t);
972 	rp = kmem_zalloc(rlen, KM_SLEEP);
973 	DR_DBG_KMEM("%s: alloc addr %p size %d\n", __func__, (void *)rp, rlen);
974 
975 	/* fill in the known data */
976 	rp->req_num = req->req_num;
977 	rp->msg_type = DR_CPU_STATUS;
978 	rp->num_records = req->num_records;
979 
980 	/* stat array for the response */
981 	stat = DR_CPU_RESP_STATS(rp);
982 
983 	/* get the status for each of the CPUs */
984 	for (idx = 0; idx < req->num_records; idx++) {
985 
986 		result = dr_cpu_status(cpuids[idx], &status);
987 
988 		if (result == DR_CPU_RES_FAILURE)
989 			walk_md = B_TRUE;
990 
991 		/* save off results of the status */
992 		stat[idx].cpuid = cpuids[idx];
993 		stat[idx].result = result;
994 		stat[idx].status = status;
995 	}
996 
997 	if (walk_md == B_FALSE)
998 		goto done;
999 
1000 	/*
1001 	 * At least one of the cpus did not have a CPU
1002 	 * structure. So, consult the MD to determine if
1003 	 * they are present.
1004 	 */
1005 
1006 	if ((mdp = md_get_handle()) == NULL) {
1007 		DR_DBG_CPU("unable to initialize MD\n");
1008 		goto done;
1009 	}
1010 
1011 	num_nodes = md_node_count(mdp);
1012 	ASSERT(num_nodes > 0);
1013 
1014 	listsz = num_nodes * sizeof (mde_cookie_t);
1015 	listp = kmem_zalloc(listsz, KM_SLEEP);
1016 	DR_DBG_KMEM("%s: alloc addr %p size %d\n",
1017 	    __func__, (void *)listp, listsz);
1018 
1019 	for (idx = 0; idx < req->num_records; idx++) {
1020 
1021 		if (stat[idx].result != DR_CPU_RES_FAILURE)
1022 			continue;
1023 
1024 		/* check the MD for the current cpuid */
1025 		cpunode = dr_cpu_find_node_md(stat[idx].cpuid, mdp, listp);
1026 
1027 		stat[idx].result = DR_CPU_RES_OK;
1028 
1029 		if (cpunode == MDE_INVAL_ELEM_COOKIE) {
1030 			stat[idx].status = DR_CPU_STAT_NOT_PRESENT;
1031 		} else {
1032 			stat[idx].status = DR_CPU_STAT_UNCONFIGURED;
1033 		}
1034 	}
1035 
1036 	DR_DBG_KMEM("%s: free addr %p size %d\n",
1037 	    __func__, (void *)listp, listsz);
1038 	kmem_free(listp, listsz);
1039 
1040 	(void) md_fini_handle(mdp);
1041 
1042 done:
1043 	*resp = rp;
1044 	*resp_len = rlen;
1045 
1046 	return (0);
1047 }
1048 
1049 static int
dr_cpu_configure(processorid_t cpuid,int * status,boolean_t force)1050 dr_cpu_configure(processorid_t cpuid, int *status, boolean_t force)
1051 {
1052 	 _NOTE(ARGUNUSED(force))
1053 	struct cpu	*cp;
1054 	int		rv = 0;
1055 
1056 	DR_DBG_CPU("dr_cpu_configure...\n");
1057 
1058 	/*
1059 	 * Build device tree node for the CPU
1060 	 */
1061 	if ((rv = dr_cpu_probe(cpuid)) != 0) {
1062 		DR_DBG_CPU("failed to probe CPU %d (%d)\n", cpuid, rv);
1063 		if (rv == EINVAL) {
1064 			*status = DR_CPU_STAT_NOT_PRESENT;
1065 			return (DR_CPU_RES_NOT_IN_MD);
1066 		}
1067 		*status = DR_CPU_STAT_UNCONFIGURED;
1068 		return (DR_CPU_RES_FAILURE);
1069 	}
1070 
1071 	mutex_enter(&cpu_lock);
1072 
1073 	/*
1074 	 * Configure the CPU
1075 	 */
1076 	if ((cp = cpu_get(cpuid)) == NULL) {
1077 
1078 		if ((rv = cpu_configure(cpuid)) != 0) {
1079 			DR_DBG_CPU("failed to configure CPU %d (%d)\n",
1080 			    cpuid, rv);
1081 			rv = DR_CPU_RES_FAILURE;
1082 			*status = DR_CPU_STAT_UNCONFIGURED;
1083 			goto done;
1084 		}
1085 
1086 		DR_DBG_CPU("CPU %d configured\n", cpuid);
1087 
1088 		/* CPU struct should exist now */
1089 		cp = cpu_get(cpuid);
1090 	}
1091 
1092 	ASSERT(cp);
1093 
1094 	/*
1095 	 * Power on the CPU. In sun4v, this brings the stopped
1096 	 * CPU into the guest from the Hypervisor.
1097 	 */
1098 	if (cpu_is_poweredoff(cp)) {
1099 
1100 		if ((rv = cpu_poweron(cp)) != 0) {
1101 			DR_DBG_CPU("failed to power on CPU %d (%d)\n",
1102 			    cpuid, rv);
1103 			rv = DR_CPU_RES_FAILURE;
1104 			*status = DR_CPU_STAT_UNCONFIGURED;
1105 			goto done;
1106 		}
1107 
1108 		DR_DBG_CPU("CPU %d powered on\n", cpuid);
1109 	}
1110 
1111 	/*
1112 	 * Online the CPU
1113 	 */
1114 	if (cpu_is_offline(cp)) {
1115 
1116 		if ((rv = cpu_online(cp)) != 0) {
1117 			DR_DBG_CPU("failed to online CPU %d (%d)\n",
1118 			    cpuid, rv);
1119 			rv = DR_CPU_RES_FAILURE;
1120 			/* offline is still configured */
1121 			*status = DR_CPU_STAT_CONFIGURED;
1122 			goto done;
1123 		}
1124 
1125 		DR_DBG_CPU("CPU %d online\n", cpuid);
1126 	}
1127 
1128 	rv = DR_CPU_RES_OK;
1129 	*status = DR_CPU_STAT_CONFIGURED;
1130 
1131 done:
1132 	mutex_exit(&cpu_lock);
1133 
1134 	return (rv);
1135 }
1136 
1137 static int
dr_cpu_unconfigure(processorid_t cpuid,int * status,boolean_t force)1138 dr_cpu_unconfigure(processorid_t cpuid, int *status, boolean_t force)
1139 {
1140 	struct cpu	*cp;
1141 	int		rv = 0;
1142 	int		cpu_flags;
1143 
1144 	DR_DBG_CPU("dr_cpu_unconfigure%s...\n", (force) ? " (force)" : "");
1145 
1146 	mutex_enter(&cpu_lock);
1147 
1148 	cp = cpu_get(cpuid);
1149 
1150 	if (cp == NULL) {
1151 		/*
1152 		 * As OS CPU structures are already torn down proceed
1153 		 * to deprobe device tree to make sure the device tree
1154 		 * is up do date.
1155 		 */
1156 		goto deprobe;
1157 	}
1158 
1159 	ASSERT(cp->cpu_id == cpuid);
1160 
1161 	/*
1162 	 * Offline the CPU
1163 	 */
1164 	if (cpu_is_active(cp)) {
1165 
1166 		/* set the force flag correctly */
1167 		cpu_flags = (force) ? CPU_FORCED : 0;
1168 
1169 		/*
1170 		 * Before we take the CPU offline, we first enable interrupts.
1171 		 * Otherwise, cpu_offline() might reject the request.  Note:
1172 		 * if the offline subsequently fails, the target cpu will be
1173 		 * left with interrupts enabled.  This is consistent with the
1174 		 * behavior of psradm(1M) and p_online(2).
1175 		 */
1176 		cpu_intr_enable(cp);
1177 
1178 		if ((rv = cpu_offline(cp, cpu_flags)) != 0) {
1179 			DR_DBG_CPU("failed to offline CPU %d (%d)\n",
1180 			    cpuid, rv);
1181 
1182 			rv = DR_CPU_RES_FAILURE;
1183 			*status = DR_CPU_STAT_CONFIGURED;
1184 			mutex_exit(&cpu_lock);
1185 			return (rv);
1186 		}
1187 
1188 		DR_DBG_CPU("CPU %d offline\n", cpuid);
1189 	}
1190 
1191 	/*
1192 	 * Power off the CPU. In sun4v, this puts the running
1193 	 * CPU into the stopped state in the Hypervisor.
1194 	 */
1195 	if (!cpu_is_poweredoff(cp)) {
1196 
1197 		if ((rv = cpu_poweroff(cp)) != 0) {
1198 			DR_DBG_CPU("failed to power off CPU %d (%d)\n",
1199 			    cpuid, rv);
1200 			rv = DR_CPU_RES_FAILURE;
1201 			*status = DR_CPU_STAT_CONFIGURED;
1202 			mutex_exit(&cpu_lock);
1203 			return (rv);
1204 		}
1205 
1206 		DR_DBG_CPU("CPU %d powered off\n", cpuid);
1207 	}
1208 
1209 	/*
1210 	 * Unconfigure the CPU
1211 	 */
1212 	if ((rv = cpu_unconfigure(cpuid)) != 0) {
1213 		DR_DBG_CPU("failed to unconfigure CPU %d (%d)\n", cpuid, rv);
1214 		rv = DR_CPU_RES_FAILURE;
1215 		*status = DR_CPU_STAT_UNCONFIGURED;
1216 		mutex_exit(&cpu_lock);
1217 		return (rv);
1218 	}
1219 
1220 	DR_DBG_CPU("CPU %d unconfigured\n", cpuid);
1221 
1222 deprobe:
1223 	mutex_exit(&cpu_lock);
1224 	/*
1225 	 * Tear down device tree.
1226 	 */
1227 	if ((rv = dr_cpu_deprobe(cpuid)) != 0) {
1228 		DR_DBG_CPU("failed to deprobe CPU %d (%d)\n", cpuid, rv);
1229 		rv = DR_CPU_RES_FAILURE;
1230 		*status = DR_CPU_STAT_UNCONFIGURED;
1231 		return (rv);
1232 	}
1233 
1234 	rv = DR_CPU_RES_OK;
1235 	*status = DR_CPU_STAT_UNCONFIGURED;
1236 
1237 	return (rv);
1238 }
1239 
1240 /*
1241  * Determine the state of a CPU. If the CPU structure is not present,
1242  * it does not attempt to determine whether or not the CPU is in the
1243  * MD. It is more efficient to do this at the higher level for all
1244  * CPUs since it may not even be necessary to search the MD if all
1245  * the CPUs are accounted for. Returns DR_CPU_RES_OK if the CPU
1246  * structure is present, and DR_CPU_RES_FAILURE otherwise as a signal
1247  * that an MD walk is necessary.
1248  */
1249 static int
dr_cpu_status(processorid_t cpuid,int * status)1250 dr_cpu_status(processorid_t cpuid, int *status)
1251 {
1252 	int		rv;
1253 	struct cpu	*cp;
1254 
1255 	DR_DBG_CPU("dr_cpu_status...\n");
1256 
1257 	mutex_enter(&cpu_lock);
1258 
1259 	if ((cp = cpu_get(cpuid)) == NULL) {
1260 		/* need to check if cpu is in the MD */
1261 		rv = DR_CPU_RES_FAILURE;
1262 		goto done;
1263 	}
1264 
1265 	if (cpu_is_poweredoff(cp)) {
1266 		/*
1267 		 * The CPU is powered off, so it is considered
1268 		 * unconfigured from the service entity point of
1269 		 * view. The CPU is not available to the system
1270 		 * and intervention by the service entity would
1271 		 * be required to change that.
1272 		 */
1273 		*status = DR_CPU_STAT_UNCONFIGURED;
1274 	} else {
1275 		/*
1276 		 * The CPU is powered on, so it is considered
1277 		 * configured from the service entity point of
1278 		 * view. It is available for use by the system
1279 		 * and service entities are not concerned about
1280 		 * the operational status (offline, online, etc.)
1281 		 * of the CPU in terms of DR.
1282 		 */
1283 		*status = DR_CPU_STAT_CONFIGURED;
1284 	}
1285 
1286 	rv = DR_CPU_RES_OK;
1287 
1288 done:
1289 	mutex_exit(&cpu_lock);
1290 
1291 	return (rv);
1292 }
1293 
1294 typedef struct {
1295 	md_t		*mdp;
1296 	mde_cookie_t	cpunode;
1297 	dev_info_t	*dip;
1298 } cb_arg_t;
1299 
1300 #define	STR_ARR_LEN	5
1301 
1302 static int
new_cpu_node(dev_info_t * new_node,void * arg,uint_t flags)1303 new_cpu_node(dev_info_t *new_node, void *arg, uint_t flags)
1304 {
1305 	_NOTE(ARGUNUSED(flags))
1306 
1307 	char		*compat;
1308 	uint64_t	freq;
1309 	uint64_t	cpuid = 0;
1310 	int		regbuf[4];
1311 	int		len = 0;
1312 	cb_arg_t	*cba;
1313 	char		*str_arr[STR_ARR_LEN];
1314 	char		*curr;
1315 	int		idx = 0;
1316 
1317 	DR_DBG_CPU("new_cpu_node...\n");
1318 
1319 	cba = (cb_arg_t *)arg;
1320 
1321 	/*
1322 	 * Add 'name' property
1323 	 */
1324 	if (ndi_prop_update_string(DDI_DEV_T_NONE, new_node,
1325 	    "name", "cpu") != DDI_SUCCESS) {
1326 		DR_DBG_CPU("new_cpu_node: failed to create 'name' property\n");
1327 		return (DDI_WALK_ERROR);
1328 	}
1329 
1330 	/*
1331 	 * Add 'compatible' property
1332 	 */
1333 	if (md_get_prop_data(cba->mdp, cba->cpunode, "compatible",
1334 	    (uint8_t **)(&compat), &len)) {
1335 		DR_DBG_CPU("new_cpu_node: failed to read 'compatible' property "
1336 		    "from MD\n");
1337 		return (DDI_WALK_ERROR);
1338 	}
1339 
1340 	DR_DBG_CPU("'compatible' len is %d\n", len);
1341 
1342 	/* parse the MD string array */
1343 	curr = compat;
1344 	while (curr < (compat + len)) {
1345 
1346 		DR_DBG_CPU("adding '%s' to 'compatible' property\n", curr);
1347 
1348 		str_arr[idx++] = curr;
1349 		curr += strlen(curr) + 1;
1350 
1351 		if (idx == STR_ARR_LEN) {
1352 			DR_DBG_CPU("exceeded str_arr len (%d)\n", STR_ARR_LEN);
1353 			break;
1354 		}
1355 	}
1356 
1357 	if (ndi_prop_update_string_array(DDI_DEV_T_NONE, new_node,
1358 	    "compatible", str_arr, idx) != DDI_SUCCESS) {
1359 		DR_DBG_CPU("new_cpu_node: failed to create 'compatible' "
1360 		    "property\n");
1361 		return (DDI_WALK_ERROR);
1362 	}
1363 
1364 	/*
1365 	 * Add 'device_type' property
1366 	 */
1367 	if (ndi_prop_update_string(DDI_DEV_T_NONE, new_node,
1368 	    "device_type", "cpu") != DDI_SUCCESS) {
1369 		DR_DBG_CPU("new_cpu_node: failed to create 'device_type' "
1370 		    "property\n");
1371 		return (DDI_WALK_ERROR);
1372 	}
1373 
1374 	/*
1375 	 * Add 'clock-frequency' property
1376 	 */
1377 	if (md_get_prop_val(cba->mdp, cba->cpunode, "clock-frequency", &freq)) {
1378 		DR_DBG_CPU("new_cpu_node: failed to read 'clock-frequency' "
1379 		    "property from MD\n");
1380 		return (DDI_WALK_ERROR);
1381 	}
1382 
1383 	if (ndi_prop_update_int(DDI_DEV_T_NONE, new_node,
1384 	    "clock-frequency", freq) != DDI_SUCCESS) {
1385 		DR_DBG_CPU("new_cpu_node: failed to create 'clock-frequency' "
1386 		    "property\n");
1387 		return (DDI_WALK_ERROR);
1388 	}
1389 
1390 	/*
1391 	 * Add 'reg' (cpuid) property
1392 	 */
1393 	if (md_get_prop_val(cba->mdp, cba->cpunode, "id", &cpuid)) {
1394 		DR_DBG_CPU("new_cpu_node: failed to read 'id' property "
1395 		    "from MD\n");
1396 		return (DDI_WALK_ERROR);
1397 	}
1398 
1399 	DR_DBG_CPU("new cpuid=0x%lx\n", cpuid);
1400 
1401 	bzero(regbuf, 4 * sizeof (int));
1402 	regbuf[0] = 0xc0000000 | cpuid;
1403 
1404 	if (ndi_prop_update_int_array(DDI_DEV_T_NONE, new_node,
1405 	    "reg", regbuf, 4) != DDI_SUCCESS) {
1406 		DR_DBG_CPU("new_cpu_node: failed to create 'reg' property\n");
1407 		return (DDI_WALK_ERROR);
1408 	}
1409 
1410 	cba->dip = new_node;
1411 
1412 	return (DDI_WALK_TERMINATE);
1413 }
1414 
1415 static int
dr_cpu_probe(processorid_t cpuid)1416 dr_cpu_probe(processorid_t cpuid)
1417 {
1418 	dev_info_t	*pdip;
1419 	dev_info_t	*dip;
1420 	devi_branch_t	br;
1421 	md_t		*mdp = NULL;
1422 	int		num_nodes;
1423 	int		rv = 0;
1424 	int		listsz;
1425 	mde_cookie_t	*listp = NULL;
1426 	cb_arg_t	cba;
1427 	mde_cookie_t	cpunode;
1428 
1429 	if ((dip = dr_cpu_find_node(cpuid)) != NULL) {
1430 		/* nothing to do */
1431 		e_ddi_branch_rele(dip);
1432 		return (0);
1433 	}
1434 
1435 	if ((mdp = md_get_handle()) == NULL) {
1436 		DR_DBG_CPU("unable to initialize machine description\n");
1437 		return (-1);
1438 	}
1439 
1440 	num_nodes = md_node_count(mdp);
1441 	ASSERT(num_nodes > 0);
1442 
1443 	listsz = num_nodes * sizeof (mde_cookie_t);
1444 	listp = kmem_zalloc(listsz, KM_SLEEP);
1445 	DR_DBG_KMEM("%s: alloc addr %p size %d\n",
1446 	    __func__, (void *)listp, listsz);
1447 
1448 	cpunode = dr_cpu_find_node_md(cpuid, mdp, listp);
1449 
1450 	if (cpunode == MDE_INVAL_ELEM_COOKIE) {
1451 		rv = EINVAL;
1452 		goto done;
1453 	}
1454 
1455 	/* pass in MD cookie for CPU */
1456 	cba.mdp = mdp;
1457 	cba.cpunode = cpunode;
1458 
1459 	br.arg = (void *)&cba;
1460 	br.type = DEVI_BRANCH_SID;
1461 	br.create.sid_branch_create = new_cpu_node;
1462 	br.devi_branch_callback = NULL;
1463 	pdip = ddi_root_node();
1464 
1465 	if ((rv = e_ddi_branch_create(pdip, &br, NULL, 0))) {
1466 		DR_DBG_CPU("e_ddi_branch_create failed: %d\n", rv);
1467 		rv = -1;
1468 		goto done;
1469 	}
1470 
1471 	DR_DBG_CPU("CPU %d probed\n", cpuid);
1472 
1473 	rv = 0;
1474 
1475 done:
1476 	if (listp) {
1477 		DR_DBG_KMEM("%s: free addr %p size %d\n",
1478 		    __func__, (void *)listp, listsz);
1479 		kmem_free(listp, listsz);
1480 	}
1481 
1482 	if (mdp)
1483 		(void) md_fini_handle(mdp);
1484 
1485 	return (rv);
1486 }
1487 
1488 static int
dr_cpu_deprobe(processorid_t cpuid)1489 dr_cpu_deprobe(processorid_t cpuid)
1490 {
1491 	dev_info_t	*fdip = NULL;
1492 	dev_info_t	*dip;
1493 
1494 	if ((dip = dr_cpu_find_node(cpuid)) == NULL) {
1495 		DR_DBG_CPU("cpuid %d already deprobed\n", cpuid);
1496 		return (0);
1497 	}
1498 
1499 	ASSERT(e_ddi_branch_held(dip));
1500 
1501 	if (e_ddi_branch_destroy(dip, &fdip, 0)) {
1502 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
1503 
1504 		DR_DBG_KMEM("%s: alloc addr %p size %d\n",
1505 		    __func__, (void *)path, MAXPATHLEN);
1506 		/*
1507 		 * If non-NULL, fdip is held and must be released.
1508 		 */
1509 		if (fdip != NULL) {
1510 			(void) ddi_pathname(fdip, path);
1511 			ddi_release_devi(fdip);
1512 		} else {
1513 			(void) ddi_pathname(dip, path);
1514 		}
1515 		cmn_err(CE_NOTE, "node removal failed: %s (%p)",
1516 		    path, (fdip) ? (void *)fdip : (void *)dip);
1517 
1518 		DR_DBG_KMEM("%s: free addr %p size %d\n",
1519 		    __func__, (void *)path, MAXPATHLEN);
1520 		kmem_free(path, MAXPATHLEN);
1521 
1522 		return (-1);
1523 	}
1524 
1525 	DR_DBG_CPU("CPU %d deprobed\n", cpuid);
1526 
1527 	return (0);
1528 }
1529 
1530 typedef struct {
1531 	processorid_t	cpuid;
1532 	dev_info_t	*dip;
1533 } dr_search_arg_t;
1534 
1535 static int
dr_cpu_check_node(dev_info_t * dip,void * arg)1536 dr_cpu_check_node(dev_info_t *dip, void *arg)
1537 {
1538 	char 		*name;
1539 	processorid_t	cpuid;
1540 	dr_search_arg_t	*sarg = (dr_search_arg_t *)arg;
1541 
1542 	if (dip == ddi_root_node()) {
1543 		return (DDI_WALK_CONTINUE);
1544 	}
1545 
1546 	name = ddi_node_name(dip);
1547 
1548 	if (strcmp(name, "cpu") != 0) {
1549 		return (DDI_WALK_PRUNECHILD);
1550 	}
1551 
1552 	cpuid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1553 	    "reg", -1);
1554 
1555 	cpuid = PROM_CFGHDL_TO_CPUID(cpuid);
1556 
1557 	DR_DBG_CPU("found cpuid=0x%x, looking for 0x%x\n", cpuid, sarg->cpuid);
1558 
1559 	if (cpuid == sarg->cpuid) {
1560 		DR_DBG_CPU("matching node\n");
1561 
1562 		/* matching node must be returned held */
1563 		if (!e_ddi_branch_held(dip))
1564 			e_ddi_branch_hold(dip);
1565 
1566 		sarg->dip = dip;
1567 		return (DDI_WALK_TERMINATE);
1568 	}
1569 
1570 	return (DDI_WALK_CONTINUE);
1571 }
1572 
1573 /*
1574  * Walk the device tree to find the dip corresponding to the cpuid
1575  * passed in. If present, the dip is returned held. The caller must
1576  * release the hold on the dip once it is no longer required. If no
1577  * matching node if found, NULL is returned.
1578  */
1579 static dev_info_t *
dr_cpu_find_node(processorid_t cpuid)1580 dr_cpu_find_node(processorid_t cpuid)
1581 {
1582 	dr_search_arg_t	arg;
1583 
1584 	DR_DBG_CPU("dr_cpu_find_node...\n");
1585 
1586 	arg.cpuid = cpuid;
1587 	arg.dip = NULL;
1588 
1589 	ddi_walk_devs(ddi_root_node(), dr_cpu_check_node, &arg);
1590 
1591 	ASSERT((arg.dip == NULL) || (e_ddi_branch_held(arg.dip)));
1592 
1593 	return ((arg.dip) ? arg.dip : NULL);
1594 }
1595 
1596 /*
1597  * Look up a particular cpuid in the MD. Returns the mde_cookie_t
1598  * representing that CPU if present, and MDE_INVAL_ELEM_COOKIE
1599  * otherwise. It is assumed the scratch array has already been
1600  * allocated so that it can accommodate the worst case scenario,
1601  * every node in the MD.
1602  */
1603 static mde_cookie_t
dr_cpu_find_node_md(processorid_t cpuid,md_t * mdp,mde_cookie_t * listp)1604 dr_cpu_find_node_md(processorid_t cpuid, md_t *mdp, mde_cookie_t *listp)
1605 {
1606 	int		idx;
1607 	int		nnodes;
1608 	mde_cookie_t	rootnode;
1609 	uint64_t	cpuid_prop;
1610 	mde_cookie_t	result = MDE_INVAL_ELEM_COOKIE;
1611 
1612 	rootnode = md_root_node(mdp);
1613 	ASSERT(rootnode != MDE_INVAL_ELEM_COOKIE);
1614 
1615 	/*
1616 	 * Scan the DAG for all the CPU nodes
1617 	 */
1618 	nnodes = md_scan_dag(mdp, rootnode, md_find_name(mdp, "cpu"),
1619 	    md_find_name(mdp, "fwd"), listp);
1620 
1621 	if (nnodes < 0) {
1622 		DR_DBG_CPU("Scan for CPUs failed\n");
1623 		return (result);
1624 	}
1625 
1626 	DR_DBG_CPU("dr_cpu_find_node_md: found %d CPUs in the MD\n", nnodes);
1627 
1628 	/*
1629 	 * Find the CPU of interest
1630 	 */
1631 	for (idx = 0; idx < nnodes; idx++) {
1632 
1633 		if (md_get_prop_val(mdp, listp[idx], "id", &cpuid_prop)) {
1634 			DR_DBG_CPU("Missing 'id' property for CPU node %d\n",
1635 			    idx);
1636 			break;
1637 		}
1638 
1639 		if (cpuid_prop == cpuid) {
1640 			/* found a match */
1641 			DR_DBG_CPU("dr_cpu_find_node_md: found CPU %d "
1642 			    "in MD\n", cpuid);
1643 			result = listp[idx];
1644 			break;
1645 		}
1646 	}
1647 
1648 	if (result == MDE_INVAL_ELEM_COOKIE) {
1649 		DR_DBG_CPU("CPU %d not in MD\n", cpuid);
1650 	}
1651 
1652 	return (result);
1653 }
1654