6514 AS_* lock macros simplification
[illumos-gate.git] / usr / src / uts / common / syscall / rlimit.c
blob9b7b6ffee6079b73fbb4afa2dca41f6edde56835
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 #include <sys/param.h>
32 #include <sys/types.h>
33 #include <sys/inttypes.h>
34 #include <sys/sysmacros.h>
35 #include <sys/systm.h>
36 #include <sys/tuneable.h>
37 #include <sys/user.h>
38 #include <sys/errno.h>
39 #include <sys/vnode.h>
40 #include <sys/file.h>
41 #include <sys/proc.h>
42 #include <sys/resource.h>
43 #include <sys/ulimit.h>
44 #include <sys/debug.h>
45 #include <sys/rctl.h>
47 #include <vm/as.h>
50 * Perhaps ulimit could be moved into a user library, as calls to
51 * getrlimit and setrlimit, were it not for binary compatibility
52 * restrictions.
54 long
55 ulimit(int cmd, long arg)
57 proc_t *p = curproc;
58 long retval;
60 switch (cmd) {
62 case UL_GFILLIM: /* Return current file size limit. */
64 rlim64_t filesize;
66 mutex_enter(&p->p_lock);
67 filesize = rctl_enforced_value(rctlproc_legacy[RLIMIT_FSIZE],
68 p->p_rctls, p);
69 mutex_exit(&p->p_lock);
71 if (get_udatamodel() == DATAMODEL_ILP32) {
73 * File size is returned in blocks for ulimit.
74 * This function is deprecated and therefore LFS API
75 * didn't define the behaviour of ulimit.
76 * Here we return maximum value of file size possible
77 * so that applications that do not check errors
78 * continue to work.
80 if (filesize > MAXOFF32_T)
81 filesize = MAXOFF32_T;
82 retval = ((int)filesize >> SCTRSHFT);
83 } else
84 retval = filesize >> SCTRSHFT;
85 break;
88 case UL_SFILLIM: /* Set new file size limit. */
90 int error = 0;
91 rlim64_t lim = (rlim64_t)arg;
92 struct rlimit64 rl64;
93 rctl_alloc_gp_t *gp = rctl_rlimit_set_prealloc(1);
95 if (lim >= (((rlim64_t)MAXOFFSET_T) >> SCTRSHFT))
96 lim = (rlim64_t)RLIM64_INFINITY;
97 else
98 lim <<= SCTRSHFT;
100 rl64.rlim_max = rl64.rlim_cur = lim;
101 mutex_enter(&p->p_lock);
102 if (error = rctl_rlimit_set(rctlproc_legacy[RLIMIT_FSIZE], p,
103 &rl64, gp, RCTL_LOCAL_DENY | RCTL_LOCAL_SIGNAL, SIGXFSZ,
104 CRED())) {
105 mutex_exit(&p->p_lock);
106 rctl_prealloc_destroy(gp);
107 return (set_errno(error));
109 mutex_exit(&p->p_lock);
110 rctl_prealloc_destroy(gp);
111 retval = arg;
112 break;
115 case UL_GMEMLIM: /* Return maximum possible break value. */
117 struct seg *seg;
118 struct seg *nextseg;
119 struct as *as = p->p_as;
120 caddr_t brkend;
121 caddr_t brkbase;
122 size_t size;
123 rlim64_t size_ctl;
124 rlim64_t vmem_ctl;
127 * Find the segment with a virtual address
128 * greater than the end of the current break.
130 nextseg = NULL;
131 mutex_enter(&p->p_lock);
132 brkbase = (caddr_t)p->p_brkbase;
133 brkend = (caddr_t)p->p_brkbase + p->p_brksize;
134 mutex_exit(&p->p_lock);
137 * Since we can't return less than the current break,
138 * initialize the return value to the current break
140 retval = (long)brkend;
142 AS_LOCK_ENTER(as, RW_READER);
143 for (seg = as_findseg(as, brkend, 0); seg != NULL;
144 seg = AS_SEGNEXT(as, seg)) {
145 if (seg->s_base >= brkend) {
146 nextseg = seg;
147 break;
151 mutex_enter(&p->p_lock);
152 size_ctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_DATA],
153 p->p_rctls, p);
154 vmem_ctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_VMEM],
155 p->p_rctls, p);
156 mutex_exit(&p->p_lock);
159 * First, calculate the maximum break value based on
160 * the user's RLIMIT_DATA, but also taking into account
161 * that this value cannot be greater than as->a_userlimit.
162 * We also take care to make sure that we don't overflow
163 * in the calculation.
166 * Since we are casting the RLIMIT_DATA value to a
167 * ulong (a 32-bit value in the 32-bit kernel) we have
168 * to pass this assertion.
170 ASSERT32((size_t)size_ctl <= UINT32_MAX);
172 size = (size_t)size_ctl;
173 if (as->a_userlimit - brkbase > size)
174 retval = MAX((size_t)retval, (size_t)(brkbase + size));
175 /* don't return less than current */
176 else
177 retval = (long)as->a_userlimit;
180 * The max break cannot extend into the next segment
182 if (nextseg != NULL)
183 retval = MIN((uintptr_t)retval,
184 (uintptr_t)nextseg->s_base);
187 * Handle the case where there is an limit on RLIMIT_VMEM
189 if (vmem_ctl < UINT64_MAX) {
190 /* calculate brkend based on the end of page */
191 caddr_t brkendpg = (caddr_t)roundup((uintptr_t)brkend,
192 PAGESIZE);
194 * Large Files: The following assertion has to pass
195 * through to ensure the correctness of the cast.
197 ASSERT32(vmem_ctl <= UINT32_MAX);
199 size = (size_t)(vmem_ctl & PAGEMASK);
201 if (as->a_size < size)
202 size -= as->a_size;
203 else
204 size = 0;
206 * Take care to not overflow the calculation
208 if (as->a_userlimit - brkendpg > size)
209 retval = MIN((size_t)retval,
210 (size_t)(brkendpg + size));
213 AS_LOCK_EXIT(as);
215 /* truncate to same boundary as sbrk */
217 switch (get_udatamodel()) {
218 default:
219 case DATAMODEL_ILP32:
220 retval = retval & ~(8-1);
221 break;
222 case DATAMODEL_LP64:
223 retval = retval & ~(16-1);
224 break;
226 break;
229 case UL_GDESLIM: /* Return approximate number of open files */
231 rlim64_t fdno_ctl;
233 mutex_enter(&curproc->p_lock);
234 fdno_ctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_NOFILE],
235 curproc->p_rctls, curproc);
236 ASSERT(fdno_ctl <= INT_MAX);
237 retval = (rlim_t)fdno_ctl;
238 mutex_exit(&curproc->p_lock);
239 break;
242 default:
243 return (set_errno(EINVAL));
246 return (retval);
249 #ifdef _SYSCALL32_IMPL
252 ulimit32(int cmd, int arg)
254 return ((int)ulimit(cmd, (long)arg));
257 #endif /* _SYSCALL32_IMPL */
259 #if defined(_ILP32) || defined(_SYSCALL32_IMPL)
262 * Large Files: getrlimit returns RLIM_SAVED_CUR or RLIM_SAVED_MAX when
263 * rlim_cur or rlim_max is not representable in 32-bit rlim_t. These
264 * values are just tokens which will be used in setrlimit to set the
265 * correct limits. The current limits are saved in the saved_rlimit members
266 * in user structures when the token is returned. setrlimit restores
267 * the limit values to these saved values when the token is passed.
268 * Consider the following common scenario of the apps:
270 * limit = getrlimit();
271 * savedlimit = limit;
272 * limit = limit1;
273 * setrlimit(limit)
274 * // execute all processes in the new rlimit state.
275 * setrlimit(savedlimit) // restore the old values.
277 * Most apps don't check error returns from getrlimit or setrlimit
278 * and this is why we return tokens when the correct value
279 * cannot be represented in rlim_t. For more discussion refer to
280 * the LFS API document.
282 * In the 64-bit kernel, all existing resource limits are treated in this
283 * manner. In the 32-bit kernel, CPU time is treated equivalently to the
284 * file size limit above; the VM-related limits are not. The macro,
285 * RLIM_SAVED(x), returns true if the resource limit should be handled in
286 * this way on the current kernel.
289 getrlimit32(int resource, struct rlimit32 *rlp)
291 struct rlimit32 rlim32;
292 struct rlimit64 rlim64;
293 struct proc *p = curproc;
294 struct user *up = PTOU(p);
295 int savecur = 0;
296 int savemax = 0;
298 if (resource < 0 || resource >= RLIM_NLIMITS)
299 return (set_errno(EINVAL));
301 mutex_enter(&p->p_lock);
302 (void) rctl_rlimit_get(rctlproc_legacy[resource], p, &rlim64);
303 mutex_exit(&p->p_lock);
305 if (rlim64.rlim_max > (rlim64_t)UINT32_MAX) {
307 if (rlim64.rlim_max == RLIM64_INFINITY)
308 rlim32.rlim_max = RLIM32_INFINITY;
309 else {
310 savemax = 1;
311 rlim32.rlim_max = RLIM32_SAVED_MAX;
312 /*CONSTCOND*/
313 ASSERT(RLIM_SAVED(resource));
316 if (rlim64.rlim_cur == RLIM64_INFINITY)
317 rlim32.rlim_cur = RLIM32_INFINITY;
318 else if (rlim64.rlim_cur == rlim64.rlim_max) {
319 savecur = 1;
320 rlim32.rlim_cur = RLIM32_SAVED_MAX;
321 /*CONSTCOND*/
322 ASSERT(RLIM_SAVED(resource));
323 } else if (rlim64.rlim_cur > (rlim64_t)UINT32_MAX) {
324 savecur = 1;
325 rlim32.rlim_cur = RLIM32_SAVED_CUR;
326 /*CONSTCOND*/
327 ASSERT(RLIM_SAVED(resource));
328 } else
329 rlim32.rlim_cur = rlim64.rlim_cur;
332 * save the current limits in user structure.
334 /*CONSTCOND*/
335 if (RLIM_SAVED(resource)) {
336 mutex_enter(&p->p_lock);
337 if (savemax)
338 up->u_saved_rlimit[resource].rlim_max =
339 rlim64.rlim_max;
340 if (savecur)
341 up->u_saved_rlimit[resource].rlim_cur =
342 rlim64.rlim_cur;
343 mutex_exit(&p->p_lock);
345 } else {
346 ASSERT(rlim64.rlim_cur <= (rlim64_t)UINT32_MAX);
347 rlim32.rlim_max = rlim64.rlim_max;
348 rlim32.rlim_cur = rlim64.rlim_cur;
351 if (copyout(&rlim32, rlp, sizeof (rlim32)))
352 return (set_errno(EFAULT));
354 return (0);
358 * See comments above getrlimit32(). When the tokens are passed in the
359 * rlimit structure the values are considered equal to the values
360 * stored in saved_rlimit members of user structure.
361 * When the user passes RLIM_INFINITY to set the resource limit to
362 * unlimited internally understand this value as RLIM64_INFINITY and
363 * let rlimit() do the job.
366 setrlimit32(int resource, struct rlimit32 *rlp)
368 struct rlimit32 rlim32;
369 struct rlimit64 rlim64;
370 struct rlimit64 saved_rlim;
371 int error;
372 struct proc *p = ttoproc(curthread);
373 struct user *up = PTOU(p);
374 rctl_alloc_gp_t *gp;
376 if (resource < 0 || resource >= RLIM_NLIMITS)
377 return (set_errno(EINVAL));
378 if (copyin(rlp, &rlim32, sizeof (rlim32)))
379 return (set_errno(EFAULT));
381 gp = rctl_rlimit_set_prealloc(1);
384 * Disallow resource limit tunnelling
386 /*CONSTCOND*/
387 if (RLIM_SAVED(resource)) {
388 mutex_enter(&p->p_lock);
389 saved_rlim = up->u_saved_rlimit[resource];
390 mutex_exit(&p->p_lock);
391 } else {
392 saved_rlim.rlim_max = (rlim64_t)rlim32.rlim_max;
393 saved_rlim.rlim_cur = (rlim64_t)rlim32.rlim_cur;
396 switch (rlim32.rlim_cur) {
397 case RLIM32_INFINITY:
398 rlim64.rlim_cur = RLIM64_INFINITY;
399 break;
400 case RLIM32_SAVED_CUR:
401 rlim64.rlim_cur = saved_rlim.rlim_cur;
402 break;
403 case RLIM32_SAVED_MAX:
404 rlim64.rlim_cur = saved_rlim.rlim_max;
405 break;
406 default:
407 rlim64.rlim_cur = (rlim64_t)rlim32.rlim_cur;
408 break;
411 switch (rlim32.rlim_max) {
412 case RLIM32_INFINITY:
413 rlim64.rlim_max = RLIM64_INFINITY;
414 break;
415 case RLIM32_SAVED_MAX:
416 rlim64.rlim_max = saved_rlim.rlim_max;
417 break;
418 case RLIM32_SAVED_CUR:
419 rlim64.rlim_max = saved_rlim.rlim_cur;
420 break;
421 default:
422 rlim64.rlim_max = (rlim64_t)rlim32.rlim_max;
423 break;
426 mutex_enter(&p->p_lock);
427 if (error = rctl_rlimit_set(rctlproc_legacy[resource], p, &rlim64, gp,
428 rctlproc_flags[resource], rctlproc_signals[resource], CRED())) {
429 mutex_exit(&p->p_lock);
430 rctl_prealloc_destroy(gp);
431 return (set_errno(error));
433 mutex_exit(&p->p_lock);
434 rctl_prealloc_destroy(gp);
436 return (0);
439 #endif /* _ILP32 && _SYSCALL32_IMPL */
442 getrlimit64(int resource, struct rlimit64 *rlp)
444 struct rlimit64 rlim64;
445 struct proc *p = ttoproc(curthread);
447 if (resource < 0 || resource >= RLIM_NLIMITS)
448 return (set_errno(EINVAL));
450 mutex_enter(&p->p_lock);
451 (void) rctl_rlimit_get(rctlproc_legacy[resource], p, &rlim64);
452 mutex_exit(&p->p_lock);
454 if (copyout(&rlim64, rlp, sizeof (rlim64)))
455 return (set_errno(EFAULT));
456 return (0);
460 setrlimit64(int resource, struct rlimit64 *rlp)
462 struct rlimit64 rlim64;
463 struct proc *p = ttoproc(curthread);
464 int error;
465 rctl_alloc_gp_t *gp;
467 if (resource < 0 || resource >= RLIM_NLIMITS)
468 return (set_errno(EINVAL));
469 if (copyin(rlp, &rlim64, sizeof (rlim64)))
470 return (set_errno(EFAULT));
472 gp = rctl_rlimit_set_prealloc(1);
474 mutex_enter(&p->p_lock);
475 if (error = rctl_rlimit_set(rctlproc_legacy[resource], p, &rlim64, gp,
476 rctlproc_flags[resource], rctlproc_signals[resource], CRED())) {
477 mutex_exit(&p->p_lock);
478 rctl_prealloc_destroy(gp);
479 return (set_errno(error));
481 mutex_exit(&p->p_lock);
482 rctl_prealloc_destroy(gp);
483 return (0);