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 2009 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/task.h>
40 #include <sys/project.h>
41 #include <sys/zone.h>
42 #include <sys/contract_impl.h>
43 #include <sys/contract/process_impl.h>
44
45 /*
46 * Bind all the threads of a process to a CPU.
47 */
48 static int
cpu_bind_process(proc_t * pp,processorid_t bind,processorid_t * obind,int * error)49 cpu_bind_process(proc_t *pp, processorid_t bind, processorid_t *obind,
50 int *error)
51 {
52 kthread_t *tp;
53 kthread_t *fp;
54 int err = 0;
55 int i;
56
57 ASSERT(MUTEX_HELD(&pidlock));
58
59 /* skip kernel processes */
60 if (pp->p_flag & SSYS) {
61 *obind = PBIND_NONE;
62 *error = ENOTSUP;
63 return (0);
64 }
65
66 mutex_enter(&pp->p_lock);
67 tp = pp->p_tlist;
68 if (tp != NULL) {
69 fp = tp;
70 do {
71 i = cpu_bind_thread(tp, bind, obind, error);
72 if (err == 0)
73 err = i;
74 } while ((tp = tp->t_forw) != fp);
75 }
76
77 mutex_exit(&pp->p_lock);
78 return (err);
79 }
80
81 /*
82 * Bind all the processes of a task to a CPU.
83 */
84 static int
cpu_bind_task(task_t * tk,processorid_t bind,processorid_t * obind,int * error)85 cpu_bind_task(task_t *tk, processorid_t bind, processorid_t *obind,
86 int *error)
87 {
88 proc_t *p;
89 int err = 0;
90 int i;
91
92 ASSERT(MUTEX_HELD(&pidlock));
93
94 if ((p = tk->tk_memb_list) == NULL)
95 return (ESRCH);
96
97 do {
98 if (!(p->p_flag & SSYS)) {
99 i = cpu_bind_process(p, bind, obind, error);
100 if (err == 0)
101 err = i;
102 }
103 } while ((p = p->p_tasknext) != tk->tk_memb_list);
104
105 return (err);
106 }
107
108 /*
109 * Bind all the processes in a project to a CPU.
110 */
111 static int
cpu_bind_project(kproject_t * kpj,processorid_t bind,processorid_t * obind,int * error)112 cpu_bind_project(kproject_t *kpj, processorid_t bind, processorid_t *obind,
113 int *error)
114 {
115 proc_t *p;
116 int err = 0;
117 int i;
118
119 ASSERT(MUTEX_HELD(&pidlock));
120
121 for (p = practive; p != NULL; p = p->p_next) {
122 if (p->p_tlist == NULL)
123 continue;
124 if (p->p_task->tk_proj == kpj && !(p->p_flag & SSYS)) {
125 i = cpu_bind_process(p, bind, obind, error);
126 if (err == 0)
127 err = i;
128 }
129 }
130 return (err);
131 }
132
133 /*
134 * Bind all the processes in a zone to a CPU.
135 */
136 int
cpu_bind_zone(zone_t * zptr,processorid_t bind,processorid_t * obind,int * error)137 cpu_bind_zone(zone_t *zptr, processorid_t bind, processorid_t *obind,
138 int *error)
139 {
140 proc_t *p;
141 int err = 0;
142 int i;
143
144 ASSERT(MUTEX_HELD(&pidlock));
145
146 for (p = practive; p != NULL; p = p->p_next) {
147 if (p->p_tlist == NULL)
148 continue;
149 if (p->p_zone == zptr && !(p->p_flag & SSYS)) {
150 i = cpu_bind_process(p, bind, obind, error);
151 if (err == 0)
152 err = i;
153 }
154 }
155 return (err);
156 }
157
158 /*
159 * Bind all the processes in a process contract to a CPU.
160 */
161 int
cpu_bind_contract(cont_process_t * ctp,processorid_t bind,processorid_t * obind,int * error)162 cpu_bind_contract(cont_process_t *ctp, processorid_t bind, processorid_t *obind,
163 int *error)
164 {
165 proc_t *p;
166 int err = 0;
167 int i;
168
169 ASSERT(MUTEX_HELD(&pidlock));
170
171 for (p = practive; p != NULL; p = p->p_next) {
172 if (p->p_tlist == NULL)
173 continue;
174 if (p->p_ct_process == ctp) {
175 i = cpu_bind_process(p, bind, obind, error);
176 if (err == 0)
177 err = i;
178 }
179 }
180 return (err);
181 }
182
183 /*
184 * processor_bind(2) - Processor binding interfaces.
185 */
186 int
processor_bind(idtype_t idtype,id_t id,processorid_t bind,processorid_t * obindp)187 processor_bind(idtype_t idtype, id_t id, processorid_t bind,
188 processorid_t *obindp)
189 {
190 processorid_t obind = PBIND_NONE;
191 int ret = 0;
192 int err = 0;
193 cpu_t *cp;
194 kthread_id_t tp;
195 proc_t *pp;
196 task_t *tk;
197 kproject_t *kpj;
198 zone_t *zptr;
199 contract_t *ct;
200
201 /*
202 * Since we might be making a binding to a processor, hold the
203 * cpu_lock so that the processor cannot be taken offline while
204 * we do this.
205 */
206 mutex_enter(&cpu_lock);
207
208 /*
209 * Check to be sure binding processor ID is valid.
210 */
211 switch (bind) {
212 default:
213 if ((cp = cpu_get(bind)) == NULL ||
214 (cp->cpu_flags & (CPU_QUIESCED | CPU_OFFLINE)))
215 ret = EINVAL;
216 else if ((cp->cpu_flags & CPU_READY) == 0)
217 ret = EIO;
218 break;
219
220 case PBIND_NONE:
221 case PBIND_QUERY:
222 case PBIND_HARD:
223 case PBIND_SOFT:
224 case PBIND_QUERY_TYPE:
225 break;
226 }
227
228 if (ret) {
229 mutex_exit(&cpu_lock);
230 return (set_errno(ret));
231 }
232
233 switch (idtype) {
234 case P_LWPID:
235 pp = curproc;
236 mutex_enter(&pp->p_lock);
237 if (id == P_MYID) {
238 ret = cpu_bind_thread(curthread, bind, &obind, &err);
239 } else {
240 int found = 0;
241
242 tp = pp->p_tlist;
243 do {
244 if (tp->t_tid == id) {
245 ret = cpu_bind_thread(tp,
246 bind, &obind, &err);
247 found = 1;
248 break;
249 }
250 } while ((tp = tp->t_forw) != pp->p_tlist);
251 if (!found)
252 ret = ESRCH;
253 }
254 mutex_exit(&pp->p_lock);
255 break;
256
257 case P_PID:
258 /*
259 * Note. Cannot use dotoprocs here because it doesn't find
260 * system class processes, which are legal to query.
261 */
262 mutex_enter(&pidlock);
263 if (id == P_MYID) {
264 ret = cpu_bind_process(curproc, bind, &obind, &err);
265 } else if ((pp = prfind(id)) != NULL) {
266 ret = cpu_bind_process(pp, bind, &obind, &err);
267 } else {
268 ret = ESRCH;
269 }
270 mutex_exit(&pidlock);
271 break;
272
273 case P_TASKID:
274 mutex_enter(&pidlock);
275 if (id == P_MYID) {
276 proc_t *p = curproc;
277 id = p->p_task->tk_tkid;
278 }
279
280 if ((tk = task_hold_by_id(id)) != NULL) {
281 ret = cpu_bind_task(tk, bind, &obind, &err);
282 mutex_exit(&pidlock);
283 task_rele(tk);
284 } else {
285 mutex_exit(&pidlock);
286 ret = ESRCH;
287 }
288 break;
289
290 case P_PROJID:
291 pp = curproc;
292 if (id == P_MYID)
293 id = curprojid();
294 if ((kpj = project_hold_by_id(id, pp->p_zone,
295 PROJECT_HOLD_FIND)) == NULL) {
296 ret = ESRCH;
297 } else {
298 mutex_enter(&pidlock);
299 ret = cpu_bind_project(kpj, bind, &obind, &err);
300 mutex_exit(&pidlock);
301 project_rele(kpj);
302 }
303 break;
304
305 case P_ZONEID:
306 if (id == P_MYID)
307 id = getzoneid();
308
309 if ((zptr = zone_find_by_id(id)) == NULL) {
310 ret = ESRCH;
311 } else {
312 mutex_enter(&pidlock);
313 ret = cpu_bind_zone(zptr, bind, &obind, &err);
314 mutex_exit(&pidlock);
315 zone_rele(zptr);
316 }
317 break;
318
319 case P_CTID:
320 if (id == P_MYID)
321 id = PRCTID(curproc);
322
323 if ((ct = contract_type_ptr(process_type, id,
324 curproc->p_zone->zone_uniqid)) == NULL) {
325 ret = ESRCH;
326 } else {
327 mutex_enter(&pidlock);
328 ret = cpu_bind_contract(ct->ct_data,
329 bind, &obind, &err);
330 mutex_exit(&pidlock);
331 contract_rele(ct);
332 }
333 break;
334
335 case P_CPUID:
336 if (id == P_MYID || bind != PBIND_NONE || cpu_get(id) == NULL)
337 ret = EINVAL;
338 else
339 ret = cpu_unbind(id, B_TRUE);
340 break;
341
342 case P_ALL:
343 if (id == P_MYID || bind != PBIND_NONE) {
344 ret = EINVAL;
345 } else {
346 int i;
347 cpu_t *cp = cpu_list;
348 do {
349 if ((cp->cpu_flags & CPU_EXISTS) == 0)
350 continue;
351 i = cpu_unbind(cp->cpu_id, B_TRUE);
352 if (ret == 0)
353 ret = i;
354 } while ((cp = cp->cpu_next) != cpu_list);
355 }
356 break;
357
358 default:
359 /*
360 * Spec says this is invalid, even though we could
361 * handle other idtypes.
362 */
363 ret = EINVAL;
364 break;
365 }
366 mutex_exit(&cpu_lock);
367
368 /*
369 * If no search error occurred, see if any permissions errors did.
370 */
371 if (ret == 0)
372 ret = err;
373
374 if (ret == 0 && obindp != NULL)
375 if (copyout((caddr_t)&obind, (caddr_t)obindp,
376 sizeof (obind)) == -1)
377 ret = EFAULT;
378 return (ret ? set_errno(ret) : 0); /* return success or failure */
379 }
380