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 2006 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 		return (0);
65 	}
66 
67 	mutex_enter(&pp->p_lock);
68 	tp = pp->p_tlist;
69 	if (tp != NULL) {
70 		fp = tp;
71 		do {
72 			i = cpu_bind_thread(tp, bind, obind, error);
73 			if (err == 0)
74 				err = i;
75 		} while ((tp = tp->t_forw) != fp);
76 	}
77 
78 	mutex_exit(&pp->p_lock);
79 	return (err);
80 }
81 
82 /*
83  * Bind all the processes of a task to a CPU.
84  */
85 static int
86 cpu_bind_task(task_t *tk, processorid_t bind, processorid_t *obind,
87     int *error)
88 {
89 	proc_t	*p;
90 	int	err = 0;
91 	int	i;
92 
93 	ASSERT(MUTEX_HELD(&pidlock));
94 
95 	if ((p = tk->tk_memb_list) == NULL)
96 		return (ESRCH);
97 
98 	do {
99 		i = cpu_bind_process(p, bind, obind, error);
100 		if (err == 0)
101 			err = i;
102 	} while ((p = p->p_tasknext) != tk->tk_memb_list);
103 
104 	return (err);
105 }
106 
107 /*
108  * Bind all the processes in a project to a CPU.
109  */
110 static int
111 cpu_bind_project(kproject_t *kpj, processorid_t bind, processorid_t *obind,
112     int *error)
113 {
114 	proc_t *p;
115 	int err = 0;
116 	int i;
117 
118 	ASSERT(MUTEX_HELD(&pidlock));
119 
120 	for (p = practive; p != NULL; p = p->p_next) {
121 		if (p->p_tlist == NULL)
122 			continue;
123 		if (p->p_task->tk_proj == kpj) {
124 			i = cpu_bind_process(p, bind, obind, error);
125 			if (err == 0)
126 				err = i;
127 		}
128 	}
129 	return (err);
130 }
131 
132 /*
133  * Bind all the processes in a zone to a CPU.
134  */
135 int
136 cpu_bind_zone(zone_t *zptr, processorid_t bind, processorid_t *obind,
137     int *error)
138 {
139 	proc_t *p;
140 	int err = 0;
141 	int i;
142 
143 	ASSERT(MUTEX_HELD(&pidlock));
144 
145 	for (p = practive; p != NULL; p = p->p_next) {
146 		if (p->p_tlist == NULL)
147 			continue;
148 		if (p->p_zone == zptr) {
149 			i = cpu_bind_process(p, bind, obind, error);
150 			if (err == 0)
151 				err = i;
152 		}
153 	}
154 	return (err);
155 }
156 
157 /*
158  * Bind all the processes in a process contract to a CPU.
159  */
160 int
161 cpu_bind_contract(cont_process_t *ctp, processorid_t bind, processorid_t *obind,
162     int *error)
163 {
164 	proc_t *p;
165 	int err = 0;
166 	int i;
167 
168 	ASSERT(MUTEX_HELD(&pidlock));
169 
170 	for (p = practive; p != NULL; p = p->p_next) {
171 		if (p->p_tlist == NULL)
172 			continue;
173 		if (p->p_ct_process == ctp) {
174 			i = cpu_bind_process(p, bind, obind, error);
175 			if (err == 0)
176 				err = i;
177 		}
178 	}
179 	return (err);
180 }
181 
182 /*
183  * processor_bind(2) - Processor binding interfaces.
184  */
185 int
186 processor_bind(idtype_t idtype, id_t id, processorid_t bind,
187     processorid_t *obindp)
188 {
189 	processorid_t	obind = PBIND_NONE;
190 	int		ret = 0;
191 	int		err = 0;
192 	cpu_t		*cp;
193 	kthread_id_t	tp;
194 	proc_t		*pp;
195 	task_t		*tk;
196 	kproject_t	*kpj;
197 	zone_t		*zptr;
198 	contract_t	*ct;
199 
200 	/*
201 	 * Since we might be making a binding to a processor, hold the
202 	 * cpu_lock so that the processor cannot be taken offline while
203 	 * we do this.
204 	 */
205 	mutex_enter(&cpu_lock);
206 
207 	/*
208 	 * Check to be sure binding processor ID is valid.
209 	 */
210 	switch (bind) {
211 	default:
212 		if ((cp = cpu_get(bind)) == NULL ||
213 		    (cp->cpu_flags & (CPU_QUIESCED | CPU_OFFLINE)))
214 			ret = EINVAL;
215 		else if ((cp->cpu_flags & CPU_READY) == 0)
216 			ret = EIO;
217 		break;
218 
219 	case PBIND_NONE:
220 	case PBIND_QUERY:
221 		break;
222 	}
223 
224 	if (ret) {
225 		mutex_exit(&cpu_lock);
226 		return (set_errno(ret));
227 	}
228 
229 	switch (idtype) {
230 	case P_LWPID:
231 		pp = curproc;
232 		mutex_enter(&pp->p_lock);
233 		if (id == P_MYID) {
234 			ret = cpu_bind_thread(curthread, bind, &obind, &err);
235 		} else {
236 			int	found = 0;
237 
238 			tp = pp->p_tlist;
239 			do {
240 				if (tp->t_tid == id) {
241 					ret = cpu_bind_thread(tp,
242 					    bind, &obind, &err);
243 					found = 1;
244 					break;
245 				}
246 			} while ((tp = tp->t_forw) != pp->p_tlist);
247 			if (!found)
248 				ret = ESRCH;
249 		}
250 		mutex_exit(&pp->p_lock);
251 		break;
252 
253 	case P_PID:
254 		/*
255 		 * Note.  Cannot use dotoprocs here because it doesn't find
256 		 * system class processes, which are legal to query.
257 		 */
258 		mutex_enter(&pidlock);
259 		if (id == P_MYID) {
260 			ret = cpu_bind_process(curproc, bind, &obind, &err);
261 		} else if ((pp = prfind(id)) != NULL) {
262 			ret = cpu_bind_process(pp, bind, &obind, &err);
263 		} else {
264 			ret = ESRCH;
265 		}
266 		mutex_exit(&pidlock);
267 		break;
268 
269 	case P_TASKID:
270 		mutex_enter(&pidlock);
271 		if (id == P_MYID) {
272 			proc_t *p = curproc;
273 			id = p->p_task->tk_tkid;
274 		}
275 
276 		if ((tk = task_hold_by_id(id)) != NULL) {
277 			ret = cpu_bind_task(tk, bind, &obind, &err);
278 			mutex_exit(&pidlock);
279 			task_rele(tk);
280 		} else {
281 			mutex_exit(&pidlock);
282 			ret = ESRCH;
283 		}
284 		break;
285 
286 	case P_PROJID:
287 		pp = curproc;
288 		if (id == P_MYID)
289 			id = curprojid();
290 		if ((kpj = project_hold_by_id(id, pp->p_zone,
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