From owner-svn-src-projects@freebsd.org Wed Jul 13 12:26:37 2016 Return-Path: Delivered-To: svn-src-projects@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id D616EB9325D for ; Wed, 13 Jul 2016 12:26:37 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 9F9FD14D0; Wed, 13 Jul 2016 12:26:37 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id u6DCQa3O057025; Wed, 13 Jul 2016 12:26:36 GMT (envelope-from hselasky@FreeBSD.org) Received: (from hselasky@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id u6DCQa7D057020; Wed, 13 Jul 2016 12:26:36 GMT (envelope-from hselasky@FreeBSD.org) Message-Id: <201607131226.u6DCQa7D057020@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: hselasky set sender to hselasky@FreeBSD.org using -f From: Hans Petter Selasky Date: Wed, 13 Jul 2016 12:26:36 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r302768 - in projects/hps_head: share/man/man9 sys/kern sys/sys X-SVN-Group: projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 13 Jul 2016 12:26:37 -0000 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,