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

322 lines
8.8 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
static char sccsid[] = "@(#)14 1.10.1.8 src/bos/kernel/proc/POWER/m_berk.c, sysproc, bos41J, 9521A_all 5/19/95 18:09:36";
/*
* COMPONENT_NAME: SYSPROC
*
* FUNCTIONS: adj_tick
* adjtime
* kgettickd
* ksettickd
*
* ORIGINS: 26,27,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,1993
* 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> /* always needed */
#include <sys/adspace.h> /* for the WRITE_CLOCK macro to work */
#include <sys/time.h> /* for the timeval structure */
#include <sys/param.h> /* to define the HZ label */
#include <sys/mstsave.h> /* mstsave area def. for asserting */
#include <sys/user.h> /* the u structure to return errnos */
#include <sys/errno.h> /* define the errno's to be returned */
#include <sys/syspest.h> /* for the ASSERT and assert macros */
#include <sys/intr.h> /* for the serialization stuff */
#include <sys/rtc.h> /* for real time clock related defines */
#include <sys/low.h> /* access the csa for asserts */
#include <sys/machine.h> /* for machine model macros */
#include <sys/systemcfg.h> /* for system config structure */
#include <sys/sys_resource.h> /* for system resource structure */
int tickadj = (uS_PER_SEC / HZ) / 10; /* standard clock adjust, ms./tick */
long bigadj = 1000000; /* use 5x skew if greater than bigadj us.*/
int uSdelta = 0; /* current clock skew, us. per tick */
extern Simple_lock tod_lock;
extern void set_time();
/*
* True if time changed through system call : adjtime or settimer.
*/
int time_adjusted = 0;
struct ppda_adjtime {
int prev_delta;
int delta;
} timedelta[MAXCPU];
/*
* NAME: adjtime (BSD)
*
* FUNCTION: Correct the time to allow synchronization of the system clock.
* Send the adjtime values to the other processors
*
* EXECUTION ENVIRONMENT:
* This routine may only be called under a process.
*
* This routine may page fault.
*
* DATA STRUCTURES:
*
* RETURN VALUE DESCRIPTION: 0 upon successful completion, -1 upon error
* with errno set as follows:
* EFAULT: An argument address referenced invalid memory;
* EPERM: The requesting process does not have the appropriate
* privilege to set the system time.
*/
int
adjtime(struct timeval *delta, struct timeval *olddelta)
{
struct timeval atv; /* timeval to adjust by */
struct timeval oatv; /* old timeval to adjust by */
register long ndelta;
int i;
int ipri;
int most_advanced;
cpu_t my_cpu=CPUID;
int ncpus=NCPUS();
ASSERT(csa->prev == NULL);
/* Only a user with authority can set the time. */
if(privcheck(SYS_CONFIG) == EPERM) {
u.u_error = EPERM;
return(-1);
}
if((copyin((caddr_t)delta, (caddr_t)&atv,
sizeof(struct timestruc_t))) != 0) {
u.u_error = EFAULT;
return(-1);
}
ndelta = atv.tv_sec * 1000000 + atv.tv_usec;
ipri = disable_lock(INTMAX, &tod_lock);
/*
* Calculate global tick adjustment
*/
if (ndelta > bigadj)
uSdelta = 5 * tickadj;
else
uSdelta = tickadj;
/*
* 1 ms is the lower limit for clock adjustments.
* truncate request to 1ms increments.
*/
if (ndelta % uSdelta)
ndelta = (ndelta / uSdelta) * uSdelta;
if (olddelta != NULL) {
oatv.tv_sec = timedelta[my_cpu].delta / 1000000;
oatv.tv_usec = timedelta[my_cpu].delta % 1000000;
}
/*
* If all processors have not made the same time adjustments
* then calculate the value needed on a per processor basis
* to catch up. Store this value in the processor's prev_delta
* field to be applied in a single increment.
*/
most_advanced = 0;
for (i = 0; i < ncpus; i++) {
if (timedelta[i].delta < 0)
if ((most_advanced == 0) ||
(most_advanced < timedelta[i].delta))
most_advanced = timedelta[i].delta;
else if (timedelta[i].delta > 0)
if ((most_advanced == 0) ||
(most_advanced > timedelta[i].delta))
most_advanced = timedelta[i].delta;
}
for (i = 0; i < ncpus; i++) {
timedelta[i].prev_delta += (timedelta[i].delta - most_advanced);
timedelta[i].delta = ndelta;
}
/* Set if time adjusted through system call */
time_adjusted = TRUE;
unlock_enable(ipri, &tod_lock);
if(olddelta != NULL) {
(void)copyout((caddr_t)&oatv, (caddr_t)olddelta,
sizeof (struct timeval));
}
return(0);
}
/*
* NAME: adj_tick
*
* FUNCTION: Adjust the time each clock tick if an adjtime() was done.
*
* EXECUTION ENVIRONMENT:
* This routine is called at INTTIMER from sys_timer().
*
* It does not page fault.
*
* EXTERNAL PROCEDURES CALLED: None
*/
void
adj_tick()
{
struct timestruc_t nt; /* current system time */
struct timestruc_t dt; /* delta to current time */
cpu_t my_cpu=CPUID;
ASSERT(csa->intpri == INTMAX);
#ifdef _POWER_MP
simple_lock(&tod_lock);
#endif
if ((timedelta[my_cpu].prev_delta == 0) &&
(timedelta[my_cpu].delta == 0)) {
#ifdef _POWER_MP
simple_unlock(&tod_lock);
#endif
return;
}
dt.tv_sec = 0;
/*
* Update the current time (ct) and the reference time
* for the next clock tick (ref_time).
*/
#ifdef _PEGASUS
if (__pegasus())
pgs_rtc_stop();
#endif
curtime(&nt);
if (timedelta[my_cpu].prev_delta < 0) {
dt.tv_nsec = -(timedelta[my_cpu].prev_delta) *
(NS_PER_SEC / (uS_PER_SEC));
ntimersub(nt, dt, nt);
timedelta[my_cpu].prev_delta = 0;
}
else if (timedelta[my_cpu].prev_delta > 0) {
dt.tv_nsec = timedelta[my_cpu].prev_delta *
(NS_PER_SEC / (uS_PER_SEC));
ntimeradd(nt, dt, nt);
timedelta[my_cpu].prev_delta = 0;
}
else {
dt.tv_nsec = uSdelta * (NS_PER_SEC / (uS_PER_SEC));
if (timedelta[my_cpu].delta < 0) {
timedelta[my_cpu].delta += uSdelta;
ntimersub(nt, dt, nt);
}
else {
timedelta[my_cpu].delta -= uSdelta;
ntimeradd(nt, dt, nt);
}
}
/* update hardware time and memory mapped time variables */
set_time(nt);
#ifdef _PEGASUS
if (__pegasus())
pgs_rtc_start();
#endif /* _PEGASUS */
/* Restore caller's interrupt priority. */
#ifdef _POWER_MP
simple_unlock(&tod_lock);
#endif
}
/*
* NAME: kgettickd, ksettickd
*
* FUNCTION: These services allow kernel extensions to set/get the
* static variables timedelta and uSdelta, which are referenced
* during the clock interrupt by sys_timer(), and are used to
* account for the drift of the clock. The clock is adjusted
* accordyingly.
*
* EXECUTION ENVIRONMENT:
* These services should not page fault.
* Caller is expected to disable interrupts.
*
*/
kgettickd(int *timed, int *tickd, int *clock_set)
{
int ipri;
cpu_t my_cpu=CPUID;
ipri = disable_lock(INTMAX, &tod_lock);
*timed = timedelta[my_cpu].delta;
*tickd = uSdelta;
/* true if time adjusted through system call. */
*clock_set = time_adjusted;
unlock_enable(ipri, &tod_lock);
return(0);
}
ksettickd(int *timed, int *tickd, int *clock_set)
{
int i;
int ipri;
int most_advanced;
int ncpus=NCPUS();
ipri = disable_lock(INTMAX, &tod_lock);
if (timed != NULL) {
/*
* If all processors have not made the same time adjustments
* then calculate the value needed on a per processor basis
* to catch up. Store this value in the processor's prev_delta
* field to be applied in a single increment.
*/
most_advanced = 0;
for (i = 0; i < ncpus; i++) {
if (timedelta[i].delta < 0)
if ((most_advanced == 0) ||
(most_advanced < timedelta[i].delta))
most_advanced = timedelta[i].delta;
else if (timedelta[i].delta > 0)
if ((most_advanced == 0) ||
(most_advanced > timedelta[i].delta))
most_advanced = timedelta[i].delta;
}
for (i = 0; i < ncpus; i++) {
timedelta[i].prev_delta +=
(timedelta[i].delta - most_advanced);
timedelta[i].delta = *timed;
}
}
if (clock_set != NULL)
time_adjusted = (*clock_set ? TRUE : FALSE);
else
time_adjusted = FALSE;
if (tickd != NULL)
uSdelta = *tickd;
unlock_enable(ipri, &tod_lock);
return(0);
}