From doublef-ctm@yandex.ru  Fri Sep  1 11:03:19 2006
Return-Path: <doublef-ctm@yandex.ru>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id 3345D16A4DE
	for <FreeBSD-gnats-submit@freebsd.org>; Fri,  1 Sep 2006 11:03:19 +0000 (UTC)
	(envelope-from doublef-ctm@yandex.ru)
Received: from mx18.yandex.ru (smtp2.yandex.ru [213.180.200.18])
	by mx1.FreeBSD.org (Postfix) with ESMTP id 50C4B43D55
	for <FreeBSD-gnats-submit@freebsd.org>; Fri,  1 Sep 2006 11:03:17 +0000 (GMT)
	(envelope-from doublef-ctm@yandex.ru)
Received: from [83.239.189.161] ([83.239.189.161]:8646 "EHLO shark" smtp-auth:
	"doublef-ctm" TLS-CIPHER: <none> TLS-PEER-CN1: <none>)
	by mail.yandex.ru with ESMTP id S3375808AbWIALDP (ORCPT
	<rfc822;FreeBSD-gnats-submit@freebsd.org>);
	Fri, 1 Sep 2006 15:03:15 +0400
Received: by shark (Postfix, from userid 1000)
	id 416A89DB4C; Fri,  1 Sep 2006 15:02:40 +0400 (MSD)
Message-Id: <20060901110240.416A89DB4C@shark>
Date: Fri,  1 Sep 2006 15:02:40 +0400 (MSD)
From: Sergey Zaharchenko <doublef-ctm@yandex.ru>
To: FreeBSD-gnats-submit@freebsd.org
Cc: Brian Somers <brian@Awfulhak.org>
Subject: ppp timer subsystem stops under certain circumstances
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         102747
>Category:       bin
>Synopsis:       [ppp] [patch] ppp timer subsystem stops under certain circumstances
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    brian
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Fri Sep 01 11:10:21 GMT 2006
>Closed-Date:    Wed Jun 17 02:21:14 UTC 2009
>Last-Modified:  Wed Jun 17 02:30:01 UTC 2009
>Originator:     Sergey Zaharchenko
>Release:        FreeBSD 7.0-CURRENT i386
>Organization:
Volgograd State Technical University
>Environment:
System: FreeBSD shark.localdomain 7.0-CURRENT FreeBSD 7.0-CURRENT #1:
Mon Aug 28 15:41:24 UTC 2006
root@shark.localdomain:/var/obj/src/usr.src/sys/GENERIC i386
>Description:
There are certain situations which cause the ppp timer subsystem to stop
(that is, there are timers in the list, but none of them ever get
triggered). This is caused by calling setitimer() with a zero it_value
in timer_InitService(). Killing ppp with SIGALRM triggers the first
timer and resumes normal operation. Here's a description of one of the
situations when the offending setitimer() call could happen.

If two timers (call them A and B) are near expiration (less than ~0.05
seconds left before triggering; A is first in the list; B->rest==0,
because they trigger at roughly the same time), and, at the same time,
ppp decides to stop A using StopTimerNoBlock(), the call adjusts
TimerList->rest to the itimer's value ``so that it reflects what's
really happening''.

Now A->rest==RESTVAL(small time value)==0, B->rest==0. Then
B->rest+=A->rest; 0+0==0; B->rest is still zero. A is dequeued, B is now
first in the list, and TimerList==B. timer_InitService() is called,
because the first timer was deleted.

timer_InitService() calls setitimer() with it_value==(a conversion
of)B->rest==0, which stops the itimer according to getitimer(2). If a
timer is later added, B will still sit there with rest==0, preventing
the itimer from launching. Neither B nor any other timer will ever be
serviced, unless some magic intervenes. An example of such magic is the
user killing ppp with SIGALRM. Then B will get serviced and dequeued,
and itimer relaunched. This is not the only situation when
TimerList->rest==0.

>How-To-Repeat:
The described situation appears only rarely in ordinary practice. They
happen when a lot of timers are being started, stopped and expired (for
example, just after the authentification succeeds). A way of detecting
the situation would be to insert the following line into timer.c:

     if (setitimer(ITIMER_REAL, &itimer, NULL) == -1)
       log_Printf(LogERROR, "Unable to set itimer (%s)\n", strerror(errno));
