xref: /openbsd-src/sys/kern/kern_resource.c (revision 91ba896db697da68a85cad0ccf0e596c2e766af5)
1 /*	$OpenBSD: kern_resource.c,v 1.48 2014/01/21 01:48:44 tedu Exp $	*/
2 /*	$NetBSD: kern_resource.c,v 1.38 1996/10/23 07:19:38 matthias Exp $	*/
3 
4 /*-
5  * Copyright (c) 1982, 1986, 1991, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  * (c) UNIX System Laboratories, Inc.
8  * All or some portions of this file are derived from material licensed
9  * to the University of California by American Telephone and Telegraph
10  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
11  * the permission of UNIX System Laboratories, Inc.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  *	@(#)kern_resource.c	8.5 (Berkeley) 1/21/94
38  */
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/file.h>
44 #include <sys/resourcevar.h>
45 #include <sys/pool.h>
46 #include <sys/proc.h>
47 #include <sys/ktrace.h>
48 #include <sys/sched.h>
49 
50 #include <sys/mount.h>
51 #include <sys/syscallargs.h>
52 
53 #include <uvm/uvm_extern.h>
54 
55 void	tuagg_sub(struct tusage *, struct proc *);
56 void	tuagg(struct process *, struct proc *);
57 
58 /*
59  * Patchable maximum data and stack limits.
60  */
61 rlim_t maxdmap = MAXDSIZ;
62 rlim_t maxsmap = MAXSSIZ;
63 
64 /*
65  * Resource controls and accounting.
66  */
67 
68 int
69 sys_getpriority(struct proc *curp, void *v, register_t *retval)
70 {
71 	struct sys_getpriority_args /* {
72 		syscallarg(int) which;
73 		syscallarg(id_t) who;
74 	} */ *uap = v;
75 	struct process *pr;
76 	int low = NZERO + PRIO_MAX + 1;
77 
78 	switch (SCARG(uap, which)) {
79 
80 	case PRIO_PROCESS:
81 		if (SCARG(uap, who) == 0)
82 			pr = curp->p_p;
83 		else
84 			pr = prfind(SCARG(uap, who));
85 		if (pr == NULL)
86 			break;
87 		if (pr->ps_nice < low)
88 			low = pr->ps_nice;
89 		break;
90 
91 	case PRIO_PGRP: {
92 		struct pgrp *pg;
93 
94 		if (SCARG(uap, who) == 0)
95 			pg = curp->p_p->ps_pgrp;
96 		else if ((pg = pgfind(SCARG(uap, who))) == NULL)
97 			break;
98 		LIST_FOREACH(pr, &pg->pg_members, ps_pglist)
99 			if (pr->ps_nice < low)
100 				low = pr->ps_nice;
101 		break;
102 	}
103 
104 	case PRIO_USER:
105 		if (SCARG(uap, who) == 0)
106 			SCARG(uap, who) = curp->p_ucred->cr_uid;
107 		LIST_FOREACH(pr, &allprocess, ps_list)
108 			if (pr->ps_cred->pc_ucred->cr_uid == SCARG(uap, who) &&
109 			    pr->ps_nice < low)
110 				low = pr->ps_nice;
111 		break;
112 
113 	default:
114 		return (EINVAL);
115 	}
116 	if (low == NZERO + PRIO_MAX + 1)
117 		return (ESRCH);
118 	*retval = low - NZERO;
119 	return (0);
120 }
121 
122 /* ARGSUSED */
123 int
124 sys_setpriority(struct proc *curp, void *v, register_t *retval)
125 {
126 	struct sys_setpriority_args /* {
127 		syscallarg(int) which;
128 		syscallarg(id_t) who;
129 		syscallarg(int) prio;
130 	} */ *uap = v;
131 	struct process *pr;
132 	int found = 0, error = 0;
133 
134 	switch (SCARG(uap, which)) {
135 
136 	case PRIO_PROCESS:
137 		if (SCARG(uap, who) == 0)
138 			pr = curp->p_p;
139 		else
140 			pr = prfind(SCARG(uap, who));
141 		if (pr == NULL)
142 			break;
143 		error = donice(curp, pr, SCARG(uap, prio));
144 		found++;
145 		break;
146 
147 	case PRIO_PGRP: {
148 		struct pgrp *pg;
149 
150 		if (SCARG(uap, who) == 0)
151 			pg = curp->p_p->ps_pgrp;
152 		else if ((pg = pgfind(SCARG(uap, who))) == NULL)
153 			break;
154 		LIST_FOREACH(pr, &pg->pg_members, ps_pglist) {
155 			error = donice(curp, pr, SCARG(uap, prio));
156 			found++;
157 		}
158 		break;
159 	}
160 
161 	case PRIO_USER:
162 		if (SCARG(uap, who) == 0)
163 			SCARG(uap, who) = curp->p_ucred->cr_uid;
164 		LIST_FOREACH(pr, &allprocess, ps_list)
165 			if (pr->ps_cred->pc_ucred->cr_uid == SCARG(uap, who)) {
166 				error = donice(curp, pr, SCARG(uap, prio));
167 				found++;
168 			}
169 		break;
170 
171 	default:
172 		return (EINVAL);
173 	}
174 	if (found == 0)
175 		return (ESRCH);
176 	return (error);
177 }
178 
179 int
180 donice(struct proc *curp, struct process *chgpr, int n)
181 {
182 	struct pcred *pcred = curp->p_cred;
183 	struct proc *p;
184 	int s;
185 
186 	if (pcred->pc_ucred->cr_uid && pcred->p_ruid &&
187 	    pcred->pc_ucred->cr_uid != chgpr->ps_cred->pc_ucred->cr_uid &&
188 	    pcred->p_ruid != chgpr->ps_cred->pc_ucred->cr_uid)
189 		return (EPERM);
190 	if (n > PRIO_MAX)
191 		n = PRIO_MAX;
192 	if (n < PRIO_MIN)
193 		n = PRIO_MIN;
194 	n += NZERO;
195 	if (n < chgpr->ps_nice && suser(curp, 0))
196 		return (EACCES);
197 	chgpr->ps_nice = n;
198 	SCHED_LOCK(s);
199 	TAILQ_FOREACH(p, &chgpr->ps_threads, p_thr_link)
200 		(void)resetpriority(p);
201 	SCHED_UNLOCK(s);
202 	return (0);
203 }
204 
205 /* ARGSUSED */
206 int
207 sys_setrlimit(struct proc *p, void *v, register_t *retval)
208 {
209 	struct sys_setrlimit_args /* {
210 		syscallarg(int) which;
211 		syscallarg(const struct rlimit *) rlp;
212 	} */ *uap = v;
213 	struct rlimit alim;
214 	int error;
215 
216 	error = copyin((caddr_t)SCARG(uap, rlp), (caddr_t)&alim,
217 		       sizeof (struct rlimit));
218 	if (error)
219 		return (error);
220 #ifdef KTRACE
221 	if (KTRPOINT(p, KTR_STRUCT))
222 		ktrrlimit(p, &alim);
223 #endif
224 	return (dosetrlimit(p, SCARG(uap, which), &alim));
225 }
226 
227 int
228 dosetrlimit(struct proc *p, u_int which, struct rlimit *limp)
229 {
230 	struct rlimit *alimp;
231 	rlim_t maxlim;
232 	int error;
233 
234 	if (which >= RLIM_NLIMITS || limp->rlim_cur > limp->rlim_max)
235 		return (EINVAL);
236 
237 	alimp = &p->p_rlimit[which];
238 	if (limp->rlim_max > alimp->rlim_max)
239 		if ((error = suser(p, 0)) != 0)
240 			return (error);
241 	if (p->p_p->ps_limit->p_refcnt > 1) {
242 		struct plimit *l = p->p_p->ps_limit;
243 
244 		/* limcopy() can sleep, so copy before decrementing refcnt */
245 		p->p_p->ps_limit = limcopy(l);
246 		limfree(l);
247 		alimp = &p->p_rlimit[which];
248 	}
249 
250 	switch (which) {
251 	case RLIMIT_DATA:
252 		maxlim = maxdmap;
253 		break;
254 	case RLIMIT_STACK:
255 		maxlim = maxsmap;
256 		break;
257 	case RLIMIT_NOFILE:
258 		maxlim = maxfiles;
259 		break;
260 	case RLIMIT_NPROC:
261 		maxlim = maxprocess;
262 		break;
263 	default:
264 		maxlim = RLIM_INFINITY;
265 		break;
266 	}
267 
268 	if (limp->rlim_max > maxlim)
269 		limp->rlim_max = maxlim;
270 	if (limp->rlim_cur > limp->rlim_max)
271 		limp->rlim_cur = limp->rlim_max;
272 
273 	if (which == RLIMIT_STACK) {
274 		/*
275 		 * Stack is allocated to the max at exec time with only
276 		 * "rlim_cur" bytes accessible.  If stack limit is going
277 		 * up make more accessible, if going down make inaccessible.
278 		 */
279 		if (limp->rlim_cur != alimp->rlim_cur) {
280 			vaddr_t addr;
281 			vsize_t size;
282 			vm_prot_t prot;
283 
284 			if (limp->rlim_cur > alimp->rlim_cur) {
285 				prot = VM_PROT_READ|VM_PROT_WRITE;
286 				size = limp->rlim_cur - alimp->rlim_cur;
287 #ifdef MACHINE_STACK_GROWS_UP
288 				addr = USRSTACK + alimp->rlim_cur;
289 #else
290 				addr = USRSTACK - limp->rlim_cur;
291 #endif
292 			} else {
293 				prot = VM_PROT_NONE;
294 				size = alimp->rlim_cur - limp->rlim_cur;
295 #ifdef MACHINE_STACK_GROWS_UP
296 				addr = USRSTACK + limp->rlim_cur;
297 #else
298 				addr = USRSTACK - alimp->rlim_cur;
299 #endif
300 			}
301 			addr = trunc_page(addr);
302 			size = round_page(size);
303 			(void) uvm_map_protect(&p->p_vmspace->vm_map,
304 					      addr, addr+size, prot, FALSE);
305 		}
306 	}
307 
308 	*alimp = *limp;
309 	return (0);
310 }
311 
312 /* ARGSUSED */
313 int
314 sys_getrlimit(struct proc *p, void *v, register_t *retval)
315 {
316 	struct sys_getrlimit_args /* {
317 		syscallarg(int) which;
318 		syscallarg(struct rlimit *) rlp;
319 	} */ *uap = v;
320 	struct rlimit *alimp;
321 	int error;
322 
323 	if (SCARG(uap, which) < 0 || SCARG(uap, which) >= RLIM_NLIMITS)
324 		return (EINVAL);
325 	alimp = &p->p_rlimit[SCARG(uap, which)];
326 	error = copyout(alimp, SCARG(uap, rlp), sizeof(struct rlimit));
327 #ifdef KTRACE
328 	if (error == 0 && KTRPOINT(p, KTR_STRUCT))
329 		ktrrlimit(p, alimp);
330 #endif
331 	return (error);
332 }
333 
334 void
335 tuagg_sub(struct tusage *tup, struct proc *p)
336 {
337 	timespecadd(&tup->tu_runtime, &p->p_rtime, &tup->tu_runtime);
338 	tup->tu_uticks += p->p_uticks;
339 	tup->tu_sticks += p->p_sticks;
340 	tup->tu_iticks += p->p_iticks;
341 }
342 
343 /*
344  * Aggregate a single thread's immediate time counts into the running
345  * totals for the thread and process
346  */
347 void
348 tuagg_unlocked(struct process *pr, struct proc *p)
349 {
350 	tuagg_sub(&pr->ps_tu, p);
351 	tuagg_sub(&p->p_tu, p);
352 	timespecclear(&p->p_rtime);
353 	p->p_uticks = 0;
354 	p->p_sticks = 0;
355 	p->p_iticks = 0;
356 }
357 
358 void
359 tuagg(struct process *pr, struct proc *p)
360 {
361 	int s;
362 
363 	SCHED_LOCK(s);
364 	tuagg_unlocked(pr, p);
365 	SCHED_UNLOCK(s);
366 }
367 
368 /*
369  * Transform the running time and tick information in a struct tusage
370  * into user, system, and interrupt time usage.
371  */
372 void
373 calctsru(struct tusage *tup, struct timespec *up, struct timespec *sp,
374     struct timespec *ip)
375 {
376 	u_quad_t st, ut, it;
377 	int freq;
378 
379 	st = tup->tu_sticks;
380 	ut = tup->tu_uticks;
381 	it = tup->tu_iticks;
382 
383 	if (st + ut + it == 0) {
384 		timespecclear(up);
385 		timespecclear(sp);
386 		if (ip != NULL)
387 			timespecclear(ip);
388 		return;
389 	}
390 
391 	freq = stathz ? stathz : hz;
392 
393 	st = st * 1000000000 / freq;
394 	sp->tv_sec = st / 1000000000;
395 	sp->tv_nsec = st % 1000000000;
396 	ut = ut * 1000000000 / freq;
397 	up->tv_sec = ut / 1000000000;
398 	up->tv_nsec = ut % 1000000000;
399 	if (ip != NULL) {
400 		it = it * 1000000000 / freq;
401 		ip->tv_sec = it / 1000000000;
402 		ip->tv_nsec = it % 1000000000;
403 	}
404 }
405 
406 void
407 calcru(struct tusage *tup, struct timeval *up, struct timeval *sp,
408     struct timeval *ip)
409 {
410 	struct timespec u, s, i;
411 
412 	calctsru(tup, &u, &s, ip != NULL ? &i : NULL);
413 	TIMESPEC_TO_TIMEVAL(up, &u);
414 	TIMESPEC_TO_TIMEVAL(sp, &s);
415 	if (ip != NULL)
416 		TIMESPEC_TO_TIMEVAL(ip, &i);
417 }
418 
419 /* ARGSUSED */
420 int
421 sys_getrusage(struct proc *p, void *v, register_t *retval)
422 {
423 	struct sys_getrusage_args /* {
424 		syscallarg(int) who;
425 		syscallarg(struct rusage *) rusage;
426 	} */ *uap = v;
427 	struct rusage ru;
428 	int error;
429 
430 	error = dogetrusage(p, SCARG(uap, who), &ru);
431 	if (error == 0)
432 		error = copyout(&ru, SCARG(uap, rusage), sizeof(ru));
433 	return (error);
434 }
435 
436 int
437 dogetrusage(struct proc *p, int who, struct rusage *rup)
438 {
439 	struct process *pr = p->p_p;
440 	struct proc *q;
441 
442 	switch (who) {
443 
444 	case RUSAGE_SELF:
445 		/* start with the sum of dead threads, if any */
446 		if (pr->ps_ru != NULL)
447 			*rup = *pr->ps_ru;
448 		else
449 			memset(rup, 0, sizeof(*rup));
450 
451 		/* add on all living threads */
452 		TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link) {
453 			ruadd(rup, &q->p_ru);
454 			tuagg(pr, q);
455 		}
456 
457 		calcru(&pr->ps_tu, &rup->ru_utime, &rup->ru_stime, NULL);
458 		break;
459 
460 	case RUSAGE_THREAD:
461 		*rup = p->p_ru;
462 		calcru(&p->p_tu, &rup->ru_utime, &rup->ru_stime, NULL);
463 		break;
464 
465 	case RUSAGE_CHILDREN:
466 		*rup = pr->ps_cru;
467 		break;
468 
469 	default:
470 		return (EINVAL);
471 	}
472 	return (0);
473 }
474 
475 void
476 ruadd(struct rusage *ru, struct rusage *ru2)
477 {
478 	long *ip, *ip2;
479 	int i;
480 
481 	timeradd(&ru->ru_utime, &ru2->ru_utime, &ru->ru_utime);
482 	timeradd(&ru->ru_stime, &ru2->ru_stime, &ru->ru_stime);
483 	if (ru->ru_maxrss < ru2->ru_maxrss)
484 		ru->ru_maxrss = ru2->ru_maxrss;
485 	ip = &ru->ru_first; ip2 = &ru2->ru_first;
486 	for (i = &ru->ru_last - &ru->ru_first; i >= 0; i--)
487 		*ip++ += *ip2++;
488 }
489 
490 struct pool plimit_pool;
491 
492 /*
493  * Make a copy of the plimit structure.
494  * We share these structures copy-on-write after fork,
495  * and copy when a limit is changed.
496  */
497 struct plimit *
498 limcopy(struct plimit *lim)
499 {
500 	struct plimit *newlim;
501 	static int initialized;
502 
503 	if (!initialized) {
504 		pool_init(&plimit_pool, sizeof(struct plimit), 0, 0, 0,
505 		    "plimitpl", &pool_allocator_nointr);
506 		initialized = 1;
507 	}
508 
509 	newlim = pool_get(&plimit_pool, PR_WAITOK);
510 	bcopy(lim->pl_rlimit, newlim->pl_rlimit,
511 	    sizeof(struct rlimit) * RLIM_NLIMITS);
512 	newlim->p_refcnt = 1;
513 	return (newlim);
514 }
515 
516 void
517 limfree(struct plimit *lim)
518 {
519 	if (--lim->p_refcnt > 0)
520 		return;
521 	pool_put(&plimit_pool, lim);
522 }
523