#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: lfs_kclean.c,v 1.4 2026/01/05 05:02:47 perseant Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/namei.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <sys/vnode.h>
#include <sys/conf.h>
#include <sys/kauth.h>
#include <sys/buf.h>
#include <sys/kthread.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_kernel.h>
#include <ufs/lfs/lfs_extern.h>
static int ino_func_setclean(struct lfs_inofuncarg *);
static int finfo_func_rewrite(struct lfs_finfofuncarg *);
static int finfo_func_setclean(struct lfs_finfofuncarg *);
static int rewrite_block(struct lfs *, struct vnode *, daddr_t, daddr_t,
size_t, int *);
static int ino_func_rewrite(struct lfs_inofuncarg *);
static int ino_func_setclean(struct lfs_inofuncarg *);
static int ino_func_checkempty(struct lfs_inofuncarg *);
static int clean(struct lfs *);
static long segselect_cb_rosenblum(struct lfs *, int, SEGUSE *, long);
static long segselect_greedy(struct lfs *, int, SEGUSE *);
static long segselect_cb_time(struct lfs *, int, SEGUSE *);
#if 0
static long segselect_cb_serial(struct lfs *, int, SEGUSE *);
#endif
static int check_clean_list(struct lfs *, ino_t);
struct lwp * lfs_cleaner_daemon = NULL;
extern kcondvar_t lfs_allclean_wakeup;
static int lfs_ncleaners = 0;
static int
ino_func_setclean(struct lfs_inofuncarg *lifa)
{
struct lfs *fs;
daddr_t offset;
struct vnode *devvp, *vp;
union lfs_dinode *dip;
struct buf *dbp, *ibp;
int error;
IFILE *ifp;
unsigned i, num;
daddr_t true_addr;
ino_t ino;
fs = lifa->fs;
offset = lifa->offset;
devvp = VTOI(fs->lfs_ivnode)->i_devvp;
error = bread(devvp, LFS_FSBTODB(fs, offset), lfs_sb_getibsize(fs),
0, &dbp);
if (error) {
DLOG((DLOG_RF, "ino_func_setclean: bread returned %d\n",
error));
return error;
}
memcpy(lifa->buf, dbp->b_data, dbp->b_bcount);
brelse(dbp, BC_AGE);
num = LFS_INOPB(fs);
for (i = num; i-- > 0; ) {
dip = DINO_IN_BLOCK(fs, lifa->buf, i);
ino = lfs_dino_getinumber(fs, dip);
if (ino == LFS_IFILE_INUM) {
true_addr = lfs_sb_getidaddr(fs);
} else {
LFS_IENTRY(ifp, fs, ino, ibp);
true_addr = lfs_if_getdaddr(fs, ifp);
brelse(ibp, 0);
}
if (offset != true_addr)
continue;
LFS_ASSERT_MAXINO(fs, ino);
if (ino == LFS_IFILE_INUM)
vp = fs->lfs_ivnode;
else
error = VFS_VGET(fs->lfs_ivnode->v_mount, ino,
LK_EXCLUSIVE | LK_NOWAIT, &vp);
if (error)
continue;
KASSERT(VTOI(vp)->i_gen == lfs_dino_getgen(fs, dip));
lfs_setclean(fs, vp);
if (vp != fs->lfs_ivnode) {
VOP_UNLOCK(vp);
vrele(vp);
}
}
return error;
}
static int
ino_func_rewrite(struct lfs_inofuncarg *lifa)
{
struct lfs *fs;
daddr_t offset;
struct vnode *devvp, *vp;
union lfs_dinode *dip;
struct buf *dbp, *ibp;
int error;
IFILE *ifp;
unsigned i, num;
daddr_t true_addr;
ino_t ino;
fs = lifa->fs;
offset = lifa->offset;
devvp = VTOI(fs->lfs_ivnode)->i_devvp;
error = bread(devvp, LFS_FSBTODB(fs, offset), lfs_sb_getibsize(fs),
0, &dbp);
if (error) {
DLOG((DLOG_RF, "ino_func_rewrite: bread returned %d\n",
error));
return error;
}
memcpy(lifa->buf, dbp->b_data, dbp->b_bcount);
brelse(dbp, BC_AGE);
num = LFS_INOPB(fs);
for (i = num; i-- > 0; ) {
dip = DINO_IN_BLOCK(fs, lifa->buf, i);
ino = lfs_dino_getinumber(fs, dip);
if (ino == LFS_IFILE_INUM) {
true_addr = lfs_sb_getidaddr(fs);
} else {
LFS_IENTRY(ifp, fs, ino, ibp);
true_addr = lfs_if_getdaddr(fs, ifp);
brelse(ibp, 0);
}
if (offset != true_addr)
continue;
if (ino == LFS_IFILE_INUM)
continue;
LFS_ASSERT_MAXINO(fs, ino);
error = check_clean_list(fs, ino);
if (error)
continue;
error = VFS_VGET(fs->lfs_ivnode->v_mount, ino,
LK_EXCLUSIVE | LK_NOWAIT, &vp);
if (error)
continue;
KASSERT(VTOI(vp)->i_gen == lfs_dino_getgen(fs, dip));
if (!(VTOI(vp)->i_state & IN_CLEANING)) {
lfs_setclean(fs, vp);
lfs_writeinode(fs, fs->lfs_sp, VTOI(vp));
}
VOP_UNLOCK(vp);
vrele(vp);
}
return error;
}
static int
rewrite_block(struct lfs *fs, struct vnode *vp, daddr_t lbn, daddr_t offset, size_t size, int *have_finfop)
{
daddr_t daddr;
int error;
struct buf *bp;
struct inode *ip;
KASSERT(have_finfop != NULL);
error = VOP_BMAP(vp, lbn, NULL, &daddr, NULL);
if (error)
return error;
if (offset != 0 && LFS_DBTOFSB(fs, daddr) != offset)
return ESTALE;
error = bread(vp, lbn, size, 0, &bp);
if (error)
return error;
if (vp == fs->lfs_ivnode) {
VOP_BWRITE(vp, bp);
} else {
if (!*have_finfop) {
ip = VTOI(vp);
lfs_acquire_finfo(fs, ip->i_number, ip->i_gen);
fs->lfs_sp->vp = vp;
*have_finfop = 1;
}
KASSERT(bp->b_vp == vp);
error = lfs_bwrite_ext(bp, BW_CLEAN);
if (error)
return error;
KASSERT(bp->b_vp == vp);
mutex_enter(&bufcache_lock);
while (lfs_gatherblock(fs->lfs_sp, bp, &bufcache_lock)) {
KASSERT(bp->b_vp != NULL);
}
mutex_exit(&bufcache_lock);
KASSERT(bp->b_flags & B_GATHERED);
KASSERT(fs->lfs_sp->cbpp[-1] == bp);
}
return 0;
}
static int
check_clean_list(struct lfs *fs, ino_t ino)
{
struct inode *ip;
TAILQ_FOREACH(ip, &fs->lfs_cleanhd, i_lfs_clean) {
if (ip->i_number == ino) {
return 0;
}
}
return EWOULDBLOCK;
}
static int
finfo_func_rewrite(struct lfs_finfofuncarg *lffa)
{
struct lfs *fs;
FINFO *fip;
daddr_t *offsetp;
int j, have_finfo, error;
size_t size, bytes;
ino_t ino;
uint32_t gen;
struct vnode *vp;
daddr_t lbn;
int *fragsp;
fs = lffa->fs;
fip = lffa->finfop;
offsetp = lffa->offsetp;
fragsp = (int *)lffa->arg;
ino = lfs_fi_getino(fs, fip);
gen = lfs_fi_getversion(fs, fip);
error = 0;
if (ino == LFS_IFILE_INUM)
vp = fs->lfs_ivnode;
else {
LFS_ASSERT_MAXINO(fs, ino);
error = check_clean_list(fs, ino);
if (error)
vp = NULL;
else
error = VFS_VGET(fs->lfs_ivnode->v_mount, ino,
LK_EXCLUSIVE|LK_NOWAIT, &vp);
}
if (error || VTOI(vp)->i_gen != gen || (vp->v_uflag & VU_DIROP)) {
if (error == 0)
error = ESTALE;
if (vp != NULL && vp != fs->lfs_ivnode) {
VOP_UNLOCK(vp);
vrele(vp);
}
vp = NULL;
bytes = ((lfs_fi_getnblocks(fs, fip) - 1) << lfs_sb_getbshift(fs))
+ lfs_fi_getlastlength(fs, fip);
*offsetp += lfs_btofsb(fs, bytes);
return error;
}
lfs_setclean(fs, vp);
size = lfs_sb_getbsize(fs);
have_finfo = 0;
for (j = 0; j < lfs_fi_getnblocks(fs, fip); ++j) {
if (j == lfs_fi_getnblocks(fs, fip) - 1)
size = lfs_fi_getlastlength(fs, fip);
if (vp != NULL && error == 0) {
lbn = lfs_fi_getblock(fs, fip, j);
error = rewrite_block(fs, vp, lbn, *offsetp,
size, &have_finfo);
if (error == ESTALE)
error = 0;
if (fragsp != NULL && error == 0)
*fragsp += lfs_btofsb(fs, size);
}
*offsetp += lfs_btofsb(fs, size);
}
if (have_finfo) {
lfs_updatemeta(fs->lfs_sp);
fs->lfs_sp->vp = NULL;
lfs_release_finfo(fs);
lfs_writeinode(fs, fs->lfs_sp, VTOI(vp));
}
if (vp != fs->lfs_ivnode) {
VOP_UNLOCK(vp);
vrele(vp);
}
return error;
}
static int
finfo_func_setclean(struct lfs_finfofuncarg *lffa)
{
struct lfs *fs;
FINFO *fip;
daddr_t *offsetp;
int error;
size_t bytes;
ino_t ino;
uint32_t gen;
struct vnode *vp;
fs = lffa->fs;
fip = lffa->finfop;
offsetp = lffa->offsetp;
ino = lfs_fi_getino(fs, fip);
gen = lfs_fi_getversion(fs, fip);
error = 0;
if (ino == LFS_IFILE_INUM)
vp = fs->lfs_ivnode;
else {
LFS_ASSERT_MAXINO(fs, ino);
error = VFS_VGET(fs->lfs_ivnode->v_mount, ino,
LK_EXCLUSIVE|LK_NOWAIT, &vp);
}
if (error == 0 && VTOI(vp)->i_gen == gen)
lfs_setclean(fs, vp);
if (vp == fs->lfs_ivnode)
vp = NULL;
else if (vp != NULL) {
VOP_UNLOCK(vp);
vrele(vp);
vp = NULL;
}
bytes = ((lfs_fi_getnblocks(fs, fip) - 1) << lfs_sb_getbshift(fs))
+ lfs_fi_getlastlength(fs, fip);
*offsetp += lfs_btofsb(fs, bytes);
return error;
}
int
lfs_rewrite_segment(struct lfs *fs, int sn, int *fragsp, kauth_cred_t cred, struct lwp *l)
{
daddr_t ooffset, offset, endpseg;
ASSERT_SEGLOCK(fs);
offset = lfs_sntod(fs, sn);
lfs_skip_superblock(fs, &offset);
endpseg = lfs_sntod(fs, sn + 1);
while (offset > 0 && offset != endpseg) {
ooffset = offset;
lfs_parse_pseg(fs, &offset, 0, cred, NULL, l,
NULL, NULL, CKSEG_CKSUM, NULL);
if (offset == ooffset)
break;
offset = ooffset;
lfs_parse_pseg(fs, &offset, 0, cred, NULL, l,
NULL, finfo_func_rewrite,
CKSEG_NONE, fragsp);
offset = ooffset;
lfs_parse_pseg(fs, &offset, 0, cred, NULL, l,
ino_func_rewrite, NULL,
CKSEG_NONE, fragsp);
}
return 0;
}
int
lfs_rewrite_segments(struct lfs *fs, int *snn, int len, int *directp, int *offsetp, struct lwp *l)
{
kauth_cred_t cred;
int i, error;
struct buf *bp;
SEGUSE *sup;
daddr_t offset, endpseg;
ASSERT_NO_SEGLOCK(fs);
cred = l ? l->l_cred : NOCRED;
lfs_writer_enter(fs, "rewritesegs");
if ((error = lfs_cleanerlock(fs)) != 0) {
lfs_writer_leave(fs);
return error;
}
for (i = 0; i < len; i++) {
error = 0;
LFS_SEGENTRY(sup, fs, snn[i], bp);
if (sup->su_flags & SEGUSE_ACTIVE
|| !(sup->su_flags & SEGUSE_DIRTY))
error = EINVAL;
brelse(bp, 0);
if (error)
break;
offset = lfs_sntod(fs, snn[i]);
lfs_skip_superblock(fs, &offset);
endpseg = lfs_sntod(fs, snn[i] + 1);
while (offset > 0 && offset != endpseg) {
lfs_parse_pseg(fs, &offset, 0, cred, NULL, l,
ino_func_setclean, finfo_func_setclean,
CKSEG_NONE, NULL);
}
}
lfs_seglock(fs, SEGM_CLEAN);
for (i = 0; i < len; i++) {
error = 0;
LFS_SEGENTRY(sup, fs, snn[i], bp);
if (sup->su_flags & SEGUSE_ACTIVE
|| !(sup->su_flags & SEGUSE_DIRTY))
error = EINVAL;
brelse(bp, 0);
if (error)
break;
error = lfs_rewrite_segment(fs, snn[i], directp, cred, l);
if (error)
break;
}
while (lfs_writeseg(fs, fs->lfs_sp))
;
*offsetp = lfs_btofsb(fs, fs->lfs_sp->bytes_written);
lfs_segunlock(fs);
lfs_cleanerunlock(fs);
lfs_writer_leave(fs);
return error;
}
static int
ino_func_checkempty(struct lfs_inofuncarg *lifa)
{
struct lfs *fs;
daddr_t offset;
struct vnode *devvp;
union lfs_dinode *dip;
struct buf *dbp, *ibp;
int error;
IFILE *ifp;
unsigned i, num;
daddr_t true_addr;
ino_t ino;
fs = lifa->fs;
offset = lifa->offset;
devvp = VTOI(fs->lfs_ivnode)->i_devvp;
error = bread(devvp, LFS_FSBTODB(fs, offset), lfs_sb_getibsize(fs),
0, &dbp);
if (error) {
DLOG((DLOG_RF, "ino_func_checkempty: bread returned %d\n",
error));
return error;
}
num = LFS_INOPB(fs);
for (i = num; i-- > 0; ) {
dip = DINO_IN_BLOCK(fs, dbp->b_data, i);
ino = lfs_dino_getinumber(fs, dip);
if (ino == LFS_IFILE_INUM) {
true_addr = lfs_sb_getidaddr(fs);
} else {
LFS_IENTRY(ifp, fs, ino, ibp);
true_addr = lfs_if_getdaddr(fs, ifp);
brelse(ibp, 0);
}
if (offset == true_addr) {
error = EEXIST;
break;
}
}
brelse(dbp, BC_AGE);
return error;
}
static int
finfo_func_checkempty(struct lfs_finfofuncarg *lffa)
{
struct lfs *fs;
FINFO *fip;
daddr_t *offsetp;
int j, error;
size_t size, bytes;
ino_t ino;
uint32_t gen;
struct vnode *vp;
daddr_t lbn, daddr;
fs = lffa->fs;
fip = lffa->finfop;
offsetp = lffa->offsetp;
ino = lfs_fi_getino(fs, fip);
gen = lfs_fi_getversion(fs, fip);
error = VFS_VGET(fs->lfs_ivnode->v_mount, ino, LK_EXCLUSIVE|LK_NOWAIT, &vp);
if (error || VTOI(vp)->i_gen != gen) {
if (error == 0)
error = ESTALE;
if (vp != NULL) {
VOP_UNLOCK(vp);
vrele(vp);
vp = NULL;
}
bytes = ((lfs_fi_getnblocks(fs, fip) - 1)
<< lfs_sb_getbshift(fs))
+ lfs_fi_getlastlength(fs, fip);
*offsetp += lfs_btofsb(fs, bytes);
return error;
}
size = lfs_sb_getbsize(fs);
for (j = 0; j < lfs_fi_getnblocks(fs, fip); ++j) {
if (j == lfs_fi_getnblocks(fs, fip) - 1)
size = lfs_fi_getlastlength(fs, fip);
if (vp != NULL) {
lbn = lfs_fi_getblock(fs, fip, j);
error = VOP_BMAP(vp, lbn, NULL, &daddr, NULL);
if (error)
break;
if (LFS_DBTOFSB(fs, daddr) == *offsetp) {
error = EEXIST;
break;
}
}
*offsetp += lfs_btofsb(fs, size);
}
VOP_UNLOCK(vp);
vrele(vp);
return error;
}
int
lfs_checkempty(struct lfs *fs, int sn, kauth_cred_t cred, struct lwp *l)
{
daddr_t offset, endpseg;
int error;
ASSERT_SEGLOCK(fs);
offset = lfs_sntod(fs, sn);
lfs_skip_superblock(fs, &offset);
endpseg = lfs_sntod(fs, sn + 1);
while (offset > 0 && offset < endpseg) {
error = lfs_parse_pseg(fs, &offset, 0, cred, NULL, l,
ino_func_checkempty,
finfo_func_checkempty,
CKSEG_NONE, NULL);
if (error)
return error;
}
return 0;
}
static long
segselect_greedy(struct lfs *fs, int sn, SEGUSE *sup)
{
return lfs_sb_getssize(fs) - sup->su_nbytes;
}
__inline static long
segselect_cb_rosenblum(struct lfs *fs, int sn, SEGUSE *sup, long age)
{
long benefit, cost;
benefit = (int64_t)lfs_sb_getssize(fs) - sup->su_nbytes -
(sup->su_nsums + 1) * lfs_sb_getfsize(fs);
if (sup->su_flags & SEGUSE_SUPERBLOCK)
benefit -= LFS_SBPAD;
if (lfs_sb_getbsize(fs) > lfs_sb_getfsize(fs))
benefit -= (lfs_sb_getbsize(fs) / 2);
if (benefit <= 0) {
return 0;
}
cost = lfs_sb_getssize(fs) + sup->su_nbytes;
return (256 * benefit * age) / cost;
}
static long
segselect_cb_time(struct lfs *fs, int sn, SEGUSE *sup)
{
long age;
age = time_second - sup->su_lastmod;
if (age < 0)
age = 0;
return segselect_cb_rosenblum(fs, sn, sup, age);
}
#if 0
static long
segselect_cb_serial(struct lfs *fs, int sn, SEGUSE *sup)
{
struct buf *bp;
uint32_t magic;
uint64_t age, serial;
daddr_t addr;
addr = lfs_segtod(fs, sn);
lfs_skip_superblock(fs, &addr);
bread(fs->lfs_devvp, LFS_FSBTODB(fs, addr),
lfs_sb_getsumsize(fs), 0, &bp);
magic = lfs_ss_getmagic(fs, ((SEGSUM *)bp->b_data));
serial = lfs_ss_getserial(fs, ((SEGSUM *)bp->b_data));
brelse(bp, 0);
if (magic != SS_MAGIC)
return 0;
age = lfs_sb_getserial(fs) - serial;
return segselect_cb_rosenblum(fs, sn, sup, age);
}
#endif
void
lfs_cleanerd(void *arg)
{
mount_iterator_t *iter;
struct mount *mp;
struct lfs *fs;
struct vfsops *vfs = NULL;
int lfsc;
int cleaned_something = 0;
vfs = vfs_getopsbyname(MOUNT_LFS);
mutex_enter(&lfs_lock);
for (;;) {
KASSERT(mutex_owned(&lfs_lock));
if (cleaned_something == 0)
cv_timedwait(&lfs_allclean_wakeup, &lfs_lock, hz/10 + 1);
KASSERT(mutex_owned(&lfs_lock));
cleaned_something = 0;
KASSERT(mutex_owned(&lfs_lock));
mutex_exit(&lfs_lock);
mountlist_iterator_init(&iter);
lfsc = 0;
while ((mp = mountlist_iterator_next(iter)) != NULL) {
KASSERT(!mutex_owned(&lfs_lock));
if (strncmp(mp->mnt_stat.f_fstypename, MOUNT_LFS,
sizeof(mp->mnt_stat.f_fstypename)) == 0) {
fs = VFSTOULFS(mp)->um_lfs;
mutex_enter(&lfs_lock);
if (fs->lfs_clean_selector == NULL) {
if (fs->lfs_autoclean_status) {
fs->lfs_autoclean_status =
LFS_AUTOCLEAN_STATUS_OFF;
cv_broadcast(&fs->lfs_cleanquitcv);
}
} else
++lfsc;
mutex_exit(&lfs_lock);
cleaned_something += clean(fs);
}
}
if (lfsc == 0) {
mutex_enter(&lfs_lock);
lfs_cleaner_daemon = NULL;
mutex_exit(&lfs_lock);
mountlist_iterator_destroy(iter);
break;
}
mountlist_iterator_destroy(iter);
mutex_enter(&lfs_lock);
}
KASSERT(!mutex_owned(&lfs_lock));
mutex_enter(&vfs_list_lock);
if (vfs != NULL)
vfs->vfs_refcount--;
mutex_exit(&vfs_list_lock);
kthread_exit(0);
}
static int
clean(struct lfs *fs)
{
struct buf *bp;
SEGUSE *sup;
int sn, maxsn, nclean, nready, nempty, nerror, nzero, again, target;
long prio, maxprio, maxeprio, thresh;
long (*func)(struct lfs *, int, SEGUSE *);
uint32_t __debugused segflags = 0;
daddr_t oldsn, bfree, avail;
int direct, offset;
mutex_enter(&lfs_lock);
func = fs->lfs_clean_selector;
mutex_exit(&lfs_lock);
if (func == NULL)
return 1;
thresh = fs->lfs_autoclean.thresh;
if (fs->lfs_flags & LFS_MUSTCLEAN)
thresh = 0;
else if (thresh < 0) {
bfree = lfs_sb_getbfree(fs)
+ lfs_segtod(fs, 1) * lfs_sb_getminfree(fs);
avail = lfs_sb_getavail(fs) - fs->lfs_ravail - fs->lfs_favail;
if (avail > bfree)
return 0;
thresh = lfs_sb_getssize(fs) * (bfree - avail)
/ (lfs_sb_getsize(fs) - avail);
if (thresh > lfs_sb_getsumsize(fs) + 5 * lfs_sb_getbsize(fs))
thresh = lfs_sb_getsumsize(fs) + 5 * lfs_sb_getbsize(fs);
if (thresh > lfs_sb_getssize(fs) - lfs_sb_getbsize(fs))
return 0;
}
target = fs->lfs_autoclean.target;
if (target <= 0) {
target = lfs_segtod(fs, 1) / 2;
}
oldsn = lfs_dtosn(fs, lfs_sb_getoffset(fs));
again = 0;
maxprio = maxeprio = -1;
nzero = nclean = nready = nempty = nerror = 0;
for (sn = 0; sn < lfs_sb_getnseg(fs); sn++) {
prio = 0;
LFS_SEGENTRY(sup, fs, sn, bp);
if (sup->su_flags & SEGUSE_ACTIVE)
prio = 0;
else if (!(sup->su_flags & SEGUSE_DIRTY))
++nclean;
else if (sup->su_flags & SEGUSE_READY)
++nready;
else if (sup->su_flags & SEGUSE_EMPTY)
++nempty;
else if (sup->su_nbytes == 0)
++nzero;
else
prio = (*func)(fs, sn, sup);
if (sup->su_flags & SEGUSE_ERROR) {
if (prio > maxeprio)
maxeprio = prio;
prio = 0;
++nerror;
}
if (prio > maxprio) {
maxprio = prio;
maxsn = sn;
segflags = sup->su_flags;
}
brelse(bp, 0);
}
DLOG((DLOG_CLEAN, "%s clean=%d/%d zero=%d empty=%d ready=%d maxsn=%d maxprio=%ld/%ld segflags=0x%lx\n",
(maxprio > thresh ? "YES" : "NO "),
nclean, (int)lfs_sb_getnseg(fs), nzero, nempty, nready,
maxsn, maxprio, (unsigned long)thresh,
(unsigned long)segflags));
if (maxprio > 0 && fs->lfs_lastcleaned == maxsn) {
LFS_SEGENTRY(sup, fs, maxsn, bp);
sup->su_flags |= SEGUSE_ERROR;
LFS_WRITESEGENTRY(sup, fs, sn, bp);
return 1;
}
if (maxprio == 0 && maxeprio > 0) {
DLOG((DLOG_CLEAN, "clear error on %d segments, try again\n",
nerror));
lfs_seguse_clrflag_all(fs, SEGUSE_ERROR);
}
if (maxprio > thresh) {
direct = offset = 0;
(void)lfs_rewrite_segments(fs, &maxsn, 1,
&direct, &offset, curlwp);
DLOG((DLOG_CLEAN, " direct=%d offset=%d\n", direct, offset));
again += direct;
fs->lfs_clean_accum += offset;
fs->lfs_lastcleaned = maxsn;
}
if (fs->lfs_flags & LFS_MUSTCLEAN) {
if (nready + nempty > 0) {
DLOG((DLOG_CLEAN, "force checkpoint with nready=%d nempty=%d nzero=%d\n",
nready, nempty, nzero));
lfs_segwrite(fs->lfs_ivnode->v_mount,
SEGM_CKP | SEGM_FORCE_CKP | SEGM_SYNC);
lfs_segwrite(fs->lfs_ivnode->v_mount,
SEGM_CKP | SEGM_FORCE_CKP | SEGM_SYNC);
++again;
}
} else if (fs->lfs_clean_accum > target) {
DLOG((DLOG_CLEAN, "checkpoint to flush\n"));
lfs_segwrite(fs->lfs_ivnode->v_mount, SEGM_CKP);
fs->lfs_clean_accum = 0;
} else if (lfs_dtosn(fs, lfs_sb_getoffset(fs)) != oldsn
|| nempty + nready > LFS_MAX_ACTIVE) {
DLOG((DLOG_CLEAN, "write to promote empty segments\n"));
lfs_segwrite(fs->lfs_ivnode->v_mount, SEGM_CKP);
fs->lfs_clean_accum = 0;
}
return again;
}
int
lfs_rewrite_file(struct lfs *fs, ino_t *inoa, int len, bool scramble,
int *directp, int *offsetp)
{
daddr_t hiblk, lbn;
struct vnode *vp;
struct inode *ip;
struct buf *bp;
int i, error;
KASSERT(directp != NULL);
KASSERT(offsetp != NULL);
*directp = 0;
if ((error = lfs_cleanerlock(fs)) != 0)
return error;
lfs_seglock(fs, 0);
for (i = 0; i < len; ++i) {
error = VFS_VGET(fs->lfs_ivnode->v_mount, inoa[i],
LK_EXCLUSIVE | LK_NOWAIT, &vp);
if (error)
goto out;
ip = VTOI(vp);
if ((vp->v_uflag & VU_DIROP) || (ip->i_flags & IN_ADIROP)) {
VOP_UNLOCK(vp);
vrele(vp);
error = EAGAIN;
goto out;
}
hiblk = lfs_lblkno(fs, ip->i_size + lfs_sb_getbsize(fs) - 1) - 1;
for (lbn = 0; lbn <= hiblk; ++lbn) {
if (scramble && (lbn & 0x01))
continue;
if (lfs_needsflush(fs)) {
lfs_segwrite(fs->lfs_ivnode->v_mount, 0);
}
error = bread(vp, lbn, lfs_blksize(fs, ip, lbn), 0, &bp);
if (error)
break;
lfs_bwrite_ext(bp, 0);
*directp += lfs_btofsb(fs, bp->b_bcount);
}
VOP_UNLOCK(vp);
vrele(vp);
if (error)
break;
}
out:
lfs_segwrite(fs->lfs_ivnode->v_mount, 0);
*offsetp += lfs_btofsb(fs, fs->lfs_sp->bytes_written);
lfs_segunlock(fs);
lfs_cleanerunlock(fs);
return error;
}
int
lfs_cleanctl(struct lfs *fs, struct lfs_autoclean_params *params)
{
long (*cleanfunc)(struct lfs *, int, SEGUSE *);
fs->lfs_autoclean = *params;
cleanfunc = NULL;
switch (fs->lfs_autoclean.mode) {
case LFS_CLEANMODE_NONE:
cleanfunc = NULL;
break;
case LFS_CLEANMODE_GREEDY:
cleanfunc = segselect_greedy;
break;
case LFS_CLEANMODE_CB:
cleanfunc = segselect_cb_time;
break;
default:
return EINVAL;
}
mutex_enter(&lfs_lock);
while (cleanfunc == NULL &&
fs->lfs_autoclean_status != LFS_AUTOCLEAN_STATUS_OFF) {
cv_wait(&fs->lfs_cleanquitcv, &lfs_lock);
}
if (fs->lfs_clean_selector == NULL && cleanfunc != NULL)
if (++lfs_ncleaners == 1) {
if (lfs_cleaner_daemon == NULL &&
kthread_create(PRI_BIO, 0, NULL,
lfs_cleanerd, NULL,
&lfs_cleaner_daemon,
"lfs_cleaner") != 0)
panic("fork lfs_cleaner");
}
if (fs->lfs_clean_selector != NULL && cleanfunc == NULL) {
if (--lfs_ncleaners == 0) {
#if 0
kthread_join(lfs_cleaner_daemon);
lfs_cleaner_daemon = NULL;
#endif
}
}
fs->lfs_clean_selector = cleanfunc;
mutex_exit(&lfs_lock);
return 0;
}