#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: lfs_alloc.c,v 1.154 2026/01/05 05:02:47 perseant Exp $");
#if defined(_KERNEL_OPT)
#include "opt_quota.h"
#endif
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/buf.h>
#include <sys/lock.h>
#include <sys/vnode.h>
#include <sys/syslog.h>
#include <sys/mount.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/proc.h>
#include <sys/kauth.h>
#include <ufs/lfs/ulfs_quotacommon.h>
#include <ufs/lfs/ulfs_inode.h>
#include <ufs/lfs/ulfsmount.h>
#include <ufs/lfs/ulfs_extern.h>
#include <ufs/lfs/lfs.h>
#include <ufs/lfs/lfs_accessors.h>
#include <ufs/lfs/lfs_extern.h>
#include <ufs/lfs/lfs_kernel.h>
int lfs_do_check_freelist = 0;
#define BMSHIFT 5
#define BMMASK ((1 << BMSHIFT) - 1)
#define SET_BITMAP_FREE(F, I) do { \
DLOG((DLOG_ALLOC, "lfs: ino %d wrd %d bit %d set\n", (int)(I), \
(int)((I) >> BMSHIFT), (int)((I) & BMMASK))); \
(F)->lfs_ino_bitmap[(I) >> BMSHIFT] |= (1U << ((I) & BMMASK)); \
} while (0)
#define CLR_BITMAP_FREE(F, I) do { \
DLOG((DLOG_ALLOC, "lfs: ino %d wrd %d bit %d clr\n", (int)(I), \
(int)((I) >> BMSHIFT), (int)((I) & BMMASK))); \
(F)->lfs_ino_bitmap[(I) >> BMSHIFT] &= ~(1U << ((I) & BMMASK)); \
} while(0)
#define ISSET_BITMAP_FREE(F, I) \
((F)->lfs_ino_bitmap[(I) >> BMSHIFT] & (1U << ((I) & BMMASK)))
int
lfs_extend_ifile(struct lfs *fs, kauth_cred_t cred)
{
struct vnode *vp;
struct inode *ip;
IFILE64 *ifp64;
IFILE32 *ifp32;
IFILE_V1 *ifp_v1;
struct buf *bp, *cbp;
int error;
daddr_t i, blkno, xmax;
ino_t oldhead, maxino, tail;
CLEANERINFO *cip;
ASSERT_SEGLOCK(fs);
vp = fs->lfs_ivnode;
ip = VTOI(vp);
blkno = lfs_lblkno(fs, ip->i_size);
if ((error = lfs_balloc(vp, ip->i_size, lfs_sb_getbsize(fs), cred, 0,
&bp)) != 0) {
return (error);
}
ip->i_size += lfs_sb_getbsize(fs);
lfs_dino_setsize(fs, ip->i_din, ip->i_size);
uvm_vnp_setsize(vp, ip->i_size);
maxino = ((ip->i_size >> lfs_sb_getbshift(fs)) - lfs_sb_getcleansz(fs) -
lfs_sb_getsegtabsz(fs)) * lfs_sb_getifpb(fs);
fs->lfs_ino_bitmap = (lfs_bm_t *)
realloc(fs->lfs_ino_bitmap, ((maxino + BMMASK) >> BMSHIFT) *
sizeof(lfs_bm_t), M_SEGMENT, M_WAITOK);
KASSERT(fs->lfs_ino_bitmap != NULL);
i = (blkno - lfs_sb_getsegtabsz(fs) - lfs_sb_getcleansz(fs)) *
lfs_sb_getifpb(fs);
xmax = i + lfs_sb_getifpb(fs);
LFS_GET_HEADFREE(fs, cip, cbp, &oldhead);
LFS_PUT_HEADFREE(fs, cip, cbp, i);
LFS_GET_TAILFREE(fs, cip, cbp, &tail);
DLOG((DLOG_ALLOC, "oldhead=%jd, i=%jd, xmax=%jd, oldtail=%jd\n",
(intmax_t)oldhead, (intmax_t)i, (intmax_t)xmax,
(intmax_t)tail));
if (tail == LFS_UNUSED_INUM) {
tail = xmax - 1;
LFS_PUT_TAILFREE(fs, cip, cbp, tail);
}
KASSERTMSG((lfs_sb_getfreehd(fs) != LFS_UNUSED_INUM),
"inode 0 allocated [2]");
if (fs->lfs_is64) {
for (ifp64 = (IFILE64 *)bp->b_data; i < xmax; ++ifp64) {
SET_BITMAP_FREE(fs, i);
ifp64->if_version = 1;
ifp64->if_daddr = LFS_UNUSED_DADDR;
ifp64->if_nextfree = ++i;
}
ifp64--;
ifp64->if_nextfree = oldhead;
} else if (lfs_sb_getversion(fs) > 1) {
for (ifp32 = (IFILE32 *)bp->b_data; i < xmax; ++ifp32) {
SET_BITMAP_FREE(fs, i);
ifp32->if_version = 1;
ifp32->if_daddr = LFS_UNUSED_DADDR;
ifp32->if_nextfree = ++i;
}
ifp32--;
ifp32->if_nextfree = oldhead;
} else {
for (ifp_v1 = (IFILE_V1 *)bp->b_data; i < xmax; ++ifp_v1) {
SET_BITMAP_FREE(fs, i);
ifp_v1->if_version = 1;
ifp_v1->if_daddr = LFS_UNUSED_DADDR;
ifp_v1->if_nextfree = ++i;
}
ifp_v1--;
ifp_v1->if_nextfree = oldhead;
}
DLOG((DLOG_ALLOC, " now head=%jd tail=%jd\n",
(intmax_t)xmax - lfs_sb_getifpb(fs), (intmax_t)tail));
(void) LFS_BWRITE_LOG(bp);
return 0;
}
int
lfs_valloc(struct vnode *pvp, int mode, kauth_cred_t cred,
ino_t *ino, int *gen)
{
struct lfs *fs;
struct buf *bp, *cbp;
IFILE *ifp;
int error;
CLEANERINFO *cip;
fs = VTOI(pvp)->i_lfs;
if (fs->lfs_ronly)
return EROFS;
if (!(fs->lfs_flags & LFS_NOTYET))
ASSERT_NO_SEGLOCK(fs);
DEBUG_CHECK_FREELIST(fs);
lfs_prelock(fs, 0);
LFS_GET_HEADFREE(fs, cip, cbp, ino);
KASSERT(*ino != LFS_UNUSED_INUM && *ino != LFS_IFILE_INUM);
DLOG((DLOG_ALLOC, "lfs_valloc: allocate inode %" PRId64 "\n",
*ino));
CLR_BITMAP_FREE(fs, *ino);
LFS_IENTRY(ifp, fs, *ino, bp);
if (lfs_if_getdaddr(fs, ifp) != LFS_UNUSED_DADDR)
panic("lfs_valloc: inuse inode %" PRId64 " on the free list",
*ino);
LFS_PUT_HEADFREE(fs, cip, cbp, lfs_if_getnextfree(fs, ifp));
DLOG((DLOG_ALLOC, "lfs_valloc: headfree %" PRId64 " -> %ju\n",
*ino, (uintmax_t)lfs_if_getnextfree(fs, ifp)));
*gen = lfs_if_getversion(fs, ifp);
lfs_if_setdaddr(fs, ifp, LFS_ILLEGAL_DADDR);
lfs_if_setnextfree(fs, ifp, LFS_UNUSED_INUM);
LFS_WRITEIENTRY(ifp, fs, *ino, bp);
if (lfs_sb_getfreehd(fs) == LFS_UNUSED_INUM) {
if ((error = lfs_extend_ifile(fs, cred)) != 0) {
LFS_PUT_HEADFREE(fs, cip, cbp, *ino);
lfs_preunlock(fs);
return error;
}
}
KASSERTMSG((lfs_sb_getfreehd(fs) != LFS_UNUSED_INUM),
"inode 0 allocated [3]");
mutex_enter(&lfs_lock);
fs->lfs_fmod = 1;
mutex_exit(&lfs_lock);
lfs_sb_addnfiles(fs, 1);
lfs_preunlock(fs);
DEBUG_CHECK_FREELIST(fs);
return 0;
}
int
lfs_valloc_fixed(struct lfs *fs, ino_t ino, int vers)
{
IFILE *ifp;
struct buf *bp, *cbp;
ino_t headino, thisino, oldnext, tailino;
CLEANERINFO *cip;
int extended = 0;
if (fs->lfs_ronly)
return EROFS;
if (!(fs->lfs_flags & LFS_NOTYET))
ASSERT_NO_SEGLOCK(fs);
DEBUG_CHECK_FREELIST(fs);
lfs_prelock(fs, 0);
while (VTOI(fs->lfs_ivnode)->i_size <= (ino /
lfs_sb_getifpb(fs) + lfs_sb_getcleansz(fs) + lfs_sb_getsegtabsz(fs))
<< lfs_sb_getbshift(fs)) {
DLOG((DLOG_ALLOC, "extend ifile to accommodate ino %jd\n",
(intmax_t)ino));
lfs_extend_ifile(fs, NOCRED);
extended = 1;
}
LFS_IENTRY(ifp, fs, ino, bp);
oldnext = lfs_if_getnextfree(fs, ifp);
brelse(bp, 0);
LFS_GET_TAILFREE(fs, cip, cbp, &tailino);
LFS_GET_HEADFREE(fs, cip, cbp, &headino);
if (headino == ino) {
LFS_PUT_HEADFREE(fs, cip, cbp, oldnext);
} else {
ino_t nextfree = 0, maxino, count;
maxino = ((VTOI(fs->lfs_ivnode)->i_size >> lfs_sb_getbshift(fs))
- lfs_sb_getcleansz(fs) - lfs_sb_getsegtabsz(fs))
* lfs_sb_getifpb(fs);
count = 0;
thisino = headino;
while (thisino != LFS_UNUSED_INUM) {
LFS_IENTRY(ifp, fs, thisino, bp);
nextfree = lfs_if_getnextfree(fs, ifp);
if (nextfree == ino ||
nextfree == LFS_UNUSED_INUM)
break;
thisino = nextfree;
brelse(bp, 0);
if (++count > maxino)
break;
}
if (count > maxino) {
panic("loop in free list");
lfs_preunlock(fs);
return ENOENT;
}
if (nextfree == LFS_UNUSED_INUM) {
brelse(bp, 0);
lfs_preunlock(fs);
if (extended)
panic("extended ifile to accommodate but inode not found");
return ENOENT;
}
lfs_if_setnextfree(fs, ifp, oldnext);
LFS_BWRITE_LOG(bp);
if (tailino == ino)
LFS_PUT_TAILFREE(fs, cip, cbp, thisino);
}
LFS_IENTRY(ifp, fs, ino, bp);
lfs_if_setversion(fs, ifp, vers);
lfs_if_setnextfree(fs, ifp, LFS_UNUSED_INUM);
lfs_if_setdaddr(fs, ifp, LFS_ILLEGAL_DADDR);
LFS_WRITEIENTRY(ifp, fs, ino, bp);
if (lfs_sb_getfreehd(fs) == LFS_UNUSED_INUM) {
int error;
if ((error = lfs_extend_ifile(fs, NOCRED)) != 0) {
LFS_PUT_HEADFREE(fs, cip, cbp, ino);
lfs_preunlock(fs);
return error;
}
}
KASSERTMSG((lfs_sb_getfreehd(fs) != LFS_UNUSED_INUM),
"inode 0 allocated [4]");
lfs_preunlock(fs);
DEBUG_CHECK_FREELIST(fs);
return 0;
}
#if 0
static inline ino_t
lfs_last_alloc_ino(struct lfs *fs)
{
ino_t ino, maxino;
maxino = ((fs->lfs_ivnode->v_size >> lfs_sb_getbshift(fs)) -
lfs_sb_getcleansz(fs) - lfs_sb_getsegtabsz(fs)) *
lfs_sb_getifpb(fs);
for (ino = maxino - 1; ino > LFS_UNUSED_INUM; --ino) {
if (ISSET_BITMAP_FREE(fs, ino) == 0)
break;
}
return ino;
}
static inline ino_t
lfs_freelist_prev(struct lfs *fs, ino_t ino)
{
ino_t tino, bound, bb, freehdbb;
if (lfs_sb_getfreehd(fs) == LFS_UNUSED_INUM) {
return LFS_UNUSED_INUM;
}
bound = ino & ~BMMASK;
for (tino = ino - 1; tino >= bound && tino > LFS_UNUSED_INUM; tino--)
if (ISSET_BITMAP_FREE(fs, tino))
return tino;
if (ino >> BMSHIFT == 0)
return LFS_UNUSED_INUM;
freehdbb = (lfs_sb_getfreehd(fs) >> BMSHIFT);
for (bb = (ino >> BMSHIFT) - 1; bb >= freehdbb && bb > 0; --bb)
if (fs->lfs_ino_bitmap[bb])
break;
if (fs->lfs_ino_bitmap[bb] == 0)
return LFS_UNUSED_INUM;
for (tino = (bb << BMSHIFT) | BMMASK; tino >= (bb << BMSHIFT) &&
tino > LFS_UNUSED_INUM; tino--)
if (ISSET_BITMAP_FREE(fs, tino))
break;
if (tino <= LFS_IFILE_INUM)
tino = LFS_UNUSED_INUM;
return tino;
}
#endif
int
lfs_vfree(struct vnode *vp, ino_t ino, int mode)
{
CLEANERINFO *cip;
struct buf *cbp, *bp;
IFILE *ifp;
struct inode *ip;
struct lfs *fs;
struct segdelta *isd, *fsd, *tmp;
ip = VTOI(vp);
fs = ip->i_lfs;
ino = ip->i_number;
ASSERT_NO_SEGLOCK(fs);
KASSERTMSG((ino != LFS_UNUSED_INUM), "inode 0 freed");
KASSERT(!fs->lfs_ronly);
DLOG((DLOG_ALLOC, "lfs_vfree: free ino %lld\n", (long long)ino));
mutex_enter(vp->v_interlock);
while (lfs_sb_getversion(fs) > 1 && WRITEINPROG(vp)) {
cv_wait(&vp->v_cv, vp->v_interlock);
}
mutex_exit(vp->v_interlock);
lfs_prelock(fs, 0);
DEBUG_CHECK_FREELIST(fs);
UNMARK_VNODE(vp);
mutex_enter(&lfs_lock);
if (vp->v_uflag & VU_DIROP) {
vp->v_uflag &= ~VU_DIROP;
--lfs_dirvcount;
--fs->lfs_dirvcount;
TAILQ_REMOVE(&fs->lfs_dchainhd, ip, i_lfs_dchain);
wakeup(&fs->lfs_dirvcount);
wakeup(&lfs_dirvcount);
mutex_exit(&lfs_lock);
vrele(vp);
RB_TREE_FOREACH_SAFE(isd, &ip->i_lfs_segdhd, tmp) {
rb_tree_remove_node(&ip->i_lfs_segdhd, isd);
fsd = rb_tree_insert_node(&fs->lfs_segdhd, isd);
if (fsd != isd) {
fsd->num += isd->num;
free(isd, M_SEGMENT);
}
}
} else {
mutex_exit(&lfs_lock);
lfs_finalize_ino_seguse(fs, ip);
}
KASSERT(!(ip->i_state & IN_CLEANING));
mutex_enter(&lfs_lock);
LFS_CLR_UINO(ip, IN_ACCESSED|IN_MODIFIED);
mutex_exit(&lfs_lock);
ip->i_state &= ~IN_ALLMOD;
ip->i_lfs_iflags |= LFSI_DELETED;
SET_BITMAP_FREE(fs, ino);
lfs_update_iaddr(fs, ip, LFS_UNUSED_DADDR);
LFS_IENTRY(ifp, fs, ino, bp);
lfs_if_setversion(fs, ifp, lfs_if_getversion(fs, ifp) + 1);
#if 0
if (lfs_sb_getversion(fs) == 1) {
#endif
ino_t nextfree;
LFS_GET_HEADFREE(fs, cip, cbp, &nextfree);
lfs_if_setnextfree(fs, ifp, nextfree);
LFS_PUT_HEADFREE(fs, cip, cbp, ino);
LFS_WRITEIENTRY(ifp, fs, ino, bp);
#if 0
} else {
ino_t tino, onf, otail;
lfs_if_setnextfree(fs, ifp, LFS_UNUSED_INUM);
LFS_WRITEIENTRY(ifp, fs, ino, bp);
tino = lfs_freelist_prev(fs, ino);
if (tino == LFS_UNUSED_INUM) {
ino_t nextfree;
LFS_IENTRY(ifp, fs, ino, bp);
LFS_GET_HEADFREE(fs, cip, cbp, &nextfree);
lfs_if_setnextfree(fs, ifp, nextfree);
LFS_PUT_HEADFREE(fs, cip, cbp, ino);
DLOG((DLOG_ALLOC, "lfs_vfree: headfree %lld -> %lld\n",
(long long)nextfree, (long long)ino));
LFS_WRITEIENTRY(ifp, fs, ino, bp);
LFS_GET_TAILFREE(fs, cip, cbp, &otail);
if (otail == LFS_UNUSED_INUM) {
LFS_PUT_TAILFREE(fs, cip, cbp, ino);
DLOG((DLOG_ALLOC, "lfs_vfree: tailfree %lld "
"-> %lld\n", (long long)otail,
(long long)ino));
}
} else {
DLOG((DLOG_ALLOC, "lfs_vfree: insert ino %lld "
" after %lld\n", ino, tino));
LFS_IENTRY(ifp, fs, tino, bp);
onf = lfs_if_getnextfree(fs, ifp);
lfs_if_setnextfree(fs, ifp, ino);
LFS_WRITEIENTRY(ifp, fs, tino, bp);
LFS_IENTRY(ifp, fs, ino, bp);
lfs_if_setnextfree(fs, ifp, onf);
LFS_WRITEIENTRY(ifp, fs, tino, bp);
if (onf == LFS_UNUSED_INUM) {
LFS_GET_TAILFREE(fs, cip, cbp, &otail);
LFS_PUT_TAILFREE(fs, cip, cbp, ino);
DLOG((DLOG_ALLOC, "lfs_vfree: tailfree %lld "
"-> %lld\n", (long long)otail,
(long long)ino));
}
}
}
#endif
mutex_enter(&lfs_lock);
fs->lfs_fmod = 1;
mutex_exit(&lfs_lock);
lfs_sb_subnfiles(fs, 1);
lfs_preunlock(fs);
DEBUG_CHECK_FREELIST(fs);
return (0);
}
void
lfs_order_freelist(struct lfs *fs, ino_t **orphanp, size_t *norphanp)
{
CLEANERINFO *cip;
IFILE *ifp = NULL;
struct buf *bp;
ino_t ino, firstino, lastino, maxino;
ino_t *orphan = NULL;
size_t norphan = 0;
size_t norphan_alloc = 0;
ASSERT_NO_SEGLOCK(fs);
lfs_prelock(fs, 0);
DEBUG_CHECK_FREELIST(fs);
maxino = ((fs->lfs_ivnode->v_size >> lfs_sb_getbshift(fs)) -
lfs_sb_getcleansz(fs) - lfs_sb_getsegtabsz(fs)) * lfs_sb_getifpb(fs);
fs->lfs_ino_bitmap =
malloc(((maxino + BMMASK) >> BMSHIFT) * sizeof(lfs_bm_t),
M_SEGMENT, M_WAITOK | M_ZERO);
KASSERT(fs->lfs_ino_bitmap != NULL);
firstino = lastino = LFS_UNUSED_INUM;
for (ino = 0; ino < maxino; ino++) {
if (ino % lfs_sb_getifpb(fs) == 0)
LFS_IENTRY(ifp, fs, ino, bp);
else
LFS_IENTRY_NEXT(ifp, fs);
if (ino == LFS_UNUSED_INUM || ino == LFS_IFILE_INUM)
continue;
if (lfs_if_getnextfree(fs, ifp) == LFS_ORPHAN_NEXTFREE(fs)) {
if (orphan == NULL) {
norphan_alloc = 32;
orphan = kmem_zalloc(sizeof(orphan[0]) *
norphan_alloc, KM_SLEEP);
} else if (norphan == norphan_alloc) {
ino_t *orphan_new;
if (norphan_alloc >= 4096)
norphan_alloc += 4096;
else
norphan_alloc *= 2;
orphan_new = kmem_zalloc(sizeof(orphan[0]) *
norphan_alloc, KM_SLEEP);
memcpy(orphan_new, orphan, sizeof(orphan[0]) *
norphan);
kmem_free(orphan, sizeof(orphan[0]) * norphan);
orphan = orphan_new;
}
orphan[norphan++] = ino;
}
if (DADDR_IS_BAD(lfs_if_getdaddr(fs, ifp))) {
if (firstino == LFS_UNUSED_INUM) {
firstino = ino;
} else {
brelse(bp, 0);
LFS_IENTRY(ifp, fs, lastino, bp);
lfs_if_setnextfree(fs, ifp, ino);
LFS_WRITEIENTRY(ifp, fs, lastino, bp);
LFS_IENTRY(ifp, fs, ino, bp);
}
lastino = ino;
SET_BITMAP_FREE(fs, ino);
}
if ((ino + 1) % lfs_sb_getifpb(fs) == 0)
brelse(bp, 0);
}
LFS_PUT_HEADFREE(fs, cip, bp, firstino);
LFS_PUT_TAILFREE(fs, cip, bp, lastino);
lfs_preunlock(fs);
if (norphan < norphan_alloc) {
ino_t *orphan_new = kmem_alloc(sizeof(orphan[0]) * norphan,
KM_SLEEP);
memcpy(orphan_new, orphan, sizeof(orphan[0]) * norphan);
kmem_free(orphan, sizeof(orphan[0]) * norphan_alloc);
orphan = orphan_new;
norphan_alloc = norphan;
}
*orphanp = orphan;
*norphanp = norphan;
DEBUG_CHECK_FREELIST(fs);
}
void
lfs_orphan(struct lfs *fs, struct vnode *vp)
{
IFILE *ifp;
struct buf *bp;
struct inode *ip;
ino_t nextfree;
int mincount;
ip = VTOI(vp);
ASSERT_NO_SEGLOCK(fs);
KASSERT(ip->i_nlink == 0);
mincount = 1;
mutex_enter(&lfs_lock);
if (ip->i_state & IN_CLEANING)
++mincount;
if (vp->v_uflag & VU_DIROP)
++mincount;
mutex_exit(&lfs_lock);
mutex_enter(vp->v_interlock);
if (vp->v_usecount <= mincount)
ip->i_state |= IN_DEAD;
mutex_exit(vp->v_interlock);
lfs_fraglock_enter(fs, RW_READER);
LFS_IENTRY(ifp, fs, ip->i_number, bp);
nextfree = lfs_if_getnextfree(fs, ifp);
if (nextfree == LFS_ORPHAN_NEXTFREE(fs)) {
brelse(bp, 0);
lfs_fraglock_exit(fs);
return;
}
KASSERT(nextfree == LFS_UNUSED_INUM);
lfs_if_setnextfree(fs, ifp, LFS_ORPHAN_NEXTFREE(fs));
LFS_WRITEIENTRY(ifp, fs, ip->i_number, bp);
lfs_fraglock_exit(fs);
}
void
lfs_free_orphans(struct lfs *fs, ino_t *orphan, size_t norphan)
{
struct vnode *vp;
size_t i;
int error;
ASSERT_NO_SEGLOCK(fs);
DEBUG_CHECK_FREELIST(fs);
for (i = 0; i < norphan; i++) {
error = VFS_VGET(fs->lfs_ivnode->v_mount, orphan[i],
LK_EXCLUSIVE, &vp);
if (error) {
printf("lfs_free_orphan vget ino %jd error %d\n",
(intmax_t)orphan[i], error);
continue;
}
vput(vp);
}
if (orphan)
kmem_free(orphan, sizeof(orphan[0]) * norphan);
DEBUG_CHECK_FREELIST(fs);
}
#ifdef DEBUG
static void dump_freelist(struct lfs *);
void
lfs_check_freelist(struct lfs *fs, const char *func, int line)
{
ino_t i, headino, maxino, thisino, tailino, nextfree;
int nfree, count;
struct inode *ip;
IFILE *ifp;
CLEANERINFO *cip;
struct buf *bp;
if (!lfs_do_check_freelist)
return;
lfs_prelock(fs, 0);
ip = VTOI(fs->lfs_ivnode);
maxino = ((ip->i_size >> lfs_sb_getbshift(fs)) - lfs_sb_getcleansz(fs) -
lfs_sb_getsegtabsz(fs)) * lfs_sb_getifpb(fs);
LFS_GET_TAILFREE(fs, cip, bp, &tailino);
nfree = 0;
for (i = LFS_IFILE_INUM + 1; i < maxino; ++i) {
LFS_IENTRY(ifp, fs, i, bp);
if (lfs_if_getdaddr(fs, ifp) == LFS_UNUSED_DADDR) {
++nfree;
if (lfs_if_getnextfree(fs, ifp) == LFS_UNUSED_INUM
&& i != tailino) {
brelse(bp, 0);
dump_freelist(fs);
printf("At %s:%d:\n", func, line);
printf("tailino=%jd, but ino=%jd"
" neither daddr nor nextfree\n",
(intmax_t)tailino, (intmax_t)i);
panic("Free list leak\n");
}
}
if (i == tailino
|| (!INUM_IS_BAD(fs, lfs_if_getnextfree(fs, ifp)))) {
if (lfs_if_getdaddr(fs, ifp) != LFS_UNUSED_DADDR) {
brelse(bp, 0);
dump_freelist(fs);
printf("At %s:%d:\n", func, line);
printf("with tailino=%jd, ino=%jd"
" daddr=0x%jx, nextfree=0x%jx\n",
(intmax_t)tailino,
(intmax_t)i,
(intmax_t)lfs_if_getdaddr(fs, ifp),
(intmax_t)lfs_if_getnextfree(fs, ifp));
panic("In use inode on free list\n");
}
}
brelse(bp, 0);
}
LFS_GET_HEADFREE(fs, cip, bp, &headino);
count = 0;
thisino = headino;
while (thisino != LFS_UNUSED_INUM) {
if (++count > maxino)
break;
LFS_IENTRY(ifp, fs, thisino, bp);
nextfree = lfs_if_getnextfree(fs, ifp);
brelse(bp, 0);
if (nextfree == LFS_UNUSED_INUM)
break;
thisino = nextfree;
}
if (count > maxino) {
dump_freelist(fs);
printf("At %s:%d:\n", func, line);
printf("count=%jd, maxino=%jd:\n",
(intmax_t)count, (intmax_t)maxino);
panic("loop in free list");
}
if (count != nfree) {
dump_freelist(fs);
printf("At %s:%d:\n", func, line);
printf("%d inodes without addresses, %d on free list\n",
nfree, count);
panic("Bad free list count");
}
if (thisino != tailino) {
dump_freelist(fs);
printf("At %s:%d:\n", func, line);
printf("Last ino %jd but tail %jd\n",
(intmax_t)thisino, (intmax_t)tailino);
panic("Bad tail");
}
lfs_preunlock(fs);
}
static void
dump_freelist(struct lfs *fs)
{
ino_t i, ni, maxino, headino, tailino;
struct inode *ip;
IFILE *ifp;
CLEANERINFO *cip;
struct buf *bp;
int count;
ip = VTOI(fs->lfs_ivnode);
maxino = ((ip->i_size >> lfs_sb_getbshift(fs)) - lfs_sb_getcleansz(fs) -
lfs_sb_getsegtabsz(fs)) * lfs_sb_getifpb(fs);
LFS_GET_HEADFREE(fs, cip, bp, &headino);
printf(" head: %jd\n", (intmax_t)headino);
LFS_GET_TAILFREE(fs, cip, bp, &tailino);
printf(" tail: %jd\n", (intmax_t)tailino);
count = 0;
for (i = LFS_IFILE_INUM + 1; i < maxino; ++i) {
LFS_IENTRY(ifp, fs, i, bp);
ni = lfs_if_getnextfree(fs, ifp);
if (ni != LFS_UNUSED_DADDR) {
printf("%jd -> %jd\n",
(intmax_t)i, (intmax_t)ni);
if (++count > 30) {
printf("...\n");
i = maxino;
}
}
brelse(bp, 0);
}
}
#endif