Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 22 Dec 1997 21:39:00 -0500
From:      Ken Key <key@cs.utk.edu>
To:        freebsd-mobile@freebsd.org
Cc:        key@cs.utk.edu
Subject:   patch for APM suspend/resume and the "calltodo" timer list.
Message-ID:  <199712230239.VAA02986@duncan.cs.utk.edu>

next in thread | raw e-mail | index | archive | help
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  <key@cs.utk.edu>

=====================>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 <key@cs.utk.edu>
+  */
+ 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 */






Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199712230239.VAA02986>