From owner-freebsd-mobile Mon Dec 22 18:39:14 1997 Return-Path: Received: (from root@localhost) by hub.freebsd.org (8.8.7/8.8.7) id SAA21806 for mobile-outgoing; Mon, 22 Dec 1997 18:39:14 -0800 (PST) (envelope-from owner-freebsd-mobile) Received: from duncan.cs.utk.edu (DUNCAN.CS.UTK.EDU [128.169.94.83]) by hub.freebsd.org (8.8.7/8.8.7) with SMTP id SAA21801 for ; Mon, 22 Dec 1997 18:39:09 -0800 (PST) (envelope-from key@cs.utk.edu) Received: from LOCALHOST.cs.utk.edu by duncan.cs.utk.edu with SMTP (cf v2.11c-UTK) id VAA02986; Mon, 22 Dec 1997 21:39:02 -0500 Message-Id: <199712230239.VAA02986@duncan.cs.utk.edu> To: freebsd-mobile@freebsd.org cc: key@cs.utk.edu Subject: patch for APM suspend/resume and the "calltodo" timer list. Date: Mon, 22 Dec 1997 21:39:00 -0500 From: Ken Key Sender: owner-freebsd-mobile@freebsd.org X-Loop: FreeBSD.org Precedence: bulk Hi Folks, I was holding off sending this patch until I could prove a problem I saw with select() under XF86 v331 was not related to this mod. I just had the failure occur on the unmodified kernel, so I can feel OK about posting this 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 had an hour timeout will fire upon resume if it expired while the machine was suspended rather than firing at 1hr + "time suspended" since the timer was posted. My motivation for doing this was the ISC DHCP client, which uses the select() timeout for calculating T1 and T2 for lease renewal. I still have some other issues with the client, but this addresses the timeout problem. (Actually, using Paul Traina's /etc/pccard_ether that kills and restarts the DHCP client on insert - and thus resume - bypasses this issue). Adding: options APM_FIXUP_CALLTODO to the kernel config enables the patch. regards, Ken Key =====================>8 cut here 8<============================== *** sys/i386/apm/apm.c-orig Mon Dec 22 18:22:25 1997 --- sys/i386/apm/apm.c Mon Dec 22 14:55:34 1997 *************** *** 344,351 **** tmp_time = time; /* because 'time' is volatile */ timevaladd(&tmp_time, &diff_time); time = tmp_time; splx(pl); ! second = resume_time.tv_sec - suspend_time.tv_sec; hour = second / 3600; second %= 3600; minute = second / 60; --- 344,365 ---- 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); ! #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; *** sys/kern/kern_clock.c-orig Mon Dec 22 18:22:47 1997 --- sys/kern/kern_clock.c Mon Dec 22 15:08:24 1997 *************** *** 1290,1292 **** --- 1290,1360 ---- 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, *t; + unsigned long delta_ticks; + int s; + int num_fired; + + /* + * How many ticks were we asleep? + * (stolen from hzto()). + */ + if (time_change->tv_sec < 0) { + /* Don't do anything */ + 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 */