diff options
author | Bryan Cantrill <bryan@joyent.com> | 2015-10-22 15:34:32 +0000 |
---|---|---|
committer | Bryan Cantrill <bryan@joyent.com> | 2015-10-22 15:34:32 +0000 |
commit | a7ccdb59b0dda2db3327038c36abe575d9a76d10 (patch) | |
tree | f1678426e2d2cfa3724089d9e22fd1a0477ad67f /usr/src | |
parent | 8fb0bbb41f1ca2e8d76f2593f394aa062713fd63 (diff) | |
download | illumos-joyent-a7ccdb59b0dda2db3327038c36abe575d9a76d10.tar.gz |
OS-4869 writes to /dev/kmem should be logged to a ring buffer
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/cmd/mdb/Makefile.common | 2 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/modules/mm/mm.c | 140 | ||||
-rw-r--r-- | usr/src/cmd/mdb/intel/amd64/mm/Makefile | 24 | ||||
-rw-r--r-- | usr/src/cmd/mdb/intel/ia32/mm/Makefile | 24 | ||||
-rw-r--r-- | usr/src/cmd/mdb/sparc/v9/mm/Makefile | 25 | ||||
-rw-r--r-- | usr/src/pkg/manifests/developer-debug-mdb.mf | 2 | ||||
-rw-r--r-- | usr/src/uts/common/io/mem.c | 50 | ||||
-rw-r--r-- | usr/src/uts/common/sys/mem.h | 13 |
8 files changed, 280 insertions, 0 deletions
diff --git a/usr/src/cmd/mdb/Makefile.common b/usr/src/cmd/mdb/Makefile.common index 6644e8bc25..9b8d438a88 100644 --- a/usr/src/cmd/mdb/Makefile.common +++ b/usr/src/cmd/mdb/Makefile.common @@ -20,6 +20,7 @@ # # Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2013 Nexenta Systems, Inc. All rights reserved. +# Copyright (c) 2015, Joyent, Inc. All rights reserved. # # MDB modules used for debugging user processes that every ISA's build # subdirectory will need to build. @@ -75,6 +76,7 @@ COMMON_MODULES_KVM = \ logindmux \ mac \ md \ + mm \ mpt_sas \ mr_sas \ nca \ diff --git a/usr/src/cmd/mdb/common/modules/mm/mm.c b/usr/src/cmd/mdb/common/modules/mm/mm.c new file mode 100644 index 0000000000..08b6aac770 --- /dev/null +++ b/usr/src/cmd/mdb/common/modules/mm/mm.c @@ -0,0 +1,140 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2015, Joyent, Inc. All rights reserved. + */ + +#include <sys/mdb_modapi.h> +#include <sys/time.h> +#include <sys/mem.h> + +typedef struct kmemlog_walk { + uintptr_t kmlw_addr; + mm_logentry_t *kmlw_entries; + int kmlw_nentries; + int kmlw_entry; + int kmlw_oldest; +} kmemlog_walk_t; + +static int +kmemlog_walk_init(mdb_walk_state_t *wsp) +{ + kmemlog_walk_t *kw; + GElf_Sym sym; + + if (mdb_lookup_by_name("mm_kmemlog", &sym) != 0) { + mdb_warn("couldn't find symbol 'mm_kmemlog'"); + return (WALK_ERR); + } + + kw = mdb_zalloc(sizeof (kmemlog_walk_t), UM_SLEEP); + kw->kmlw_entries = mdb_zalloc(sym.st_size, UM_SLEEP); + kw->kmlw_addr = sym.st_value; + + if (mdb_vread(kw->kmlw_entries, sym.st_size, sym.st_value) == -1) { + mdb_warn("couldn't read log at %p", sym.st_value); + mdb_free(kw->kmlw_entries, sym.st_size); + mdb_free(kw, sizeof (kmemlog_walk_t)); + return (WALK_ERR); + } + + kw->kmlw_nentries = sym.st_size / sizeof (mm_logentry_t); + + mdb_readvar(&kw->kmlw_entry, "mm_kmemlogent"); + kw->kmlw_oldest = kw->kmlw_entry; + wsp->walk_data = kw; + + return (WALK_NEXT); +} + +static int +kmemlog_walk_step(mdb_walk_state_t *wsp) +{ + kmemlog_walk_t *kw = wsp->walk_data; + mm_logentry_t *ent; + int rval = WALK_NEXT; + + ent = &kw->kmlw_entries[kw->kmlw_entry]; + + if (++kw->kmlw_entry == kw->kmlw_nentries) + kw->kmlw_entry = 0; + + if (ent->mle_hrtime != 0) { + rval = wsp->walk_callback(kw->kmlw_addr + ((uintptr_t)ent - + (uintptr_t)kw->kmlw_entries), ent, wsp->walk_cbdata); + } + + if (rval == WALK_NEXT && kw->kmlw_entry == kw->kmlw_oldest) + return (WALK_DONE); + + return (rval); +} + +static void +kmemlog_walk_fini(mdb_walk_state_t *wsp) +{ + kmemlog_walk_t *kw = wsp->walk_data; + + mdb_free(kw->kmlw_entries, kw->kmlw_nentries * sizeof (mm_logentry_t)); + mdb_free(kw, sizeof (kmemlog_walk_t)); +} + +static int +kmemlog(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + mm_logentry_t ent; + + if (!(flags & DCMD_ADDRSPEC)) { + if (mdb_walk_dcmd("kmemlog", "kmemlog", argc, argv) == -1) { + mdb_warn("can't walk 'kmemlog'"); + return (DCMD_ERR); + } + return (DCMD_OK); + } + + if (DCMD_HDRSPEC(flags)) { + mdb_printf("%?s %-20s %?s %5s %s\n", + "ADDR", "TIME", "VADDR", "PID", "PSARGS"); + } + + if (mdb_vread(&ent, sizeof (ent), addr) == -1) { + mdb_warn("can't read mm_logentry_t at %p", addr); + return (DCMD_ERR); + } + + mdb_printf("%?p %-20Y %?p %5d %s\n", + addr, ent.mle_hrestime.tv_sec, ent.mle_vaddr, ent.mle_pid, + ent.mle_psargs); + + return (DCMD_OK); +} + +static const mdb_dcmd_t dcmds[] = { + { "kmemlog", NULL, "print log of writes via /dev/kmem", kmemlog }, + { NULL } +}; + +static const mdb_walker_t walkers[] = { + { "kmemlog", "walk entries in /dev/kmem write log", + kmemlog_walk_init, kmemlog_walk_step, kmemlog_walk_fini }, + { NULL } +}; + +static const mdb_modinfo_t modinfo = { + MDB_API_VERSION, dcmds, walkers +}; + +const mdb_modinfo_t * +_mdb_init(void) +{ + return (&modinfo); +} diff --git a/usr/src/cmd/mdb/intel/amd64/mm/Makefile b/usr/src/cmd/mdb/intel/amd64/mm/Makefile new file mode 100644 index 0000000000..216385a60c --- /dev/null +++ b/usr/src/cmd/mdb/intel/amd64/mm/Makefile @@ -0,0 +1,24 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2015, Joyent, Inc. All rights reserved. +# + +MODULE = mm.so +MDBTGT = kvm + +MODSRCS = mm.c + +include ../../../../Makefile.cmd +include ../../../../Makefile.cmd.64 +include ../../Makefile.amd64 +include ../../../Makefile.module diff --git a/usr/src/cmd/mdb/intel/ia32/mm/Makefile b/usr/src/cmd/mdb/intel/ia32/mm/Makefile new file mode 100644 index 0000000000..e37ee86753 --- /dev/null +++ b/usr/src/cmd/mdb/intel/ia32/mm/Makefile @@ -0,0 +1,24 @@ +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2015, Joyent, Inc. All rights reserved. +# + +MODULE = mm.so +MDBTGT = kvm + +MODSRCS = mm.c + +include ../../../../Makefile.cmd +include ../../Makefile.ia32 +include ../../../Makefile.module diff --git a/usr/src/cmd/mdb/sparc/v9/mm/Makefile b/usr/src/cmd/mdb/sparc/v9/mm/Makefile new file mode 100644 index 0000000000..c49f4f4484 --- /dev/null +++ b/usr/src/cmd/mdb/sparc/v9/mm/Makefile @@ -0,0 +1,25 @@ +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2015, Joyent, Inc. All rights reserved. +# + +MODULE = mm.so +MDBTGT = kvm + +MODSRCS = mm.c + +include ../../../../Makefile.cmd +include ../../../../Makefile.cmd.64 +include ../../Makefile.sparcv9 +include ../../../Makefile.module diff --git a/usr/src/pkg/manifests/developer-debug-mdb.mf b/usr/src/pkg/manifests/developer-debug-mdb.mf index 926e63d69f..b7d6bb4630 100644 --- a/usr/src/pkg/manifests/developer-debug-mdb.mf +++ b/usr/src/pkg/manifests/developer-debug-mdb.mf @@ -225,6 +225,7 @@ file path=usr/lib/mdb/kvm/$(ARCH64)/mac.so group=sys mode=0555 file path=usr/lib/mdb/kvm/$(ARCH64)/md.so group=sys mode=0555 $(i386_ONLY)file path=usr/lib/mdb/kvm/$(ARCH64)/mdb_kb.so group=sys mode=0555 file path=usr/lib/mdb/kvm/$(ARCH64)/mdb_ks.so group=sys mode=0555 +file path=usr/lib/mdb/kvm/$(ARCH64)/mm.so group=sys mode=0555 file path=usr/lib/mdb/kvm/$(ARCH64)/mpt.so group=sys mode=0555 file path=usr/lib/mdb/kvm/$(ARCH64)/mpt_sas.so group=sys mode=0555 file path=usr/lib/mdb/kvm/$(ARCH64)/mr_sas.so group=sys mode=0555 @@ -263,6 +264,7 @@ $(i386_ONLY)file path=usr/lib/mdb/kvm/mac.so group=sys mode=0555 $(i386_ONLY)file path=usr/lib/mdb/kvm/md.so group=sys mode=0555 $(i386_ONLY)file path=usr/lib/mdb/kvm/mdb_kb.so group=sys mode=0555 $(i386_ONLY)file path=usr/lib/mdb/kvm/mdb_ks.so group=sys mode=0555 +$(i386_ONLY)file path=usr/lib/mdb/kvm/mm.so group=sys mode=0555 $(i386_ONLY)file path=usr/lib/mdb/kvm/mpt.so group=sys mode=0555 $(i386_ONLY)file path=usr/lib/mdb/kvm/mpt_sas.so group=sys mode=0555 $(i386_ONLY)file path=usr/lib/mdb/kvm/mr_sas.so group=sys mode=0555 diff --git a/usr/src/uts/common/io/mem.c b/usr/src/uts/common/io/mem.c index 7a703c5750..c0173a6f42 100644 --- a/usr/src/uts/common/io/mem.c +++ b/usr/src/uts/common/io/mem.c @@ -24,6 +24,10 @@ */ /* + * Copyright (c) 2015, Joyent, Inc. All rights reserved. + */ + +/* * Memory special file */ @@ -95,6 +99,49 @@ static int mm_kstat_snapshot(kstat_t *ksp, void *buf, int rw); static int mm_read_mem_name(intptr_t data, mem_name_t *mem_name); +#define MM_KMEMLOG_NENTRIES 64 + +static int mm_kmemlogent; +static mm_logentry_t mm_kmemlog[MM_KMEMLOG_NENTRIES]; + +/* + * On kmem/allmem writes, we log information that might be useful in the event + * that a write is errant (that is, due to operator error) and induces a later + * problem. Note that (in particular) in the event of such operator-induced + * corruption, a search over the kernel address space for the corrupted + * address will yield the ring buffer entry that recorded the write. And + * should it seem baroque or otherwise unnecessary, yes, we need this kind of + * auditing facility and yes, we learned that the hard way: disturbingly, + * there exist recommendations for "tuning" the system that involve writing to + * kernel memory addresses via the kernel debugger, and -- as we discovered -- + * these can easily be applied incorrectly or unsafely, yielding an entirely + * undebuggable "can't happen" kind of panic. + */ +static void +mm_logkmem(struct uio *uio) +{ + mm_logentry_t *ent; + proc_t *p = curthread->t_procp; + + mutex_enter(&mm_lock); + + ent = &mm_kmemlog[mm_kmemlogent++]; + + if (mm_kmemlogent == MM_KMEMLOG_NENTRIES) + mm_kmemlogent = 0; + + ent->mle_vaddr = (uintptr_t)uio->uio_loffset; + ent->mle_len = uio->uio_resid; + gethrestime(&ent->mle_hrestime); + ent->mle_hrtime = gethrtime(); + ent->mle_pid = p->p_pidp->pid_id; + + (void) strncpy(ent->mle_psargs, + p->p_user.u_psargs, sizeof (ent->mle_psargs)); + + mutex_exit(&mm_lock); +} + /*ARGSUSED1*/ static int mm_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) @@ -354,6 +401,9 @@ mmrw(dev_t dev, struct uio *uio, enum uio_rw rw, cred_t *cred) if ((error = plat_mem_do_mmio(uio, rw)) != ENOTSUP) break; + if (rw == UIO_WRITE) + mm_logkmem(uio); + /* * If vaddr does not map a valid page, as_pagelock() * will return failure. Hence we can't check the diff --git a/usr/src/uts/common/sys/mem.h b/usr/src/uts/common/sys/mem.h index d0ac1223fc..5101b3ec17 100644 --- a/usr/src/uts/common/sys/mem.h +++ b/usr/src/uts/common/sys/mem.h @@ -23,6 +23,10 @@ * Use is subject to license terms. */ +/* + * Copyright (c) 2015, Joyent, Inc. All rights reserved. + */ + #ifndef _SYS_MEM_H #define _SYS_MEM_H @@ -128,6 +132,15 @@ extern pfn_t impl_obmem_pfnum(pfn_t); extern int plat_mem_do_mmio(struct uio *, enum uio_rw); +typedef struct mm_logentry { + uintptr_t mle_vaddr; /* vaddr being written to */ + size_t mle_len; /* length of write */ + timespec_t mle_hrestime; /* hrestime at time of write */ + hrtime_t mle_hrtime; /* hrtime at time of write */ + pid_t mle_pid; /* pid of writing process */ + char mle_psargs[80]; /* psargs of writing process */ +} mm_logentry_t; + #endif /* _KERNEL */ #ifdef __cplusplus |