Re: callout_drain either broken or man page needs updating

From: Matthew Macy <mmacy_at_nextbsd.org>
Date: Thu, 14 Jul 2016 22:14:46 -0700
 ---- On Thu, 14 Jul 2016 21:21:57 -0700 Hans Petter Selasky <hps_at_selasky.org> wrote ---- 
 > On 07/15/16 05:45, Matthew Macy wrote: 
 > > glebius last commit needs some further re-work. 
 >  
 > Hi, 
 >  
 > Glebius commit needs to be backed out, at least the API change that  
 > changes the return value when calling callout_stop() when the callout is  
 > scheduled and being serviced. Simply because there is code out there,  
 > like Mattew and others have discovered that is "refcounting" on the  
 > callout_reset() and expecting that a subsequent callout_stop() will  
 > return 1 to "unref". 

Yes. This is the cause of the "refcnt 0 on LLE at boot..." regression.

-M



 >  
 > If you consider this impossible, maybe a fourth return value is needed  
 > for CANCELLED and DRAINING . 
 >  
 > Further, getting the callouts straight in the TCP stack is a matter of  
 > doing the locking correctly, which some has called "my magic bullet" and  
 > not the return values. I've proposed in the following revision  
 > https://svnweb.freebsd.org/changeset/base/302768 to add a new callout  
 > API that accepts a locking function so that the callout code can run its  
 > cancelled checks at the right place for situations where more than one  
 > lock is needed. 
 >  
 > Consider this case: 
 >  
 > > void 
 > > tcp_timer_2msl(void *xtp) 
 > > { 
 > >         struct tcpcb *tp = xtp; 
 > >         struct inpcb *inp; 
 > >         CURVNET_SET(tp->t_vnet); 
 > > #ifdef TCPDEBUG 
 > >         int ostate; 
 > > 
 > >         ostate = tp->t_state; 
 > > #endif 
 > >         INP_INFO_RLOCK(&V_tcbinfo); 
 > >         inp = tp->t_inpcb; 
 > >         KASSERT(inp != NULL, ("%s: tp %p tp->t_inpcb == NULL", __func__, tp)); 
 > >         INP_WLOCK(inp); 
 > >         tcp_free_sackholes(tp); 
 > >         if (callout_pending(&tp->t_timers->tt_2msl) || 
 > >             !callout_active(&tp->t_timers->tt_2msl)) { 
 >  
 > Here we have custom in-house race check that doesn't affect the return  
 > value of callout_reset() nor callout_stop(). 
 >  
 > >                 INP_WUNLOCK(tp->t_inpcb); 
 > >                 INP_INFO_RUNLOCK(&V_tcbinfo); 
 > >                 CURVNET_RESTORE(); 
 > >                 return; 
 >  
 >  
 > I propose the following solution: 
 >  
 > > 
 > > static void 
 > > tcp_timer_2msl_lock(void *xtp, int do_lock) 
 > > { 
 > >     struct tcpcb *tp = xtp; 
 > >     struct inpcb *inp; 
 > > 
 > >     inp = tp->t_inpcb; 
 > > 
 > >     if (do_lock) { 
 > >         CURVNET_SET(tp->t_vnet); 
 > >         INP_INFO_RLOCK(&V_tcbinfo); 
 > >         INP_WLOCK(inp); 
 > >     } else { 
 > >         INP_WUNLOCK(inp); 
 > >         INP_INFO_RUNLOCK(&V_tcbinfo); 
 > >         CURVNET_RESTORE(); 
 > >     } 
 > > } 
 > > 
 >  
 > callout_init_lock_function(&callout, &tcp_timer_2msl_lock,  
 > CALLOUT_RETURNUNLOCKED); 
 >  
 > Then in softclock_call_cc() it will look like this: 
 >  
 > > 
 > >     CC_UNLOCK(cc); 
 > >     if (c_lock != NULL) { 
 > >         if (have locking function) 
 > >             tcp_timer_2msl_lock(c_arg, 1); 
 > >         else 
 > >             class->lc_lock(c_lock, lock_status); 
 > >         /* 
 > >          * The callout may have been cancelled 
 > >          * while we switched locks. 
 > >          */ 
 >  
 > Actually "CC_LOCK(cc)" should be in-front of cc_exec_cancel() to avoid  
 > races testing, setting and clearing this variable, like done in hps_head. 
 >  
 > >         if (cc_exec_cancel(cc, direct)) { 
 > >             if (have locking function) 
 > >                 tcp_timer_2msl_lock(c_arg, 0); 
 > >             else 
 > >                 class->lc_unlock(c_lock); 
 > >             goto skip; 
 > >                } 
 >  >                cc_exec_cancel(cc, direct) = true; 
 > > 
 > > .... 
 > > 
 > > skip: 
 > >     if ((c_iflags & CALLOUT_RETURNUNLOCKED) == 0) { 
 > >         if (have locking function) 
 > >             ... 
 > >         else 
 > >             class->lc_unlock(c_lock); 
 > >     } 
 >  
 > The whole point about this is to make the the cancelled check atomic. 
 >  
 > 1) Lock TCP 
 > 2) Lock CC_LOCK() 
 > 3) change callout state 
 >  
 > --HPS 
 > _______________________________________________ 
 > freebsd-current_at_freebsd.org mailing list 
 > https://lists.freebsd.org/mailman/listinfo/freebsd-current 
 > To unsubscribe, send any mail to "freebsd-current-unsubscribe_at_freebsd.org" 
 > 
Received on Fri Jul 15 2016 - 03:14:49 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:41:06 UTC