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 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 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 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