root/usr/src/uts/common/fs/ufs/lufs_debug.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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
 */

/*
 * Copyright 2007 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;