summaryrefslogtreecommitdiff
path: root/sys/kern
diff options
context:
space:
mode:
authorVisa Hankala <visa@cvs.openbsd.org>2018-05-16 14:53:44 +0000
committerVisa Hankala <visa@cvs.openbsd.org>2018-05-16 14:53:44 +0000
commit4f333c3b2e0f1d3454aee26e003bf33ec0fdcdb5 (patch)
treee9351725d004a27632843811a7a62952119adcba /sys/kern
parentd3cb355fa2aa377efebbeeeb7f9d2cd732761849 (diff)
Add kern.witnesswatch sysctl for controlling witness(4). By default,
lock order checking is disabled but it can be enabled at runtime. Suggested by deraadt@ / mpi@ OK mpi@
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/kern_sysctl.c7
-rw-r--r--sys/kern/subr_witness.c65
2 files changed, 58 insertions, 14 deletions
diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c
index 56134f4d597..439fa57fe7d 100644
--- a/sys/kern/kern_sysctl.c
+++ b/sys/kern/kern_sysctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_sysctl.c,v 1.336 2018/05/08 14:15:30 mpi Exp $ */
+/* $OpenBSD: kern_sysctl.c,v 1.337 2018/05/16 14:53:43 visa Exp $ */
/* $NetBSD: kern_sysctl.c,v 1.17 1996/05/20 17:49:05 mrg Exp $ */
/*-
@@ -79,6 +79,7 @@
#include <sys/sched.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
+#include <sys/witness.h>
#include <uvm/uvm_extern.h>
@@ -650,6 +651,10 @@ kern_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
dnsjackport = port;
return 0;
}
+#ifdef WITNESS
+ case KERN_WITNESSWATCH:
+ return witness_sysctl_watch(oldp, oldlenp, newp, newlen);
+#endif
default:
return (EOPNOTSUPP);
}
diff --git a/sys/kern/subr_witness.c b/sys/kern/subr_witness.c
index 38999b97971..e47ea4a5b13 100644
--- a/sys/kern/subr_witness.c
+++ b/sys/kern/subr_witness.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: subr_witness.c,v 1.13 2018/05/09 03:43:45 visa Exp $ */
+/* $OpenBSD: subr_witness.c,v 1.14 2018/05/16 14:53:43 visa Exp $ */
/*-
* Copyright (c) 2008 Isilon Systems, Inc.
@@ -333,7 +333,7 @@ static void witness_ddb_display_list(int(*prnt)(const char *fmt, ...),
static void witness_ddb_level_descendants(struct witness *parent, int l);
static void witness_ddb_list(struct proc *td);
#endif
-static void witness_debugger(int cond, const char *msg);
+static void witness_debugger(int dump);
static void witness_free(struct witness *m);
static struct witness *witness_get(void);
static uint32_t witness_hash_djb2(const uint8_t *key, uint32_t size);
@@ -363,7 +363,11 @@ static void witness_setflag(struct lock_object *lock, int flag, int set);
* may be toggled. However, witness cannot be reenabled once it is
* completely disabled.
*/
-static int witness_watch = 1;
+#ifdef WITNESS_WATCH
+static int witness_watch = 3;
+#else
+static int witness_watch = 0;
+#endif
#ifdef WITNESS_SKIPSPIN
int witness_skipspin = 1;
@@ -875,7 +879,7 @@ witness_checkorder(struct lock_object *lock, int flags, const char *file,
fixup_filename(plock->li_file), plock->li_line);
db_printf(" 2nd %s @ %s:%d\n", lock->lo_name,
fixup_filename(file), line);
- witness_debugger(1, __func__);
+ witness_debugger(1);
} else
mtx_leave(&w_mtx);
goto out_splx;
@@ -1035,7 +1039,7 @@ witness_checkorder(struct lock_object *lock, int flags, const char *file,
lock->lo_name, w->w_type->lt_name,
fixup_filename(file), line);
}
- witness_debugger(1, __func__);
+ witness_debugger(0);
goto out_splx;
}
}
@@ -1404,10 +1408,12 @@ witness_warn(int flags, struct lock_object *lock, const char *fmt, ...)
(flags & WARN_SLEEPOK) != 0 ? "non-sleepable " : "");
n += witness_list_locks(&lock_list, printf);
}
- if (flags & WARN_PANIC && n)
- panic("%s", __func__);
- else
- witness_debugger(n, __func__);
+ if (n > 0) {
+ if (flags & WARN_PANIC)
+ panic("%s", __func__);
+ else
+ witness_debugger(1);
+ }
return (n);
}
@@ -2460,10 +2466,43 @@ witness_increment_graph_generation(void)
}
static void
-witness_debugger(int cond, const char *msg)
+witness_debugger(int dump)
{
- if (!cond)
- return;
+ switch (witness_watch) {
+ case 1:
+ break;
+ case 2:
+ if (dump)
+ db_stack_dump();
+ break;
+ case 3:
+ if (dump)
+ db_stack_dump();
+ db_enter();
+ break;
+ default:
+ panic("witness: locking error");
+ }
+}
+
+int
+witness_sysctl_watch(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
+{
+ int error;
+ int value;
- db_enter();
+ value = witness_watch;
+ error = sysctl_int(oldp, oldlenp, newp, newlen, &value);
+ if (error == 0 && newp != NULL) {
+ if (value >= -1 && value <= 3) {
+ mtx_enter(&w_mtx);
+ if (value < 0 || witness_watch >= 0)
+ witness_watch = value;
+ else
+ error = EINVAL;
+ mtx_leave(&w_mtx);
+ } else
+ error = EINVAL;
+ }
+ return (error);
}