1 /* $NetBSD: kern_cpu.c,v 1.26 2008/04/12 17:16:09 ad Exp $ */ 2 3 /*- 4 * Copyright (c) 2007, 2008 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Andrew Doran. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /*- 40 * Copyright (c)2007 YAMAMOTO Takashi, 41 * All rights reserved. 42 * 43 * Redistribution and use in source and binary forms, with or without 44 * modification, are permitted provided that the following conditions 45 * are met: 46 * 1. Redistributions of source code must retain the above copyright 47 * notice, this list of conditions and the following disclaimer. 48 * 2. Redistributions in binary form must reproduce the above copyright 49 * notice, this list of conditions and the following disclaimer in the 50 * documentation and/or other materials provided with the distribution. 51 * 52 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 55 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 62 * SUCH DAMAGE. 63 */ 64 65 #include <sys/cdefs.h> 66 67 __KERNEL_RCSID(0, "$NetBSD: kern_cpu.c,v 1.26 2008/04/12 17:16:09 ad Exp $"); 68 69 #include <sys/param.h> 70 #include <sys/systm.h> 71 #include <sys/idle.h> 72 #include <sys/sched.h> 73 #include <sys/intr.h> 74 #include <sys/conf.h> 75 #include <sys/cpu.h> 76 #include <sys/cpuio.h> 77 #include <sys/proc.h> 78 #include <sys/percpu.h> 79 #include <sys/kernel.h> 80 #include <sys/kauth.h> 81 #include <sys/xcall.h> 82 #include <sys/pool.h> 83 #include <sys/kmem.h> 84 #include <sys/select.h> 85 #include <sys/namei.h> 86 87 #include <uvm/uvm_extern.h> 88 89 void cpuctlattach(int); 90 91 static void cpu_xc_online(struct cpu_info *); 92 static void cpu_xc_offline(struct cpu_info *); 93 94 dev_type_ioctl(cpuctl_ioctl); 95 96 const struct cdevsw cpuctl_cdevsw = { 97 nullopen, nullclose, nullread, nullwrite, cpuctl_ioctl, 98 nullstop, notty, nopoll, nommap, nokqfilter, 99 D_OTHER | D_MPSAFE 100 }; 101 102 kmutex_t cpu_lock; 103 int ncpu; 104 int ncpuonline; 105 bool mp_online; 106 struct cpuqueue cpu_queue = CIRCLEQ_HEAD_INITIALIZER(cpu_queue); 107 108 static struct cpu_info *cpu_infos[MAXCPUS]; 109 110 int 111 mi_cpu_attach(struct cpu_info *ci) 112 { 113 int error; 114 115 ci->ci_index = ncpu; 116 cpu_infos[cpu_index(ci)] = ci; 117 CIRCLEQ_INSERT_TAIL(&cpu_queue, ci, ci_data.cpu_qchain); 118 119 sched_cpuattach(ci); 120 uvm_cpu_attach(ci); 121 122 error = create_idle_lwp(ci); 123 if (error != 0) { 124 /* XXX revert sched_cpuattach */ 125 return error; 126 } 127 128 if (ci == curcpu()) 129 ci->ci_data.cpu_onproc = curlwp; 130 else 131 ci->ci_data.cpu_onproc = ci->ci_data.cpu_idlelwp; 132 133 percpu_init_cpu(ci); 134 softint_init(ci); 135 xc_init_cpu(ci); 136 pool_cache_cpu_init(ci); 137 selsysinit(ci); 138 cache_cpu_init(ci); 139 TAILQ_INIT(&ci->ci_data.cpu_biodone); 140 ncpu++; 141 ncpuonline++; 142 143 return 0; 144 } 145 146 void 147 cpuctlattach(int dummy) 148 { 149 150 } 151 152 int 153 cpuctl_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l) 154 { 155 CPU_INFO_ITERATOR cii; 156 cpustate_t *cs; 157 struct cpu_info *ci; 158 int error, i; 159 u_int id; 160 161 error = 0; 162 163 mutex_enter(&cpu_lock); 164 switch (cmd) { 165 case IOC_CPU_SETSTATE: 166 cs = data; 167 error = kauth_authorize_system(l->l_cred, 168 KAUTH_SYSTEM_CPU, KAUTH_REQ_SYSTEM_CPU_SETSTATE, cs, NULL, 169 NULL); 170 if (error != 0) 171 break; 172 if ((ci = cpu_lookup(cs->cs_id)) == NULL) { 173 error = ESRCH; 174 break; 175 } 176 if (!cs->cs_intr) { 177 error = EOPNOTSUPP; 178 break; 179 } 180 error = cpu_setonline(ci, cs->cs_online); 181 break; 182 183 case IOC_CPU_GETSTATE: 184 cs = data; 185 id = cs->cs_id; 186 memset(cs, 0, sizeof(*cs)); 187 cs->cs_id = id; 188 if ((ci = cpu_lookup(id)) == NULL) { 189 error = ESRCH; 190 break; 191 } 192 if ((ci->ci_schedstate.spc_flags & SPCF_OFFLINE) != 0) 193 cs->cs_online = false; 194 else 195 cs->cs_online = true; 196 cs->cs_intr = true; 197 cs->cs_lastmod = ci->ci_schedstate.spc_lastmod; 198 break; 199 200 case IOC_CPU_MAPID: 201 i = 0; 202 for (CPU_INFO_FOREACH(cii, ci)) { 203 if (i++ == *(int *)data) 204 break; 205 } 206 if (ci == NULL) 207 error = ESRCH; 208 else 209 *(int *)data = ci->ci_cpuid; 210 break; 211 212 case IOC_CPU_GETCOUNT: 213 *(int *)data = ncpu; 214 break; 215 216 default: 217 error = ENOTTY; 218 break; 219 } 220 mutex_exit(&cpu_lock); 221 222 return error; 223 } 224 225 struct cpu_info * 226 cpu_lookup(cpuid_t id) 227 { 228 CPU_INFO_ITERATOR cii; 229 struct cpu_info *ci; 230 231 for (CPU_INFO_FOREACH(cii, ci)) { 232 if (ci->ci_cpuid == id) 233 return ci; 234 } 235 236 return NULL; 237 } 238 239 struct cpu_info * 240 cpu_lookup_byindex(u_int idx) 241 { 242 struct cpu_info *ci = cpu_infos[idx]; 243 244 KASSERT(idx < MAXCPUS); 245 KASSERT(ci == NULL || cpu_index(ci) == idx); 246 247 return ci; 248 } 249 250 static void 251 cpu_xc_offline(struct cpu_info *ci) 252 { 253 struct schedstate_percpu *spc, *mspc = NULL; 254 struct cpu_info *mci; 255 struct lwp *l; 256 CPU_INFO_ITERATOR cii; 257 int s; 258 259 spc = &ci->ci_schedstate; 260 s = splsched(); 261 spc->spc_flags |= SPCF_OFFLINE; 262 splx(s); 263 264 /* Take the first available CPU for the migration */ 265 for (CPU_INFO_FOREACH(cii, mci)) { 266 mspc = &mci->ci_schedstate; 267 if ((mspc->spc_flags & SPCF_OFFLINE) == 0) 268 break; 269 } 270 KASSERT(mci != NULL); 271 272 /* 273 * Migrate all non-bound threads to the other CPU. 274 * Please note, that this runs from the xcall thread, thus handling 275 * of LSONPROC is not needed. 276 */ 277 mutex_enter(&proclist_lock); 278 279 /* 280 * Note that threads on the runqueue might sleep after this, but 281 * sched_takecpu() would migrate such threads to the appropriate CPU. 282 */ 283 LIST_FOREACH(l, &alllwp, l_list) { 284 lwp_lock(l); 285 if (l->l_cpu == ci && (l->l_stat == LSSLEEP || 286 l->l_stat == LSSTOP || l->l_stat == LSSUSPENDED)) { 287 KASSERT((l->l_flag & LW_RUNNING) == 0); 288 l->l_cpu = mci; 289 } 290 lwp_unlock(l); 291 } 292 293 /* Double-lock the run-queues */ 294 spc_dlock(ci, mci); 295 296 /* Handle LSRUN and LSIDL cases */ 297 LIST_FOREACH(l, &alllwp, l_list) { 298 if (l->l_cpu != ci || (l->l_pflag & LP_BOUND)) 299 continue; 300 if (l->l_stat == LSRUN && (l->l_flag & LW_INMEM) != 0) { 301 sched_dequeue(l); 302 l->l_cpu = mci; 303 lwp_setlock(l, mspc->spc_mutex); 304 sched_enqueue(l, false); 305 } else if (l->l_stat == LSRUN || l->l_stat == LSIDL) { 306 l->l_cpu = mci; 307 lwp_setlock(l, mspc->spc_mutex); 308 } 309 } 310 spc_dunlock(ci, mci); 311 mutex_exit(&proclist_lock); 312 313 #ifdef __HAVE_MD_CPU_OFFLINE 314 cpu_offline_md(); 315 #endif 316 } 317 318 static void 319 cpu_xc_online(struct cpu_info *ci) 320 { 321 struct schedstate_percpu *spc; 322 int s; 323 324 spc = &ci->ci_schedstate; 325 s = splsched(); 326 spc->spc_flags &= ~SPCF_OFFLINE; 327 splx(s); 328 } 329 330 int 331 cpu_setonline(struct cpu_info *ci, bool online) 332 { 333 struct schedstate_percpu *spc; 334 CPU_INFO_ITERATOR cii; 335 struct cpu_info *ci2; 336 uint64_t where; 337 xcfunc_t func; 338 int nonline; 339 340 spc = &ci->ci_schedstate; 341 342 KASSERT(mutex_owned(&cpu_lock)); 343 344 if (online) { 345 if ((spc->spc_flags & SPCF_OFFLINE) == 0) 346 return 0; 347 func = (xcfunc_t)cpu_xc_online; 348 ncpuonline++; 349 } else { 350 if ((spc->spc_flags & SPCF_OFFLINE) != 0) 351 return 0; 352 nonline = 0; 353 for (CPU_INFO_FOREACH(cii, ci2)) { 354 nonline += ((ci2->ci_schedstate.spc_flags & 355 SPCF_OFFLINE) == 0); 356 } 357 if (nonline == 1) 358 return EBUSY; 359 func = (xcfunc_t)cpu_xc_offline; 360 ncpuonline--; 361 } 362 363 where = xc_unicast(0, func, ci, NULL, ci); 364 xc_wait(where); 365 if (online) { 366 KASSERT((spc->spc_flags & SPCF_OFFLINE) == 0); 367 } else { 368 KASSERT(spc->spc_flags & SPCF_OFFLINE); 369 } 370 spc->spc_lastmod = time_second; 371 372 return 0; 373 } 374