+    if (TimerList->rest==0) raise(SIGSTOP);
   }

recompile and reinstall ppp, add "set log timer" to ppp.conf and run ppp
in the following script:

#!/bin/sh
ppp -dd whatever
# We assume 100 seconds is enough to connect and authenticate to the peer
until ps -o state,comm | grep '^T *ppp$'; do killall -INT ppp; sleep 100;done

The script should exit when the offending call has just been issued. You
can then ``killall -CONT ppp'' and observe the absence of any timer
activity in the log. You can ``killall -ALRM ppp'' to revive it.

>Fix:
Never calling timer_InitService() when TimerList->rest==0 seems to be
the right thing to do.

The following is a patch which avoids situations when TimerList->rest==0
and timer_InitService() is called. 

---------------------------------------------------------------------cut here
--- ppp-7.0-CURRENT.orig/timer.c	Wed Jan  5 19:25:29 2005
+++ ppp-7.0-CURRENT/timer.c	Fri Sep  1 13:37:08 2006
@@ -94,9 +94,16 @@
     return;
   }
 
-  /* Adjust our first delta so that it reflects what's really happening */
+  /*
+   * Adjust the tick count so that it roughly corresponds to reality.
+   * Changing TimerList->rest might cause it to become zero (if
+   * getitimer() returns a value close to zero), and the
+   * timer_InitService() call will call setitimer() with zero it_value,
+   * stopping the itimer.
+   */
+
   if (TimerList && getitimer(ITIMER_REAL, &itimer) == 0)
