svn commit: r302768 - in projects/hps_head: share/man/man9 sys/kern sys/sys

Hans Petter Selasky hselasky at FreeBSD.org
Wed Jul 13 12:26:37 UTC 2016


Author: hselasky
Date: Wed Jul 13 12:26:36 2016
New Revision: 302768
URL: https://svnweb.freebsd.org/changeset/base/302768

Log:
  Implement new callout_init_lock_function() callout API function to
  support locking constructions.
  
  The purpose of this function is to allow a function callback to do
  locking before the callback is called, in order to solve race
  conditions. The function callback, callout_lock_func_t, is passed two
  arguments. The first is the callout's callback argument, and the
  second is an integer, if set, indicates a lock operation, and if
  cleared, indicates an unlock operation.
  
  Some clients of the callout API like the TCP stack use locking
  constructions, that means one more more locks locked in series. By
  using callout_init_lock_function() these clients can lock all the
  locks required in order to atomically to complete their operations,
  instead of using lock-unlock-lock sequences which open up for races.

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

Modified: projects/hps_head/share/man/man9/Makefile
==============================================================================
--- projects/hps_head/share/man/man9/Makefile	Wed Jul 13 11:58:21 2016	(r302767)
+++ projects/hps_head/share/man/man9/Makefile	Wed Jul 13 12:26:36 2016	(r302768)
@@ -1751,6 +1751,7 @@ MLINKS+=timeout.9 callout.9 \
 	timeout.9 callout_init_mtx.9 \
 	timeout.9 callout_init_rm.9 \
 	timeout.9 callout_init_rw.9 \
+	timeout.9 callout_init_lock_function.9 \
 	timeout.9 callout_pending.9 \
 	timeout.9 callout_reset.9 \
 	timeout.9 callout_reset_curcpu.9 \

Modified: projects/hps_head/share/man/man9/timeout.9
==============================================================================
--- projects/hps_head/share/man/man9/timeout.9	Wed Jul 13 11:58:21 2016	(r302767)
+++ projects/hps_head/share/man/man9/timeout.9	Wed Jul 13 12:26:36 2016	(r302768)
@@ -42,6 +42,7 @@
 .Nm callout_init_mtx ,
 .Nm callout_init_rm ,
 .Nm callout_init_rw ,
+.Nm callout_init_lock_function ,
 .Nm callout_pending ,
 .Nm callout_reset ,
 .Nm callout_reset_curcpu ,
@@ -65,6 +66,7 @@
 .Bd -literal
 typedef void timeout_t (void *);
 typedef void callout_func_t (void *);
+typedef void callout_lock_func_t (void *, int do_lock);
 .Ed
 .Ft int
 .Fn callout_active "struct callout *c"
@@ -87,6 +89,8 @@ struct callout_handle handle = CALLOUT_H
 .Fn callout_init_rm "struct callout *c" "struct rmlock *rm" "int flags"
 .Ft void
 .Fn callout_init_rw "struct callout *c" "struct rwlock *rw" "int flags"
+.Ft void
+.Fn callout_init_lock_function "struct callout *c" "callout_lock_func_t *func" "int flags"
 .Ft int
 .Fn callout_pending "struct callout *c"
 .Ft int
@@ -247,6 +251,13 @@ flag.
 This function is similar to
 .Fn callout_init_mtx ,
 but it accepts a read/write type of lock.
+.Pp
+.Ft void
+.Fn callout_init_lock_function "struct callout *c" "callout_lock_func_t *func" "int flags"
+This function is similar to
+.Fn callout_init_mtx ,
+but it accepts a callback function which does locking and unlocking.
+This is useful when more than one lock is involved protecting a structure.
 .Sh SCHEDULING CALLOUTS
 .Ft struct callout_handle
 .Fn timeout "timeout_t *func" "void *arg" "int ticks"

Modified: projects/hps_head/sys/kern/kern_timeout.c
==============================================================================
--- projects/hps_head/sys/kern/kern_timeout.c	Wed Jul 13 11:58:21 2016	(r302767)
+++ projects/hps_head/sys/kern/kern_timeout.c	Wed Jul 13 12:26:36 2016	(r302768)
@@ -136,7 +136,7 @@ struct callout_args {
 	int	cpu;			/* CPU we're scheduled on */
 };
 
