From e1d6dc656db4b7f1ae0f43c96dd215aab5ffd3a9 Mon Sep 17 00:00:00 2001 From: Nate Williams Date: Tue, 23 Dec 1997 16:32:35 +0000 Subject: [PATCH] This patch causes the "calltodo" timer list to be decremented by the amount of time that the laptop was suspending. Thus, select() calls that might have suspended rather than firing at 1hr + "time suspended" since the timer was posted. Adding: options APM_FIXUP_CALLTODO to the kernel config enables the patch. [ This patch was slightly modified to use a consistant indent style and I removed some unused local variables. After this has been tested a few weeks we'll make the options the default, so for now I'm now documenting it in LINT. Mike can later if he wants. ] Reviewed by: Mike Smith Submitted by: Ken Key --- sys/i386/apm/apm.c | 18 +++++++++-- sys/i386/bios/apm.c | 18 +++++++++-- sys/kern/kern_clock.c | 69 ++++++++++++++++++++++++++++++++++++++++- sys/kern/kern_tc.c | 69 ++++++++++++++++++++++++++++++++++++++++- sys/kern/kern_timeout.c | 69 ++++++++++++++++++++++++++++++++++++++++- 5 files changed, 236 insertions(+), 7 deletions(-) diff --git a/sys/i386/apm/apm.c b/sys/i386/apm/apm.c index 372efdf5dbc..59067c30378 100644 --- a/sys/i386/apm/apm.c +++ b/sys/i386/apm/apm.c @@ -15,7 +15,7 @@ * * Sep, 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) * - * $Id: apm.c,v 1.65 1997/11/12 04:12:43 jdp Exp $ + * $Id: apm.c,v 1.66 1997/12/04 02:40:00 imp Exp $ */ #include @@ -365,8 +365,22 @@ apm_default_resume(void *arg) tmp_time = time; /* because 'time' is volatile */ timevaladd(&tmp_time, &diff_time); time = tmp_time; +#ifdef APM_FIXUP_CALLTODO + /* Calculate the delta time suspended */ + timevalsub(&resume_time, &suspend_time); + /* Fixup the calltodo list with the delta time. */ + adjust_timeout_calltodo(&resume_time); +#endif /* APM_FIXUP_CALLTODOK */ splx(pl); - second = resume_time.tv_sec - suspend_time.tv_sec; +#ifndef APM_FIXUP_CALLTODO + second = resume_time.tv_sec - suspend_time.tv_sec; +#else /* APM_FIXUP_CALLTODO */ + /* + * We've already calculated resume_time to be the delta between + * the suspend and the resume. + */ + second = resume_time.tv_sec; +#endif /* APM_FIXUP_CALLTODO */ hour = second / 3600; second %= 3600; minute = second / 60; diff --git a/sys/i386/bios/apm.c b/sys/i386/bios/apm.c index 372efdf5dbc..59067c30378 100644 --- a/sys/i386/bios/apm.c +++ b/sys/i386/bios/apm.c @@ -15,7 +15,7 @@ * * Sep, 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) * - * $Id: apm.c,v 1.65 1997/11/12 04:12:43 jdp Exp $ + * $Id: apm.c,v 1.66 1997/12/04 02:40:00 imp Exp $ */ #include @@ -365,8 +365,22 @@ apm_default_resume(void *arg) tmp_time = time; /* because 'time' is volatile */ timevaladd(&tmp_time, &diff_time); time = tmp_time; +#ifdef APM_FIXUP_CALLTODO + /* Calculate the delta time suspended */ + timevalsub(&resume_time, &suspend_time); + /* Fixup the calltodo list with the delta time. */ + adjust_timeout_calltodo(&resume_time); +#endif /* APM_FIXUP_CALLTODOK */ splx(pl); - second = resume_time.tv_sec - suspend_time.tv_sec; +#ifndef APM_FIXUP_CALLTODO + second = resume_time.tv_sec - suspend_time.tv_sec; +#else /* APM_FIXUP_CALLTODO */ + /* + * We've already calculated resume_time to be the delta between + * the suspend and the resume. + */ + second = resume_time.tv_sec; +#endif /* APM_FIXUP_CALLTODO */ hour = second / 3600; second %= 3600; minute = second / 60; diff --git a/sys/kern/kern_clock.c b/sys/kern/kern_clock.c index 220e4b53485..4d28fd59791 100644 --- a/sys/kern/kern_clock.c +++ b/sys/kern/kern_clock.c @@ -36,7 +36,7 @@ * SUCH DAMAGE. * * @(#)kern_clock.c 8.5 (Berkeley) 1/21/94 - * $Id: kern_clock.c,v 1.45 1997/11/24 15:15:27 bde Exp $ + * $Id: kern_clock.c,v 1.46 1997/12/08 22:56:10 fsmp Exp $ */ /* Portions of this software are covered by the following: */ @@ -1358,3 +1358,70 @@ hardpps(tvp, usec) pps_intcnt++; } #endif /* PPS_SYNC */ + +#ifdef APM_FIXUP_CALLTODO +/* + * Adjust the kernel calltodo timeout list. This routine is used after + * an APM resume to recalculate the calltodo timer list values with the + * number of hz's we have been sleeping. The next hardclock() will detect + * that there are fired timers and run softclock() to execute them. + * + * Please note, I have not done an exhaustive analysis of what code this + * might break. I am motivated to have my select()'s and alarm()'s that + * have expired during suspend firing upon resume so that the applications + * which set the timer can do the maintanence the timer was for as close + * as possible to the originally intended time. Testing this code for a + * week showed that resuming from a suspend resulted in 22 to 25 timers + * firing, which seemed independant on whether the suspend was 2 hours or + * 2 days. Your milage may vary. - Ken Key + */ +void +adjust_timeout_calltodo(time_change) + struct timeval *time_change; +{ + register struct callout *p; + unsigned long delta_ticks; + int s; + + /* + * How many ticks were we asleep? + * (stolen from hzto()). + */ + + /* Don't do anything */ + if (time_change->tv_sec < 0) + return; + else if (time_change->tv_sec <= LONG_MAX / 1000000) + delta_ticks = (time_change->tv_sec * 1000000 + + time_change->tv_usec + (tick - 1)) / tick + 1; + else if (time_change->tv_sec <= LONG_MAX / hz) + delta_ticks = time_change->tv_sec * hz + + (time_change->tv_usec + (tick - 1)) / tick + 1; + else + delta_ticks = LONG_MAX; + + if (delta_ticks > INT_MAX) + delta_ticks = INT_MAX; + + /* + * Now rip through the timer calltodo list looking for timers + * to expire. + */ + + /* don't collide with softclock() */ + s = splhigh(); + for (p = calltodo.c_next; p != NULL; p = p->c_next) { + p->c_time -= delta_ticks; + + /* Break if the timer had more time on it than delta_ticks */ + if (p->c_time > 0) + break; + + /* take back the ticks the timer didn't use (p->c_time <= 0) */ + delta_ticks = -p->c_time; + } + splx(s); + + return; +} +#endif /* APM_FIXUP_CALLTODO */ diff --git a/sys/kern/kern_tc.c b/sys/kern/kern_tc.c index 220e4b53485..4d28fd59791 100644 --- a/sys/kern/kern_tc.c +++ b/sys/kern/kern_tc.c @@ -36,7 +36,7 @@ * SUCH DAMAGE. * * @(#)kern_clock.c 8.5 (Berkeley) 1/21/94 - * $Id: kern_clock.c,v 1.45 1997/11/24 15:15:27 bde Exp $ + * $Id: kern_clock.c,v 1.46 1997/12/08 22:56:10 fsmp Exp $ */ /* Portions of this software are covered by the following: */ @@ -1358,3 +1358,70 @@ hardpps(tvp, usec) pps_intcnt++; } #endif /* PPS_SYNC */ + +#ifdef APM_FIXUP_CALLTODO +/* + * Adjust the kernel calltodo timeout list. This routine is used after + * an APM resume to recalculate the calltodo timer list values with the + * number of hz's we have been sleeping. The next hardclock() will detect + * that there are fired timers and run softclock() to execute them. + * + * Please note, I have not done an exhaustive analysis of what code this + * might break. I am motivated to have my select()'s and alarm()'s that + * have expired during suspend firing upon resume so that the applications + * which set the timer can do the maintanence the timer was for as close + * as possible to the originally intended time. Testing this code for a + * week showed that resuming from a suspend resulted in 22 to 25 timers + * firing, which seemed independant on whether the suspend was 2 hours or + * 2 days. Your milage may vary. - Ken Key + */ +void +adjust_timeout_calltodo(time_change) + struct timeval *time_change; +{ + register struct callout *p; + unsigned long delta_ticks; + int s; + + /* + * How many ticks were we asleep? + * (stolen from hzto()). + */ + + /* Don't do anything */ + if (time_change->tv_sec < 0) + return; + else if (time_change->tv_sec <= LONG_MAX / 1000000) + delta_ticks = (time_change->tv_sec * 1000000 + + time_change->tv_usec + (tick - 1)) / tick + 1; + else if (time_change->tv_sec <= LONG_MAX / hz) + delta_ticks = time_change->tv_sec * hz + + (time_change->tv_usec + (tick - 1)) / tick + 1; + else + delta_ticks = LONG_MAX; + + if (delta_ticks > INT_MAX) + delta_ticks = INT_MAX; + + /* + * Now rip through the timer calltodo list looking for timers + * to expire. + */ + + /* don't collide with softclock() */ + s = splhigh(); + for (p = calltodo.c_next; p != NULL; p = p->c_next) { + p->c_time -= delta_ticks; + + /* Break if the timer had more time on it than delta_ticks */ + if (p->c_time > 0) + break; + + /* take back the ticks the timer didn't use (p->c_time <= 0) */ + delta_ticks = -p->c_time; + } + splx(s); + + return; +} +#endif /* APM_FIXUP_CALLTODO */ diff --git a/sys/kern/kern_timeout.c b/sys/kern/kern_timeout.c index 220e4b53485..4d28fd59791 100644 --- a/sys/kern/kern_timeout.c +++ b/sys/kern/kern_timeout.c @@ -36,7 +36,7 @@ * SUCH DAMAGE. * * @(#)kern_clock.c 8.5 (Berkeley) 1/21/94 - * $Id: kern_clock.c,v 1.45 1997/11/24 15:15:27 bde Exp $ + * $Id: kern_clock.c,v 1.46 1997/12/08 22:56:10 fsmp Exp $ */ /* Portions of this software are covered by the following: */ @@ -1358,3 +1358,70 @@ hardpps(tvp, usec) pps_intcnt++; } #endif /* PPS_SYNC */ + +#ifdef APM_FIXUP_CALLTODO +/* + * Adjust the kernel calltodo timeout list. This routine is used after + * an APM resume to recalculate the calltodo timer list values with the + * number of hz's we have been sleeping. The next hardclock() will detect + * that there are fired timers and run softclock() to execute them. + * + * Please note, I have not done an exhaustive analysis of what code this + * might break. I am motivated to have my select()'s and alarm()'s that + * have expired during suspend firing upon resume so that the applications + * which set the timer can do the maintanence the timer was for as close + * as possible to the originally intended time. Testing this code for a + * week showed that resuming from a suspend resulted in 22 to 25 timers + * firing, which seemed independant on whether the suspend was 2 hours or + * 2 days. Your milage may vary. - Ken Key + */ +void +adjust_timeout_calltodo(time_change) + struct timeval *time_change; +{ + register struct callout *p; + unsigned long delta_ticks; + int s; + + /* + * How many ticks were we asleep? + * (stolen from hzto()). + */ + + /* Don't do anything */ + if (time_change->tv_sec < 0) + return; + else if (time_change->tv_sec <= LONG_MAX / 1000000) + delta_ticks = (time_change->tv_sec * 1000000 + + time_change->tv_usec + (tick - 1)) / tick + 1; + else if (time_change->tv_sec <= LONG_MAX / hz) + delta_ticks = time_change->tv_sec * hz + + (time_change->tv_usec + (tick - 1)) / tick + 1; + else + delta_ticks = LONG_MAX; + + if (delta_ticks > INT_MAX) + delta_ticks = INT_MAX; + + /* + * Now rip through the timer calltodo list looking for timers + * to expire. + */ + + /* don't collide with softclock() */ + s = splhigh(); + for (p = calltodo.c_next; p != NULL; p = p->c_next) { + p->c_time -= delta_ticks; + + /* Break if the timer had more time on it than delta_ticks */ + if (p->c_time > 0) + break; + + /* take back the ticks the timer didn't use (p->c_time <= 0) */ + delta_ticks = -p->c_time; + } + splx(s); + + return; +} +#endif /* APM_FIXUP_CALLTODO */