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