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