-    TimerList->rest = RESTVAL(itimer);
+    ticks=(RESTVAL(itimer)-(long)TimerList->rest);
 
   pt = NULL;
   for (t = TimerList; t; t = t->next) {
@@ -156,14 +163,24 @@
 	timer_TermService();	/* Terminate Timer Service */
     }
     if (t->next) {
+      unsigned t_rest=t->rest;
       if (!pt) {		/* t (tp) was the first in the list */
         struct itimerval itimer;
 
         if (getitimer(ITIMER_REAL, &itimer) == 0)
-          t->rest = RESTVAL(itimer);
+          t_rest = RESTVAL(itimer);
       }
-      t->next->rest += t->rest;
-      if (!pt)			/* t->next is now the first in the list */
+      t->next->rest += t_rest;
+
+      /*
+       * It's still possible for t->next->rest to be zero (just
+       * because the next timer should trigger right after the
+       * one being stopped), in which case calling timer_InitService()
+       * would be a bad idea. Don't do that, then. In this case, the
+       * SIGALRM will trigger the next timer, so that's OK.
+       */
+
+      if (!pt&&(t->next->rest>0)) /* t->next is now the first in the list */
         timer_InitService(1);
     }
   } else {
@@ -235,11 +252,18 @@
 {
   struct itimerval itimer;
   struct pppTimer *pt;
-  u_long rest = 0;
+  long rest = 0;	/* should allow negative values */
+
+  /*
+   * Adjust the base time so that the deltas reflect what's really
+   * happening. Changing TimerList->rest might cause it to become zero
+   * (if getitimer() returns a value close to zero), and the
+   * timer_InitService() call will call setitimer() with zero it_value,
+   * stopping the itimer.
+   */
 
-  /* Adjust our first delta so that it reflects what's really happening */
   if (TimerList && getitimer(ITIMER_REAL, &itimer) == 0)
-    TimerList->rest = RESTVAL(itimer);
+    rest=(RESTVAL(itimer)-(long)TimerList->rest);
 
 #define SECS(val)	((val) / SECTICKS)
 #define HSECS(val)	(((val) % SECTICKS) * 100 / SECTICKS)
---------------------------------------------------------------------cut here

Another way to fix the problem is to simply check for TimerList->rest==0
after setitimer() in timer_InitService() and do a raise(SIGALRM) then.
It's simpler, but some of the accuracy is lost.
>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->brian 
Responsible-Changed-By: glebius 
Responsible-Changed-When: Sun Sep 3 13:56:36 UTC 2006 
Responsible-Changed-Why:  
Assign to ppp(8) maintainer. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=102747 
State-Changed-From-To: open->patched 
State-Changed-By: brian 
State-Changed-When: Tue May 26 05:30:20 UTC 2009 
State-Changed-Why:  
Fixed in head - r192798. 
I'll MFC in 3 weeks. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=102747 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/102747: commit references a PR
Date: Tue, 26 May 2009 07:32:18 +0000 (UTC)

 Author: brian
 Date: Tue May 26 07:32:08 2009
 New Revision: 192798
 URL: http://svn.freebsd.org/changeset/base/192798
 
 Log:
   Fix a race that can stall the timer when we remove a timer that has another
   timer with a <0.05 second delta next to it.
   
   This is done by avoiding the possibility of updating the first residual
   time delta in the timer list to zero.
   
   PR:		102747
   Submitted by:	Sergey Zaharchenko - doublef-ctm at yandex dot ru
   MFC after:	3 weeks
 
 Modified:
   head/usr.sbin/ppp/timer.c
 
 Modified: head/usr.sbin/ppp/timer.c
 ==============================================================================
 --- head/usr.sbin/ppp/timer.c	Tue May 26 07:29:17 2009	(r192797)
 +++ head/usr.sbin/ppp/timer.c	Tue May 26 07:32:08 2009	(r192798)
 @@ -1,5 +1,5 @@
  /*-
 - * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
 + * Copyright (c) 1996 - 2001, 2009 Brian Somers <brian@Awfulhak.org>
   *          based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
   *                           Internet Initiative Japan, Inc (IIJ)
   * All rights reserved.
 @@ -94,9 +94,12 @@ timer_Start(struct pppTimer *tp)
      return;
    }
  
 -  /* Adjust our first delta so that it reflects what's really happening */
 +  /*
 +   * We just need to insert tp in the correct relative place.  We don't
 +   * need to adjust TimerList->rest (yet).
 +   */
    if (TimerList && getitimer(ITIMER_REAL, &itimer) == 0)
 -    TimerList->rest = RESTVAL(itimer);
 +    ticks = RESTVAL(itimer) - TimerList->rest;
  
    pt = NULL;
    for (t = TimerList; t; t = t->next) {
 @@ -132,6 +135,7 @@ timer_Start(struct pppTimer *tp)
  static void
  StopTimerNoBlock(struct pppTimer *tp)
  {
 +  struct itimerval itimer;
    struct pppTimer *t, *pt;
  
    /*
 @@ -156,14 +160,11 @@ StopTimerNoBlock(struct pppTimer *tp)
  	timer_TermService();	/* Terminate Timer Service */
      }
      if (t->next) {
 -      if (!pt) {		/* t (tp) was the first in the list */
 -        struct itimerval itimer;
 -
 -        if (getitimer(ITIMER_REAL, &itimer) == 0)
 -          t->rest = RESTVAL(itimer);
 -      }
 -      t->next->rest += t->rest;
 -      if (!pt)			/* t->next is now the first in the list */
 +      if (!pt && getitimer(ITIMER_REAL, &itimer) == 0)
 +        t->next->rest += RESTVAL(itimer); /* t (tp) was the first in the list */
 +      else
 +        t->next->rest += t->rest;
 +      if (!pt && t->next->rest > 0)   /* t->next is now the first in the list */
          timer_InitService(1);
      }
    } else {
 @@ -235,11 +236,19 @@ timer_Show(int LogLevel, struct prompt *
  {
    struct itimerval itimer;
    struct pppTimer *pt;
 -  u_long rest = 0;
 +  long rest;
  
 -  /* Adjust our first delta so that it reflects what's really happening */
 +  /*
 +   * Adjust the base time so that the deltas reflect what's really
 +   * happening.  Changing TimerList->rest might cause it to become zero
 +   * (if getitimer() returns a value close to zero), and the
 +   * timer_InitService() call will call setitimer() with zero it_value,
 +   * stopping the itimer... so be careful!
 +   */
    if (TimerList && getitimer(ITIMER_REAL, &itimer) == 0)
 -    TimerList->rest = RESTVAL(itimer);
 +    rest = RESTVAL(itimer) - TimerList->rest;
 +  else
 +    rest = 0;
  
  #define SECS(val)	((val) / SECTICKS)
  #define HSECS(val)	(((val) % SECTICKS) * 100 / SECTICKS)
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
State-Changed-From-To: patched->closed 
State-Changed-By: brian 
State-Changed-When: Wed Jun 17 02:20:46 UTC 2009 
State-Changed-Why:  
Submitted to stable - r194318 

http://www.freebsd.org/cgi/query-pr.cgi?pr=102747 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/102747: commit references a PR
Date: Wed, 17 Jun 2009 02:20:36 +0000 (UTC)

 Author: brian
 Date: Wed Jun 17 02:20:26 2009
 New Revision: 194318
 URL: http://svn.freebsd.org/changeset/base/194318
 
 Log:
   MFC: r192798: Fix a race that can stall the timer
   
   PR:		102747
   Submitted by:	Sergey Zaharchenko - doublef-ctm at yandex dot ru
 
 Modified:
   stable/7/usr.sbin/ppp/   (props changed)
   stable/7/usr.sbin/ppp/timer.c
 
 Modified: stable/7/usr.sbin/ppp/timer.c
 ==============================================================================
 --- stable/7/usr.sbin/ppp/timer.c	Wed Jun 17 01:55:42 2009	(r194317)
 +++ stable/7/usr.sbin/ppp/timer.c	Wed Jun 17 02:20:26 2009	(r194318)
 @@ -1,5 +1,5 @@
  /*-
 - * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
 + * Copyright (c) 1996 - 2001, 2009 Brian Somers <brian@Awfulhak.org>
   *          based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
   *                           Internet Initiative Japan, Inc (IIJ)
   * All rights reserved.
 @@ -94,9 +94,12 @@ timer_Start(struct pppTimer *tp)
      return;
    }
  
 -  /* Adjust our first delta so that it reflects what's really happening */
 +  /*
 +   * We just need to insert tp in the correct relative place.  We don't
 +   * need to adjust TimerList->rest (yet).
 +   */
    if (TimerList && getitimer(ITIMER_REAL, &itimer) == 0)
 -    TimerList->rest = RESTVAL(itimer);
 +    ticks = RESTVAL(itimer) - TimerList->rest;
  
    pt = NULL;
    for (t = TimerList; t; t = t->next) {
 @@ -132,6 +135,7 @@ timer_Start(struct pppTimer *tp)
  static void
  StopTimerNoBlock(struct pppTimer *tp)
  {
 +  struct itimerval itimer;
    struct pppTimer *t, *pt;
  
    /*
 @@ -156,14 +160,11 @@ StopTimerNoBlock(struct pppTimer *tp)
  	timer_TermService();	/* Terminate Timer Service */
      }
      if (t->next) {
 -      if (!pt) {		/* t (tp) was the first in the list */
 -        struct itimerval itimer;
 -
 -        if (getitimer(ITIMER_REAL, &itimer) == 0)
 -          t->rest = RESTVAL(itimer);
 -      }
 -      t->next->rest += t->rest;
 -      if (!pt)			/* t->next is now the first in the list */
 +      if (!pt && getitimer(ITIMER_REAL, &itimer) == 0)
 +        t->next->rest += RESTVAL(itimer); /* t (tp) was the first in the list */
 +      else
 +        t->next->rest += t->rest;
 +      if (!pt && t->next->rest > 0)   /* t->next is now the first in the list */
          timer_InitService(1);
      }
    } else {
 @@ -235,11 +236,19 @@ timer_Show(int LogLevel, struct prompt *
  {
    struct itimerval itimer;
    struct pppTimer *pt;
 -  u_long rest = 0;
 +  long rest;
  
 -  /* Adjust our first delta so that it reflects what's really happening */
 +  /*
 +   * Adjust the base time so that the deltas reflect what's really
 +   * happening.  Changing TimerList->rest might cause it to become zero
 +   * (if getitimer() returns a value close to zero), and the
 +   * timer_InitService() call will call setitimer() with zero it_value,
 +   * stopping the itimer... so be careful!
 +   */
    if (TimerList && getitimer(ITIMER_REAL, &itimer) == 0)
 -    TimerList->rest = RESTVAL(itimer);
 +    rest = RESTVAL(itimer) - TimerList->rest;
 +  else
 +    rest = 0;
  
  #define SECS(val)	((val) / SECTICKS)
  #define HSECS(val)	(((val) % SECTICKS) * 100 / SECTICKS)
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
>Unformatted:
