/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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;