xref: /illumos-gate/usr/src/uts/common/syscall/processor_bind.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/param.h>
31 #include <sys/var.h>
32 #include <sys/thread.h>
33 #include <sys/cpuvar.h>
34 #include <sys/kstat.h>
35 #include <sys/uadmin.h>
36 #include <sys/systm.h>
37 #include <sys/errno.h>
38 #include <sys/cmn_err.h>
39 #include <sys/procset.h>
40 #include <sys/processor.h>
41 #include <sys/debug.h>
42 #include <sys/task.h>
43 #include <sys/project.h>
44 #include <sys/zone.h>
45 #include <sys/contract_impl.h>
46 #include <sys/contract/process_impl.h>
47 
48 /*
49  * Bind all the threads of a process to a CPU.
50  */
51 static int
52 cpu_bind_process(proc_t *pp, processorid_t bind, processorid_t *obind,
53     int *error)
54 {
55 	kthread_t	*tp;
56 	kthread_t	*fp;
57 	int		err = 0;
58 	int		i;
59 
60 	ASSERT(MUTEX_HELD(&pidlock));
61 
62 	/* skip kernel processes */
63 	if (pp->p_flag & SSYS) {
64 		*obind = PBIND_NONE;
65 		return (0);
66 	}
67 
68 	mutex_enter(&pp->p_lock);
69 	tp = pp->p_tlist;
70 	if (tp != NULL) {
71 		fp = tp;
72 		do {
73 			i = cpu_bind_thread(tp, bind, obind, error);
74 			if (err == 0)
75 				err = i;
76 		} while ((tp = tp->t_forw) != fp);
77 	}
78 
79 	mutex_exit(&pp->p_lock);
80 	return (err);
81 }
82 
83 /*
84  * Bind all the processes of a task to a CPU.
85  */
86 static int
87 cpu_bind_task(task_t *tk, processorid_t bind, processorid_t *obind,
88     int *error)
89 {
90 	proc_t	*p;
91 	int	err = 0;
92 	int	i;
93 
94 	ASSERT(MUTEX_HELD(&pidlock));
95 
96 	if ((p = tk->tk_memb_list) == NULL)
97 		return (ESRCH);
98 
99 	do {
100 		i = cpu_bind_process(p, bind, obind, error);
101 		if (err == 0)
102 			err = i;
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
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) {
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
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) {
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
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
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 		break;
223 	}
224 
225 	if (ret) {
226 		mutex_exit(&cpu_lock);
227 		return (set_errno(ret));
228 	}
229 
230 	switch (idtype) {
231 	case P_LWPID:
232 		pp = curproc;
233 		mutex_enter(&pp->p_lock);
234 		if (id == P_MYID) {
235 			ret = cpu_bind_thread(curthread, bind, &obind, &err);
236 		} else {
237 			int	found = 0;
238 
239 			tp = pp->p_tlist;
240 			do {
241 				if (tp->t_tid == id) {
242 					ret = cpu_bind_thread(tp,
243 					    bind, &obind, &err);
244 					found = 1;
245 					break;
246 				}
247 			} while ((tp = tp->t_forw) != pp->p_tlist);
248 			if (!found)
249 				ret = ESRCH;
250 		}
251 		mutex_exit(&pp->p_lock);
252 		break;
253 
254 	case P_PID:
255 		/*
256 		 * Note.  Cannot use dotoprocs here because it doesn't find
257 		 * system class processes, which are legal to query.
258 		 */
259 		mutex_enter(&pidlock);
260 		if (id == P_MYID) {
261 			ret = cpu_bind_process(curproc, bind, &obind, &err);
262 		} else if ((pp = prfind(id)) != NULL) {
263 			ret = cpu_bind_process(pp, bind, &obind, &err);
264 		} else {
265 			ret = ESRCH;
266 		}
267 		mutex_exit(&pidlock);
268 		break;
269 
270 	case P_TASKID:
271 		mutex_enter(&pidlock);
272 		if (id == P_MYID) {
273 			proc_t *p = curproc;
274 			id = p->p_task->tk_tkid;
275 		}
276 
277 		if ((tk = task_hold_by_id(id)) != NULL) {
278 			ret = cpu_bind_task(tk, bind, &obind, &err);
279 			mutex_exit(&pidlock);
280 			task_rele(tk);
281 		} else {
282 			mutex_exit(&pidlock);
283 			ret = ESRCH;
284 		}
285 		break;
286 
287 	case P_PROJID:
288 		if (id == P_MYID)
289 			id = curprojid();
290 		if ((kpj = project_hold_by_id(id, getzoneid(),
291 		    PROJECT_HOLD_FIND)) == NULL) {
292 			ret = ESRCH;
293 		} else {
294 			mutex_enter(&pidlock);
295 			ret = cpu_bind_project(kpj, bind, &obind, &err);
296 			mutex_exit(&pidlock);
297 			project_rele(kpj);
298 		}
299 		break;
300 
301 	case P_ZONEID:
302 		if (id == P_MYID)
303 			id = getzoneid();
304 
305 		if ((zptr = zone_find_by_id(id)) == NULL) {
306 			ret = ESRCH;
307 		} else {
308 			mutex_enter(&pidlock);
309 			ret = cpu_bind_zone(zptr, bind, &obind, &err);
310 			mutex_exit(&pidlock);
311 			zone_rele(zptr);
312 		}
313 		break;
314 
315 	case P_CTID:
316 		if (id == P_MYID)
317 			id = PRCTID(curproc);
318 
319 		if ((ct = contract_type_ptr(process_type, id,
320 		    curproc->p_zone->zone_uniqid)) == NULL) {
321 			ret = ESRCH;
322 		} else {
323 			mutex_enter(&pidlock);
324 			ret = cpu_bind_contract(ct->ct_data,
325 			    bind, &obind, &err);
326 			mutex_exit(&pidlock);
327 			contract_rele(ct);
328 		}
329 		break;
330 
331 	case P_CPUID:
332 		if (id == P_MYID || bind != PBIND_NONE || cpu_get(id) == NULL)
333 			ret = EINVAL;
334 		else
335 			ret = cpu_unbind(id);
336 		break;
337 
338 	case P_ALL:
339 		if (id == P_MYID || bind != PBIND_NONE) {
340 			ret = EINVAL;
341 		} else {
342 			int i;
343 			cpu_t *cp = cpu_list;
344 			do {
345 				if ((cp->cpu_flags & CPU_EXISTS) == 0)
346 					continue;
347 				i = cpu_unbind(cp->cpu_id);
348 				if (ret == 0)
349 					ret = i;
350 			} while ((cp = cp->cpu_next) != cpu_list);
351 		}
352 		break;
353 
354 	default:
355 		/*
356 		 * Spec says this is invalid, even though we could
357 		 * handle other idtypes.
358 		 */
359 		ret = EINVAL;
360 		break;
361 	}
362 	mutex_exit(&cpu_lock);
363 
364 	/*
365 	 * If no search error occurred, see if any permissions errors did.
366 	 */
367 	if (ret == 0)
368 		ret = err;
369 
370 	if (ret == 0 && obindp != NULL)
371 		if (copyout((caddr_t)&obind, (caddr_t)obindp,
372 		    sizeof (obind)) == -1)
373 			ret = EFAULT;
374 	return (ret ? set_errno(ret) : 0);	/* return success or failure */
375 }
376