static char sccsid[] = "@(#)55 1.38.1.21 src/bos/kernel/proc/resource.c, sysproc, bos411, 9428A410j 6/10/94 06:37:46"; /* * COMPONENT_NAME: SYSPROC * * FUNCTIONS: PGBNDUP * get_pgrp_nice * getpriority * getrlimit * getrusage * set_pgrp_nice * setpriority * setrlimit * * ORIGINS: 27, 3, 83 * * This module contains IBM CONFIDENTIAL code. -- (IBM * Confidential Restricted when combined with the aggregated * modules for this product) * SOURCE MATERIALS * * (C) COPYRIGHT International Business Machines Corp. 1988, 1994 * All Rights Reserved * US Government Users Restricted Rights - Use, duplication or * disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ /* * LEVEL 1, 5 Years Bull Confidential Information */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ld_data.h" #define NANOTOMICRO 1000 /* * EXTERNAL PROCEDURES CALLED: */ extern int copyin(); extern int copyout(); extern timevaladd(); extern donice(); typedef int nice_t; typedef char error_t; #define PGBNDUP(x) ( ((x)+PAGESIZE-1) & ~((long)(PAGESIZE-1)) ) #define OK 0 /* * NAME: getpriority() * * FUNCTION: * The scheduling "nice" value of the process, process group, or * user, as indicated by "which" and "who". "Which" is one of * PRIO_PROCESS, PRIO_PGRP, or PRIO_USER, and "who" is interpreted * relative to "which" (a process indentifier, process group indentifier, * and a user ID, respectively). A zero value of "who" denotes the * current process, process group, or user. * * GLOBAL DATA STRUCTURES USED: * Process table. (proc[0] though proc[n]) * Process table lock (proc_lock) * * EXECUTION ENVIRONMENT: * BSD System Call. * Can page fault. * * RETURN VALUE DESCRIPTION: * -1 - if an error was detected; in which case u_error * has been set to EINVAL or ESRCH. * * - OR - * * the lowest 'nice' value (i.e. most favored) of all the * specified process. The nice value returned will be in * range PRIO_MIN <= nice <= PRIO_MAX (i.e. berkley's * range for 'nice'). * * NOTES: * Since Berkley's 'nice' value range is returned, a -1 * can be legitimately returned. If -1 does indicate an * error, u_error has been set. */ getpriority( int which, int who ) /* * which : identifies how the "who" parameter is handled. * who : Process, process group, or UID. */ { register struct proc *p; int ret_value = P_NICE_MAX + 1; TRCHKGT_SYSC(GETPRIORITY,which,who,NULL,NULL,NULL); simple_lock(&proc_tbl_lock); switch (which) { case PRIO_PROCESS: /* * If the "who" parameter was given to us as a zero, then * we must assume it is the currently running process that * is to be used. If the "who" parameter was non-zero, we * need to try to make sure it is a real process. */ if (who == 0) p = curproc; else /* * Be sure that the "who" parameter is a * pid. */ if ( !(p = VALIDATE_PID(who)) || p->p_stat == SNONE || p->p_stat == SIDL || p->p_stat == SZOMB ) { break; } ret_value = EXTRACT_NICE(p); break; case PRIO_PGRP: /* * If the "who" parameter was given to us as a zero, then * we must assume it is the currently running process's * group is to be used. */ if (who == 0) { p = curproc; } else /* * Be sure that the "who" parameter is a * pid. And that the group anchor is valid. * The group leader could have exited and in * the zombie state, but as long as the group * anchor is valid, there are other processes * in the group. */ if ( !((p = VALIDATE_PID(who)) && (p = p->p_ganchor) && (p->p_pgrp == who))) break; ret_value = get_pgrp_nice( p ); break; case PRIO_USER: /* * If the "who" parameter was given to us as a zero, then * we must assume it is the currently running process's * uid is to be used. */ if (who == 0) who = curproc -> p_uid; for (p = &proc[0]; p < max_proc; p++) { /* * need to skip over 'inactive' process slots */ if ( p->p_stat != SNONE && p->p_stat != SIDL && p->p_stat != SZOMB && p->p_uid == who ) { ret_value = MIN(ret_value,EXTRACT_NICE(p)); } } break; default: u.u_error = EINVAL; } simple_unlock(&proc_tbl_lock); if (u.u_error != 0) return(-1); /* * If low hasn't been changed from the time we came into * this routine or only processes with fixed priorities * were found, then ret_value is equal to P_NICE_MAX + 1. * In either case the return value should be converted to -1 * and u_error should be set to ESRCH. * Otherwise, we need to adjust the 'nice' value to * Berkley's range. */ if (ret_value == P_NICE_MAX + 1) { u.u_error = ESRCH; return(-1); } /* * Return what we did find, adjusting to Berkley's nice value * range. */ return( ret_value - 20 ); } /* * NAME: setpriority() * * FUNCTION: * The scheduling "nice" value of the process, process group, or * user, as indicated by "which" and "who". "Which" is one of * PRIO_PROCESS, PRIO_PGRP, or PRIO_USER, and "who" is interpreted * relative to "which" (a process indentifier, process group indentifier, * and a user ID, respectively). A zero value of "who" denotes the * current process, process group, or user. The "nice" parameter is * a value in the range -20 to 20. Lower priorities cause more favorable * scheduling. * * The setpriority() call sets the "nice" values of all the * specified processes to the specified value. If the specified value * is less than -20, a value of -20 is used. If it is greater than * 20, a value of 20 is used. Only processes that have SET_PROC & * US1.PRIORITY system privlege may lower "nice" values. * * GLOBAL DATA STRUCTURES USED: * * EXECUTION ENVIRONMENT: * BSD System Call. * Can Page Fault. * * RETURN VALUE DESCRIPTION: * 0 - all the specified process have their nice value * reset and their priorities recalculated. * -1 - some/all of the specified processes nice values * could not be reset (u_error has been set). */ int setpriority( int which, int who, nice_t nice ) /* * which : This identifies how the "who" param is handled. * who : Process, process group, or UID. * nice : New "nice" value request. */ { register struct proc *p; error_t save_u_error; int ret_value = 0; static int svcnum = 0; struct ucred *crp; if(audit_flag && audit_svcstart("PROC_SetPri", &svcnum, 1, nice)){ audit_svcfinis(); } TRCHKGT_SYSC(SETPRIORITY,which,who,nice,NULL,NULL); /* * make any range adjustments and 'deBERKLEYize' the nice value */ if ( nice > PRIO_MAX ) nice = PRIO_MAX; if ( nice < PRIO_MIN ) nice = PRIO_MIN; nice = nice + 20; crp = crref(); simple_lock(&proc_tbl_lock); switch (which) { case PRIO_PROCESS: /* * If the "who" parameter was given to us as a zero, then * we must assume it is the currently running process that * is to be used. If the "who" parameter was non-zero, we * need to try to make sure it is a real process. */ if (who == 0) p = curproc; else /* * Be sure that the "who" parameter is a * pid. */ if ( !(p = VALIDATE_PID(who)) || p->p_stat == SNONE || p->p_stat == SIDL || p->p_stat == SZOMB ) { u.u_error = ESRCH; ret_value = -1; break; } ret_value = donice(p, nice, crp); break; case PRIO_PGRP: /* * If the "who" parameter was given to us as a zero, then * we must assume it is the currently running process's * process group that is to be used. If the "who" * parameter was non-zero, we need to try to make sure it * is a real process. */ if (who == 0) p = curproc; else /* * Be sure that the "who" parameter is a * pid. And that the group anchor is valid. * The group leader could have exited and in * the zombie state, but as long as the group * anchor is valid, there are other processes * in the group. */ if ( !((p = VALIDATE_PID(who)) && (p = p->p_ganchor) && (p->p_pgrp == who))) { u.u_error = ESRCH; ret_value = -1; break; } ret_value = set_pgrp_nice(p, nice, crp); break; case PRIO_USER: /* * If the "who" parameter was given to us as a zero, then * we must assume it is the currently running process's * uid is to be used. * NOTE: if all the processes with the same uid have fixed * priorities then return ERSCH. Since the function * 'donice' sets u_error if a process is inactive or * has a fixed priority to ERSCH, we need to * save/restore u_error under this condition. * u_error should be set to ERSRCH only if * all the processes with the same uid have fixed * priorities or are inactive. */ if (who == 0) who = curproc -> p_uid; ret_value = 0; /* assume we won't find a match */ for (p = &proc[0]; p < max_proc; p++) { /* * If the UID matches, then try to change * the "nice" value to the desired value. */ if (p->p_uid == who) { save_u_error = u.u_error; (void) donice(p, nice, crp); if (u.u_error == ESRCH) u.u_error = save_u_error; else ret_value++; /* indicate that a process was found */ } } if (ret_value == 0) /* didn't find process */ u.u_error = ESRCH; if (u.u_error != 0) ret_value = -1; else ret_value = 0; break; default: u.u_error = EINVAL; ret_value = -1; } simple_unlock(&proc_tbl_lock); crfree(crp); return(ret_value); } /* does system call stuff then calls xsetrlimit above * can page fault. */ setrlimit( int resource, struct rlimit *rlp ) /* int resource; Desired resource to change. */ /* struct rlimit *rlp; Address in user space to read from. */ { struct rlimit alim; static int svcnum = 0; int rc; if(audit_flag && audit_svcstart("PROC_Limits", &svcnum, 0)){ audit_svcfinis(); } TRCHKLT_SYSC(SETRLIMIT,resource); /* * If the caller makes an unresonable request, * slam the door in his face. */ if (resource >= RLIM_NLIMITS || resource < RLIMIT_CPU) { u.u_error = EINVAL; return(-1); } /* * If we can't read what the caller has, let him know it. */ if (copyin((caddr_t)rlp,(caddr_t)&alim,sizeof(struct rlimit)) != OK){ u.u_error = EFAULT; return(-1); } simple_lock(&U.U_handy_lock); rc = xsetrlimit(resource,&alim); simple_unlock(&U.U_handy_lock); return rc; } /* * NAME: getrlimit() * * FUNCTION: * Limits on the consumption of system resources by the current * process and each process it creates may be retrieved with the * getrlimit() call. * * INPUT PARAMETER DESCRIPTION: * The "resource" may be one of the following: * * RLIMIT_CPU The maximum amount of CPU time (in seconds) to be used * by each process. * * RLIMIT_FSIZE The largest size, in bytes, of any single file that can * be created. * * RLIMIT_DATA The maximum size, in bytes, of the data segment for a * process; this defines how far a program may extend its * "break" value with the sbrk() system call. * * RLIMIT_STACK The maximum size, in bytes, of the stack segment for * a process. This defines how far a program's stack * segment may be extended. Stack expansion is performed * automatically by the system. * * RLIMIT_CORE The largest size, in bytes, of a "core" file that may * be created. * * RLIMIT_RSS The maximum size, in bytes, to which a process's * resident set size may grow. This imposes a limit on * the amount of physical memory to be given to a process. * If memory is tight, the system will prefer to take * memory from processes that are exceeding their declared * resident set size. * * EXECUTION ENVIRONMENT: * BSD System Call. * can Page Fault. * * RETURN VALUE DESCRIPTION: * A return value of zero indicates the call succeeded, changing or * returning the resource limit. A return value of -1 indicates that * an error occurred, and an error code is stored in the global variable * "errno". */ getrlimit( int resource, struct rlimit *rlp ) /* int resource; The resource requested. */ /* struct rlimit *rlp; Address of rlimit struct in user space. */ { struct rlimit tmplim; TRCHKLT_SYSC(GETRLIMIT,resource); /* * If the resource parameter is greater than that specified * within the system, return an error. */ if (resource >= RLIM_NLIMITS || resource < RLIMIT_CPU) { u.u_error = EINVAL; return(-1); } /* * If the caller has a bogus address for rlp, give him * an error return. No locking is required since the * the data is not referenced by the caller under a lock. */ if (copyout((caddr_t)&U.U_rlimit[ resource ], (caddr_t)rlp, sizeof (struct rlimit)) != OK) { u.u_error = EFAULT; return(-1); } return(0); } /* * NAME: getrusage() * * FUNCTION: * The getrusage() call returns information describing the resources * utilized by the current process, or all its terminated child * processes. The "who" parameter is one of RUSAGE_SELF or * RUSAGE_CHILDREN. The buffer to which "rusage" points will be filled * in as described in sys/resource.h. * * EXECUTION ENVIRONMENT: * BSD System Call. * Can Page Fault. * * RETURN VALUE DESCRIPTION: * A zero return value indicates that the call succeeded. A return * value of -1 indicates that an error occurred, and an error code is * stored in the global variable "errno". */ getrusage( int who, struct rusage *rusage) /* who: Your process or it's children. */ /* rusage: Place in user space to write it. */ { struct rusage rup; long ixrss; /* shared memory size */ long idrss; /* unshared data size */ TRCHKLT_SYSC(GETRUSAGE,who); switch (who) { case RUSAGE_SELF: U.U_ru.ru_isrss = 0; U.U_ru.ru_nswap = 0; U.U_ru.ru_minflt = curproc->p_minflt; U.U_ru.ru_majflt = curproc->p_majflt; idrss = 4 * vms_rusage(U.U_adspace.srval[PRIVSEG]); idrss += 4 * bd_rusage(&U); ixrss = 4 * vms_rusage(U.U_adspace.srval[TEXTSEG]); if (U.U_adspace.alloc & ((unsigned)0x80000000 >> SHDATASEG)) idrss += 4 * vms_rusage(U.U_adspace.srval[SHDATASEG]); if (OVFL_EXISTS((struct loader_anchor *)U.U_loader)) idrss += 4 * vms_rusage(((struct loader_anchor *) (U.U_loader))->la_ovfl_srval); /* Only update if no error from vms_rusage */ if (idrss >= 0 && ixrss >= 0) while ((idrss + ixrss) > U.U_ru.ru_maxrss && !compare_and_swap(&U.U_ru.ru_maxrss, &U.U_ru.ru_maxrss, idrss + ixrss)); rup = U.U_ru; rup.ru_utime.tv_usec = rup.ru_utime.tv_usec / NANOTOMICRO; rup.ru_stime.tv_usec = rup.ru_stime.tv_usec / NANOTOMICRO; break; case RUSAGE_CHILDREN: U.U_cru.ru_isrss = 0; /* Not supported */ U.U_cru.ru_nswap = 0; /* Not supported */ rup = U.U_cru; rup.ru_utime.tv_usec = rup.ru_utime.tv_usec / NANOTOMICRO; rup.ru_stime.tv_usec = rup.ru_stime.tv_usec / NANOTOMICRO; break; default: u.u_error = EINVAL; return(-1); } /* * If the caller has a bogus address for rusage, give him * an error return. */ if (copyout((caddr_t)&rup, (caddr_t)rusage, sizeof (struct rusage)) != OK) { u.u_error = EFAULT; return(-1); } return(0); } /* * NAME: get_pgrp_nice() * * FUNCTION: * This routine finds the lowest "nice" value within a process group. * * GLOBAL DATA STRUCTURES USED: * Process table. (proc[0] though proc[n]) * * EXECUTION ENVIRONMENT: * Cannot page fault, since it disables interrupts. * * RETURN VALUE DESCRIPTION: * Returns the lowest nice value found. * - OR - * P_NICE_MAX + 1 if a process could not be found, or * the process(es) had a fixed priority. * * NOTES: * The nice value returned will be in the range * P_NICE_MIN <= nice <= P_NICE_MAX. */ nice_t get_pgrp_nice( struct proc *p /* process structure */ ) { nice_t low; /* lowest nice value so far */ int ipri; /* saved interrupt priority */ ASSERT(lock_mine(&proc_tbl_lock)); /* read only reference */ /* * If we don't belong to a process group, then we are * by ourselves and we return our own "nice" value. */ if (p->p_pgrp == 0) return(EXTRACT_NICE(p)); /* * determine lowest nice value of process group by extracting * nice value of processes in the process group list. */ p = PROCPTR(p->p_pgrp); p = p->p_ganchor; /* begin with a process in the pgrp */ low = EXTRACT_NICE(p); /* extract nice value */ for (p = p->p_pgrpl; p; p = p->p_pgrpl) { low = MIN(low,EXTRACT_NICE(p)); /* save minimum nice value */ } return(low); } /* * NAME: set_pgrp_nice() * * FUNCTION: * This routine sets the "nice" value for a given process group. * * GLOBAL DATA STRUCTURES USED: * Process structure. * * EXECUTION ENVIRONMENT: * Cannot page fault, interrupts are disabled. * * RETURN VALUE DESCRIPTION: * -1 - some or all of the process's nice value within * the group could not be reset. u_error has been * set to the appropriate value. * 0 - all the process's nice value have been reset * and their priorities have been recalculated. * */ set_pgrp_nice( struct proc *p, nice_t nice, struct ucred *crp) /* * p : pointer to an 'active' process * nice : new nice value */ { error_t save_u_error; /* save u_error */ int nice_value_set=FALSE; /* niced a process */ int ipri; /* save interrupt level */ ASSERT(lock_mine(&proc_tbl_lock)); /* read only references */ /* * If we don't belong to a process group, then we are * by ourselves and we just reset our own nice value */ if (p->p_pgrp == 0) { return (donice(p, nice, crp)); } /* * This loop attempts to set everyone within this process group * to the new "nice" value. * NOTE: if all the processes in the the group have fixed priorities * then return ERSCH. Since the function 'donice' sets * u_error if a process is inactive or has a fixed priority * to ERSCH, we need to save/restore u_error under this * condition. u_error should be set to ERSRCH only if * all the processes in the group are inactive or have * fixed priorities. */ p = PROCPTR(p->p_pgrp); p = p->p_ganchor; /* begin with a process in the pgrp */ /* loop on process in p->p_pgrp on linked p_pgrpl list */ for (; p; p = p->p_pgrpl) { save_u_error = u.u_error; (void) donice(p, nice, crp); if (u.u_error == ESRCH) { /* process either had a fixed priority or was inactive */ u.u_error = save_u_error; /* restore prior error code */ } else nice_value_set = TRUE; /* it was a valid process to nice */ } if (nice_value_set == FALSE) /* never found a valid process */ u.u_error = ESRCH; return (u.u_error ? -1 : 0); }