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