Index: sys/kern/kern_timeout.c =================================================================== --- sys/kern/kern_timeout.c +++ sys/kern/kern_timeout.c @@ -65,6 +65,7 @@ #ifdef DDB #include +#include #include #endif @@ -143,12 +144,14 @@ struct cc_exec { struct callout *cc_curr; void (*cc_drain)(void *); + void *cc_last_func; + void *cc_last_arg; #ifdef SMP void (*ce_migration_func)(void *); void *ce_migration_arg; - int ce_migration_cpu; sbintime_t ce_migration_time; sbintime_t ce_migration_prec; + int ce_migration_cpu; #endif bool cc_cancel; bool cc_waiting; @@ -177,6 +180,8 @@ #define callout_migrating(c) ((c)->c_iflags & CALLOUT_DFRMIGRATION) #define cc_exec_curr(cc, dir) cc->cc_exec_entity[dir].cc_curr +#define cc_exec_last_func(cc, dir) cc->cc_exec_entity[dir].cc_last_func +#define cc_exec_last_arg(cc, dir) cc->cc_exec_entity[dir].cc_last_arg #define cc_exec_drain(cc, dir) cc->cc_exec_entity[dir].cc_drain #define cc_exec_next(cc) cc->cc_next #define cc_exec_cancel(cc, dir) cc->cc_exec_entity[dir].cc_cancel @@ -686,6 +691,8 @@ c->c_iflags &= ~CALLOUT_PENDING; cc_exec_curr(cc, direct) = c; + cc_exec_last_func(cc, direct) = c_func; + cc_exec_last_arg(cc, direct) = c_arg; cc_exec_cancel(cc, direct) = false; cc_exec_drain(cc, direct) = NULL; CC_UNLOCK(cc); @@ -1670,4 +1677,34 @@ _show_callout((struct callout *)addr); } + +static void +_show_last_callout(struct callout_cpu *cc, int direct, const char *dirstr) +{ + void *func, *arg; + + func = cc_exec_last_func(cc, direct); + arg = cc_exec_last_arg(cc, direct); + db_printf("last%s callout function: %p ", dirstr, func); + db_printsym((db_expr_t)func, DB_STGY_ANY); + db_printf("\nlast%s callout argument: %p\n", dirstr, arg); +} + +DB_SHOW_COMMAND(callout_last, db_show_callout_last) +{ + struct callout_cpu *cc; + + if (have_addr) { + if (addr < 0 || addr > mp_maxid || CPU_ABSENT(addr)) { + db_printf("no such cpu: %d\n", (int)addr); + return; + } + cc = CC_CPU(addr); + } else { + cc = CC_CPU(timeout_cpu); + } + + _show_last_callout(cc, 0, ""); + _show_last_callout(cc, 1, " direct"); +} #endif /* DDB */ Index: tools/test/callout_free/Makefile =================================================================== --- /dev/null +++ tools/test/callout_free/Makefile @@ -0,0 +1,4 @@ +KMOD= callout_free +SRCS= callout_free.c + +.include Index: tools/test/callout_free/callout_free.c =================================================================== --- /dev/null +++ tools/test/callout_free/callout_free.c @@ -0,0 +1,87 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Eric van Gyzen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Free a pending callout. This was useful for testing the + * "show callout_last" ddb command. + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct callout callout_free; +static struct mtx callout_free_mutex; +static int callout_free_arg; + +static void +callout_free_func(void *arg) +{ + printf("squirrel!\n"); + mtx_destroy(&callout_free_mutex); + memset(&callout_free, 'C', sizeof(callout_free)); +} + +static int +callout_free_load(module_t mod, int cmd, void *arg) +{ + int error; + + switch (cmd) { + case MOD_LOAD: + mtx_init(&callout_free_mutex, "callout_free", NULL, MTX_DEF); + /* + * Do not pass CALLOUT_RETURNUNLOCKED so the callout + * subsystem will unlock the "destroyed" mutex. + */ + callout_init_mtx(&callout_free, &callout_free_mutex, 0); + printf("callout_free_func = %p\n", callout_free_func); + printf("callout_free_arg = %p\n", &callout_free_arg); + callout_reset(&callout_free, hz/10, callout_free_func, + &callout_free_arg); + error = 0; + break; + + case MOD_UNLOAD: + error = 0; + break; + + default: + error = EOPNOTSUPP; + break; + } + + return (error); +} + +DEV_MODULE(callout_free, callout_free_load, NULL);