#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/vm.h>
#include <sys/proc.h>
#include <sys/file.h>
#include <sys/conf.h>
#include <sys/kmem.h>
#include <sys/mem.h>
#include <sys/mman.h>
#include <sys/vnode.h>
#include <sys/errno.h>
#include <sys/memlist.h>
#include <sys/dumphdr.h>
#include <sys/dumpadm.h>
#include <sys/ksyms.h>
#include <sys/compress.h>
#include <sys/stream.h>
#include <sys/strsun.h>
#include <sys/cmn_err.h>
#include <sys/bitmap.h>
#include <sys/modctl.h>
#include <sys/utsname.h>
#include <sys/systeminfo.h>
#include <sys/vmem.h>
#include <sys/log.h>
#include <sys/var.h>
#include <sys/debug.h>
#include <sys/sunddi.h>
#include <fs/fs_subr.h>
#include <sys/fs/snode.h>
#include <sys/ontrap.h>
#include <sys/panic.h>
#include <sys/dkio.h>
#include <sys/vtoc.h>
#include <sys/errorq.h>
#include <sys/fm/util.h>
#include <sys/fs/zfs.h>
#include <vm/hat.h>
#include <vm/as.h>
#include <vm/page.h>
#include <vm/pvn.h>
#include <vm/seg.h>
#include <vm/seg_kmem.h>
#include <sys/clock_impl.h>
#include <sys/hold_page.h>
#include <sys/cpu.h>
#include <bzip2/bzlib.h>
#define ONE_GIG (1024 * 1024 * 1024UL)
kmutex_t dump_lock;
dumphdr_t *dumphdr;
int dump_conflags = DUMP_KERNEL;
vnode_t *dumpvp;
u_offset_t dumpvp_size;
char *dumppath;
int dump_timeout = 120;
int dump_timeleft;
int dump_ioerr;
int dump_check_used;
char *dump_stack_scratch;
uint_t dump_ncpu_low = 4;
uint_t dump_bzip2_level = 1;
#define MINCPU_NOT_SET ((uint_t)-1)
uint_t dump_plat_mincpu = MINCPU_NOT_SET;
uint_t dump_kmem_permap = 1024;
uint_t dump_kmem_pages = 0;
#define NCBUF_PER_HELPER 2
#define NCMAP_PER_HELPER 4
#define MINHELPERS (dump_ncpu_low)
#define MINCBUFS (MINHELPERS * NCBUF_PER_HELPER)
#define DUMP_1KB ((size_t)1 << 10)
#define DUMP_1MB ((size_t)1 << 20)
#define CBUF_SIZE ((size_t)1 << 17)
#define CBUF_MAPSHIFT (22)
#define CBUF_MAPSIZE ((size_t)1 << CBUF_MAPSHIFT)
#define CBUF_MAPNP ((size_t)1 << (CBUF_MAPSHIFT - PAGESHIFT))
#define PERPAGES \
PERPAGE(bitmap) PERPAGE(map) PERPAGE(unmap) \
PERPAGE(copy) PERPAGE(compress) \
PERPAGE(write) \
PERPAGE(inwait) PERPAGE(outwait)
typedef struct perpage {
#define PERPAGE(x) hrtime_t x;
PERPAGES
#undef PERPAGE
} perpage_t;
#define COLLECT_METRICS
#ifdef COLLECT_METRICS
uint_t dump_metrics_on = 0;
#define HRSTART(v, m) v##ts.m = gethrtime()
#define HRSTOP(v, m) v.m += gethrtime() - v##ts.m
#define HRBEGIN(v, m, s) v##ts.m = gethrtime(); v.size += s
#define HREND(v, m) v.m += gethrtime() - v##ts.m
#define HRNORM(v, m, n) v.m /= (n)
#else
#define HRSTART(v, m)
#define HRSTOP(v, m)
#define HRBEGIN(v, m, s)
#define HREND(v, m)
#define HRNORM(v, m, n)
#endif
typedef enum cbufstate {
CBUF_FREEMAP,
CBUF_INREADY,
CBUF_USEDMAP,
CBUF_FREEBUF,
CBUF_WRITE,
CBUF_ERRMSG
} cbufstate_t;
typedef struct cbuf cbuf_t;
struct cbuf {
cbuf_t *next;
cbufstate_t state;
size_t used;
size_t size;
char *buf;
pgcnt_t pagenum;
pgcnt_t bitnum;
pfn_t pfn;
int off;
};
static char dump_osimage_uuid[36 + 1];
#define isdigit(ch) ((ch) >= '0' && (ch) <= '9')
#define isxdigit(ch) (isdigit(ch) || ((ch) >= 'a' && (ch) <= 'f') || \
((ch) >= 'A' && (ch) <= 'F'))
typedef struct cqueue {
cbuf_t *volatile first;
cbuf_t *last;
hrtime_t ts;
hrtime_t empty;
kmutex_t mutex;
kcondvar_t cv;
lock_t spinlock;
volatile uint_t open;
} cqueue_t;
#define CQ_IS_EMPTY(q) \
(ds->q.first == NULL)
#define CQ_OPEN(q) \
atomic_inc_uint(&ds->q.open)
#define CQ_CLOSE(q) \
dumpsys_close_cq(&ds->q, ds->live)
#define CQ_PUT(q, cp, st) \
dumpsys_put_cq(&ds->q, cp, st, ds->live)
#define CQ_GET(q) \
dumpsys_get_cq(&ds->q, ds->live)
typedef struct dumpsync {
pgcnt_t npages;
pgcnt_t pages_mapped;
pgcnt_t pages_used;
size_t nwrite;
uint_t live;
uint_t neednl;
uint_t percent;
uint_t percent_done;
int sec_done;
cqueue_t freebufq;
cqueue_t mainq;
cqueue_t helperq;
cqueue_t writerq;
hrtime_t start;
hrtime_t elapsed;
hrtime_t iotime;
hrtime_t iowait;
hrtime_t iowaitts;
perpage_t perpage;
perpage_t perpagets;
int dumpcpu;
} dumpsync_t;
static dumpsync_t dumpsync;
typedef struct helper {
int helper;
int tag;
perpage_t perpage;
perpage_t perpagets;
taskqid_t taskqid;
int in, out;
cbuf_t *cpin, *cpout, *cperr;
dumpsync_t *ds;
size_t used;
char *page;
char *lzbuf;
bz_stream bzstream;
} helper_t;
#define MAINHELPER (-1)
#define FREEHELPER (-2)
#define DONEHELPER (-3)
typedef struct dumpcfg {
int threshold;
int nhelper;
int nhelper_used;
int ncmap;
int ncbuf;
int ncbuf_used;
uint_t clevel;
helper_t *helper;
cbuf_t *cmap;
cbuf_t *cbuf;
ulong_t *helpermap;
ulong_t *bitmap;
ulong_t *rbitmap;
pgcnt_t bitmapsize;
pgcnt_t rbitmapsize;
pgcnt_t found4m;
pgcnt_t foundsm;
pid_t *pids;
size_t maxsize;
size_t maxvmsize;
char *maxvm;
lock_t helper_lock;
char helpers_wanted;
} dumpcfg_t;
static dumpcfg_t dumpcfg;
typedef struct dumpbuf {
vnode_t *cdev_vp;
len_t vp_limit;
offset_t vp_off;
char *cur;
char *start;
char *end;
size_t size;
size_t iosize;
} dumpbuf_t;
dumpbuf_t dumpbuf;
#define DUMP_HELPER_MAX_WAIT 1000
static int dumpbuf_fraction = 12;
static size_t dumpbuf_limit = 8 * DUMP_1MB;
static size_t
dumpbuf_iosize(size_t xfer_size)
{
size_t iosize = ptob(physmem >> dumpbuf_fraction);
if (iosize < PAGESIZE)
iosize = PAGESIZE;
else if (iosize > xfer_size)
iosize = xfer_size;
if (iosize > dumpbuf_limit)
iosize = dumpbuf_limit;
return (iosize & PAGEMASK);
}
static void
dumpbuf_resize(void)
{
char *old_buf = dumpbuf.start;
size_t old_size = dumpbuf.size;
char *new_buf;
size_t new_size;
ASSERT(MUTEX_HELD(&dump_lock));
new_size = dumpbuf_iosize(MAX(dumpbuf.iosize, maxphys));
if (new_size <= old_size)
return;
new_buf = kmem_alloc(new_size, KM_SLEEP);
dumpbuf.size = new_size;
dumpbuf.start = new_buf;
dumpbuf.end = new_buf + new_size;
kmem_free(old_buf, old_size);
}
static void
dump_update_clevel()
{
int tag;
size_t bz2size;
helper_t *hp, *hpend;
cbuf_t *cp, *cpend;
dumpcfg_t *old = &dumpcfg;
dumpcfg_t newcfg = *old;
dumpcfg_t *new = &newcfg;
ASSERT(MUTEX_HELD(&dump_lock));
if (old->helper != NULL) {
hpend = &old->helper[old->nhelper];
for (hp = old->helper; hp != hpend; hp++) {
if (hp->lzbuf != NULL)
kmem_free(hp->lzbuf, PAGESIZE);
if (hp->page != NULL)
kmem_free(hp->page, PAGESIZE);
}
kmem_free(old->helper, old->nhelper * sizeof (helper_t));
cpend = &old->cmap[old->ncmap];
for (cp = old->cmap; cp != cpend; cp++)
vmem_xfree(heap_arena, cp->buf, CBUF_MAPSIZE);
kmem_free(old->cmap, old->ncmap * sizeof (cbuf_t));
cpend = &old->cbuf[old->ncbuf];
for (cp = old->cbuf; cp != cpend; cp++)
if (cp->buf != NULL)
kmem_free(cp->buf, cp->size);
kmem_free(old->cbuf, old->ncbuf * sizeof (cbuf_t));
if (old->maxvmsize > 0)
vmem_xfree(heap_arena, old->maxvm, old->maxvmsize);
}
new->nhelper = ncpus - 1;
if (new->nhelper < 1)
new->nhelper = 1;
if (new->nhelper > DUMP_MAX_NHELPER)
new->nhelper = DUMP_MAX_NHELPER;
if (dump_plat_mincpu == MINCPU_NOT_SET)
dump_plat_mincpu = dump_plat_mincpu_default;
new->threshold = dump_plat_mincpu;
if (dumpbuf.iosize >= DUMP_1MB)
new->threshold *= 3;
else if (dumpbuf.iosize >= (256 * DUMP_1KB))
new->threshold *= 2;
if (dump_plat_mincpu == 0 || new->nhelper < 2) {
new->clevel = 0;
new->nhelper = 1;
} else if ((new->nhelper + 1) >= new->threshold) {
new->clevel = DUMP_CLEVEL_BZIP2;
} else {
new->clevel = DUMP_CLEVEL_LZJB;
}
if (new->clevel == 0) {
new->ncbuf = 1;
new->ncmap = 1;
} else {
new->ncbuf = NCBUF_PER_HELPER * new->nhelper;
new->ncmap = NCMAP_PER_HELPER * new->nhelper;
}
bz2size = BZ2_bzCompressInitSize(dump_bzip2_level);
new->maxsize = 0;
new->maxvmsize = 0;
new->maxvm = NULL;
tag = 1;
new->helper = kmem_zalloc(new->nhelper * sizeof (helper_t), KM_SLEEP);
hpend = &new->helper[new->nhelper];
for (hp = new->helper; hp != hpend; hp++) {
hp->tag = tag++;
if (hp < &new->helper[MINHELPERS]) {
hp->lzbuf = kmem_alloc(PAGESIZE, KM_SLEEP);
hp->page = kmem_alloc(PAGESIZE, KM_SLEEP);
} else if (new->clevel < DUMP_CLEVEL_BZIP2) {
new->maxsize += 2 * PAGESIZE;
} else {
new->maxsize += PAGESIZE;
}
if (new->clevel >= DUMP_CLEVEL_BZIP2)
new->maxsize += bz2size;
}
new->cbuf = kmem_zalloc(new->ncbuf * sizeof (cbuf_t), KM_SLEEP);
cpend = &new->cbuf[new->ncbuf];
for (cp = new->cbuf; cp != cpend; cp++) {
cp->state = CBUF_FREEBUF;
cp->size = CBUF_SIZE;
if (cp < &new->cbuf[MINCBUFS])
cp->buf = kmem_alloc(cp->size, KM_SLEEP);
else
new->maxsize += cp->size;
}
new->cmap = kmem_zalloc(new->ncmap * sizeof (cbuf_t), KM_SLEEP);
cpend = &new->cmap[new->ncmap];
for (cp = new->cmap; cp != cpend; cp++) {
cp->state = CBUF_FREEMAP;
cp->size = CBUF_MAPSIZE;
cp->buf = vmem_xalloc(heap_arena, CBUF_MAPSIZE, CBUF_MAPSIZE,
0, 0, NULL, NULL, VM_SLEEP);
}
if (new->maxsize > 0) {
new->maxsize = P2ROUNDUP(new->maxsize, PAGESIZE);
new->maxvmsize = P2ROUNDUP(new->maxsize, CBUF_MAPSIZE);
new->maxvm = vmem_xalloc(heap_arena, new->maxvmsize,
CBUF_MAPSIZE, 0, 0, NULL, NULL, VM_SLEEP);
}
if (dump_kmem_pages == 0) {
if (physmem > (16 * ONE_GIG) / PAGESIZE)
dump_kmem_pages = 20;
else
dump_kmem_pages = 8;
}
kmem_dump_init((new->ncmap * dump_kmem_permap) +
(dump_kmem_pages * PAGESIZE));
*old = *new;
}
typedef struct dumpmlw {
struct memlist *mp;
pgcnt_t basenum;
pgcnt_t mppages;
pgcnt_t mpleft;
pfn_t mpaddr;
} dumpmlw_t;
static inline void
dump_init_memlist_walker(dumpmlw_t *pw)
{
pw->mp = phys_install;
pw->basenum = 0;
pw->mppages = pw->mp->ml_size >> PAGESHIFT;
pw->mpleft = pw->mppages;
pw->mpaddr = pw->mp->ml_address >> PAGESHIFT;
}
static pfn_t
dump_bitnum_to_pfn(pgcnt_t bitnum, dumpmlw_t *pw)
{
bitnum -= pw->basenum;
while (pw->mp != NULL) {
if (bitnum < pw->mppages) {
pw->mpleft = pw->mppages - bitnum;
return (pw->mpaddr + bitnum);
}
bitnum -= pw->mppages;
pw->basenum += pw->mppages;
pw->mp = pw->mp->ml_next;
if (pw->mp != NULL) {
pw->mppages = pw->mp->ml_size >> PAGESHIFT;
pw->mpleft = pw->mppages;
pw->mpaddr = pw->mp->ml_address >> PAGESHIFT;
}
}
return (PFN_INVALID);
}
static pgcnt_t
dump_pfn_to_bitnum(pfn_t pfn)
{
struct memlist *mp;
pgcnt_t bitnum = 0;
for (mp = phys_install; mp != NULL; mp = mp->ml_next) {
if (pfn >= (mp->ml_address >> PAGESHIFT) &&
pfn < ((mp->ml_address + mp->ml_size) >> PAGESHIFT))
return (bitnum + pfn - (mp->ml_address >> PAGESHIFT));
bitnum += mp->ml_size >> PAGESHIFT;
}
return ((pgcnt_t)-1);
}
#define CBUF_MAPP2R(pfn) ((pfn) >> (CBUF_MAPSHIFT - PAGESHIFT))
static void
dump_set_used(pfn_t pfn)
{
pgcnt_t bitnum, rbitnum;
bitnum = dump_pfn_to_bitnum(pfn);
ASSERT(bitnum != (pgcnt_t)-1);
rbitnum = CBUF_MAPP2R(bitnum);
ASSERT(rbitnum < dumpcfg.rbitmapsize);
BT_SET(dumpcfg.rbitmap, rbitnum);
}
int
dump_test_used(pfn_t pfn)
{
pgcnt_t bitnum, rbitnum;
bitnum = dump_pfn_to_bitnum(pfn);
ASSERT(bitnum != (pgcnt_t)-1);
rbitnum = CBUF_MAPP2R(bitnum);
ASSERT(rbitnum < dumpcfg.rbitmapsize);
return (BT_TEST(dumpcfg.rbitmap, rbitnum));
}
static void *
dumpbzalloc(void *opaque, int items, int size)
{
size_t *sz;
char *ret;
ASSERT(opaque != NULL);
sz = opaque;
ret = dumpcfg.maxvm + *sz;
*sz += items * size;
*sz = P2ROUNDUP(*sz, BZ2_BZALLOC_ALIGN);
ASSERT(*sz <= dumpcfg.maxvmsize);
return (ret);
}
static void
dumpbzfree(void *opaque, void *addr)
{
}
static inline int
dump_pfn_check(pfn_t pfn)
{
page_t *pp = page_numtopp_nolock(pfn);
if (pp == NULL || pp->p_pagenum != pfn ||
#if defined(__sparc)
pp->p_vnode == &promvp ||
#else
PP_ISBOOTPAGES(pp) ||
#endif
pp->p_toxic != 0)
return (0);
return (1);
}
static inline int
dump_range_check(pgcnt_t start, pgcnt_t end, pfn_t pfn)
{
for (; start < end; start++, pfn++) {
if (BT_TEST(dumpcfg.bitmap, start))
return (0);
if (!dump_pfn_check(pfn))
return (0);
}
return (1);
}
static void
dumpsys_get_maxmem()
{
dumpcfg_t *cfg = &dumpcfg;
cbuf_t *endcp = &cfg->cbuf[cfg->ncbuf];
helper_t *endhp = &cfg->helper[cfg->nhelper];
pgcnt_t bitnum, end;
size_t sz, endsz, bz2size;
pfn_t pfn, off;
cbuf_t *cp;
helper_t *hp, *ohp;
dumpmlw_t mlw;
int k;
if (dump_plat_mincpu == 0) {
cfg->clevel = 0;
return;
}
if (cfg->maxsize == 0 || cfg->clevel < DUMP_CLEVEL_LZJB ||
(dump_conflags & DUMP_ALL) != 0) {
if (cfg->clevel > DUMP_CLEVEL_LZJB)
cfg->clevel = DUMP_CLEVEL_LZJB;
return;
}
sz = 0;
cfg->found4m = 0;
cfg->foundsm = 0;
bzero(dumpcfg.rbitmap, BT_SIZEOFMAP(dumpcfg.rbitmapsize));
dump_init_memlist_walker(&mlw);
for (bitnum = 0; bitnum < dumpcfg.bitmapsize; bitnum = end) {
dump_timeleft = dump_timeout;
end = bitnum + CBUF_MAPNP;
pfn = dump_bitnum_to_pfn(bitnum, &mlw);
ASSERT(pfn != PFN_INVALID);
if (mlw.mpleft < CBUF_MAPNP) {
end = bitnum + mlw.mpleft;
continue;
}
off = P2PHASE(pfn, CBUF_MAPNP);
if (off != 0) {
end -= off;
continue;
}
if (!dump_range_check(bitnum, end, pfn))
continue;
ASSERT((sz + CBUF_MAPSIZE) <= cfg->maxvmsize);
hat_devload(kas.a_hat, cfg->maxvm + sz, CBUF_MAPSIZE, pfn,
PROT_READ | PROT_WRITE, HAT_LOAD_NOCONSIST);
sz += CBUF_MAPSIZE;
cfg->found4m++;
dump_set_used(pfn);
dump_set_used(pfn + CBUF_MAPNP - 1);
if (sz >= cfg->maxsize)
goto foundmax;
}
dump_init_memlist_walker(&mlw);
for (bitnum = 0; bitnum < dumpcfg.bitmapsize; bitnum = end) {
dump_timeleft = dump_timeout;
end = bitnum + CBUF_MAPNP;
pfn = dump_bitnum_to_pfn(bitnum, &mlw);
ASSERT(pfn != PFN_INVALID);
off = P2PHASE(pfn, CBUF_MAPNP);
if (mlw.mpleft < CBUF_MAPNP) {
end = bitnum + mlw.mpleft;
} else if (off != 0) {
end -= off;
} else if (cfg->found4m && dump_test_used(pfn)) {
continue;
}
for (; bitnum < end; bitnum++, pfn++) {
dump_timeleft = dump_timeout;
if (BT_TEST(dumpcfg.bitmap, bitnum))
continue;
if (!dump_pfn_check(pfn))
continue;
ASSERT((sz + PAGESIZE) <= cfg->maxvmsize);
hat_devload(kas.a_hat, cfg->maxvm + sz, PAGESIZE, pfn,
PROT_READ | PROT_WRITE, HAT_LOAD_NOCONSIST);
sz += PAGESIZE;
cfg->foundsm++;
dump_set_used(pfn);
if (sz >= cfg->maxsize)
goto foundmax;
}
}
endsz = (cfg->maxsize * cfg->threshold) / cfg->nhelper;
if (sz < endsz) {
cfg->clevel = DUMP_CLEVEL_LZJB;
}
foundmax:
endsz = sz;
sz = 0;
bz2size = BZ2_bzCompressInitSize(dump_bzip2_level);
cp = &cfg->cbuf[MINCBUFS];
ohp = cfg->helper;
for (hp = cfg->helper; hp < endhp; hp++) {
if (hp->page == NULL) {
if (cfg->clevel <= DUMP_CLEVEL_LZJB) {
if ((sz + (2 * PAGESIZE)) > endsz)
break;
hp->page = cfg->maxvm + sz;
sz += PAGESIZE;
hp->lzbuf = cfg->maxvm + sz;
sz += PAGESIZE;
} else if (ohp->lzbuf != NULL) {
hp->page = ohp->lzbuf;
ohp->lzbuf = NULL;
++ohp;
} else {
if ((sz + PAGESIZE) > endsz)
break;
hp->page = cfg->maxvm + sz;
sz += PAGESIZE;
}
}
for (k = 0; cp < endcp && (sz + CBUF_SIZE) <= endsz &&
k < NCBUF_PER_HELPER; k++) {
cp->state = CBUF_FREEBUF;
cp->size = CBUF_SIZE;
cp->buf = cfg->maxvm + sz;
sz += CBUF_SIZE;
++cp;
}
if (cfg->clevel >= DUMP_CLEVEL_BZIP2) {
if ((sz + bz2size) > endsz) {
hp->page = NULL;
break;
} else {
hp->bzstream.opaque = &sz;
hp->bzstream.bzalloc = dumpbzalloc;
hp->bzstream.bzfree = dumpbzfree;
(void) BZ2_bzCompressInit(&hp->bzstream,
dump_bzip2_level, 0, 0);
hp->bzstream.opaque = NULL;
}
}
}
for (; cp < endcp && (sz + CBUF_SIZE) <= endsz; cp++) {
cp->state = CBUF_FREEBUF;
cp->size = CBUF_SIZE;
cp->buf = cfg->maxvm + sz;
sz += CBUF_SIZE;
}
if (cfg->found4m || cfg->foundsm)
dump_check_used = 1;
ASSERT(sz <= endsz);
}
static void
dumphdr_init(void)
{
pgcnt_t npages = 0;
ASSERT(MUTEX_HELD(&dump_lock));
if (dumphdr == NULL) {
dumphdr = kmem_zalloc(sizeof (dumphdr_t), KM_SLEEP);
dumphdr->dump_magic = DUMP_MAGIC;
dumphdr->dump_version = DUMP_VERSION;
dumphdr->dump_wordsize = DUMP_WORDSIZE;
dumphdr->dump_pageshift = PAGESHIFT;
dumphdr->dump_pagesize = PAGESIZE;
dumphdr->dump_utsname = utsname;
(void) strcpy(dumphdr->dump_platform, platform);
dumpbuf.size = dumpbuf_iosize(maxphys);
dumpbuf.start = kmem_alloc(dumpbuf.size, KM_SLEEP);
dumpbuf.end = dumpbuf.start + dumpbuf.size;
dumpcfg.pids = kmem_alloc(v.v_proc * sizeof (pid_t), KM_SLEEP);
dumpcfg.helpermap = kmem_zalloc(BT_SIZEOFMAP(NCPU), KM_SLEEP);
LOCK_INIT_HELD(&dumpcfg.helper_lock);
dump_stack_scratch = kmem_alloc(STACK_BUF_SIZE, KM_SLEEP);
(void) strncpy(dumphdr->dump_uuid, dump_get_uuid(),
sizeof (dumphdr->dump_uuid));
}
npages = num_phys_pages();
if (dumpcfg.bitmapsize != npages) {
size_t rlen = CBUF_MAPP2R(P2ROUNDUP(npages, CBUF_MAPNP));
void *map = kmem_alloc(BT_SIZEOFMAP(npages), KM_SLEEP);
void *rmap = kmem_alloc(BT_SIZEOFMAP(rlen), KM_SLEEP);
if (dumpcfg.bitmap != NULL)
kmem_free(dumpcfg.bitmap, BT_SIZEOFMAP(dumpcfg.
bitmapsize));
if (dumpcfg.rbitmap != NULL)
kmem_free(dumpcfg.rbitmap, BT_SIZEOFMAP(dumpcfg.
rbitmapsize));
dumpcfg.bitmap = map;
dumpcfg.bitmapsize = npages;
dumpcfg.rbitmap = rmap;
dumpcfg.rbitmapsize = rlen;
}
}
int
dumpinit(vnode_t *vp, char *name, int justchecking)
{
vnode_t *cvp;
vattr_t vattr;
vnode_t *cdev_vp;
int error = 0;
ASSERT(MUTEX_HELD(&dump_lock));
dumphdr_init();
cvp = common_specvp(vp);
if (cvp == dumpvp)
return (0);
if ((error = VOP_OPEN(&cvp, FREAD | FWRITE, kcred, NULL)) != 0)
return (error);
vattr.va_mask = AT_SIZE | AT_TYPE | AT_RDEV;
if ((error = VOP_GETATTR(cvp, &vattr, 0, kcred, NULL)) == 0) {
if (vattr.va_type == VBLK || vattr.va_type == VCHR) {
if (devopsp[getmajor(vattr.va_rdev)]->
devo_cb_ops->cb_dump == nodev)
error = ENOTSUP;
else if (vfs_devismounted(vattr.va_rdev))
error = EBUSY;
if (strcmp(ddi_driver_name(VTOS(cvp)->s_dip),
ZFS_DRIVER) == 0 &&
IS_SWAPVP(common_specvp(cvp)))
error = EBUSY;
} else {
if (vn_matchopval(cvp, VOPNAME_DUMP, fs_nosys) ||
!IS_SWAPVP(cvp))
error = ENOTSUP;
}
}
if (error == 0 && vattr.va_size < 2 * DUMP_LOGSIZE + DUMP_ERPTSIZE)
error = ENOSPC;
if (error || justchecking) {
(void) VOP_CLOSE(cvp, FREAD | FWRITE, 1, (offset_t)0,
kcred, NULL);
return (error);
}
VN_HOLD(cvp);
if (dumpvp != NULL)
dumpfini();
dumpvp = cvp;
dumpvp_size = vattr.va_size & -DUMP_OFFSET;
dumppath = kmem_alloc(strlen(name) + 1, KM_SLEEP);
(void) strcpy(dumppath, name);
dumpbuf.iosize = 0;
if (cvp->v_type == VBLK &&
(cdev_vp = makespecvp(VTOS(cvp)->s_dev, VCHR)) != NULL) {
if (VOP_OPEN(&cdev_vp, FREAD | FWRITE, kcred, NULL) == 0) {
size_t blk_size;
struct dk_cinfo dki;
struct dk_minfo minf;
if (VOP_IOCTL(cdev_vp, DKIOCGMEDIAINFO,
(intptr_t)&minf, FKIOCTL, kcred, NULL, NULL)
== 0 && minf.dki_lbsize != 0)
blk_size = minf.dki_lbsize;
else
blk_size = DEV_BSIZE;
if (VOP_IOCTL(cdev_vp, DKIOCINFO, (intptr_t)&dki,
FKIOCTL, kcred, NULL, NULL) == 0) {
dumpbuf.iosize = dki.dki_maxtransfer * blk_size;
dumpbuf_resize();
}
if (strcmp(dki.dki_dname, ZVOL_DRIVER) == 0) {
if (IS_SWAPVP(common_specvp(cvp)))
error = EBUSY;
else if ((error = VOP_IOCTL(cdev_vp,
DKIOCDUMPINIT, 0, FKIOCTL, kcred,
NULL, NULL)) != 0)
dumpfini();
}
(void) VOP_CLOSE(cdev_vp, FREAD | FWRITE, 1, 0,
kcred, NULL);
}
VN_RELE(cdev_vp);
}
cmn_err(CE_CONT, "?dump on %s size %llu MB\n", name, dumpvp_size >> 20);
dump_update_clevel();
return (error);
}
void
dumpfini(void)
{
vattr_t vattr;
boolean_t is_zfs = B_FALSE;
vnode_t *cdev_vp;
ASSERT(MUTEX_HELD(&dump_lock));
kmem_free(dumppath, strlen(dumppath) + 1);
vattr.va_mask = AT_RDEV;
if (VOP_GETATTR(dumpvp, &vattr, 0, kcred, NULL) == 0) {
is_zfs = (getmajor(vattr.va_rdev) ==
ddi_name_to_major(ZFS_DRIVER)) ? B_TRUE : B_FALSE;
}
if (is_zfs &&
(cdev_vp = makespecvp(VTOS(dumpvp)->s_dev, VCHR)) != NULL) {
if (VOP_OPEN(&cdev_vp, FREAD | FWRITE, kcred, NULL) == 0) {
(void) VOP_IOCTL(cdev_vp, DKIOCDUMPFINI, 0, FKIOCTL,
kcred, NULL, NULL);
(void) VOP_CLOSE(cdev_vp, FREAD | FWRITE, 1, 0,
kcred, NULL);
}
VN_RELE(cdev_vp);
}
(void) VOP_CLOSE(dumpvp, FREAD | FWRITE, 1, (offset_t)0, kcred, NULL);
VN_RELE(dumpvp);
dumpvp = NULL;
dumpvp_size = 0;
dumppath = NULL;
}
static offset_t
dumpvp_flush(void)
{
size_t size = P2ROUNDUP(dumpbuf.cur - dumpbuf.start, PAGESIZE);
hrtime_t iotime;
int err;
if (dumpbuf.vp_off + size > dumpbuf.vp_limit) {
dump_ioerr = ENOSPC;
dumpbuf.vp_off = dumpbuf.vp_limit;
} else if (size != 0) {
iotime = gethrtime();
dumpsync.iowait += iotime - dumpsync.iowaitts;
if (panicstr)
err = VOP_DUMP(dumpvp, dumpbuf.start,
lbtodb(dumpbuf.vp_off), btod(size), NULL);
else
err = vn_rdwr(UIO_WRITE, dumpbuf.cdev_vp != NULL ?
dumpbuf.cdev_vp : dumpvp, dumpbuf.start, size,
dumpbuf.vp_off, UIO_SYSSPACE, 0, dumpbuf.vp_limit,
kcred, 0);
if (err && dump_ioerr == 0)
dump_ioerr = err;
dumpsync.iowaitts = gethrtime();
dumpsync.iotime += dumpsync.iowaitts - iotime;
dumpsync.nwrite += size;
dumpbuf.vp_off += size;
}
dumpbuf.cur = dumpbuf.start;
dump_timeleft = dump_timeout;
return (dumpbuf.vp_off);
}
void
dumpvp_write(const void *va, size_t size)
{
size_t len, off, sz;
while (size != 0) {
len = MIN(size, dumpbuf.end - dumpbuf.cur);
if (len == 0) {
off = P2PHASE(dumpbuf.vp_off, dumpbuf.size);
if (off == 0 || !ISP2(dumpbuf.size)) {
(void) dumpvp_flush();
} else {
sz = dumpbuf.size - off;
dumpbuf.cur = dumpbuf.start + sz;
(void) dumpvp_flush();
ovbcopy(dumpbuf.start + sz, dumpbuf.start, off);
dumpbuf.cur += off;
}
} else {
bcopy(va, dumpbuf.cur, len);
va = (char *)va + len;
dumpbuf.cur += len;
size -= len;
}
}
}
static void
dumpvp_ksyms_write(const void *src, void *dst, size_t size)
{
dumpvp_write(src, size);
}
void
dump_addpage(struct as *as, void *va, pfn_t pfn)
{
mem_vtop_t mem_vtop;
pgcnt_t bitnum;
if ((bitnum = dump_pfn_to_bitnum(pfn)) != (pgcnt_t)-1) {
if (!BT_TEST(dumpcfg.bitmap, bitnum)) {
dumphdr->dump_npages++;
BT_SET(dumpcfg.bitmap, bitnum);
}
dumphdr->dump_nvtop++;
mem_vtop.m_as = as;
mem_vtop.m_va = va;
mem_vtop.m_pfn = pfn;
dumpvp_write(&mem_vtop, sizeof (mem_vtop_t));
}
dump_timeleft = dump_timeout;
}
void
dump_page(pfn_t pfn)
{
pgcnt_t bitnum;
if ((bitnum = dump_pfn_to_bitnum(pfn)) != (pgcnt_t)-1) {
if (!BT_TEST(dumpcfg.bitmap, bitnum)) {
dumphdr->dump_npages++;
BT_SET(dumpcfg.bitmap, bitnum);
}
}
dump_timeleft = dump_timeout;
}
static void
dump_as(struct as *as)
{
struct seg *seg;
AS_LOCK_ENTER(as, RW_READER);
for (seg = AS_SEGFIRST(as); seg; seg = AS_SEGNEXT(as, seg)) {
if (seg->s_as != as)
break;
if (seg->s_ops == NULL)
continue;
SEGOP_DUMP(seg);
}
AS_LOCK_EXIT(as);
if (seg != NULL)
cmn_err(CE_WARN, "invalid segment %p in address space %p",
(void *)seg, (void *)as);
}
static int
dump_process(pid_t pid)
{
proc_t *p = sprlock(pid);
if (p == NULL)
return (-1);
if (p->p_as != &kas) {
mutex_exit(&p->p_lock);
dump_as(p->p_as);
mutex_enter(&p->p_lock);
}
sprunlock(p);
return (0);
}
void
dump_summary(void)
{
u_offset_t dumpvp_start;
summary_dump_t sd;
if (dumpvp == NULL || dumphdr == NULL)
return;
dumpbuf.cur = dumpbuf.start;
dumpbuf.vp_limit = dumpvp_size - (DUMP_OFFSET + DUMP_LOGSIZE +
DUMP_ERPTSIZE);
dumpvp_start = dumpbuf.vp_limit - DUMP_SUMMARYSIZE;
dumpbuf.vp_off = dumpvp_start;
sd.sd_magic = SUMMARY_MAGIC;
sd.sd_ssum = checksum32(dump_stack_scratch, STACK_BUF_SIZE);
dumpvp_write(&sd, sizeof (sd));
dumpvp_write(dump_stack_scratch, STACK_BUF_SIZE);
sd.sd_magic = 0;
dumpvp_write(&sd, sizeof (sd));
(void) dumpvp_flush();
}
void
dump_ereports(void)
{
u_offset_t dumpvp_start;
erpt_dump_t ed;
if (dumpvp == NULL || dumphdr == NULL)
return;
dumpbuf.cur = dumpbuf.start;
dumpbuf.vp_limit = dumpvp_size - (DUMP_OFFSET + DUMP_LOGSIZE);
dumpvp_start = dumpbuf.vp_limit - DUMP_ERPTSIZE;
dumpbuf.vp_off = dumpvp_start;
fm_ereport_dump();
if (panicstr)
errorq_dump();
bzero(&ed, sizeof (ed));
dumpvp_write(&ed, sizeof (ed));
(void) dumpvp_flush();
if (!panicstr) {
(void) VOP_PUTPAGE(dumpvp, dumpvp_start,
(size_t)(dumpbuf.vp_off - dumpvp_start),
B_INVAL | B_FORCE, kcred, NULL);
}
}
void
dump_messages(void)
{
log_dump_t ld;
mblk_t *mctl, *mdata;
queue_t *q, *qlast;
u_offset_t dumpvp_start;
if (dumpvp == NULL || dumphdr == NULL || log_consq == NULL)
return;
dumpbuf.cur = dumpbuf.start;
dumpbuf.vp_limit = dumpvp_size - DUMP_OFFSET;
dumpvp_start = dumpbuf.vp_limit - DUMP_LOGSIZE;
dumpbuf.vp_off = dumpvp_start;
qlast = NULL;
do {
for (q = log_consq; q->q_next != qlast; q = q->q_next)
continue;
for (mctl = q->q_first; mctl != NULL; mctl = mctl->b_next) {
dump_timeleft = dump_timeout;
mdata = mctl->b_cont;
ld.ld_magic = LOG_MAGIC;
ld.ld_msgsize = MBLKL(mctl->b_cont);
ld.ld_csum = checksum32(mctl->b_rptr, MBLKL(mctl));
ld.ld_msum = checksum32(mdata->b_rptr, MBLKL(mdata));
dumpvp_write(&ld, sizeof (ld));
dumpvp_write(mctl->b_rptr, MBLKL(mctl));
dumpvp_write(mdata->b_rptr, MBLKL(mdata));
}
} while ((qlast = q) != log_consq);
ld.ld_magic = 0;
dumpvp_write(&ld, sizeof (ld));
(void) dumpvp_flush();
if (!panicstr) {
(void) VOP_PUTPAGE(dumpvp, dumpvp_start,
(size_t)(dumpbuf.vp_off - dumpvp_start),
B_INVAL | B_FORCE, kcred, NULL);
}
}
static int
dump_pagecopy(void *src, void *dst)
{
long *wsrc = (long *)src;
long *wdst = (long *)dst;
const ulong_t ncopies = PAGESIZE / sizeof (long);
volatile int w = 0;
volatile int ueoff = -1;
on_trap_data_t otd;
if (on_trap(&otd, OT_DATA_EC | OT_DATA_ACCESS)) {
if (ueoff == -1)
ueoff = w * sizeof (long);
#ifdef _LP64
if (otd.ot_trap & OT_DATA_EC)
wdst[w++] = 0x00badecc00badecc;
else
wdst[w++] = 0x00badadd00badadd;
#else
if (otd.ot_trap & OT_DATA_EC)
wdst[w++] = 0x00badecc;
else
wdst[w++] = 0x00badadd;
#endif
}
while (w < ncopies) {
wdst[w] = wsrc[w];
w++;
}
no_trap();
return (ueoff);
}
static void
dumpsys_close_cq(cqueue_t *cq, int live)
{
if (live) {
mutex_enter(&cq->mutex);
atomic_dec_uint(&cq->open);
cv_signal(&cq->cv);
mutex_exit(&cq->mutex);
} else {
atomic_dec_uint(&cq->open);
}
}
static inline void
dumpsys_spinlock(lock_t *lp)
{
uint_t backoff = 0;
int loop_count = 0;
while (LOCK_HELD(lp) || !lock_spin_try(lp)) {
if (++loop_count >= ncpus) {
backoff = mutex_lock_backoff(0);
loop_count = 0;
} else {
backoff = mutex_lock_backoff(backoff);
}
mutex_lock_delay(backoff);
}
}
static inline void
dumpsys_spinunlock(lock_t *lp)
{
lock_clear(lp);
}
static inline void
dumpsys_lock(cqueue_t *cq, int live)
{
if (live)
mutex_enter(&cq->mutex);
else
dumpsys_spinlock(&cq->spinlock);
}
static inline void
dumpsys_unlock(cqueue_t *cq, int live, int signal)
{
if (live) {
if (signal)
cv_signal(&cq->cv);
mutex_exit(&cq->mutex);
} else {
dumpsys_spinunlock(&cq->spinlock);
}
}
static void
dumpsys_wait_cq(cqueue_t *cq, int live)
{
if (live) {
cv_wait(&cq->cv, &cq->mutex);
} else {
dumpsys_spinunlock(&cq->spinlock);
while (cq->open)
if (cq->first)
break;
dumpsys_spinlock(&cq->spinlock);
}
}
static void
dumpsys_put_cq(cqueue_t *cq, cbuf_t *cp, int newstate, int live)
{
if (cp == NULL)
return;
dumpsys_lock(cq, live);
if (cq->ts != 0) {
cq->empty += gethrtime() - cq->ts;
cq->ts = 0;
}
cp->state = newstate;
cp->next = NULL;
if (cq->last == NULL)
cq->first = cp;
else
cq->last->next = cp;
cq->last = cp;
dumpsys_unlock(cq, live, 1);
}
static cbuf_t *
dumpsys_get_cq(cqueue_t *cq, int live)
{
cbuf_t *cp;
hrtime_t now = gethrtime();
dumpsys_lock(cq, live);
while (1) {
cp = (cbuf_t *)cq->first;
if (cp == NULL) {
if (cq->open == 0)
break;
dumpsys_wait_cq(cq, live);
continue;
}
cq->first = cp->next;
if (cq->first == NULL) {
cq->last = NULL;
cq->ts = now;
}
break;
}
dumpsys_unlock(cq, live, cq->first != NULL || cq->open == 0);
return (cp);
}
static void
dumpsys_errmsg(helper_t *hp, const char *fmt, ...)
{
dumpsync_t *ds = hp->ds;
cbuf_t *cp = hp->cperr;
va_list adx;
if (hp->helper == MAINHELPER) {
if (fmt != NULL) {
if (ds->neednl) {
uprintf("\n");
ds->neednl = 0;
}
va_start(adx, fmt);
vuprintf(fmt, adx);
va_end(adx);
}
} else if (fmt == NULL) {
if (cp != NULL) {
CQ_PUT(mainq, cp, CBUF_ERRMSG);
hp->cperr = NULL;
}
} else {
if (hp->cperr == NULL) {
cp = CQ_GET(freebufq);
hp->cperr = cp;
cp->used = 0;
}
va_start(adx, fmt);
cp->used += vsnprintf(cp->buf + cp->used, cp->size - cp->used,
fmt, adx);
va_end(adx);
if ((cp->used + LOG_MSGSIZE) > cp->size) {
CQ_PUT(mainq, cp, CBUF_ERRMSG);
hp->cperr = NULL;
}
}
}
static void
dumpsys_swrite(helper_t *hp, cbuf_t *cp, size_t used)
{
dumpsync_t *ds = hp->ds;
if (hp->helper == MAINHELPER) {
HRSTART(ds->perpage, write);
dumpvp_write(cp->buf, used);
HRSTOP(ds->perpage, write);
CQ_PUT(freebufq, cp, CBUF_FREEBUF);
} else {
cp->used = used;
CQ_PUT(mainq, cp, CBUF_WRITE);
}
}
static int
dumpsys_copy_page(helper_t *hp, int offset)
{
cbuf_t *cp = hp->cpin;
int ueoff;
ASSERT(cp->off + offset + PAGESIZE <= cp->size);
ASSERT(BT_TEST(dumpcfg.bitmap, cp->bitnum));
ueoff = dump_pagecopy(cp->buf + cp->off + offset, hp->page);
if (ueoff != -1) {
uint64_t pa = ptob(cp->pfn) + offset + ueoff;
dumpsys_errmsg(hp, "cpu %d: memory error at PA 0x%08x.%08x\n",
CPU->cpu_id, (uint32_t)(pa >> 32), (uint32_t)pa);
}
offset += PAGESIZE;
cp->bitnum++;
while (cp->off + offset < cp->size) {
if (BT_TEST(dumpcfg.bitmap, cp->bitnum))
break;
offset += PAGESIZE;
cp->bitnum++;
}
return (offset);
}
static int
dumpsys_sread(helper_t *hp)
{
dumpsync_t *ds = hp->ds;
while (1) {
if (hp->cpin == NULL) {
HRSTART(hp->perpage, inwait);
while (1) {
hp->cpin = CQ_GET(helperq);
dump_timeleft = dump_timeout;
if (hp->cpin == NULL)
break;
if (!dump_ioerr)
break;
hp->cpin->used = 0;
CQ_PUT(mainq, hp->cpin, CBUF_USEDMAP);
}
HRSTOP(hp->perpage, inwait);
if (hp->cpin == NULL)
break;
hp->in = 0;
hp->used = 0;
}
if (hp->used < hp->cpin->used) {
ASSERT(hp->in != -1);
HRSTART(hp->perpage, copy);
hp->in = dumpsys_copy_page(hp, hp->in);
hp->used += PAGESIZE;
HRSTOP(hp->perpage, copy);
break;
} else {
if (panicstr && hp->helper != MAINHELPER)
hat_flush_range(kas.a_hat,
hp->cpin->buf, hp->cpin->size);
dumpsys_errmsg(hp, NULL);
CQ_PUT(mainq, hp->cpin, CBUF_USEDMAP);
hp->cpin = NULL;
}
}
return (hp->cpin != NULL);
}
static void
dumpsys_bzrun(helper_t *hp, void *buf, size_t size, int mode)
{
dumpsync_t *ds = hp->ds;
const int CSIZE = sizeof (dumpcsize_t);
bz_stream *ps = &hp->bzstream;
int rc = 0;
uint32_t csize;
dumpcsize_t cs;
if (size > 0) {
ps->avail_in = size;
ps->next_in = buf;
}
while (1) {
if (ps->avail_in == 0 && mode == BZ_RUN)
break;
if (hp->cpout == NULL) {
HRSTART(hp->perpage, outwait);
hp->cpout = CQ_GET(freebufq);
HRSTOP(hp->perpage, outwait);
ps->avail_out = hp->cpout->size - CSIZE;
ps->next_out = hp->cpout->buf + CSIZE;
}
HRSTART(hp->perpage, compress);
rc = BZ2_bzCompress(ps, mode);
HRSTOP(hp->perpage, compress);
if (mode == BZ_RUN && rc != BZ_RUN_OK) {
dumpsys_errmsg(hp, "%d: BZ_RUN error %s at page %lx\n",
hp->helper, BZ2_bzErrorString(rc),
hp->cpin->pagenum);
break;
}
if (ps->avail_out == 0 || mode == BZ_FINISH) {
csize = hp->cpout->size - CSIZE - ps->avail_out;
cs = DUMP_SET_TAG(csize, hp->tag);
if (csize > 0) {
(void) memcpy(hp->cpout->buf, &cs, CSIZE);
dumpsys_swrite(hp, hp->cpout, csize + CSIZE);
hp->cpout = NULL;
}
}
if (mode == BZ_FINISH) {
if (rc == BZ_STREAM_END)
break;
if (rc != BZ_FINISH_OK) {
dumpsys_errmsg(hp, "%d: BZ_FINISH error %s\n",
hp->helper, BZ2_bzErrorString(rc));
break;
}
}
}
if (mode == BZ_FINISH) {
(void) BZ2_bzCompressReset(&hp->bzstream);
if (hp->cpout != NULL) {
hp->cpout->used = 0;
CQ_PUT(mainq, hp->cpout, CBUF_ERRMSG);
hp->cpout = NULL;
}
}
}
static void
dumpsys_bz2compress(helper_t *hp)
{
dumpsync_t *ds = hp->ds;
dumpstreamhdr_t sh;
(void) strcpy(sh.stream_magic, DUMP_STREAM_MAGIC);
sh.stream_pagenum = (pgcnt_t)-1;
sh.stream_npages = 0;
hp->cpin = NULL;
hp->cpout = NULL;
hp->cperr = NULL;
hp->in = 0;
hp->out = 0;
hp->bzstream.avail_in = 0;
CQ_OPEN(mainq);
while (dumpsys_sread(hp)) {
if (sh.stream_pagenum != hp->cpin->pagenum) {
sh.stream_pagenum = hp->cpin->pagenum;
sh.stream_npages = btop(hp->cpin->used);
dumpsys_bzrun(hp, &sh, sizeof (sh), BZ_RUN);
}
dumpsys_bzrun(hp, hp->page, PAGESIZE, 0);
}
if (sh.stream_pagenum != (pgcnt_t)-1) {
dumpsys_bzrun(hp, NULL, 0, BZ_FINISH);
dumpsys_errmsg(hp, NULL);
}
ASSERT(hp->cpin == NULL && hp->cpout == NULL && hp->cperr == NULL);
CQ_CLOSE(mainq);
}
static void
dumpsys_lzjbrun(helper_t *hp, size_t csize, void *buf, size_t size)
{
dumpsync_t *ds = hp->ds;
const int CSIZE = sizeof (dumpcsize_t);
dumpcsize_t cs;
size_t osize = csize > 0 ? CSIZE + size : size;
if (size == 0 && hp->cpout == NULL)
return;
if (size == 0 ||
hp->cpout != NULL && hp->out + osize > hp->cpout->size) {
cs = DUMP_SET_TAG(hp->out - CSIZE, hp->tag);
(void) memcpy(hp->cpout->buf, &cs, CSIZE);
dumpsys_swrite(hp, hp->cpout, hp->out);
hp->cpout = NULL;
if (size == 0)
return;
}
if (hp->cpout == NULL) {
HRSTART(hp->perpage, outwait);
hp->cpout = CQ_GET(freebufq);
HRSTOP(hp->perpage, outwait);
hp->out = CSIZE;
}
if (csize > 0) {
cs = DUMP_SET_TAG(csize, 0);
(void) memcpy(hp->cpout->buf + hp->out, &cs, CSIZE);
hp->out += CSIZE;
}
(void) memcpy(hp->cpout->buf + hp->out, buf, size);
hp->out += size;
}
static void
dumpsys_lzjbcompress(helper_t *hp)
{
dumpsync_t *ds = hp->ds;
size_t csize;
dumpstreamhdr_t sh;
(void) strcpy(sh.stream_magic, DUMP_STREAM_MAGIC);
sh.stream_pagenum = (pfn_t)-1;
sh.stream_npages = 0;
hp->cpin = NULL;
hp->cpout = NULL;
hp->cperr = NULL;
hp->in = 0;
hp->out = 0;
CQ_OPEN(mainq);
while (dumpsys_sread(hp)) {
if (sh.stream_pagenum != hp->cpin->pagenum) {
sh.stream_pagenum = hp->cpin->pagenum;
sh.stream_npages = btop(hp->cpin->used);
dumpsys_lzjbrun(hp, 0, &sh, sizeof (sh));
}
HRSTART(hp->perpage, compress);
csize = compress(hp->page, hp->lzbuf, PAGESIZE);
HRSTOP(hp->perpage, compress);
ASSERT(csize > 0 && csize <= PAGESIZE);
dumpsys_lzjbrun(hp, csize, hp->lzbuf, csize);
}
if (sh.stream_pagenum != (pfn_t)-1) {
dumpsys_lzjbrun(hp, 0, NULL, 0);
dumpsys_errmsg(hp, NULL);
}
ASSERT(hp->cpin == NULL && hp->cpout == NULL && hp->cperr == NULL);
CQ_CLOSE(mainq);
}
void
dumpsys_helper()
{
dumpsys_spinlock(&dumpcfg.helper_lock);
if (dumpcfg.helpers_wanted) {
helper_t *hp, *hpend = &dumpcfg.helper[dumpcfg.nhelper];
for (hp = dumpcfg.helper; hp != hpend; hp++) {
if (hp->helper == FREEHELPER) {
hp->helper = CPU->cpu_id;
BT_SET(dumpcfg.helpermap, CPU->cpu_seqid);
dumpsys_spinunlock(&dumpcfg.helper_lock);
if (dumpcfg.clevel < DUMP_CLEVEL_BZIP2)
dumpsys_lzjbcompress(hp);
else
dumpsys_bz2compress(hp);
hp->helper = DONEHELPER;
return;
}
}
dumpcfg.helpers_wanted = 0;
}
dumpsys_spinunlock(&dumpcfg.helper_lock);
}
void
dumpsys_helper_nw()
{
if (dumpcfg.helpers_wanted)
dumpsys_helper();
}
static void
dumpsys_live_helper(void *arg)
{
helper_t *hp = arg;
BT_ATOMIC_SET(dumpcfg.helpermap, CPU->cpu_seqid);
if (dumpcfg.clevel < DUMP_CLEVEL_BZIP2)
dumpsys_lzjbcompress(hp);
else
dumpsys_bz2compress(hp);
}
static void
dumpsys_lzjb_page(helper_t *hp, cbuf_t *cp)
{
dumpsync_t *ds = hp->ds;
uint32_t csize;
hp->helper = MAINHELPER;
hp->in = 0;
hp->used = 0;
hp->cpin = cp;
while (hp->used < cp->used) {
HRSTART(hp->perpage, copy);
hp->in = dumpsys_copy_page(hp, hp->in);
hp->used += PAGESIZE;
HRSTOP(hp->perpage, copy);
HRSTART(hp->perpage, compress);
csize = compress(hp->page, hp->lzbuf, PAGESIZE);
HRSTOP(hp->perpage, compress);
HRSTART(hp->perpage, write);
dumpvp_write(&csize, sizeof (csize));
dumpvp_write(hp->lzbuf, csize);
HRSTOP(hp->perpage, write);
}
CQ_PUT(mainq, hp->cpin, CBUF_USEDMAP);
hp->cpin = NULL;
}
static void
dumpsys_main_task(void *arg)
{
dumpsync_t *ds = arg;
pgcnt_t pagenum = 0, bitnum = 0, hibitnum;
dumpmlw_t mlw;
cbuf_t *cp;
pgcnt_t baseoff, pfnoff;
pfn_t base, pfn;
boolean_t dumpserial;
int i;
dumpserial = B_TRUE;
if (dump_plat_mincpu != 0 && dumpcfg.clevel != 0) {
hrtime_t hrtmax = MSEC2NSEC(DUMP_HELPER_MAX_WAIT);
hrtime_t hrtstart = gethrtime();
for (;;) {
for (i = 0; i < BT_BITOUL(NCPU); ++i) {
if (dumpcfg.helpermap[i] != 0) {
dumpserial = B_FALSE;
break;
}
}
if ((!dumpserial) ||
((gethrtime() - hrtstart) >= hrtmax)) {
break;
}
SMT_PAUSE();
}
if (dumpserial) {
dumpcfg.clevel = 0;
if (dumpcfg.helper[0].lzbuf == NULL) {
dumpcfg.helper[0].lzbuf =
dumpcfg.helper[1].page;
}
}
}
dump_init_memlist_walker(&mlw);
for (;;) {
int sec = (gethrtime() - ds->start) / NANOSEC;
if (ds->percent > ds->percent_done || sec > ds->sec_done) {
ds->sec_done = sec;
ds->percent_done = ds->percent;
uprintf("^\rdumping: %2d:%02d %3d%% done",
sec / 60, sec % 60, ds->percent);
ds->neednl = 1;
}
while (CQ_IS_EMPTY(mainq) && !CQ_IS_EMPTY(writerq)) {
cp = CQ_GET(writerq);
if (cp == NULL)
break;
dump_timeleft = dump_timeout;
HRSTART(ds->perpage, write);
dumpvp_write(cp->buf, cp->used);
HRSTOP(ds->perpage, write);
CQ_PUT(freebufq, cp, CBUF_FREEBUF);
}
cp = CQ_GET(mainq);
if (cp == NULL) {
if (!CQ_IS_EMPTY(writerq))
continue;
break;
}
dump_timeleft = dump_timeout;
switch (cp->state) {
case CBUF_FREEMAP:
if (bitnum >= dumpcfg.bitmapsize)
break;
if (dump_ioerr) {
bitnum = dumpcfg.bitmapsize;
CQ_CLOSE(helperq);
break;
}
HRSTART(ds->perpage, bitmap);
for (; bitnum < dumpcfg.bitmapsize; bitnum++)
if (BT_TEST(dumpcfg.bitmap, bitnum))
break;
HRSTOP(ds->perpage, bitmap);
dump_timeleft = dump_timeout;
if (bitnum >= dumpcfg.bitmapsize) {
CQ_CLOSE(helperq);
break;
}
pfn = dump_bitnum_to_pfn(bitnum, &mlw);
ASSERT(pfn != PFN_INVALID);
ASSERT(bitnum + mlw.mpleft <= dumpcfg.bitmapsize);
base = P2ALIGN(pfn, CBUF_MAPNP);
if (base < mlw.mpaddr) {
base = mlw.mpaddr;
baseoff = P2PHASE(base, CBUF_MAPNP);
} else {
baseoff = 0;
}
pfnoff = pfn - base;
if (pfnoff + mlw.mpleft < CBUF_MAPNP) {
hibitnum = bitnum + mlw.mpleft;
cp->size = ptob(pfnoff + mlw.mpleft);
} else {
hibitnum = bitnum - pfnoff + CBUF_MAPNP -
baseoff;
cp->size = CBUF_MAPSIZE - ptob(baseoff);
}
cp->pfn = pfn;
cp->bitnum = bitnum++;
cp->pagenum = pagenum++;
cp->off = ptob(pfnoff);
for (; bitnum < hibitnum; bitnum++)
if (BT_TEST(dumpcfg.bitmap, bitnum))
pagenum++;
dump_timeleft = dump_timeout;
cp->used = ptob(pagenum - cp->pagenum);
HRSTART(ds->perpage, map);
hat_devload(kas.a_hat, cp->buf, cp->size, base,
PROT_READ, HAT_LOAD_NOCONSIST);
HRSTOP(ds->perpage, map);
ds->pages_mapped += btop(cp->size);
ds->pages_used += pagenum - cp->pagenum;
CQ_OPEN(mainq);
if (dumpserial) {
dumpsys_lzjb_page(dumpcfg.helper, cp);
} else {
CQ_PUT(helperq, cp, CBUF_INREADY);
}
if (bitnum >= dumpcfg.bitmapsize)
CQ_CLOSE(helperq);
break;
case CBUF_USEDMAP:
ds->npages += btop(cp->used);
HRSTART(ds->perpage, unmap);
hat_unload(kas.a_hat, cp->buf, cp->size, HAT_UNLOAD);
HRSTOP(ds->perpage, unmap);
if (bitnum < dumpcfg.bitmapsize)
CQ_PUT(mainq, cp, CBUF_FREEMAP);
CQ_CLOSE(mainq);
ASSERT(ds->npages <= dumphdr->dump_npages);
ds->percent = ds->npages * 100LL / dumphdr->dump_npages;
break;
case CBUF_WRITE:
CQ_PUT(writerq, cp, CBUF_WRITE);
break;
case CBUF_ERRMSG:
if (cp->used > 0) {
cp->buf[cp->size - 2] = '\n';
cp->buf[cp->size - 1] = '\0';
if (ds->neednl) {
uprintf("\n%s", cp->buf);
ds->neednl = 0;
} else {
uprintf("%s", cp->buf);
}
drv_usecwait(200000);
dump_timeleft = dump_timeout;
}
CQ_PUT(freebufq, cp, CBUF_FREEBUF);
break;
default:
uprintf("dump: unexpected buffer state %d, "
"buffer will be lost\n", cp->state);
break;
}
}
}
#ifdef COLLECT_METRICS
size_t
dumpsys_metrics(dumpsync_t *ds, char *buf, size_t size)
{
dumpcfg_t *cfg = &dumpcfg;
int myid = CPU->cpu_seqid;
int i, compress_ratio;
int sec, iorate;
helper_t *hp, *hpend = &cfg->helper[cfg->nhelper];
char *e = buf + size;
char *p = buf;
sec = ds->elapsed / (1000 * 1000 * 1000ULL);
if (sec < 1)
sec = 1;
if (ds->iotime < 1)
ds->iotime = 1;
iorate = (ds->nwrite * 100000ULL) / ds->iotime;
compress_ratio = 100LL * ds->npages / btopr(ds->nwrite + 1);
#define P(...) (p += p < e ? snprintf(p, e - p, __VA_ARGS__) : 0)
P("Master cpu_seqid,%d\n", CPU->cpu_seqid);
P("Master cpu_id,%d\n", CPU->cpu_id);
P("dump_flags,0x%x\n", dumphdr->dump_flags);
P("dump_ioerr,%d\n", dump_ioerr);
P("Helpers:\n");
for (i = 0; i < ncpus; i++) {
if ((i & 15) == 0)
P(",,%03d,", i);
if (i == myid)
P(" M");
else if (BT_TEST(cfg->helpermap, i))
P("%4d", cpu_seq[i]->cpu_id);
else
P(" *");
if ((i & 15) == 15)
P("\n");
}
P("ncbuf_used,%d\n", cfg->ncbuf_used);
P("ncmap,%d\n", cfg->ncmap);
P("Found %ldM ranges,%ld\n", (CBUF_MAPSIZE / DUMP_1MB), cfg->found4m);
P("Found small pages,%ld\n", cfg->foundsm);
P("Compression level,%d\n", cfg->clevel);
P("Compression type,%s %s", cfg->clevel == 0 ? "serial" : "parallel",
cfg->clevel >= DUMP_CLEVEL_BZIP2 ? "bzip2" : "lzjb");
if (cfg->clevel >= DUMP_CLEVEL_BZIP2)
P(" (level %d)\n", dump_bzip2_level);
else
P("\n");
P("Compression ratio,%d.%02d\n", compress_ratio / 100, compress_ratio %
100);
P("nhelper_used,%d\n", cfg->nhelper_used);
P("Dump I/O rate MBS,%d.%02d\n", iorate / 100, iorate % 100);
P("..total bytes,%lld\n", (u_longlong_t)ds->nwrite);
P("..total nsec,%lld\n", (u_longlong_t)ds->iotime);
P("dumpbuf.iosize,%ld\n", dumpbuf.iosize);
P("dumpbuf.size,%ld\n", dumpbuf.size);
P("Dump pages/sec,%llu\n", (u_longlong_t)ds->npages / sec);
P("Dump pages,%llu\n", (u_longlong_t)ds->npages);
P("Dump time,%d\n", sec);
if (ds->pages_mapped > 0)
P("per-cent map utilization,%d\n", (int)((100 * ds->pages_used)
/ ds->pages_mapped));
P("\nPer-page metrics:\n");
if (ds->npages > 0) {
for (hp = cfg->helper; hp != hpend; hp++) {
#define PERPAGE(x) ds->perpage.x += hp->perpage.x;
PERPAGES;
#undef PERPAGE
}
#define PERPAGE(x) \
P("%s nsec/page,%d\n", #x, (int)(ds->perpage.x / ds->npages));
PERPAGES;
#undef PERPAGE
P("freebufq.empty,%d\n", (int)(ds->freebufq.empty /
ds->npages));
P("helperq.empty,%d\n", (int)(ds->helperq.empty /
ds->npages));
P("writerq.empty,%d\n", (int)(ds->writerq.empty /
ds->npages));
P("mainq.empty,%d\n", (int)(ds->mainq.empty / ds->npages));
P("I/O wait nsec/page,%llu\n", (u_longlong_t)(ds->iowait /
ds->npages));
}
#undef P
if (p < e)
bzero(p, e - p);
return (p - buf);
}
#endif
void
dumpsys(void)
{
dumpsync_t *ds = &dumpsync;
taskq_t *livetaskq = NULL;
pfn_t pfn;
pgcnt_t bitnum;
proc_t *p;
helper_t *hp, *hpend = &dumpcfg.helper[dumpcfg.nhelper];
cbuf_t *cp;
pid_t npids, pidx;
char *content;
char *buf;
size_t size;
int save_dump_clevel;
dumpmlw_t mlw;
dumpcsize_t datatag;
dumpdatahdr_t datahdr;
if (dumpvp == NULL || dumphdr == NULL) {
uprintf("skipping system dump - no dump device configured\n");
if (panicstr) {
dumpcfg.helpers_wanted = 0;
dumpsys_spinunlock(&dumpcfg.helper_lock);
}
return;
}
dumpbuf.cur = dumpbuf.start;
ASSERT(dumpcfg.nhelper > 0);
bzero(ds, sizeof (*ds));
ds->dumpcpu = CPU->cpu_id;
if (dumpvp->v_flag & VISSWAP)
dumphdr->dump_start = P2ROUNDUP(dumpvp_size / 5, DUMP_OFFSET);
else
dumphdr->dump_start = DUMP_OFFSET;
dumphdr->dump_flags = DF_VALID | DF_COMPLETE | DF_LIVE | DF_COMPRESSED;
dumphdr->dump_crashtime = gethrestime_sec();
dumphdr->dump_npages = 0;
dumphdr->dump_nvtop = 0;
bzero(dumpcfg.bitmap, BT_SIZEOFMAP(dumpcfg.bitmapsize));
dump_timeleft = dump_timeout;
if (panicstr) {
dumphdr->dump_flags &= ~DF_LIVE;
(void) VOP_DUMPCTL(dumpvp, DUMP_FREE, NULL, NULL);
(void) VOP_DUMPCTL(dumpvp, DUMP_ALLOC, NULL, NULL);
(void) vsnprintf(dumphdr->dump_panicstring, DUMP_PANICSIZE,
panicstr, panicargs);
}
if (dump_conflags & DUMP_ALL)
content = "all";
else if (dump_conflags & DUMP_CURPROC)
content = "kernel + curproc";
else
content = "kernel";
uprintf("dumping to %s, offset %lld, content: %s\n", dumppath,
dumphdr->dump_start, content);
bcopy(utsname.nodename, dumphdr->dump_utsname.nodename, SYS_NMLN);
if (!panicstr) {
vnode_t *cdev_vp, *cmn_cdev_vp;
ASSERT(dumpbuf.cdev_vp == NULL);
cdev_vp = makespecvp(VTOS(dumpvp)->s_dev, VCHR);
if (cdev_vp != NULL) {
cmn_cdev_vp = common_specvp(cdev_vp);
if (VOP_OPEN(&cmn_cdev_vp, FREAD | FWRITE, kcred, NULL)
== 0) {
if (vn_has_cached_data(dumpvp))
(void) pvn_vplist_dirty(dumpvp, 0, NULL,
B_INVAL | B_TRUNC, kcred);
dumpbuf.cdev_vp = cmn_cdev_vp;
} else {
VN_RELE(cdev_vp);
}
}
}
lbolt_debug_entry();
dumpbuf.vp_limit = dumpvp_size - DUMP_LOGSIZE - DUMP_OFFSET -
DUMP_ERPTSIZE;
dumpbuf.vp_off = dumphdr->dump_ksyms = dumphdr->dump_start + PAGESIZE;
dumphdr->dump_ksyms_size = dumphdr->dump_ksyms_csize =
ksyms_snapshot(dumpvp_ksyms_write, NULL, LONG_MAX);
dumphdr->dump_map = dumpvp_flush();
dump_as(&kas);
dumphdr->dump_nvtop += dump_plat_addr();
hat_dump();
if (dump_conflags & DUMP_ALL) {
mutex_enter(&pidlock);
for (npids = 0, p = practive; p != NULL; p = p->p_next)
dumpcfg.pids[npids++] = p->p_pid;
mutex_exit(&pidlock);
for (pidx = 0; pidx < npids; pidx++)
(void) dump_process(dumpcfg.pids[pidx]);
dump_init_memlist_walker(&mlw);
for (bitnum = 0; bitnum < dumpcfg.bitmapsize; bitnum++) {
dump_timeleft = dump_timeout;
pfn = dump_bitnum_to_pfn(bitnum, &mlw);
if (plat_hold_page(pfn, PLAT_HOLD_NO_LOCK, NULL) !=
PLAT_HOLD_OK)
continue;
BT_SET(dumpcfg.bitmap, bitnum);
}
dumphdr->dump_npages = dumpcfg.bitmapsize;
dumphdr->dump_flags |= DF_ALL;
} else if (dump_conflags & DUMP_CURPROC) {
npids = 0;
if (panicstr) {
if (panic_thread != NULL &&
panic_thread->t_procp != NULL &&
panic_thread->t_procp != &p0) {
dumpcfg.pids[npids++] =
panic_thread->t_procp->p_pid;
}
} else {
dumpcfg.pids[npids++] = curthread->t_procp->p_pid;
}
if (npids && dump_process(dumpcfg.pids[0]) == 0)
dumphdr->dump_flags |= DF_CURPROC;
else
dumphdr->dump_flags |= DF_KERNEL;
} else {
dumphdr->dump_flags |= DF_KERNEL;
}
dumphdr->dump_hashmask = (1 << highbit(dumphdr->dump_nvtop - 1)) - 1;
dumphdr->dump_pfn = dumpvp_flush();
dump_init_memlist_walker(&mlw);
for (bitnum = 0; bitnum < dumpcfg.bitmapsize; bitnum++) {
dump_timeleft = dump_timeout;
if (!BT_TEST(dumpcfg.bitmap, bitnum))
continue;
pfn = dump_bitnum_to_pfn(bitnum, &mlw);
ASSERT(pfn != PFN_INVALID);
dumpvp_write(&pfn, sizeof (pfn_t));
}
dump_plat_pfn();
dumphdr->dump_data = dumpvp_flush();
bzero(dumpcfg.helpermap, BT_SIZEOFMAP(NCPU));
ds->live = dumpcfg.clevel > 0 &&
(dumphdr->dump_flags & DF_LIVE) != 0;
save_dump_clevel = dumpcfg.clevel;
if (panicstr)
dumpsys_get_maxmem();
else if (dumpcfg.clevel >= DUMP_CLEVEL_BZIP2)
dumpcfg.clevel = DUMP_CLEVEL_LZJB;
dumpcfg.nhelper_used = 0;
for (hp = dumpcfg.helper; hp != hpend; hp++) {
if (hp->page == NULL) {
hp->helper = DONEHELPER;
continue;
}
++dumpcfg.nhelper_used;
hp->helper = FREEHELPER;
hp->taskqid = 0;
hp->ds = ds;
bzero(&hp->perpage, sizeof (hp->perpage));
if (dumpcfg.clevel >= DUMP_CLEVEL_BZIP2)
(void) BZ2_bzCompressReset(&hp->bzstream);
}
CQ_OPEN(freebufq);
CQ_OPEN(helperq);
dumpcfg.ncbuf_used = 0;
for (cp = dumpcfg.cbuf; cp != &dumpcfg.cbuf[dumpcfg.ncbuf]; cp++) {
if (cp->buf != NULL) {
CQ_PUT(freebufq, cp, CBUF_FREEBUF);
++dumpcfg.ncbuf_used;
}
}
for (cp = dumpcfg.cmap; cp != &dumpcfg.cmap[dumpcfg.ncmap]; cp++)
CQ_PUT(mainq, cp, CBUF_FREEMAP);
ds->start = gethrtime();
ds->iowaitts = ds->start;
if (ds->live) {
int n = dumpcfg.nhelper_used;
int pri = MINCLSYSPRI - 25;
livetaskq = taskq_create("LiveDump", n, pri, n, n,
TASKQ_PREPOPULATE);
for (hp = dumpcfg.helper; hp != hpend; hp++) {
if (hp->page == NULL)
continue;
hp->helper = hp - dumpcfg.helper;
hp->taskqid = taskq_dispatch(livetaskq,
dumpsys_live_helper, (void *)hp, TQ_NOSLEEP);
}
} else {
if (panicstr)
kmem_dump_begin();
dumpcfg.helpers_wanted = dumpcfg.clevel > 0;
dumpsys_spinunlock(&dumpcfg.helper_lock);
}
dumpsys_main_task(ds);
ds->elapsed = gethrtime() - ds->start;
if (ds->elapsed < 1)
ds->elapsed = 1;
if (livetaskq != NULL)
taskq_destroy(livetaskq);
if (ds->neednl) {
uprintf("\n");
ds->neednl = 0;
}
dumphdr->dump_npages = ds->npages;
dumphdr->dump_npages += dump_plat_data(dumpcfg.cbuf[0].buf);
if (dump_ioerr || ds->npages < dumphdr->dump_npages)
dumphdr->dump_flags &= ~DF_COMPLETE;
datatag = 0;
dumpvp_write(&datatag, sizeof (datatag));
bzero(&datahdr, sizeof (datahdr));
buf = dumpcfg.cbuf[0].buf;
size = MIN(dumpcfg.cbuf[0].size, DUMP_OFFSET - sizeof (dumphdr_t) -
sizeof (dumpdatahdr_t));
if (panicstr) {
datahdr.dump_metrics = kmem_dump_finish(buf, size);
buf += datahdr.dump_metrics;
size -= datahdr.dump_metrics;
}
if (panicstr)
dumphdr->dump_fm_panic = is_fm_panic();
datahdr.dump_datahdr_magic = DUMP_DATAHDR_MAGIC;
datahdr.dump_datahdr_version = DUMP_DATAHDR_VERSION;
datahdr.dump_maxcsize = CBUF_SIZE;
datahdr.dump_maxrange = CBUF_MAPSIZE / PAGESIZE;
datahdr.dump_nstreams = dumpcfg.nhelper_used;
datahdr.dump_clevel = dumpcfg.clevel;
#ifdef COLLECT_METRICS
if (dump_metrics_on)
datahdr.dump_metrics += dumpsys_metrics(ds, buf, size);
#endif
datahdr.dump_data_csize = dumpvp_flush() - dumphdr->dump_data;
dumpbuf.vp_off = dumphdr->dump_start;
dumpvp_write(dumphdr, sizeof (dumphdr_t));
(void) dumpvp_flush();
dumpbuf.vp_limit = dumpvp_size;
dumpbuf.vp_off = dumpbuf.vp_limit - DUMP_OFFSET;
dumpvp_write(dumphdr, sizeof (dumphdr_t));
dumpvp_write(&datahdr, sizeof (dumpdatahdr_t));
dumpvp_write(dumpcfg.cbuf[0].buf, datahdr.dump_metrics);
(void) dumpvp_flush();
uprintf("\r%3d%% done: %llu pages dumped, ",
ds->percent_done, (u_longlong_t)ds->npages);
if (dump_ioerr == 0) {
uprintf("dump succeeded\n");
} else {
uprintf("dump failed: error %d\n", dump_ioerr);
#ifdef DEBUG
if (panicstr)
debug_enter("dump failed");
#endif
}
if (panicstr) {
dump_summary();
dump_ereports();
dump_messages();
}
delay(2 * hz);
dump_timeleft = 0;
dump_ioerr = 0;
if (!panicstr) {
dumpcfg.clevel = save_dump_clevel;
if (dumpbuf.cdev_vp != NULL) {
(void) VOP_CLOSE(dumpbuf.cdev_vp, FREAD | FWRITE, 1, 0,
kcred, NULL);
VN_RELE(dumpbuf.cdev_vp);
dumpbuf.cdev_vp = NULL;
}
}
}
void
dump_resize()
{
mutex_enter(&dump_lock);
dumphdr_init();
dumpbuf_resize();
dump_update_clevel();
mutex_exit(&dump_lock);
}
int
dumpvp_resize()
{
int error;
vattr_t vattr;
mutex_enter(&dump_lock);
vattr.va_mask = AT_SIZE;
if ((error = VOP_GETATTR(dumpvp, &vattr, 0, kcred, NULL)) != 0) {
mutex_exit(&dump_lock);
return (error);
}
if (error == 0 && vattr.va_size < 2 * DUMP_LOGSIZE + DUMP_ERPTSIZE) {
mutex_exit(&dump_lock);
return (ENOSPC);
}
dumpvp_size = vattr.va_size & -DUMP_OFFSET;
mutex_exit(&dump_lock);
return (0);
}
int
dump_set_uuid(const char *uuidstr)
{
const char *ptr;
int i;
if (uuidstr == NULL || strnlen(uuidstr, 36 + 1) != 36)
return (EINVAL);
for (i = 0, ptr = uuidstr; i < 36; i++, ptr++) {
switch (i) {
case 8:
case 13:
case 18:
case 23:
if (*ptr != '-')
return (EINVAL);
break;
default:
if (!isxdigit(*ptr))
return (EINVAL);
break;
}
}
if (dump_osimage_uuid[0] != '\0')
return (EALREADY);
(void) strncpy(dump_osimage_uuid, uuidstr, 36 + 1);
cmn_err(CE_CONT, "?This illumos instance has UUID %s\n",
dump_osimage_uuid);
return (0);
}
const char *
dump_get_uuid(void)
{
return (dump_osimage_uuid[0] != '\0' ? dump_osimage_uuid : "");
}