#include <sys/types.h>
#include <sys/t_lock.h>
#include <sys/param.h>
#include <sys/tuneable.h>
#include <sys/inline.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/var.h>
#include <sys/buf.h>
#include <sys/vfs.h>
#include <sys/cred.h>
#include <sys/kmem.h>
#include <sys/vnode.h>
#include <sys/swap.h>
#include <sys/vm.h>
#include <sys/debug.h>
#include <sys/cmn_err.h>
#include <sys/sysinfo.h>
#include <sys/callb.h>
#include <sys/reboot.h>
#include <sys/time.h>
#include <sys/fs/ufs_inode.h>
#include <sys/fs/ufs_bio.h>
#include <vm/hat.h>
#include <vm/page.h>
#include <vm/pvn.h>
#include <vm/seg_kmem.h>
int doiflush = 1;
int dopageflush = 1;
int fsflush_iflush_delay = 60;
kcondvar_t fsflush_cv;
static kmutex_t fsflush_lock;
ksema_t fsflush_sema;
typedef struct {
ulong_t fsf_scan;
ulong_t fsf_examined;
ulong_t fsf_locked;
ulong_t fsf_modified;
ulong_t fsf_coalesce;
ulong_t fsf_time;
ulong_t fsf_releases;
} fsf_stat_t;
fsf_stat_t fsf_recent;
fsf_stat_t fsf_total;
ulong_t fsf_cycles;
#define MAX_PAGESIZES 32
static ulong_t fsf_npgsz;
static pgcnt_t fsf_pgcnt[MAX_PAGESIZES];
static pgcnt_t fsf_mask[MAX_PAGESIZES];
static void
fsflush_do_pages()
{
vnode_t *vp;
ulong_t pcount;
hrtime_t timer = gethrtime();
ulong_t releases = 0;
ulong_t nexamined = 0;
ulong_t nlocked = 0;
ulong_t nmodified = 0;
ulong_t ncoalesce = 0;
ulong_t cnt;
int mod;
int fspage = 1;
u_offset_t offset;
uint_t szc;
page_t *coal_page = NULL;
uint_t coal_szc = 0;
uint_t coal_cnt = 0;
static ulong_t nscan = 0;
static pgcnt_t last_total_pages = 0;
static page_t *pp = NULL;
if (total_pages != last_total_pages) {
last_total_pages = total_pages;
nscan = (last_total_pages * (tune.t_fsflushr))/v.v_autoup;
}
if (pp == NULL)
pp = memsegs->pages;
pcount = 0;
while (pcount < nscan) {
if (pp->p_szc && fspage == 0) {
pfn_t pfn;
pfn = page_pptonum(pp);
cnt = page_get_pagecnt(pp->p_szc);
cnt -= pfn & (cnt - 1);
} else
cnt = 1;
pp = page_nextn(pp, cnt);
prefetch_page_r((void *)pp);
ASSERT(pp != NULL);
pcount += cnt;
++nexamined;
if (PP_ISSWAP(pp)) {
fspage = 0;
coal_page = NULL;
continue;
}
if (PP_ISFREE(pp)) {
fspage = 0;
szc = pp->p_szc;
if (pp->p_vnode != NULL || szc == fsf_npgsz - 1) {
coal_page = NULL;
continue;
}
if (coal_page == NULL || coal_szc != szc) {
if ((page_pptonum(pp) & fsf_mask[szc]) != 0) {
coal_page = NULL;
continue;
}
coal_page = pp;
coal_szc = szc;
coal_cnt = 1;
continue;
}
++coal_cnt;
if (coal_cnt < fsf_pgcnt[coal_szc])
continue;
++ncoalesce;
(void) page_promote_size(coal_page, coal_szc);
coal_page = NULL;
continue;
} else {
coal_page = NULL;
}
if (PP_ISKAS(pp) ||
PAGE_LOCKED(pp) ||
pp->p_lckcnt != 0 ||
pp->p_cowcnt != 0) {
fspage = 0;
continue;
}
if (!page_trylock(pp, SE_EXCL))
continue;
++nlocked;
vp = pp->p_vnode;
if (PP_ISSWAP(pp) ||
PP_ISFREE(pp) ||
vp == NULL ||
PP_ISKAS(pp) ||
(vp->v_flag & VISSWAP) != 0) {
page_unlock(pp);
fspage = 0;
continue;
}
if (pp->p_lckcnt != 0 || pp->p_cowcnt != 0) {
page_unlock(pp);
continue;
}
fspage = 1;
ASSERT(vp->v_type != VCHR);
if (IS_VMODSORT(vp))
mod = hat_ismod(pp);
else
mod = hat_pagesync(pp,
HAT_SYNC_DONTZERO | HAT_SYNC_STOPON_MOD) & P_MOD;
if (mod) {
++nmodified;
offset = pp->p_offset;
VN_HOLD(vp);
page_unlock(pp);
(void) VOP_PUTPAGE(vp, offset, PAGESIZE, B_ASYNC,
kcred, NULL);
VN_RELE(vp);
} else {
if (hat_page_is_mapped(pp) == 0) {
++releases;
(void) page_release(pp, 1);
} else {
page_unlock(pp);
}
}
}
if (++fsf_cycles == 1000000) {
fsf_cycles = 0;
fsf_total.fsf_scan = 0;
fsf_total.fsf_examined = 0;
fsf_total.fsf_locked = 0;
fsf_total.fsf_modified = 0;
fsf_total.fsf_coalesce = 0;
fsf_total.fsf_time = 0;
fsf_total.fsf_releases = 0;
} else {
fsf_total.fsf_scan += fsf_recent.fsf_scan = nscan;
fsf_total.fsf_examined += fsf_recent.fsf_examined = nexamined;
fsf_total.fsf_locked += fsf_recent.fsf_locked = nlocked;
fsf_total.fsf_modified += fsf_recent.fsf_modified = nmodified;
fsf_total.fsf_coalesce += fsf_recent.fsf_coalesce = ncoalesce;
fsf_total.fsf_time += fsf_recent.fsf_time = gethrtime() - timer;
fsf_total.fsf_releases += fsf_recent.fsf_releases = releases;
}
}
void
fsflush()
{
struct buf *bp, *dwp;
struct hbuf *hp;
int autoup;
unsigned int ix, icount, count = 0;
callb_cpr_t cprinfo;
uint_t bcount;
kmutex_t *hmp;
struct vfssw *vswp;
proc_fsflush = ttoproc(curthread);
proc_fsflush->p_cstime = 0;
proc_fsflush->p_stime = 0;
proc_fsflush->p_cutime = 0;
proc_fsflush->p_utime = 0;
bcopy("fsflush", curproc->p_user.u_psargs, 8);
bcopy("fsflush", curproc->p_user.u_comm, 7);
mutex_init(&fsflush_lock, NULL, MUTEX_DEFAULT, NULL);
sema_init(&fsflush_sema, 0, NULL, SEMA_DEFAULT, NULL);
fsf_npgsz = page_num_pagesizes();
ASSERT(fsf_npgsz < MAX_PAGESIZES);
for (ix = 0; ix < fsf_npgsz - 1; ++ix) {
fsf_pgcnt[ix] =
page_get_pagesize(ix + 1) / page_get_pagesize(ix);
fsf_mask[ix] = page_get_pagecnt(ix + 1) - 1;
}
autoup = v.v_autoup * hz;
icount = v.v_autoup / tune.t_fsflushr;
CALLB_CPR_INIT(&cprinfo, &fsflush_lock, callb_generic_cpr, "fsflush");
loop:
sema_v(&fsflush_sema);
mutex_enter(&fsflush_lock);
CALLB_CPR_SAFE_BEGIN(&cprinfo);
cv_wait(&fsflush_cv, &fsflush_lock);
CALLB_CPR_SAFE_END(&cprinfo, &fsflush_lock);
mutex_exit(&fsflush_lock);
sema_p(&fsflush_sema);
bcount = 0;
for (ix = 0; ix < v.v_hbuf; ix++) {
hp = &hbuf[ix];
dwp = (struct buf *)&dwbuf[ix];
bcount += (hp->b_length);
if (dwp->av_forw == dwp) {
continue;
}
hmp = &hbuf[ix].b_lock;
mutex_enter(hmp);
bp = dwp->av_forw;
while (bp != dwp) {
ASSERT(bp->b_flags & B_DELWRI);
if ((bp->b_flags & B_DELWRI) &&
(ddi_get_lbolt() - bp->b_start >= autoup) &&
sema_tryp(&bp->b_sem)) {
bp->b_flags |= B_ASYNC;
hp->b_length--;
notavail(bp);
mutex_exit(hmp);
if (bp->b_vp == NULL) {
BWRITE(bp);
} else {
UFS_BWRITE(VTOI(bp->b_vp)->i_ufsvfs,
bp);
}
mutex_enter(hmp);
bp = dwp->av_forw;
} else {
bp = bp->av_forw;
}
}
mutex_exit(hmp);
}
bfreelist.b_bcount = bcount;
if (dopageflush)
fsflush_do_pages();
if (!doiflush)
goto loop;
if ((boothowto & RB_SINGLE) == 0 &&
(ddi_get_lbolt64() / hz) < fsflush_iflush_delay)
goto loop;
if (++count >= icount) {
count = 0;
RLOCK_VFSSW();
for (vswp = &vfssw[1]; vswp < &vfssw[nfstype]; vswp++) {
if (ALLOCATED_VFSSW(vswp) && VFS_INSTALLED(vswp)) {
vfs_refvfssw(vswp);
RUNLOCK_VFSSW();
(void) fsop_sync_by_kind(vswp - vfssw,
SYNC_ATTR, kcred);
vfs_unrefvfssw(vswp);
RLOCK_VFSSW();
}
}
RUNLOCK_VFSSW();
}
goto loop;
}