1 /* $NetBSD: kern_cpu.c,v 1.21 2008/02/14 14:26:57 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.21 2008/02/14 14:26:57 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 85 #include <uvm/uvm_extern.h> 86 87 void cpuctlattach(int); 88 89 static void cpu_xc_online(struct cpu_info *); 90 static void cpu_xc_offline(struct cpu_info *); 91 92 dev_type_ioctl(cpuctl_ioctl); 93 94 const struct cdevsw cpuctl_cdevsw = { 95 nullopen, nullclose, nullread, nullwrite, cpuctl_ioctl, 96 nullstop, notty, nopoll, nommap, nokqfilter, 97 D_OTHER | D_MPSAFE 98 }; 99 100 kmutex_t cpu_lock; 101 int ncpu; 102 int ncpuonline; 103 bool mp_online; 104 105 static struct cpu_info *cpu_infos[MAXCPUS]; 106 107 int 108 mi_cpu_attach(struct cpu_info *ci) 109 { 110 struct schedstate_percpu *spc = &ci->ci_schedstate; 111 int error; 112 113 ci->ci_index = ncpu; 114 115 KASSERT(sizeof(kmutex_t) <= CACHE_LINE_SIZE); 116 spc->spc_lwplock = kmem_alloc(CACHE_LINE_SIZE, KM_SLEEP); 117 mutex_init(spc->spc_lwplock, MUTEX_DEFAULT, IPL_SCHED); 118 sched_cpuattach(ci); 119 uvm_cpu_attach(ci); 120 121 error = create_idle_lwp(ci); 122 if (error != 0) { 123 /* XXX revert sched_cpuattach */ 124 return error; 125 } 126 127 if (ci == curcpu()) 128 ci->ci_data.cpu_onproc = curlwp; 129 else 130 ci->ci_data.cpu_onproc = ci->ci_data.cpu_idlelwp; 131 132 percpu_init_cpu(ci); 133 softint_init(ci); 134 xc_init_cpu(ci); 135 pool_cache_cpu_init(ci); 136 TAILQ_INIT(&ci->ci_data.cpu_biodone); 137 ncpu++; 138 ncpuonline++; 139 cpu_infos[cpu_index(ci)] = ci; 140 141 return 0; 142 } 143 144 void 145 cpuctlattach(int dummy) 146 { 147 148 } 149 150 int 151 cpuctl_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l) 152 { 153 CPU_INFO_ITERATOR cii; 154 cpustate_t *cs; 155 struct cpu_info *ci; 156 int error, i; 157 u_int id; 158 159 error = 0; 160 161 mutex_enter(&cpu_lock); 162 switch (cmd) { 163 case IOC_CPU_SETSTATE: 164 cs = data; 165 error = kauth_authorize_system(l->l_cred, 166 KAUTH_SYSTEM_CPU, KAUTH_REQ_SYSTEM_CPU_SETSTATE, cs, NULL, 167 NULL); 168 if (error != 0) 169 break; 170 if ((ci = cpu_lookup(cs->cs_id)) == NULL) { 171 error = ESRCH; 172 break; 173 } 174 if (!cs->cs_intr) { 175 error = EOPNOTSUPP; 176 break; 177 } 178 error = cpu_setonline(ci, cs->cs_online); 179 break; 180 181 case IOC_CPU_GETSTATE: 182 cs = data; 183 id = cs->cs_id; 184 memset(cs, 0, sizeof(*cs)); 185 cs->cs_id = id; 186 if ((ci = cpu_lookup(id)) == NULL) { 187 error = ESRCH; 188 break; 189 } 190 if ((ci->ci_schedstate.spc_flags & SPCF_OFFLINE) != 0) 191 cs->cs_online = false; 192 else 193 cs->cs_online = true; 194 cs->cs_intr = true; 195 cs->cs_lastmod = ci->ci_schedstate.spc_lastmod; 196 break; 197 198 case IOC_CPU_MAPID: 199 i = 0; 200 for (CPU_INFO_FOREACH(cii, ci)) { 201 if (i++ == *(int *)data) 202 break; 203 } 204 if (ci == NULL) 205 error = ESRCH; 206 else 207 *(int *)data = ci->ci_cpuid; 208 break; 209 210 case IOC_CPU_GETCOUNT: 211 *(int *)data = ncpu; 212 break; 213 214 default: 215 error = ENOTTY; 216 break; 217 } 218 mutex_exit(&cpu_lock); 219 220 return error; 221 } 222 223 struct cpu_info * 224 cpu_lookup(cpuid_t id) 225 { 226 CPU_INFO_ITERATOR cii; 227 struct cpu_info *ci; 228 229 for (CPU_INFO_FOREACH(cii, ci)) { 230 if (ci->ci_cpuid == id) 231 return ci; 232 } 233 234 return NULL; 235 } 236 237 struct cpu_info * 238 cpu_lookup_byindex(u_int idx) 239 { 240 struct cpu_info *ci = cpu_infos[idx]; 241 242 KASSERT(idx < MAXCPUS); 243 KASSERT(ci == NULL || cpu_index(ci) == idx); 244 245 return ci; 246 } 247 248 static void 249 cpu_xc_offline(struct cpu_info *ci) 250 { 251 struct schedstate_percpu *spc, *mspc = NULL; 252 struct cpu_info *mci; 253 struct lwp *l; 254 CPU_INFO_ITERATOR cii; 255 int s; 256 257 spc = &ci->ci_schedstate; 258 s = splsched(); 259 spc->spc_flags |= SPCF_OFFLINE; 260 splx(s); 261 262 /* Take the first available CPU for the migration */ 263 for (CPU_INFO_FOREACH(cii, mci)) { 264 mspc = &mci->ci_schedstate; 265 if ((mspc->spc_flags & SPCF_OFFLINE) == 0) 266 break; 267 } 268 KASSERT(mci != NULL); 269 270 /* 271 * Migrate all non-bound threads to the other CPU. 272 * Please note, that this runs from the xcall thread, thus handling 273 * of LSONPROC is not needed. 274 */ 275 mutex_enter(&proclist_lock); 276 277 /* 278 * Note that threads on the runqueue might sleep after this, but 279 * sched_takecpu() would migrate such threads to the appropriate CPU. 280 */ 281 LIST_FOREACH(l, &alllwp, l_list) { 282 lwp_lock(l); 283 if (l->l_cpu == ci && (l->l_stat == LSSLEEP || 284 l->l_stat == LSSTOP || l->l_stat == LSSUSPENDED)) { 285 KASSERT((l->l_flag & LW_RUNNING) == 0); 286 l->l_cpu = mci; 287 } 288 lwp_unlock(l); 289 } 290 291 /* Double-lock the run-queues */ 292 spc_dlock(ci, mci); 293 294 /* Handle LSRUN and LSIDL cases */ 295 LIST_FOREACH(l, &alllwp, l_list) { 296 if (l->l_cpu != ci || (l->l_flag & LW_BOUND)) 297 continue; 298 if (l->l_stat == LSRUN && (l->l_flag & LW_INMEM) != 0) { 299 sched_dequeue(l); 300 l->l_cpu = mci; 301 lwp_setlock(l, mspc->spc_mutex); 302 sched_enqueue(l, false); 303 } else if (l->l_stat == LSRUN || l->l_stat == LSIDL) { 304 l->l_cpu = mci; 305 lwp_setlock(l, mspc->spc_mutex); 306 } 307 } 308 spc_dunlock(ci, mci); 309 mutex_exit(&proclist_lock); 310 311 #ifdef __HAVE_MD_CPU_OFFLINE 312 cpu_offline_md(); 313 #endif 314 } 315 316 static void 317 cpu_xc_online(struct cpu_info *ci) 318 { 319 struct schedstate_percpu *spc; 320 int s; 321 322 spc = &ci->ci_schedstate; 323 s = splsched(); 324 spc->spc_flags &= ~SPCF_OFFLINE; 325 splx(s); 326 } 327 328 int 329 cpu_setonline(struct cpu_info *ci, bool online) 330 { 331 struct schedstate_percpu *spc; 332 CPU_INFO_ITERATOR cii; 333 struct cpu_info *ci2; 334 uint64_t where; 335 xcfunc_t func; 336 int nonline; 337 338 spc = &ci->ci_schedstate; 339 340 KASSERT(mutex_owned(&cpu_lock)); 341 342 if (online) { 343 if ((spc->spc_flags & SPCF_OFFLINE) == 0) 344 return 0; 345 func = (xcfunc_t)cpu_xc_online; 346 ncpuonline++; 347 } else { 348 if ((spc->spc_flags & SPCF_OFFLINE) != 0) 349 return 0; 350 nonline = 0; 351 for (CPU_INFO_FOREACH(cii, ci2)) { 352 nonline += ((ci2->ci_schedstate.spc_flags & 353 SPCF_OFFLINE) == 0); 354 } 355 if (nonline == 1) 356 return EBUSY; 357 func = (xcfunc_t)cpu_xc_offline; 358 ncpuonline--; 359 } 360 361 where = xc_unicast(0, func, ci, NULL, ci); 362 xc_wait(where); 363 if (online) { 364 KASSERT((spc->spc_flags & SPCF_OFFLINE) == 0); 365 } else { 366 KASSERT(spc->spc_flags & SPCF_OFFLINE); 367 } 368 spc->spc_lastmod = time_second; 369 370 return 0; 371 } 372