lufs_debug.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/systm.h>
#include <sys/types.h>
#include <sys/vnode.h>
#include <sys/buf.h>
#include <sys/ddi.h>
#include <sys/errno.h>
#include <sys/sysmacros.h>
#include <sys/debug.h>
#include <sys/kmem.h>
#include <sys/conf.h>
#include <sys/proc.h>
#include <sys/cmn_err.h>
#include <sys/fs/ufs_inode.h>
#include <sys/fs/ufs_filio.h>
#include <sys/fs/ufs_log.h>
#ifdef DEBUG
/*
* DEBUG ROUTINES
* THESE ROUTINES ARE ONLY USED WHEN ASSERTS ARE ENABLED
*/
static kmutex_t toptracelock;
static int toptraceindex;
int toptracemax = 1024; /* global so it can be set */
struct toptrace {
enum delta_type dtyp;
kthread_t *thread;
dev_t dev;
long arg2;
long arg3;
long long arg1;
} *toptrace;
static void
top_trace(enum delta_type dtyp, dev_t dev, long long arg1, long arg2, long arg3)
{
if (toptrace == NULL) {
toptraceindex = 0;
toptrace = kmem_zalloc((size_t)
(sizeof (struct toptrace) * toptracemax), KM_SLEEP);
}
mutex_enter(&toptracelock);
toptrace[toptraceindex].dtyp = dtyp;
toptrace[toptraceindex].thread = curthread;
toptrace[toptraceindex].dev = dev;
toptrace[toptraceindex].arg1 = arg1;
toptrace[toptraceindex].arg2 = arg2;
toptrace[toptraceindex].arg3 = arg3;
if (++toptraceindex == toptracemax)
toptraceindex = 0;
else {
toptrace[toptraceindex].dtyp = (enum delta_type)-1;
toptrace[toptraceindex].thread = (kthread_t *)-1;
toptrace[toptraceindex].dev = (dev_t)-1;
toptrace[toptraceindex].arg1 = -1;
toptrace[toptraceindex].arg2 = -1;
}
mutex_exit(&toptracelock);
}
/*
* add a range into the metadata map
*/
void
top_mataadd(ufsvfs_t *ufsvfsp, offset_t mof, off_t nb)
{
ml_unit_t *ul = ufsvfsp->vfs_log;
ASSERT(ufsvfsp->vfs_dev == ul->un_dev);
deltamap_add(ul->un_matamap, mof, nb, 0, 0, 0, NULL);
}
/*
* delete a range from the metadata map
*/
void
top_matadel(ufsvfs_t *ufsvfsp, offset_t mof, off_t nb)
{
ml_unit_t *ul = ufsvfsp->vfs_log;
ASSERT(ufsvfsp->vfs_dev == ul->un_dev);
ASSERT(!matamap_overlap(ul->un_deltamap, mof, nb));
deltamap_del(ul->un_matamap, mof, nb);
}
/*
* clear the entries from the metadata map
*/
void
top_mataclr(ufsvfs_t *ufsvfsp)
{
ml_unit_t *ul = ufsvfsp->vfs_log;
ASSERT(ufsvfsp->vfs_dev == ul->un_dev);
map_free_entries(ul->un_matamap);
map_free_entries(ul->un_deltamap);
}
int
top_begin_debug(ml_unit_t *ul, top_t topid, ulong_t size)
{
threadtrans_t *tp;
if (ul->un_debug & MT_TRACE)
top_trace(DT_BOT, ul->un_dev,
(long long)topid, (long)size, (long)0);
ASSERT(curthread->t_flag & T_DONTBLOCK);
tp = tsd_get(topkey);
if (tp == NULL) {
tp = kmem_zalloc(sizeof (threadtrans_t), KM_SLEEP);
(void) tsd_set(topkey, tp);
}
tp->topid = topid;
tp->esize = size;
tp->rsize = 0;
tp->dev = ul->un_dev;
return (1);
}
int
top_end_debug(ml_unit_t *ul, mt_map_t *mtm, top_t topid, ulong_t size)
{
threadtrans_t *tp;
ASSERT(curthread->t_flag & T_DONTBLOCK);
ASSERT((tp = (threadtrans_t *)tsd_get(topkey)) != NULL);
ASSERT((tp->dev == ul->un_dev) && (tp->topid == topid) &&
(tp->esize == size));
ASSERT(((ul->un_debug & MT_SIZE) == 0) || (tp->rsize <= tp->esize));
mtm->mtm_tops->mtm_top_num[topid]++;
mtm->mtm_tops->mtm_top_size_etot[topid] += tp->esize;
mtm->mtm_tops->mtm_top_size_rtot[topid] += tp->rsize;
if (tp->rsize > mtm->mtm_tops->mtm_top_size_max[topid])
mtm->mtm_tops->mtm_top_size_max[topid] = tp->rsize;
if (mtm->mtm_tops->mtm_top_size_min[topid] == 0)
mtm->mtm_tops->mtm_top_size_min[topid] =
tp->rsize;
else
if (tp->rsize < mtm->mtm_tops->mtm_top_size_min[topid])
mtm->mtm_tops->mtm_top_size_min[topid] =
tp->rsize;
if (ul->un_debug & MT_TRACE)
top_trace(DT_EOT, ul->un_dev, (long long)topid,
(long)tp->rsize, (long)0);
return (1);
}
int
top_delta_debug(
ml_unit_t *ul,
offset_t mof,
off_t nb,
delta_t dtyp)
{
struct threadtrans *tp;
ASSERT(curthread->t_flag & T_DONTBLOCK);
/*
* check for delta contained fully within matamap
*/
ASSERT((ul->un_matamap == NULL) ||
matamap_within(ul->un_matamap, mof, nb));
/*
* maintain transaction info
*/
if (ul->un_debug & MT_TRANSACT)
ul->un_logmap->mtm_tops->mtm_delta_num[dtyp]++;
/*
* check transaction stuff
*/
if (ul->un_debug & MT_TRANSACT) {
tp = (struct threadtrans *)tsd_get(topkey);
ASSERT(tp);
switch (dtyp) {
case DT_CANCEL:
case DT_ABZERO:
if (!matamap_within(ul->un_deltamap, mof, nb))
tp->rsize += sizeof (struct delta);
break;
default:
if (!matamap_within(ul->un_deltamap, mof, nb))
tp->rsize += nb + sizeof (struct delta);
break;
}
} else
return (1);
if (ul->un_debug & MT_TRACE)
top_trace(dtyp, ul->un_dev, mof, (long)nb, (long)0);
return (1);
}
int
top_roll_debug(ml_unit_t *ul)
{
logmap_roll_dev(ul);
return (1);
}
int
top_init_debug(void)
{
mutex_init(&toptracelock, NULL, MUTEX_DEFAULT, NULL);
return (1);
}
struct topstats_link {
struct topstats_link *ts_next;
dev_t ts_dev;
struct topstats ts_stats;
};
struct topstats_link *topstats_anchor = NULL;
/*
* DEBUG ROUTINES
* from debug portion of *_map.c
*/
/*
* scan test support
*/
int
logmap_logscan_debug(mt_map_t *mtm, mapentry_t *age)
{
mapentry_t *me;
ml_unit_t *ul;
off_t head, trimroll, lof;
/*
* remember location of youngest rolled delta
*/
mutex_enter(&mtm->mtm_mutex);
ul = mtm->mtm_ul;
head = ul->un_head_lof;
trimroll = mtm->mtm_trimrlof;
for (me = age; me; me = me->me_agenext) {
lof = me->me_lof;
if (trimroll == 0)
trimroll = lof;
if (lof >= head) {
if (trimroll >= head && trimroll <= lof)
trimroll = lof;
} else {
if (trimroll <= lof || trimroll >= head)
trimroll = lof;
}
}
mtm->mtm_trimrlof = trimroll;
mutex_exit(&mtm->mtm_mutex);
return (1);
}
/*
* scan test support
*/
int
logmap_logscan_commit_debug(off_t lof, mt_map_t *mtm)
{
off_t oldtrimc, newtrimc, trimroll;
trimroll = mtm->mtm_trimrlof;
oldtrimc = mtm->mtm_trimclof;
newtrimc = mtm->mtm_trimclof = dbtob(btod(lof));
/*
* can't trim prior to transaction w/rolled delta
*/
if (trimroll)
if (newtrimc >= oldtrimc) {
if (trimroll <= newtrimc && trimroll >= oldtrimc)
mtm->mtm_trimalof = newtrimc;
} else {
if (trimroll >= oldtrimc || trimroll <= newtrimc)
mtm->mtm_trimalof = newtrimc;
}
return (1);
}
int
logmap_logscan_add_debug(struct delta *dp, mt_map_t *mtm)
{
if ((dp->d_typ == DT_AB) || (dp->d_typ == DT_INODE))
mtm->mtm_trimalof = mtm->mtm_trimclof;
return (1);
}
/*
* log-read after log-write
*/
int
map_check_ldl_write(ml_unit_t *ul, caddr_t va, offset_t vamof, mapentry_t *me)
{
caddr_t bufp;
ASSERT(me->me_nb);
ASSERT((me->me_flags & ME_AGE) == 0);
/* Alloc a buf */
bufp = kmem_alloc(me->me_nb, KM_SLEEP);
/* Do the read */
me->me_agenext = NULL;
if (ldl_read(ul, bufp, me->me_mof, me->me_nb, me) == 0) {
ASSERT(bcmp(bufp, va + (me->me_mof - vamof), me->me_nb) == 0);
}
kmem_free(bufp, me->me_nb);
return (1);
}
/*
* Cleanup a map struct
*/
int
map_put_debug(mt_map_t *mtm)
{
struct topstats_link *tsl, **ptsl;
if (mtm->mtm_tops == NULL)
return (1);
/* Don't free this, cause the next snarf will want it */
if ((lufs_debug & MT_TRANSACT) != 0)
return (1);
ptsl = &topstats_anchor;
tsl = topstats_anchor;
while (tsl) {
if (mtm->mtm_tops == &tsl->ts_stats) {
mtm->mtm_tops = NULL;
*ptsl = tsl->ts_next;
kmem_free(tsl, sizeof (*tsl));
return (1);
}
ptsl = &tsl->ts_next;
tsl = tsl->ts_next;
}
return (1);
}
int
map_get_debug(ml_unit_t *ul, mt_map_t *mtm)
{
struct topstats_link *tsl;
if ((ul->un_debug & MT_TRANSACT) == 0)
return (1);
if (mtm->mtm_type != logmaptype)
return (1);
tsl = topstats_anchor;
while (tsl) {
if (tsl->ts_dev == ul->un_dev) {
mtm->mtm_tops = &(tsl->ts_stats);
return (1);
}
tsl = tsl->ts_next;
}
tsl = kmem_zalloc(sizeof (*tsl), KM_SLEEP);
tsl->ts_dev = ul->un_dev;
tsl->ts_next = topstats_anchor;
topstats_anchor = tsl;
mtm->mtm_tops = &tsl->ts_stats;
return (1);
}
/*
* check a map's list
*/
int
map_check_linkage(mt_map_t *mtm)
{
int i;
int hashed;
int nexted;
int preved;
int ncancel;
mapentry_t *me;
off_t olof;
off_t firstlof;
int wrapped;
mutex_enter(&mtm->mtm_mutex);
ASSERT(mtm->mtm_nme >= 0);
/*
* verify the entries on the hash
*/
hashed = 0;
for (i = 0; i < mtm->mtm_nhash; ++i) {
for (me = *(mtm->mtm_hash+i); me; me = me->me_hash) {
++hashed;
ASSERT(me->me_flags & ME_HASH);
ASSERT((me->me_flags & ME_LIST) == 0);
}
}
ASSERT(hashed >= mtm->mtm_nme);
/*
* verify the doubly linked list of all entries
*/
nexted = 0;
for (me = mtm->mtm_next; me != (mapentry_t *)mtm; me = me->me_next)
nexted++;
preved = 0;
for (me = mtm->mtm_prev; me != (mapentry_t *)mtm; me = me->me_prev)
preved++;
ASSERT(nexted == preved);
ASSERT(nexted == hashed);
/*
* verify the cancel list
*/
ncancel = 0;
for (me = mtm->mtm_cancel; me; me = me->me_cancel) {
++ncancel;
ASSERT(me->me_flags & ME_CANCEL);
}
/*
* verify the logmap's log offsets
*/
if (mtm->mtm_type == logmaptype) {
olof = mtm->mtm_next->me_lof;
firstlof = olof;
wrapped = 0;
/*
* Make sure to skip any mapentries whose me_lof = 0
* and me_type == DT_CANCEL, these are mapentries
* in place just to mark user block deletions as not
* available for allocate within the same moby transaction
* in case we crash before it is comitted. Skip these
* entries in the checks below as they are not applicable.
*/
for (me = mtm->mtm_next->me_next;
me != (mapentry_t *)mtm;
me = me->me_next) {
if (me->me_lof == 0 && me->me_dt == DT_CANCEL)
continue;
if (firstlof == 0) {
olof = me->me_lof;
firstlof = olof;
if (me->me_next != (mapentry_t *)mtm)
me = me->me_next;
continue;
}
ASSERT(me->me_lof != olof);
if (wrapped) {
ASSERT(me->me_lof > olof);
ASSERT(me->me_lof < firstlof);
olof = me->me_lof;
continue;
}
if (me->me_lof < olof) {
ASSERT(me->me_lof < firstlof);
wrapped = 1;
olof = me->me_lof;
continue;
}
ASSERT(me->me_lof > firstlof);
ASSERT(me->me_lof < mtm->mtm_ul->un_eol_lof);
olof = me->me_lof;
}
}
mutex_exit(&mtm->mtm_mutex);
return (1);
}
/*
* check for overlap
*/
int
matamap_overlap(mt_map_t *mtm, offset_t mof, off_t nb)
{
off_t hnb;
mapentry_t *me;
mapentry_t **mep;
for (hnb = 0; nb; nb -= hnb, mof += hnb) {
hnb = MAPBLOCKSIZE - (mof & MAPBLOCKOFF);
if (hnb > nb)
hnb = nb;
/*
* search for dup entry
*/
mep = MAP_HASH(mof, mtm);
mutex_enter(&mtm->mtm_mutex);
for (me = *mep; me; me = me->me_hash)
if (DATAoverlapME(mof, hnb, me))
break;
mutex_exit(&mtm->mtm_mutex);
/*
* overlap detected
*/
if (me)
return (1);
}
return (0);
}
/*
* check for within
*/
int
matamap_within(mt_map_t *mtm, offset_t mof, off_t nb)
{
off_t hnb;
mapentry_t *me;
mapentry_t **mep;
int scans = 0;
int withins = 0;
for (hnb = 0; nb && scans == withins; nb -= hnb, mof += hnb) {
scans++;
hnb = MAPBLOCKSIZE - (mof & MAPBLOCKOFF);
if (hnb > nb)
hnb = nb;
/*
* search for within entry
*/
mep = MAP_HASH(mof, mtm);
mutex_enter(&mtm->mtm_mutex);
for (me = *mep; me; me = me->me_hash)
if (DATAwithinME(mof, hnb, me)) {
withins++;
break;
}
mutex_exit(&mtm->mtm_mutex);
}
return (scans == withins);
}
int
ldl_sethead_debug(ml_unit_t *ul)
{
mt_map_t *mtm = ul->un_logmap;
off_t trimr = mtm->mtm_trimrlof;
off_t head = ul->un_head_lof;
off_t tail = ul->un_tail_lof;
if (head <= tail) {
if (trimr < head || trimr >= tail)
mtm->mtm_trimrlof = 0;
} else {
if (trimr >= tail && trimr < head)
mtm->mtm_trimrlof = 0;
}
return (1);
}
int
lufs_initialize_debug(ml_odunit_t *ud)
{
ud->od_debug = lufs_debug;
return (1);
}
#endif /* DEBUG */
/*
* lufs_debug controls the debug level for TSufs, and is only used
* for a debug kernel. It's referenced by ufs_ioctl() and so is
* not under #ifdef DEBUG compilation.
*/
uint_t lufs_debug;