Files
Arquivotheca.AIX-4.1.3/bos/kernel/proc/resource.c
seta75D d6fe8fe829 Init
2021-10-11 22:19:34 -03:00

767 lines
19 KiB
C

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 <sys/types.h>
#include <sys/param.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/prio_calc.h>
#include <sys/errno.h>
#include <sys/lockl.h>
#include <sys/intr.h>
#include <sys/resource.h>
#include <sys/trchkid.h>
#include <sys/cred.h>
#include <sys/lock.h>
#include <sys/pseg.h>
#include <sys/audit.h>
#include <sys/uio.h>
#include <sys/systm.h>
#include <sys/syspest.h>
#include <sys/malloc.h>
#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);
}