-typedef void callout_mutex_op_t(struct lock_object *);
+typedef void callout_mutex_op_t(void *, struct lock_object *);
 
 struct callout_mutex_ops {
 	callout_mutex_op_t *lock;
@@ -147,7 +147,7 @@ enum {
 	CALLOUT_LC_UNUSED_0,
 	CALLOUT_LC_UNUSED_1,
 	CALLOUT_LC_UNUSED_2,
-	CALLOUT_LC_UNUSED_3,
+	CALLOUT_LC_FUNCTION,
 	CALLOUT_LC_SPIN,
 	CALLOUT_LC_MUTEX,
 	CALLOUT_LC_RW,
@@ -155,61 +155,75 @@ enum {
 };
 
 static void
-callout_mutex_op_none(struct lock_object *lock)
+callout_mutex_op_none(void *arg, struct lock_object *lock)
 {
 }
 
 static void
-callout_mutex_lock(struct lock_object *lock)
+callout_function_lock(void *arg, struct lock_object *lock)
+{
+
+	((callout_lock_func_t *)lock)(arg, 1);
+}
+
+static void
+callout_function_unlock(void *arg, struct lock_object *lock)
+{
+
+	((callout_lock_func_t *)lock)(arg, 0);
+}
+
+static void
+callout_mutex_lock(void *arg, struct lock_object *lock)
 {
 
 	mtx_lock((struct mtx *)lock);
 }
 
 static void
-callout_mutex_unlock(struct lock_object *lock)
+callout_mutex_unlock(void *arg, struct lock_object *lock)
 {
 
 	mtx_unlock((struct mtx *)lock);
 }
 
 static void
-callout_mutex_lock_spin(struct lock_object *lock)
+callout_mutex_lock_spin(void *arg, struct lock_object *lock)
 {
 
 	mtx_lock_spin((struct mtx *)lock);
 }
 
 static void
-callout_mutex_unlock_spin(struct lock_object *lock)
+callout_mutex_unlock_spin(void *arg, struct lock_object *lock)
 {
 
 	mtx_unlock_spin((struct mtx *)lock);
 }
 
 static void
-callout_rm_wlock(struct lock_object *lock)
+callout_rm_wlock(void *arg, struct lock_object *lock)
 {
 
 	rm_wlock((struct rmlock *)lock);
 }
 
 static void
-callout_rm_wunlock(struct lock_object *lock)
+callout_rm_wunlock(void *arg, struct lock_object *lock)
 {
 
 	rm_wunlock((struct rmlock *)lock);
 }
 
 static void
-callout_rw_wlock(struct lock_object *lock)
+callout_rw_wlock(void *arg, struct lock_object *lock)
 {
 
 	rw_wlock((struct rwlock *)lock);
 }
 
 static void
-callout_rw_wunlock(struct lock_object *lock)
+callout_rw_wunlock(void *arg, struct lock_object *lock)
 {
 
 	rw_wunlock((struct rwlock *)lock);
@@ -228,9 +242,9 @@ static const struct callout_mutex_ops ca
 		.lock = callout_mutex_op_none,
 		.unlock = callout_mutex_op_none,
 	},
-	[CALLOUT_LC_UNUSED_3] = {
-		.lock = callout_mutex_op_none,
-		.unlock = callout_mutex_op_none,
+	[CALLOUT_LC_FUNCTION] = {
+		.lock = callout_function_lock,
+		.unlock = callout_function_unlock,
 	},
 	[CALLOUT_LC_SPIN] = {
 		.lock = callout_mutex_lock_spin,
@@ -251,17 +265,17 @@ static const struct callout_mutex_ops ca
 };
 
 static inline void
-callout_lock_client(int c_flags, struct lock_object *c_lock)
+callout_lock_client(int c_flags, void *c_arg, struct lock_object *c_lock)
 {
 
-	callout_mutex_ops[CALLOUT_GET_LC(c_flags)].lock(c_lock);
+	callout_mutex_ops[CALLOUT_GET_LC(c_flags)].lock(c_arg, c_lock);
 }
 
 static inline void
-callout_unlock_client(int c_flags, struct lock_object *c_lock)
+callout_unlock_client(int c_flags, void *c_arg, struct lock_object *c_lock)
 {
 
-	callout_mutex_ops[CALLOUT_GET_LC(c_flags)].unlock(c_lock);
+	callout_mutex_ops[CALLOUT_GET_LC(c_flags)].unlock(c_arg, c_lock);
 }
 
 /*
@@ -788,7 +802,7 @@ softclock_call_cc(struct callout *c, str
 
 		/* unlocked region for switching locks */
 
-		callout_lock_client(c_flags, c_lock);
+		callout_lock_client(c_flags, c_arg, c_lock);
 
 		/*
 		 * Check if the callout may have been cancelled while
@@ -798,7 +812,7 @@ softclock_call_cc(struct callout *c, str
 		 */
 		CC_LOCK(cc);
 		if (cc_exec_cancel(cc, direct)) {
-			callout_unlock_client(c_flags, c_lock);
+			callout_unlock_client(c_flags, c_arg, c_lock);
 			goto skip_cc_locked;
 		}
 		if (c_lock == &Giant.lock_object) {
@@ -859,7 +873,7 @@ softclock_call_cc(struct callout *c, str
 	 * "c->c_flags":
 	 */
 	if ((c_flags & CALLOUT_RETURNUNLOCKED) == 0)
-		callout_unlock_client(c_flags, c_lock);
+		callout_unlock_client(c_flags, c_arg, c_lock);
 
 	CC_LOCK(cc);
 
@@ -1203,13 +1217,13 @@ callout_reset_sbt_on(struct callout *c, 
 int
 callout_schedule_on(struct callout *c, int to_ticks, int cpu)
 {
-	return callout_reset_on(c, to_ticks, c->c_func, c->c_arg, cpu);
+	return (callout_reset_on(c, to_ticks, c->c_func, c->c_arg, cpu));
 }
 
 int
 callout_schedule(struct callout *c, int to_ticks)
 {
-	return callout_reset_on(c, to_ticks, c->c_func, c->c_arg, c->c_cpu);
+	return (callout_reset_on(c, to_ticks, c->c_func, c->c_arg, c->c_cpu));
 }
 
 int
@@ -1240,8 +1254,6 @@ callout_drain(struct callout *c)
 	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
 	    "Draining callout");
 
-	callout_lock_client(c->c_flags, c->c_lock);
-
 	/* at this point the "c->c_cpu" field is not changing */
 
 	retval = callout_async_drain(c, &callout_drain_function);
@@ -1256,12 +1268,6 @@ callout_drain(struct callout *c)
 		cc = callout_lock(c);
 		direct = ((c->c_flags & CALLOUT_DIRECT) != 0);
 
-		/*
-		 * We've gotten our callout CPU lock, it is safe to
-		 * drop the initial lock:
-		 */
-		callout_unlock_client(c->c_flags, c->c_lock);
-
 		/* Wait for drain to complete */
 		while (cc_exec_curr(cc, direct) == c) {
 			msleep_spin(&callout_drain_function,
@@ -1269,8 +1275,6 @@ callout_drain(struct callout *c)
 		}
 
 		CC_UNLOCK(cc);
-	} else {
-		callout_unlock_client(c->c_flags, c->c_lock);
 	}
 
 	CTR4(KTR_CALLOUT, "%s: %p func %p arg %p",
@@ -1284,18 +1288,34 @@ void
 callout_init(struct callout *c, int mpsafe)
 {
 	if (mpsafe) {
-		_callout_init_lock(c, NULL, CALLOUT_RETURNUNLOCKED);
+		callout_init_lock_object(c, NULL, CALLOUT_RETURNUNLOCKED);
 	} else {
-		_callout_init_lock(c, &Giant.lock_object, 0);
+		callout_init_lock_object(c, &Giant.lock_object, 0);
 	}
 }
 
 void
-_callout_init_lock(struct callout *c, struct lock_object *lock, int flags)
+callout_init_lock_function(struct callout *c, callout_lock_func_t *lock_fn, int flags)
+{
+	bzero(c, sizeof *c);
+
+	KASSERT((flags & ~CALLOUT_RETURNUNLOCKED) == 0,
+	    ("callout_init_lock_function: bad flags 0x%08x", flags));
+	KASSERT(lock_fn != NULL,
+	    ("callout_init_lock_function: lock function is NULL"));
+	flags &= CALLOUT_RETURNUNLOCKED;
+	flags |= CALLOUT_SET_LC(CALLOUT_LC_FUNCTION);
+	c->c_lock = (struct lock_object *)lock_fn;
+	c->c_flags = flags;
+	c->c_cpu = timeout_cpu;
+}
+
+void
+callout_init_lock_object(struct callout *c, struct lock_object *lock, int flags)
 {
 	bzero(c, sizeof *c);
 	KASSERT((flags & ~CALLOUT_RETURNUNLOCKED) == 0,
-	    ("callout_init_lock: bad flags 0x%08x", flags));
+	    ("callout_init_lock_object: bad flags 0x%08x", flags));
 	flags &= CALLOUT_RETURNUNLOCKED;
 	if (lock != NULL) {
 		struct lock_class *class = LOCK_CLASS(lock);
@@ -1308,7 +1328,8 @@ _callout_init_lock(struct callout *c, st
 		else if (class == &lock_class_rw)
 			flags |= CALLOUT_SET_LC(CALLOUT_LC_RW);
 		else
-			panic("callout_init_lock: Unsupported lock class '%s'\n", class->lc_name);
+			panic("callout_init_lock_object: Unsupported lock class '%s'\n",
+			    class->lc_name);
 	} else {
 		flags |= CALLOUT_SET_LC(CALLOUT_LC_UNUSED_0);
 	}

Modified: projects/hps_head/sys/sys/_callout.h
==============================================================================
--- projects/hps_head/sys/sys/_callout.h	Wed Jul 13 11:58:21 2016	(r302767)
+++ projects/hps_head/sys/sys/_callout.h	Wed Jul 13 12:26:36 2016	(r302768)
@@ -47,6 +47,7 @@ SLIST_HEAD(callout_slist, callout);
 TAILQ_HEAD(callout_tailq, callout);
 
 typedef void callout_func_t(void *);
+typedef void callout_lock_func_t(void *, int);
 
 struct callout {
 	union {
@@ -58,7 +59,7 @@ struct callout {
 	sbintime_t c_precision;			/* delta allowed wrt opt */
 	void	*c_arg;				/* function argument */
 	callout_func_t *c_func;			/* function to call */
-	struct lock_object *c_lock;		/* lock to handle */
+	struct lock_object *c_lock;		/* pointer to lock handle */
 	int	c_flags;			/* state of this entry */
 	volatile int c_cpu;			/* CPU we're scheduled on */
 };

Modified: projects/hps_head/sys/sys/callout.h
==============================================================================
--- projects/hps_head/sys/sys/callout.h	Wed Jul 13 11:58:21 2016	(r302767)
+++ projects/hps_head/sys/sys/callout.h	Wed Jul 13 12:26:36 2016	(r302768)
@@ -75,15 +75,16 @@ struct callout_handle {
 int	callout_drain(struct callout *);
 int	callout_async_drain(struct callout *, callout_func_t *);
 void	callout_init(struct callout *, int);
-void	_callout_init_lock(struct callout *, struct lock_object *, int);
-#define	callout_init_mtx(c, mtx, flags)					\
-	_callout_init_lock((c), ((mtx) != NULL) ? &(mtx)->lock_object :	\
+void	callout_init_lock_function(struct callout *, callout_lock_func_t *, int);
+void	callout_init_lock_object(struct callout *, struct lock_object *, int);
+#define	callout_init_mtx(c, mtx, flags)	\
+	callout_init_lock_object((c), ((mtx) != NULL) ? &(mtx)->lock_object : \
 	    NULL, (flags))
-#define	callout_init_rm(c, rm, flags)					\
-	_callout_init_lock((c), ((rm) != NULL) ? &(rm)->lock_object : 	\
+#define	callout_init_rm(c, rm, flags) \
+	callout_init_lock_object((c), ((rm) != NULL) ? &(rm)->lock_object : \
 	    NULL, (flags))
-#define	callout_init_rw(c, rw, flags)					\
-	_callout_init_lock((c), ((rw) != NULL) ? &(rw)->lock_object :	\
+#define	callout_init_rw(c, rw, flags) \
+	callout_init_lock_object((c), ((rw) != NULL) ? &(rw)->lock_object : \
 	   NULL, (flags))
 #define	callout_pending(c)	((c)->c_flags & CALLOUT_PENDING)
 int	callout_reset_sbt_on(struct callout *, sbintime_t, sbintime_t,


More information about the svn-src-projects mailing list