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  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Copyright 2019 Joyent, Inc.
28  */
29 
30 #include <sys/types.h>
31 #include <sys/param.h>
32 #include <sys/var.h>
33 #include <sys/thread.h>
34 #include <sys/cpuvar.h>
35 #include <sys/kstat.h>
36 #include <sys/uadmin.h>
37 #include <sys/systm.h>
38 #include <sys/errno.h>
39 #include <sys/cmn_err.h>
40 #include <sys/procset.h>
41 #include <sys/processor.h>
42 #include <sys/debug.h>
43 #include <sys/policy.h>
44 #include <sys/smt.h>
45 
46 /*
47  * CPU state diagram
48  *
49  *                  P_SPARE
50  * P_POWEROFF <---> P_OFFLINE <---> P_ONLINE <---> P_NOINTR
51  *                  P_FAULTED
52  *                  P_DISABLED
53  */
54 int
p_online_internal_locked(processorid_t cpun,int new_status,int * old_status)55 p_online_internal_locked(processorid_t cpun, int new_status, int *old_status)
56 {
57 	cpu_t	*cp;
58 	int	status;
59 	int	error = 0;
60 	int	flags = 0;
61 
62 	ASSERT(MUTEX_HELD(&cpu_lock));
63 
64 	if (cpun == P_ALL_SIBLINGS) {
65 		if (new_status != P_DISABLED) {
66 			error = EINVAL;
67 			goto out;
68 		}
69 
70 		return (smt_disable());
71 	}
72 
73 	if ((cp = cpu_get(cpun)) == NULL) {
74 		error = EINVAL;
75 		goto out;
76 	}
77 
78 	if (new_status & P_FORCED)
79 		flags = CPU_FORCED;
80 	*old_status = status = cpu_get_state(cp); /* get processor status */
81 	new_status &= ~P_FORCED;
82 
83 	/*
84 	 * Perform credentials check.
85 	 */
86 	switch (new_status) {
87 	case P_STATUS:
88 		goto out;
89 	case P_ONLINE:
90 	case P_OFFLINE:
91 	case P_NOINTR:
92 	case P_FAULTED:
93 	case P_SPARE:
94 		if (secpolicy_ponline(CRED()) != 0)
95 			error = EPERM;
96 		break;
97 	case P_DISABLED:
98 	default:
99 		error = EINVAL;
100 		break;
101 	}
102 
103 	if (error)
104 		goto out;
105 
106 	/*
107 	 * return 0 if the CPU is already in the desired new state.
108 	 */
109 	if (status == new_status)
110 		goto out;
111 
112 	switch (new_status) {
113 	case P_ONLINE:
114 		switch (status) {
115 		case P_POWEROFF:
116 			/*
117 			 * If CPU is powered off, power it on.
118 			 */
119 			if (error = cpu_poweron(cp))
120 				break;
121 			ASSERT(cpu_get_state(cp) == P_OFFLINE);
122 			/* FALLTHROUGH */
123 		case P_DISABLED:
124 		case P_OFFLINE:
125 		case P_FAULTED:
126 		case P_SPARE:
127 			/*
128 			 * If CPU is in one of the offline states,
129 			 * bring it online.
130 			 */
131 			error = cpu_online(cp, flags);
132 			break;
133 		case P_NOINTR:
134 			cpu_intr_enable(cp);
135 			break;
136 		}
137 		break;
138 
139 	case P_OFFLINE:
140 		switch (status) {
141 		case P_NOINTR:
142 			/*
143 			 * Before we take the CPU offline, we first enable I/O
144 			 * interrupts.
145 			 */
146 			cpu_intr_enable(cp);
147 			/* FALLTHROUGH */
148 		case P_ONLINE:
149 		case P_DISABLED:
150 		case P_FAULTED:
151 		case P_SPARE:
152 			/*
153 			 * CPU is online, or in a special offline state.
154 			 * Take it offline.
155 			 */
156 			error = cpu_offline(cp, flags);
157 			break;
158 		case P_POWEROFF:
159 			/*
160 			 * If CPU is powered off, power it on.
161 			 */
162 			error = cpu_poweron(cp);
163 			break;
164 		}
165 		break;
166 
167 	case P_NOINTR:
168 		switch (status) {
169 		case P_POWEROFF:
170 			/*
171 			 * if CPU is powered off, power it on.
172 			 */
173 			if (error = cpu_poweron(cp))
174 				break;
175 			ASSERT(cpu_get_state(cp) == P_OFFLINE);
176 			/* FALLTHROUGH */
177 		case P_DISABLED:
178 		case P_OFFLINE:
179 		case P_FAULTED:
180 		case P_SPARE:
181 			/*
182 			 * First, bring the CPU online.
183 			 */
184 			if (error = cpu_online(cp, flags))
185 				break;
186 			/* FALLTHROUGH */
187 		case P_ONLINE:
188 			/*
189 			 * CPU is now online.  Try to disable interrupts.
190 			 */
191 			error = cpu_intr_disable(cp);
192 			break;
193 		}
194 		break;
195 
196 	case P_FAULTED:
197 		switch (status) {
198 		case P_POWEROFF:
199 			/*
200 			 * If CPU is powered off, power it on.
201 			 */
202 			if (error = cpu_poweron(cp))
203 				break;
204 			ASSERT(cpu_get_state(cp) == P_OFFLINE);
205 			/*FALLTHROUGH*/
206 		case P_DISABLED:
207 		case P_OFFLINE:
208 		case P_ONLINE:
209 		case P_NOINTR:
210 		case P_SPARE:
211 			/*
212 			 * Mark this CPU as faulted.
213 			 */
214 			error = cpu_faulted(cp, flags);
215 			break;
216 		}
217 		break;
218 
219 	case P_SPARE:
220 		switch (status) {
221 		case P_POWEROFF:
222 			/*
223 			 * If CPU is powered off, power it on.
224 			 */
225 			if (error = cpu_poweron(cp))
226 				break;
227 			ASSERT(cpu_get_state(cp) == P_OFFLINE);
228 			/*FALLTHROUGH*/
229 		case P_DISABLED:
230 		case P_OFFLINE:
231 		case P_FAULTED:
232 		case P_ONLINE:
233 		case P_NOINTR:
234 			/*
235 			 * Mark this CPU as a spare.
236 			 */
237 			error = cpu_spare(cp, flags);
238 			break;
239 		}
240 		break;
241 	}
242 out:
243 	return (error);
244 }
245 
246 int
p_online_internal(processorid_t cpun,int new_status,int * old_status)247 p_online_internal(processorid_t cpun, int new_status, int *old_status)
248 {
249 	int rc;
250 
251 	mutex_enter(&cpu_lock);		/* protects CPU states */
252 	rc = p_online_internal_locked(cpun, new_status, old_status);
253 	mutex_exit(&cpu_lock);
254 
255 	return (rc);
256 }
257 
258 /*
259  * p_online(2) - get/change processor operational status.
260  *
261  *   As noted in os/cpu.c, the P_ONLINE and other state constants are for use
262  *   only in this system call path and other paths conveying CPU state to
263  *   userland.  In general, other kernel consumers should be using the accessor
264  *   functions in uts/common/os/cpu.c.
265  */
266 int
p_online(processorid_t cpun,int new_status)267 p_online(processorid_t cpun, int new_status)
268 {
269 	int ret;
270 	int old_status;
271 
272 	ret = p_online_internal(cpun, new_status, &old_status);
273 	if (ret != 0)
274 		return (set_errno(ret));
275 	return (old_status);
276 }
277