xref: /illumos-gate/usr/src/uts/common/syscall/processor_bind.c (revision bea83d026ee1bd1b2a2419e1d0232f107a5d7d9b)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/types.h>
29 #include <sys/param.h>
30 #include <sys/var.h>
31 #include <sys/thread.h>
32 #include <sys/cpuvar.h>
33 #include <sys/kstat.h>
34 #include <sys/uadmin.h>
35 #include <sys/systm.h>
36 #include <sys/errno.h>
37 #include <sys/cmn_err.h>
38 #include <sys/procset.h>
39 #include <sys/processor.h>
40 #include <sys/debug.h>
41 #include <sys/task.h>
42 #include <sys/project.h>
43 #include <sys/zone.h>
44 #include <sys/contract_impl.h>
45 #include <sys/contract/process_impl.h>
46 
47 /*
48  * Bind all the threads of a process to a CPU.
49  */
50 static int
51 cpu_bind_process(proc_t *pp, processorid_t bind, processorid_t *obind,
52     int *error)
53 {
54 	kthread_t	*tp;
55 	kthread_t	*fp;
56 	int		err = 0;
57 	int		i;
58 
59 	ASSERT(MUTEX_HELD(&pidlock));
60 
61 	/* skip kernel processes */
62 	if (pp->p_flag & SSYS) {
63 		*obind = PBIND_NONE;
64 		*error = EPERM;
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 		pp = curproc;
289 		if (id == P_MYID)
290 			id = curprojid();
291 		if ((kpj = project_hold_by_id(id, pp->p_zone,
292 		    PROJECT_HOLD_FIND)) == NULL) {
293 			ret = ESRCH;
294 		} else {
295 			mutex_enter(&pidlock);
296 			ret = cpu_bind_project(kpj, bind, &obind, &err);
297 			mutex_exit(&pidlock);
298 			project_rele(kpj);
299 		}
300 		break;
301 
302 	case P_ZONEID:
303 		if (id == P_MYID)
304 			id = getzoneid();
305 
306 		if ((zptr = zone_find_by_id(id)) == NULL) {
307 			ret = ESRCH;
308 		} else {
309 			mutex_enter(&pidlock);
310 			ret = cpu_bind_zone(zptr, bind, &obind, &err);
311 			mutex_exit(&pidlock);
312 			zone_rele(zptr);
313 		}
314 		break;
315 
316 	case P_CTID:
317 		if (id == P_MYID)
318 			id = PRCTID(curproc);
319 
320 		if ((ct = contract_type_ptr(process_type, id,
321 		    curproc->p_zone->zone_uniqid)) == NULL) {
322 			ret = ESRCH;
323 		} else {
324 			mutex_enter(&pidlock);
325 			ret = cpu_bind_contract(ct->ct_data,
326 			    bind, &obind, &err);
327 			mutex_exit(&pidlock);
328 			contract_rele(ct);
329 		}
330 		break;
331 
332 	case P_CPUID:
333 		if (id == P_MYID || bind != PBIND_NONE || cpu_get(id) == NULL)
334 			ret = EINVAL;
335 		else
336 			ret = cpu_unbind(id);
337 		break;
338 
339 	case P_ALL:
340 		if (id == P_MYID || bind != PBIND_NONE) {
341 			ret = EINVAL;
342 		} else {
343 			int i;
344 			cpu_t *cp = cpu_list;
345 			do {
346 				if ((cp->cpu_flags & CPU_EXISTS) == 0)
347 					continue;
348 				i = cpu_unbind(cp->cpu_id);
349 				if (ret == 0)
350 					ret = i;
351 			} while ((cp = cp->cpu_next) != cpu_list);
352 		}
353 		break;
354 
355 	default:
356 		/*
357 		 * Spec says this is invalid, even though we could
358 		 * handle other idtypes.
359 		 */
360 		ret = EINVAL;
361 		break;
362 	}
363 	mutex_exit(&cpu_lock);
364 
365 	/*
366 	 * If no search error occurred, see if any permissions errors did.
367 	 */
368 	if (ret == 0)
369 		ret = err;
370 
371 	if (ret == 0 && obindp != NULL)
372 		if (copyout((caddr_t)&obind, (caddr_t)obindp,
373 		    sizeof (obind)) == -1)
374 			ret = EFAULT;
375 	return (ret ? set_errno(ret) : 0);	/* return success or failure */
376 }
377