Author: hselasky
Date: Mon Sep 14 10:52:26 2015
New Revision: 287780
URL: https://svnweb.freebsd.org/changeset/base/287780

Log:
  Implement callout_drain_async(), inspired by the projects/hps_head
  branch.
  
  This function is used to drain a callout via a callback instead of
  blocking the caller until the drain is complete. Refer to the
  callout_drain_async() manual page for a detailed description.
  
  Limitation: If a lock is used with the callout, the callout can only
  be drained asynchronously one time unless the callout_init_mtx()
  function is called again. This limitation is not present in
  projects/hps_head and will require more invasive changes to the
  timeout code, which was not in the scope of this patch.
  
  Differential Revision:        https://reviews.freebsd.org/D3521
  Reviewed by:          wblock
  MFC after:            1 month

Modified:
  head/share/man/man9/Makefile
  head/share/man/man9/timeout.9
  head/sys/kern/kern_timeout.c
  head/sys/sys/_callout.h
  head/sys/sys/callout.h

Modified: head/share/man/man9/Makefile
==============================================================================
--- head/share/man/man9/Makefile        Mon Sep 14 10:28:47 2015        
(r287779)
+++ head/share/man/man9/Makefile        Mon Sep 14 10:52:26 2015        
(r287780)
@@ -1641,6 +1641,7 @@ MLINKS+=timeout.9 callout.9 \
        timeout.9 callout_active.9 \
        timeout.9 callout_deactivate.9 \
        timeout.9 callout_drain.9 \
+       timeout.9 callout_drain_async.9 \
        timeout.9 callout_handle_init.9 \
        timeout.9 callout_init.9 \
        timeout.9 callout_init_mtx.9 \

Modified: head/share/man/man9/timeout.9
==============================================================================
--- head/share/man/man9/timeout.9       Mon Sep 14 10:28:47 2015        
(r287779)
+++ head/share/man/man9/timeout.9       Mon Sep 14 10:52:26 2015        
(r287780)
@@ -36,6 +36,7 @@
 .Nm callout_active ,
 .Nm callout_deactivate ,
 .Nm callout_drain ,
+.Nm callout_drain_async ,
 .Nm callout_handle_init ,
 .Nm callout_init ,
 .Nm callout_init_mtx ,
@@ -70,6 +71,8 @@ typedef void timeout_t (void *);
 .Fn callout_deactivate "struct callout *c"
 .Ft int
 .Fn callout_drain "struct callout *c"
+.Ft int
+.Fn callout_drain_async "struct callout *c" "callout_func_t *fn" "void *arg"
 .Ft void
 .Fn callout_handle_init "struct callout_handle *handle"
 .Bd -literal
@@ -264,6 +267,24 @@ fully stopped before
 .Fn callout_drain
 returns.
 .Pp
+The function
+.Fn callout_drain_async
+is non-blocking and works the same as the
+.Fn callout_stop
+function.
+When this function returns non-zero, do not call it again until the callback 
function given by
+.Fa fn
+has been called with argument
+.Fa arg .
+Only one of
+.Fn callout_drain
+or
+.Fn callout_drain_async
+should be called at a time to drain a callout.
+If this function returns zero, it is safe to free the callout structure 
pointed to by the
+.Fa c
+argument immediately.
+.Pp
 The
 .Fn callout_reset
 and

Modified: head/sys/kern/kern_timeout.c
==============================================================================
--- head/sys/kern/kern_timeout.c        Mon Sep 14 10:28:47 2015        
(r287779)
+++ head/sys/kern/kern_timeout.c        Mon Sep 14 10:52:26 2015        
(r287780)
@@ -1145,6 +1145,45 @@ callout_schedule(struct callout *c, int 
 }
 
 int
+callout_drain_async(struct callout *c, callout_func_t *func, void *arg)
+{
+       struct callout_cpu *cc;
+       struct lock_class *class;
+       int retval;
+       int direct;
+
+       /* stop callout */
+       callout_stop(c);
+
+       /* check if callback is being called */
+       cc = callout_lock(c);
+       if (c->c_iflags & CALLOUT_DIRECT) {
+               direct = 1;
+       } else {
+               direct = 0;
+       }
+       retval = (cc_exec_curr(cc, direct) == c);
+
+       /* drop locks, if any */
+       if (retval && c->c_lock != NULL &&
+           c->c_lock != &Giant.lock_object) {
+               /* ensure we are properly locked */
+               class = LOCK_CLASS(c->c_lock);
+               class->lc_assert(c->c_lock, LA_XLOCKED);
+               /* the final callback should not be called locked */
+               c->c_lock = NULL;
+               c->c_iflags |= CALLOUT_RETURNUNLOCKED;
+       }
+       CC_UNLOCK(cc);
+
+       /* check if we should queue final callback */
+       if (retval)
+               callout_reset(c, 1, func, arg);
+
+       return (retval);
+}
+
+int
 _callout_stop_safe(struct callout *c, int safe)
 {
        struct callout_cpu *cc, *old_cc;

Modified: head/sys/sys/_callout.h
==============================================================================
--- head/sys/sys/_callout.h     Mon Sep 14 10:28:47 2015        (r287779)
+++ head/sys/sys/_callout.h     Mon Sep 14 10:52:26 2015        (r287780)
@@ -46,6 +46,8 @@ LIST_HEAD(callout_list, callout);
 SLIST_HEAD(callout_slist, callout);
 TAILQ_HEAD(callout_tailq, callout);
 
+typedef void callout_func_t(void *);
+
 struct callout {
        union {
                LIST_ENTRY(callout) le;

Modified: head/sys/sys/callout.h
==============================================================================
--- head/sys/sys/callout.h      Mon Sep 14 10:28:47 2015        (r287779)
+++ head/sys/sys/callout.h      Mon Sep 14 10:52:26 2015        (r287780)
@@ -82,6 +82,7 @@ struct callout_handle {
 #define        callout_active(c)       ((c)->c_flags & CALLOUT_ACTIVE)
 #define        callout_deactivate(c)   ((c)->c_flags &= ~CALLOUT_ACTIVE)
 #define        callout_drain(c)        _callout_stop_safe(c, 1)
+int    callout_drain_async(struct callout *, callout_func_t *, void *);
 void   callout_init(struct callout *, int);
 void   _callout_init_lock(struct callout *, struct lock_object *, int);
 #define        callout_init_mtx(c, mtx, flags)                                 
\
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to